@servicetitan/navigation 14.2.0 → 14.3.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.
Files changed (89) hide show
  1. package/dist/components/titan-layout/__tests__/layout-sidebar-links-internal.test.d.ts +2 -0
  2. package/dist/components/titan-layout/__tests__/layout-sidebar-links-internal.test.d.ts.map +1 -0
  3. package/dist/components/titan-layout/__tests__/navigation-catalog-reset.test.d.ts +2 -0
  4. package/dist/components/titan-layout/__tests__/navigation-catalog-reset.test.d.ts.map +1 -0
  5. package/dist/components/titan-layout/interface.d.ts +66 -0
  6. package/dist/components/titan-layout/interface.d.ts.map +1 -1
  7. package/dist/components/titan-layout/interface.js.map +1 -1
  8. package/dist/components/titan-layout/layout-sidebar-links-internal.d.ts +3 -0
  9. package/dist/components/titan-layout/layout-sidebar-links-internal.d.ts.map +1 -1
  10. package/dist/components/titan-layout/layout-sidebar-links-internal.js +58 -13
  11. package/dist/components/titan-layout/layout-sidebar-links-internal.js.map +1 -1
  12. package/dist/components/titan-layout/layout-sidebar.d.ts +14 -0
  13. package/dist/components/titan-layout/layout-sidebar.d.ts.map +1 -1
  14. package/dist/components/titan-layout/layout-sidebar.js +169 -97
  15. package/dist/components/titan-layout/layout-sidebar.js.map +1 -1
  16. package/dist/components/titan-layout/layout-sidebar.module.less +230 -144
  17. package/dist/components/titan-layout/layout-sidebar.module.less.d.ts +16 -5
  18. package/dist/components/titan-layout/navigation-catalog/index.d.ts +3 -0
  19. package/dist/components/titan-layout/navigation-catalog/index.d.ts.map +1 -0
  20. package/dist/components/titan-layout/navigation-catalog/index.js +4 -0
  21. package/dist/components/titan-layout/navigation-catalog/index.js.map +1 -0
  22. package/dist/components/titan-layout/navigation-catalog/navigation-catalog-footer.d.ts +8 -0
  23. package/dist/components/titan-layout/navigation-catalog/navigation-catalog-footer.d.ts.map +1 -0
  24. package/dist/components/titan-layout/navigation-catalog/navigation-catalog-footer.js +88 -0
  25. package/dist/components/titan-layout/navigation-catalog/navigation-catalog-footer.js.map +1 -0
  26. package/dist/components/titan-layout/navigation-catalog/navigation-catalog-trigger.d.ts +21 -0
  27. package/dist/components/titan-layout/navigation-catalog/navigation-catalog-trigger.d.ts.map +1 -0
  28. package/dist/components/titan-layout/navigation-catalog/navigation-catalog-trigger.js +138 -0
  29. package/dist/components/titan-layout/navigation-catalog/navigation-catalog-trigger.js.map +1 -0
  30. package/dist/components/titan-layout/navigation-catalog/navigation-catalog-trigger.module.less +89 -0
  31. package/dist/components/titan-layout/navigation-catalog/navigation-catalog-trigger.module.less.d.ts +9 -0
  32. package/dist/components/titan-layout/navigation-catalog/navigation-catalog.d.ts +14 -0
  33. package/dist/components/titan-layout/navigation-catalog/navigation-catalog.d.ts.map +1 -0
  34. package/dist/components/titan-layout/navigation-catalog/navigation-catalog.js +382 -0
  35. package/dist/components/titan-layout/navigation-catalog/navigation-catalog.js.map +1 -0
  36. package/dist/components/titan-layout/navigation-catalog/navigation-catalog.module.less +436 -0
  37. package/dist/components/titan-layout/navigation-catalog/navigation-catalog.module.less.d.ts +24 -0
  38. package/dist/components/titan-layout/navigation-scroll-indicators.d.ts +12 -0
  39. package/dist/components/titan-layout/navigation-scroll-indicators.d.ts.map +1 -0
  40. package/dist/components/titan-layout/navigation-scroll-indicators.js +48 -0
  41. package/dist/components/titan-layout/navigation-scroll-indicators.js.map +1 -0
  42. package/dist/components/titan-layout/titan-layout.d.ts +29 -1
  43. package/dist/components/titan-layout/titan-layout.d.ts.map +1 -1
  44. package/dist/components/titan-layout/titan-layout.js +46 -6
  45. package/dist/components/titan-layout/titan-layout.js.map +1 -1
  46. package/dist/components/titan-layout/titan-layout.module.less +2 -1
  47. package/dist/components/titan-layout/use-navigation-scroll.d.ts +20 -0
  48. package/dist/components/titan-layout/use-navigation-scroll.d.ts.map +1 -0
  49. package/dist/components/titan-layout/use-navigation-scroll.js +140 -0
  50. package/dist/components/titan-layout/use-navigation-scroll.js.map +1 -0
  51. package/dist/utils/__tests__/navigation-notifications.test.d.ts +2 -0
  52. package/dist/utils/__tests__/navigation-notifications.test.d.ts.map +1 -0
  53. package/dist/utils/__tests__/submenu-state.test.d.ts +2 -0
  54. package/dist/utils/__tests__/submenu-state.test.d.ts.map +1 -0
  55. package/dist/utils/navigation-notifications.d.ts +21 -0
  56. package/dist/utils/navigation-notifications.d.ts.map +1 -0
  57. package/dist/utils/navigation-notifications.js +39 -0
  58. package/dist/utils/navigation-notifications.js.map +1 -0
  59. package/dist/utils/side-nav.js +3 -1
  60. package/dist/utils/side-nav.js.map +1 -1
  61. package/dist/utils/submenu-state.d.ts +3 -0
  62. package/dist/utils/submenu-state.d.ts.map +1 -0
  63. package/dist/utils/submenu-state.js +15 -0
  64. package/dist/utils/submenu-state.js.map +1 -0
  65. package/package.json +2 -2
  66. package/src/components/titan-layout/__tests__/layout-sidebar-links-internal.test.tsx +225 -0
  67. package/src/components/titan-layout/__tests__/navigation-catalog-reset.test.tsx +186 -0
  68. package/src/components/titan-layout/interface.ts +73 -0
  69. package/src/components/titan-layout/layout-sidebar-links-internal.tsx +86 -14
  70. package/src/components/titan-layout/layout-sidebar.module.less +230 -144
  71. package/src/components/titan-layout/layout-sidebar.module.less.d.ts +16 -5
  72. package/src/components/titan-layout/layout-sidebar.tsx +193 -117
  73. package/src/components/titan-layout/navigation-catalog/index.ts +2 -0
  74. package/src/components/titan-layout/navigation-catalog/navigation-catalog-footer.tsx +88 -0
  75. package/src/components/titan-layout/navigation-catalog/navigation-catalog-trigger.module.less +89 -0
  76. package/src/components/titan-layout/navigation-catalog/navigation-catalog-trigger.module.less.d.ts +9 -0
  77. package/src/components/titan-layout/navigation-catalog/navigation-catalog-trigger.tsx +170 -0
  78. package/src/components/titan-layout/navigation-catalog/navigation-catalog.module.less +436 -0
  79. package/src/components/titan-layout/navigation-catalog/navigation-catalog.module.less.d.ts +24 -0
  80. package/src/components/titan-layout/navigation-catalog/navigation-catalog.tsx +458 -0
  81. package/src/components/titan-layout/navigation-scroll-indicators.tsx +80 -0
  82. package/src/components/titan-layout/titan-layout.module.less +2 -1
  83. package/src/components/titan-layout/titan-layout.tsx +81 -5
  84. package/src/components/titan-layout/use-navigation-scroll.ts +145 -0
  85. package/src/utils/__tests__/navigation-notifications.test.ts +70 -0
  86. package/src/utils/__tests__/submenu-state.test.ts +39 -0
  87. package/src/utils/navigation-notifications.ts +59 -0
  88. package/src/utils/side-nav.ts +1 -1
  89. package/src/utils/submenu-state.ts +16 -0
@@ -0,0 +1,436 @@
1
+ @import (reference) '@servicetitan/tokens/core/tokens.less';
2
+
3
+ /*
4
+ * Scoped to `.anvil2.anvil2` so these rules outrank anvil2's own.
5
+ *
6
+ * anvil2 nests every component rule under `.anvil2`, giving it two classes of specificity,
7
+ * which a bare class here loses to. Doubling the scope wins outright and leaves the selectors
8
+ * inside it plain, rather than each one having to defend itself with `!important`.
9
+ */
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
+ */
24
+ @catalog-column: 15rem;
25
+ @catalog-gap: 1.5rem;
26
+
27
+ // The dialog's own horizontal inset, shared by the search field's row and the footer.
28
+ @catalog-padding: @spacing-2;
29
+
30
+ /*
31
+ * The rail's own background, from `@bg-color` in `@servicetitan/navigation`'s
32
+ * `layout-sidebar.module.less`, its hover on top of it, and the trigger chip this catalog opens
33
+ * from.
34
+ *
35
+ * Repeated as literals because they are LESS variables private to that package, not custom
36
+ * properties, so there is nothing to reference at runtime. The rail paints them unconditionally and
37
+ * has done since it was written, so the risk of drift is low and the failure is cosmetic.
38
+ *
39
+ * They are here because the catalog is the rail's menu and should read as part of it. anvil2's dark
40
+ * mode is a neutral grey (#1a1a1a) and the rail is a navy, so taking the mode alone would put two
41
+ * different darks side by side on screen.
42
+ *
43
+ * Translucent white rather than a mixed-down navy throughout: a translucent layer tints whatever it
44
+ * covers, so these keep the surface's hue and only lift it, which is what makes them read as part of
45
+ * the same surface rather than as patches on it.
46
+ */
47
+ @rail-surface: #0f1d26;
48
+ @rail-hover: rgba(255, 255, 255, 0.08);
49
+ @rail-field: rgba(255, 255, 255, 0.06);
50
+ @rail-field-border: rgba(255, 255, 255, 0.16);
51
+
52
+ /**
53
+ * Repaints the dialog in the rail's navy instead of anvil2's neutral dark, and lightens its
54
+ * backdrop instead of anvil2's default near-black scrim.
55
+ *
56
+ * `--a2-dialog-background-color`, `--a2-dialog-border-color` and `--a2-dialog-backdrop-color` are
57
+ * the hooks anvil2's Dialog reads inside its own `var()` chains, ahead of the tokens they otherwise
58
+ * fall back to. Setting those rather than `background-color`/`::backdrop` directly means no
59
+ * specificity contest — nothing else defines these properties, they are only read — and the
60
+ * background one reaches the header and content panels too, which paint from the same variable.
61
+ *
62
+ * The border goes with the background. Left alone it resolves to a flat `#404040`, which is the
63
+ * last neutral grey on the surface and outlines the whole dialog in it. The same translucent white
64
+ * the field's edge uses puts the outline back in the surface's own colour.
65
+ *
66
+ * `--a2-color-neutral-0` for the backdrop is the same override anvil2's own `fullScreen` variant
67
+ * uses in place of its default `neutral-900` — a supported swap, not a hack.
68
+ */
69
+ .dialog {
70
+ --a2-dialog-background-color: @rail-surface;
71
+ --a2-dialog-border-color: @rail-field-border;
72
+ --a2-dialog-backdrop-color: var(--a2-color-neutral-0, #ffffff);
73
+
74
+ /*
75
+ * Pins the dialog's size to the viewport instead of its content, so opening it, searching, and
76
+ * clearing the search all leave it exactly where it was.
77
+ *
78
+ * anvil2 centres the dialog by giving it `inset: 0` and `margin: auto`; overriding `top` and
79
+ * `height` here (with the matching margins zeroed so they cannot re-introduce the auto-centring
80
+ * they replace) fixes the vertical position and size, while `left`/`right` stay on the anvil2
81
+ * default and keep centring horizontally. `--nav-offset-top` is the rail's own top-nav height
82
+ * custom property, already threaded through this package for the same purpose.
83
+ *
84
+ * `> *` reaches anvil2's own content wrapper, which otherwise sizes to its content rather than
85
+ * to this now-fixed height — there is no class hook for it, but the dialog only ever has that
86
+ * one child.
87
+ */
88
+ top: calc(var(--nav-offset-top) + 10px);
89
+ bottom: auto;
90
+ height: calc(100vh - 112px);
91
+ margin-top: @spacing-0;
92
+ margin-bottom: @spacing-0;
93
+
94
+ > * {
95
+ height: 100%;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Stacks the search above the columns.
101
+ *
102
+ * `Dialog.Content` is `display: flex` with the default row direction, so its children line up
103
+ * side by side. Without this the search field became a flex item next to the column block and sat
104
+ * in the left third of the dialog, reading as a fourth column.
105
+ */
106
+ .body {
107
+ display: flex;
108
+ flex: 1;
109
+ min-width: 0;
110
+ flex-direction: column;
111
+ }
112
+
113
+ /**
114
+ * Lets the results area, rather than the dialog's own scroller, take the overflow.
115
+ *
116
+ * `Dialog.Content` is a flex item of that scroller, and a flex item will not shrink below its
117
+ * content by default. Without this the scroller is the thing that overflows, which scrolls the
118
+ * search field off the top along with the results.
119
+ */
120
+ .content {
121
+ min-height: 0;
122
+ }
123
+
124
+ // Results scroll above the full-width footer; allow its content to wrap on narrow screens.
125
+ .footer {
126
+ box-sizing: border-box;
127
+ min-height: 56px;
128
+ flex: none;
129
+ flex-wrap: wrap;
130
+ align-items: center;
131
+ justify-content: center;
132
+ gap: @spacing-1 @spacing-3;
133
+ padding: 12px @catalog-padding;
134
+ background-color: var(--a2-background-color-secondary);
135
+ text-align: center;
136
+ }
137
+
138
+ .footerActions {
139
+ display: flex;
140
+ flex: none;
141
+ gap: @spacing-1;
142
+ }
143
+
144
+ /*
145
+ * Matched to the rail's own "Go to" field, which is what the user clicked to get here — the same
146
+ * control, so it should not change colour on the way.
147
+ *
148
+ * anvil2's field is an opaque neutral (#1a1a1a on a #545454 border) which, like the row hover, is
149
+ * the right lightness but the wrong hue against the navy. The rail's translucent white gives the
150
+ * same lift while keeping the surface's colour, and these are its exact values.
151
+ */
152
+ .search {
153
+ --a2-text-field-input-wrapper-background-color: @rail-field;
154
+ --a2-text-field-input-wrapper-border-color-default: @rail-field-border;
155
+
156
+ width: 100%;
157
+ margin-bottom: 1.5rem;
158
+ }
159
+
160
+ /*
161
+ * Fills whatever the now fixed-height dialog leaves under the search field, instead of sizing to
162
+ * 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
+ */
168
+ .results {
169
+ flex: 1;
170
+ min-height: 0;
171
+ overflow-y: auto;
172
+
173
+ /*
174
+ * Reserve the scrollbar's width whether or not it is showing, so a query that stops the results
175
+ * overflowing does not shift the columns sideways as the scrollbar disappears.
176
+ */
177
+ scrollbar-gutter: stable;
178
+ }
179
+
180
+ /**
181
+ * Column width rather than column count, so a narrow dialog drops a column instead of squeezing all
182
+ * three.
183
+ *
184
+ * The dialog tracks the viewport below its cap, so `column-count: 3` had to divide whatever it was
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
+ */
198
+
199
+ /*
200
+ * `column-rule` rather than a border on the categories, because the line belongs to the boundary
201
+ * between columns and not to any category. Multi-column draws it once per gap, only where a gap
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);
215
+ }
216
+
217
+ /*
218
+ * A category and its heading stay in the same column.
219
+ *
220
+ * Without this a tall category flowed across a column boundary and its remainder arrived at the top
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.
228
+ */
229
+
230
+ /*
231
+ * Full column width, so every star in the catalog lands on the column's right edge just inside the
232
+ * divider. The rows are flex containers with `flex: 1` on the link, so the star is pushed to
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.
235
+ */
236
+ .group {
237
+ break-inside: avoid;
238
+ margin-bottom: 1.5rem;
239
+ }
240
+
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
+ */
258
+ .groupTitle {
259
+ display: block;
260
+ break-after: avoid;
261
+ padding: @spacing-0 0.5rem;
262
+ 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
+ }
270
+
271
+ /* A module and its pages are one unit and stay in the same column. */
272
+ .module-group {
273
+ break-inside: avoid;
274
+ margin-bottom: 0.75rem;
275
+ }
276
+
277
+ .row {
278
+ display: flex;
279
+ align-items: center;
280
+ gap: 0.25rem;
281
+ }
282
+
283
+ .link {
284
+ display: flex;
285
+ flex: 1;
286
+ min-width: 0;
287
+ align-items: center;
288
+ gap: 0.5rem;
289
+ padding: 0.25rem 0.5rem;
290
+ border-radius: var(--a2-border-radius-medium, 4px);
291
+ text-decoration: none;
292
+
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
+ &:hover,
300
+ &:focus-visible {
301
+ background-color: @rail-hover;
302
+ text-decoration: none;
303
+ }
304
+ }
305
+
306
+ .moduleIcon {
307
+ flex: none;
308
+ }
309
+
310
+ /*
311
+ * Level 2 of three, and the anchor of the scale: full-strength foreground at the body size, which
312
+ * everything else is set against. The destination is the usable part of the row, so it carries the
313
+ * weight and the brighter colour; the category heading above it is deliberately quieter.
314
+ */
315
+ .moduleTitle {
316
+ flex: 1;
317
+ min-width: 0;
318
+ overflow: hidden;
319
+ text-overflow: ellipsis;
320
+ white-space: nowrap;
321
+ color: var(--a2-foreground-color-default, light-dark(#040404, #ffffff));
322
+ font-weight: var(--a2-font-weight-semibold, 600);
323
+ }
324
+
325
+ /* Indented to sit under the module's title rather than its icon. */
326
+ .childLink {
327
+ padding-left: 2rem;
328
+ }
329
+
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
+ */
341
+ .childTitle {
342
+ flex: 1;
343
+ min-width: 0;
344
+ overflow: hidden;
345
+ text-overflow: ellipsis;
346
+ white-space: nowrap;
347
+ color: var(--a2-foreground-color-default, light-dark(#040404, #ffffff));
348
+ font-size: 0.8125rem;
349
+ }
350
+
351
+ /*
352
+ * anvil2 positions a Badge absolutely with a translate, because its usual job is to overlay the
353
+ * corner of an icon. Here it is a sibling in a row and has to sit in the flow, so the position
354
+ * and the transform are both undone — the same override the rail applies to its own submenu
355
+ * counters in `layout-sidebar.module.less`.
356
+ *
357
+ * No `!important` needed: anvil2 nests its rule as `.anvil2 ._badge_x`, and the enclosing
358
+ * `.anvil2.anvil2` scope already outranks that.
359
+ *
360
+ * `flex: none` because the title beside it takes the slack — without it a long name would
361
+ * squeeze the count instead of truncating itself, and a truncated count is worse than a
362
+ * truncated name.
363
+ */
364
+ .badge[data-anv][data-anv] {
365
+ position: relative;
366
+ transform: none;
367
+ flex: none;
368
+ }
369
+
370
+ // 2rem to match the star button's box, so both align down the column. Always visible, unlike
371
+ // the star, and #707070 to match the Pantheon frame.
372
+ .lock {
373
+ display: inline-flex;
374
+ width: 2rem;
375
+ height: 2rem;
376
+ flex: none;
377
+ align-items: center;
378
+ justify-content: center;
379
+ color: var(--a2-color-neutral-500, #707070);
380
+ }
381
+
382
+ .star {
383
+ flex: none;
384
+ opacity: 0;
385
+
386
+ &:focus-visible {
387
+ opacity: 1;
388
+ }
389
+ }
390
+
391
+ .row:hover .star {
392
+ opacity: 0.5;
393
+
394
+ &:hover {
395
+ opacity: 1;
396
+ }
397
+ }
398
+
399
+ .row:focus-within .star {
400
+ opacity: 0.5;
401
+ }
402
+
403
+ /*
404
+ * After the hover rules, which match at the same specificity, so a pinned star stays lit.
405
+ *
406
+ * The token's dark value, `yellow-300`, is a pale gold that clears the surface below at 13.5:1.
407
+ * Note that the light value on the other side of this token is `yellow-500` (#ffbe00), which only
408
+ * manages 1.66:1 on white — under the 3:1 WCAG 1.4.11 asks of a graphical control. That branch is
409
+ * unreachable while the catalog follows the rail, but it is the thing to fix first if this surface
410
+ * is ever made light.
411
+ */
412
+ .starActive,
413
+ .row:hover .starActive,
414
+ .row:focus-within .starActive {
415
+ color: var(--a2-status-color-warning, light-dark(#ffbe00, #ffe278));
416
+ opacity: 1;
417
+ }
418
+
419
+ /*
420
+ * Centred in the fixed-height results area rather than left stranded against its top edge.
421
+ *
422
+ * `border-box` so the padding is counted inside the `100%`. Under the default `content-box` the
423
+ * vertical padding is added to it, and the message ended up 64px taller than the area it sits in —
424
+ * enough to raise a scrollbar next to a single line of text.
425
+ */
426
+ .empty {
427
+ display: flex;
428
+ box-sizing: border-box;
429
+ height: 100%;
430
+ align-items: center;
431
+ justify-content: center;
432
+ padding: 2rem 0.5rem;
433
+ text-align: center;
434
+ color: var(--a2-foreground-color-subdued, light-dark(#707070, #c4c4c4));
435
+ }
436
+ }
@@ -0,0 +1,24 @@
1
+ export const __esModule: true;
2
+ export const badge: string;
3
+ export const body: string;
4
+ export const childLink: string;
5
+ export const childTitle: string;
6
+ export const columns: string;
7
+ export const content: string;
8
+ export const dialog: string;
9
+ export const empty: string;
10
+ export const footer: string;
11
+ export const footerActions: string;
12
+ export const group: string;
13
+ export const groupTitle: string;
14
+ export const link: string;
15
+ export const lock: string;
16
+ export const moduleGroup: string;
17
+ export const moduleIcon: string;
18
+ export const moduleTitle: string;
19
+ export const results: string;
20
+ export const row: string;
21
+ export const search: string;
22
+ export const star: string;
23
+ export const starActive: string;
24
+
@@ -0,0 +1,12 @@
1
+ import { RefObject } from 'react';
2
+ export declare function NavigationScrollIndicators({ expanded, above, below, hasContentAbove, hasContentBelow, aboveIndicatorRef, belowIndicatorRef, onReveal, }: {
3
+ expanded: boolean;
4
+ above: number;
5
+ below: number;
6
+ hasContentAbove: boolean;
7
+ hasContentBelow: boolean;
8
+ aboveIndicatorRef: RefObject<HTMLDivElement>;
9
+ belowIndicatorRef: RefObject<HTMLDivElement>;
10
+ onReveal(direction: 'above' | 'below'): void;
11
+ }): import("react/jsx-runtime").JSX.Element;
12
+ //# sourceMappingURL=navigation-scroll-indicators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation-scroll-indicators.d.ts","sourceRoot":"","sources":["../../../src/components/titan-layout/navigation-scroll-indicators.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAY,SAAS,EAAE,MAAM,OAAO,CAAC;AAG5C,wBAAgB,0BAA0B,CAAC,EACvC,QAAQ,EACR,KAAK,EACL,KAAK,EACL,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,QAAQ,GACX,EAAE;IACC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,iBAAiB,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC;IAC7C,iBAAiB,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC;IAC7C,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAAC;CAChD,2CAsDA"}
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from '@servicetitan/anvil2';
3
+ import SvgArrowDown from '@servicetitan/anvil2/assets/icons/material/round/arrow_downward.svg';
4
+ import SvgArrowUp from '@servicetitan/anvil2/assets/icons/material/round/arrow_upward.svg';
5
+ import classNames from 'classnames';
6
+ import { Fragment } from 'react';
7
+ import * as Styles from './layout-sidebar.module.less';
8
+ export function NavigationScrollIndicators({ expanded, above, below, hasContentAbove, hasContentBelow, aboveIndicatorRef, belowIndicatorRef, onReveal }) {
9
+ return /*#__PURE__*/ _jsx(Fragment, {
10
+ children: [
11
+ 'above',
12
+ 'below'
13
+ ].map((direction)=>{
14
+ const isAbove = direction === 'above';
15
+ const count = isAbove ? above : below;
16
+ const hasContent = isAbove ? hasContentAbove : hasContentBelow;
17
+ const label = `${count} ${count === 1 ? 'notification' : 'notifications'}`;
18
+ return /*#__PURE__*/ _jsxs(Fragment, {
19
+ children: [
20
+ hasContent && /*#__PURE__*/ _jsx("div", {
21
+ className: classNames(Styles.scrollShadow, isAbove ? Styles.scrollShadowAbove : Styles.scrollShadowBelow)
22
+ }),
23
+ count > 0 && /*#__PURE__*/ _jsx("div", {
24
+ ref: isAbove ? aboveIndicatorRef : belowIndicatorRef,
25
+ className: classNames(Styles.scrollIndicator, isAbove ? Styles.scrollIndicatorAbove : Styles.scrollIndicatorBelow),
26
+ children: /*#__PURE__*/ _jsx(Button, {
27
+ appearance: "secondary",
28
+ size: "xsmall",
29
+ icon: {
30
+ before: isAbove ? SvgArrowUp : SvgArrowDown
31
+ },
32
+ className: Styles.notificationButton,
33
+ "data-cy": `navigation-notifications-${direction}`,
34
+ "aria-label": `${label} ${direction}. Show nearest notification`,
35
+ onClick: (event)=>{
36
+ event.stopPropagation();
37
+ onReveal(direction);
38
+ },
39
+ children: expanded ? label : count >= 100 ? '99+' : count
40
+ })
41
+ })
42
+ ]
43
+ }, direction);
44
+ })
45
+ });
46
+ }
47
+
48
+ //# sourceMappingURL=navigation-scroll-indicators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/titan-layout/navigation-scroll-indicators.tsx"],"sourcesContent":["import { Button } from '@servicetitan/anvil2';\nimport SvgArrowDown from '@servicetitan/anvil2/assets/icons/material/round/arrow_downward.svg';\nimport SvgArrowUp from '@servicetitan/anvil2/assets/icons/material/round/arrow_upward.svg';\nimport classNames from 'classnames';\nimport { Fragment, RefObject } from 'react';\nimport * as Styles from './layout-sidebar.module.less';\n\nexport function NavigationScrollIndicators({\n expanded,\n above,\n below,\n hasContentAbove,\n hasContentBelow,\n aboveIndicatorRef,\n belowIndicatorRef,\n onReveal,\n}: {\n expanded: boolean;\n above: number;\n below: number;\n hasContentAbove: boolean;\n hasContentBelow: boolean;\n aboveIndicatorRef: RefObject<HTMLDivElement>;\n belowIndicatorRef: RefObject<HTMLDivElement>;\n onReveal(direction: 'above' | 'below'): void;\n}) {\n return (\n <Fragment>\n {(['above', 'below'] as const).map(direction => {\n const isAbove = direction === 'above';\n const count = isAbove ? above : below;\n const hasContent = isAbove ? hasContentAbove : hasContentBelow;\n const label = `${count} ${count === 1 ? 'notification' : 'notifications'}`;\n return (\n <Fragment key={direction}>\n {/*\n * Its own element rather than the pill's background: the pill's height\n * insets the rail's counted area, and this gradient hides nothing, so it\n * must not. See `useNavigationScroll`.\n */}\n {hasContent && (\n <div\n className={classNames(\n Styles.scrollShadow,\n isAbove ? Styles.scrollShadowAbove : Styles.scrollShadowBelow\n )}\n />\n )}\n {count > 0 && (\n <div\n ref={isAbove ? aboveIndicatorRef : belowIndicatorRef}\n className={classNames(\n Styles.scrollIndicator,\n isAbove\n ? Styles.scrollIndicatorAbove\n : Styles.scrollIndicatorBelow\n )}\n >\n <Button\n appearance=\"secondary\"\n size=\"xsmall\"\n icon={{ before: isAbove ? SvgArrowUp : SvgArrowDown }}\n className={Styles.notificationButton}\n data-cy={`navigation-notifications-${direction}`}\n aria-label={`${label} ${direction}. Show nearest notification`}\n onClick={event => {\n event.stopPropagation();\n onReveal(direction);\n }}\n >\n {expanded ? label : count >= 100 ? '99+' : count}\n </Button>\n </div>\n )}\n </Fragment>\n );\n })}\n </Fragment>\n );\n}\n"],"names":["Button","SvgArrowDown","SvgArrowUp","classNames","Fragment","Styles","NavigationScrollIndicators","expanded","above","below","hasContentAbove","hasContentBelow","aboveIndicatorRef","belowIndicatorRef","onReveal","map","direction","isAbove","count","hasContent","label","div","className","scrollShadow","scrollShadowAbove","scrollShadowBelow","ref","scrollIndicator","scrollIndicatorAbove","scrollIndicatorBelow","appearance","size","icon","before","notificationButton","data-cy","aria-label","onClick","event","stopPropagation"],"mappings":";AAAA,SAASA,MAAM,QAAQ,uBAAuB;AAC9C,OAAOC,kBAAkB,sEAAsE;AAC/F,OAAOC,gBAAgB,oEAAoE;AAC3F,OAAOC,gBAAgB,aAAa;AACpC,SAASC,QAAQ,QAAmB,QAAQ;AAC5C,YAAYC,YAAY,+BAA+B;AAEvD,OAAO,SAASC,2BAA2B,EACvCC,QAAQ,EACRC,KAAK,EACLC,KAAK,EACLC,eAAe,EACfC,eAAe,EACfC,iBAAiB,EACjBC,iBAAiB,EACjBC,QAAQ,EAUX;IACG,qBACI,KAACV;kBACI,AAAC;YAAC;YAAS;SAAQ,CAAWW,GAAG,CAACC,CAAAA;YAC/B,MAAMC,UAAUD,cAAc;YAC9B,MAAME,QAAQD,UAAUT,QAAQC;YAChC,MAAMU,aAAaF,UAAUP,kBAAkBC;YAC/C,MAAMS,QAAQ,GAAGF,MAAM,CAAC,EAAEA,UAAU,IAAI,iBAAiB,iBAAiB;YAC1E,qBACI,MAACd;;oBAMIe,4BACG,KAACE;wBACGC,WAAWnB,WACPE,OAAOkB,YAAY,EACnBN,UAAUZ,OAAOmB,iBAAiB,GAAGnB,OAAOoB,iBAAiB;;oBAIxEP,QAAQ,mBACL,KAACG;wBACGK,KAAKT,UAAUL,oBAAoBC;wBACnCS,WAAWnB,WACPE,OAAOsB,eAAe,EACtBV,UACMZ,OAAOuB,oBAAoB,GAC3BvB,OAAOwB,oBAAoB;kCAGrC,cAAA,KAAC7B;4BACG8B,YAAW;4BACXC,MAAK;4BACLC,MAAM;gCAAEC,QAAQhB,UAAUf,aAAaD;4BAAa;4BACpDqB,WAAWjB,OAAO6B,kBAAkB;4BACpCC,WAAS,CAAC,yBAAyB,EAAEnB,WAAW;4BAChDoB,cAAY,GAAGhB,MAAM,CAAC,EAAEJ,UAAU,2BAA2B,CAAC;4BAC9DqB,SAASC,CAAAA;gCACLA,MAAMC,eAAe;gCACrBzB,SAASE;4BACb;sCAECT,WAAWa,QAAQF,SAAS,MAAM,QAAQA;;;;eApC5CF;QA0CvB;;AAGZ"}
@@ -1,6 +1,7 @@
1
1
  import { ComponentPropsWithoutRef, FC, ReactElement, ReactNode } from 'react';
2
2
  import { NavigationItemData } from '../../utils/navigation';
3
3
  import { NavLinkComponentProps } from '../../utils/navigation-context';
4
+ import { PinnedNavigationConfig } from './interface';
4
5
  import { TitanLayoutLogoProps } from './layout-logo';
5
6
  export type TitanLayoutProps = Omit<ComponentPropsWithoutRef<'div'>, 'children' | 'style'> & {
6
7
  /** layout appearance */
@@ -15,6 +16,11 @@ export type TitanLayoutProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'
15
16
  navigationComponent?: FC<NavLinkComponentProps>;
16
17
  /** data for main navigation links */
17
18
  navigationMainItems?: NavigationItemData[];
19
+ /**
20
+ * Turns the navigation catalog on: renders a trigger in the sidebar and the dialog behind it.
21
+ * Omit it and the navigation is unchanged.
22
+ */
23
+ pinnedNavigation?: PinnedNavigationConfig;
18
24
  /** data for overflow navigation links (used only with top variant) */
19
25
  navigationOverflowItems?: NavigationItemData[];
20
26
  /** logo props */
@@ -27,6 +33,13 @@ export type TitanLayoutProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'
27
33
  openSubmenus?: string[];
28
34
  /** submenu accordion open state change handler */
29
35
  onOpenSubmenuChange?: (id: string, opened: boolean) => void;
36
+ /**
37
+ * Receives the complete next set of open modules for individual and bulk actions.
38
+ * Preferred over onOpenSubmenuChange when provided, so bulk updates are atomic.
39
+ * Required for the rail's `Expand All` / `Collapse All` control, which is hidden
40
+ * for hosts that supply only the per-module `onOpenSubmenuChange`.
41
+ */
42
+ onOpenSubmenusChange?: (ids: string[]) => void;
30
43
  /** navigation link click handler */
31
44
  onNavigationLinkClick?: (itemId: string, submenuItemId?: string) => void;
32
45
  /** content header content */
@@ -35,6 +48,21 @@ export type TitanLayoutProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'
35
48
  top?: ReactElement;
36
49
  /** top links for side navigation */
37
50
  sideTop?: ReactElement[];
51
+ /**
52
+ * Destinations pinned above the sidebar's divider, ahead of `sideTop`.
53
+ *
54
+ * Rendered with the same machinery as `navigationMainItems`, so one with pages beneath it is
55
+ * an expandable group rather than a bare link. `sideTop` is unchanged and still takes elements
56
+ * the host builds itself; the two can be used together.
57
+ */
58
+ sideTopItems?: NavigationItemData[];
59
+ /**
60
+ * Whether the sidebar shows the scroll and overflow-notification indicators.
61
+ *
62
+ * Defaults on, so a host that says nothing keeps them. Navigation versions that predate the
63
+ * indicators pass `false`.
64
+ */
65
+ navigationScrollIndicators?: boolean;
38
66
  /**
39
67
  * profile element for layout
40
68
  * @see ProfileDropdown
@@ -67,7 +95,7 @@ export type TitanLayoutProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'
67
95
  noResponsive?: boolean;
68
96
  };
69
97
  export declare const useViewportConfigs: (noResponsive?: boolean) => void;
70
- declare function TitanLayoutComponent({ appearance, navVariant, id, children, contentOnly, navigationComponent, header, top, profile, expanded, openSubmenus, logo, onExpandedChange, onOpenSubmenuChange, navigationMainItems, navigationOverflowItems, onNavigationLinkClick, extraLinks, extraLinksTop, extraText, minContentWidth, noResponsive, sideTop, }: TitanLayoutProps): import("react/jsx-runtime").JSX.Element;
98
+ declare function TitanLayoutComponent({ appearance, navVariant, id, children, contentOnly, navigationComponent, header, top, profile, expanded, openSubmenus, logo, onExpandedChange, onOpenSubmenuChange, onOpenSubmenusChange, navigationMainItems, pinnedNavigation, navigationOverflowItems, onNavigationLinkClick, extraLinks, extraLinksTop, extraText, minContentWidth, noResponsive, sideTop, sideTopItems, navigationScrollIndicators, }: TitanLayoutProps): import("react/jsx-runtime").JSX.Element;
71
99
  export declare const TitanLayout: typeof TitanLayoutComponent & {
72
100
  Link: FC<import("./interface").TitanLayoutLinkProps>;
73
101
  Trigger: FC<import("./interface").TitanLayoutTriggerProps>;
@@ -1 +1 @@
1
- {"version":3,"file":"titan-layout.d.ts","sourceRoot":"","sources":["../../../src/components/titan-layout/titan-layout.tsx"],"names":[],"mappings":"AACA,OAAO,EAEH,wBAAwB,EACxB,EAAE,EAGF,YAAY,EACZ,SAAS,EAMZ,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAA2B,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAUhG,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAOrD,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,GAAG;IACzF,wBAAwB;IACxB,UAAU,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAE5C,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAE5B,uBAAuB;IACvB,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB,kDAAkD;IAClD,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,oCAAoC;IACpC,mBAAmB,CAAC,EAAE,EAAE,CAAC,qBAAqB,CAAC,CAAC;IAEhD,qCAAqC;IACrC,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAE3C,sEAAsE;IACtE,uBAAuB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAE/C,iBAAiB;IACjB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAE5B,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oCAAoC;IACpC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAE/C,sCAAsC;IACtC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,kDAAkD;IAClD,mBAAmB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAE5D,oCAAoC;IACpC,qBAAqB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAEzE,6BAA6B;IAC7B,MAAM,CAAC,EAAE,YAAY,CAAC;IAEtB,qCAAqC;IACrC,GAAG,CAAC,EAAE,YAAY,CAAC;IAEnB,oCAAoC;IACpC,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IAEzB;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB;;;OAGG;IACH,UAAU,CAAC,EAAE,YAAY,CAAC;IAE1B;;;OAGG;IACH,aAAa,CAAC,EAAE,YAAY,CAAC;IAE7B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAyBF,eAAO,MAAM,kBAAkB,GAAI,eAAe,OAAO,SAUxD,CAAC;AAEF,iBAAS,oBAAoB,CAAC,EAC1B,UAAqB,EACrB,UAAmB,EACnB,EAAE,EACF,QAAQ,EACR,WAAW,EACX,mBAAmB,EACnB,MAAM,EACN,GAAG,EACH,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,IAAI,EACJ,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,EACrB,UAAU,EACV,aAAa,EACb,SAAS,EACT,eAAe,EACf,YAAY,EACZ,OAAO,GACV,EAAE,gBAAgB,2CAgRlB;AAqFD,eAAO,MAAM,WAAW;;;CAGtB,CAAC"}
1
+ {"version":3,"file":"titan-layout.d.ts","sourceRoot":"","sources":["../../../src/components/titan-layout/titan-layout.tsx"],"names":[],"mappings":"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"}