mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
style(sidebar): add resource menu return row and refine dark UI details
- Add a main-menu row that reopens the resource menu, with a placeholder row - Show only the other view as an icon in the view switcher - Remove the active tile glow and the collapsed Search frame - Tint the dark command palette purple and whiten focused-row details - Soften the toast edge and give the toast icon tile 3D depth - Document the decisions in DESIGN.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
59928035b7
commit
dff2674466
@@ -35,8 +35,8 @@ change.
|
||||
- **One accent.** Coolify purple in both themes (`#6b16ed`, hover `#7317ff`,
|
||||
pressed `#5a12c7`, depth `#4a0fa3`, light text and icons `#a391ff`). Yellow
|
||||
is only for real warnings. The dark sidebar, including the logo cell, is
|
||||
purple-black (`#120e1a`) with a faint brand wash. The page canvas stays
|
||||
neutral.
|
||||
purple-black (`#120e1a`) with a faint brand wash. The dark command palette
|
||||
has a light purple tint. The page canvas stays neutral.
|
||||
- **Quiet text.** Sentence case. A clear text ladder: primary, dim, faint.
|
||||
No em dashes in UI copy.
|
||||
|
||||
@@ -142,7 +142,8 @@ Rules for the tokens:
|
||||
`--ui-menu-bg`, never a translucent value, or the content behind shows
|
||||
through.
|
||||
- Keep the page canvas, cards, menus, and logs pure neutral (chroma 0). The
|
||||
dark sidebar and the logo cell above it are the tinted surface.
|
||||
dark sidebar, the logo cell above it, and the dark command palette are the
|
||||
tinted surfaces.
|
||||
- **Dark lightness level.** The canvas is at 14.48% and the header panel at
|
||||
19.13%. To
|
||||
change the level, move every dark surface and border token by the same
|
||||
@@ -232,7 +233,7 @@ Class names below are examples. Keep the values and structure.
|
||||
border-color: #8a4dff;
|
||||
background: linear-gradient(180deg, var(--ui-accent-top), var(--ui-accent-bottom));
|
||||
--ui-depth-color: var(--ui-accent-edge);
|
||||
box-shadow: 0 2px 0 var(--ui-accent-edge), 0 0 16px rgb(115 23 255 / 0.50);
|
||||
box-shadow: 0 2px 0 var(--ui-accent-edge); /* no glow: the row clips it */
|
||||
}
|
||||
```
|
||||
|
||||
@@ -242,6 +243,17 @@ Class names below are examples. Keep the values and structure.
|
||||
hidden until the row is hovered, focused, or current.
|
||||
- Group rows under section labels (11px in light mode; 10.5px uppercase muted
|
||||
lavender in dark mode), and separate groups with a 1px divider.
|
||||
- Do not put a glow (a blurred `box-shadow`) on a tile. The current row clips
|
||||
it, so it shows as a dim square behind the tile.
|
||||
- In the collapsed sidebar, Search is a tile like the other items: no row fill
|
||||
and no frame around it. Only the expanded sidebar shows Search as an input.
|
||||
- A menu that replaces another menu in place starts with a back row: a
|
||||
centered label and a 12px chevron on the side of the direction. The back row
|
||||
points left (`‹ Dashboard`). The row that opens the resource menu again from
|
||||
the main menu points right (`adonisjs ›`) and shows the resource name.
|
||||
- Keep the menu items in the same place on every page. When a page has no
|
||||
resource menu, keep an empty row of the same height where the resource row
|
||||
would be.
|
||||
|
||||
### Card (outer box, title row, well)
|
||||
|
||||
@@ -357,6 +369,17 @@ Class names below are examples. Keep the values and structure.
|
||||
- Grid view tiles of a collection are cards without a well. In dark mode they
|
||||
are `--ui-panel`, the same as the header bar.
|
||||
|
||||
### View switcher
|
||||
|
||||
- A segmented control (`.view-toggle`) on the right of a collection toolbar.
|
||||
- Table and grid buttons are icon-only (30px square, 14px icon). The name is in
|
||||
`title` and `aria-label`.
|
||||
- Show only the view that is not current. In table view the control shows the
|
||||
grid icon; in grid view, the list icon. Do not show a selected pill for the
|
||||
current view.
|
||||
- Text links stay labelled: the `Canvas` link with its "Try it" badge, and the
|
||||
`List view` link that is the only way back from a canvas.
|
||||
|
||||
### Status badge
|
||||
|
||||
```css
|
||||
@@ -388,6 +411,22 @@ Class names below are examples. Keep the values and structure.
|
||||
- Toasts are compact cards (max width 26rem) with a status icon tile, a title,
|
||||
an optional description, and a dismiss button. They close after 4 seconds
|
||||
and pause on hover.
|
||||
- The toast edge blends into the toast background. In dark mode it is
|
||||
`rgb(255 255 255 / 0.05)`, not the control border.
|
||||
- The status icon tile is 3D, like a navigation tile: a 1px border and a 2px
|
||||
bottom edge in the status color (`currentColor` at 22% and 30%).
|
||||
|
||||
### Command palette
|
||||
|
||||
- In dark mode the palette has a light brand tint: the shell is
|
||||
`color-mix(in oklab, #6b16ed 10%, <elevated>)` with a
|
||||
`rgb(163 145 255 / 0.22)` ring, and the results body is 6% purple with a
|
||||
`rgb(163 145 255 / 0.12)` ring. The search icon is `#a391ff`. The
|
||||
placeholder is muted lavender.
|
||||
- On the selected (focused) row in dark mode, the parts on a purple fill are
|
||||
white: the quick-command and type badges, the icon tile glyph (for example
|
||||
the plus of a create row), and the chevron. Purple text on a purple fill is
|
||||
not readable.
|
||||
|
||||
### Logs and code
|
||||
|
||||
@@ -468,8 +507,8 @@ The interface is compact and product-focused:
|
||||
- outline Reicon glyphs through `<x-reicon>`;
|
||||
- the Coolify purple brand accent in both themes (`#6b16ed`); yellow only for
|
||||
real warnings, such as "Update available";
|
||||
- the active sidebar item is a purple gradient tile with a soft glow. The row
|
||||
itself stays transparent;
|
||||
- the active sidebar item is a purple gradient tile with a 2px depth edge and
|
||||
no glow. The row itself stays transparent;
|
||||
- sentence-case labels and headings;
|
||||
- never use the em dash (`—`) in UI copy. Prefer a period, colon, comma, or
|
||||
ASCII hyphen (`-`) for empty cells and separators.
|
||||
@@ -596,10 +635,10 @@ value. The dark ladder is `--color-app` 10, `--color-panel` 20,
|
||||
`--coollabs-elevated` 22, `--coollabs-base` 28,
|
||||
`--coollabs-recessed` 34 (sRGB, r=g=b), which reads as distinct surfaces.
|
||||
|
||||
Temperature: the page canvas, cards, menus, listboxes, modals, logs, and the
|
||||
command palette stay **pure neutral gray** (r=g=b). Do not tint those. The
|
||||
dark sidebar (`--color-sidebar`, `#120e1a`) is the exception: it carries a
|
||||
faint brand-purple wash. The light page canvas uses `bg-neutral-50` (not
|
||||
Temperature: the page canvas, cards, menus, listboxes, modals, and logs stay
|
||||
**pure neutral gray** (r=g=b). Do not tint those. The dark sidebar
|
||||
(`--color-sidebar`, `#120e1a`) and the dark command palette are the exceptions:
|
||||
they carry a faint brand-purple wash. The light page canvas uses `bg-neutral-50` (not
|
||||
`bg-gray-50`, which is faintly cool) so it matches the neutral cards and chrome.
|
||||
The only intentional color is the purple brand accent. Yellow is for warnings.
|
||||
- **Layer cards** use the outer box and inset well from section 5.
|
||||
@@ -608,7 +647,10 @@ The only intentional color is the purple brand accent. Yellow is for warnings.
|
||||
Nested pages keep the full trail (project, environment, resource).
|
||||
- **Pagination** is hidden when there is only one page (both arrows disabled).
|
||||
- **Project cards** spell out their counts: "1 environment · 35 resources".
|
||||
- **The view switcher** shows every view. The current one is a purple pill.
|
||||
- **The view switcher** (`x-canvas-view-toggle`,
|
||||
`x-shared-variables.view-controls`) shows only the other view, as an icon
|
||||
button: the grid icon in table view, the list icon in grid view. The labelled
|
||||
`Canvas` link and the `List view` link on canvas pages keep their text.
|
||||
- **Tables and collection tiles** outside a layer card use the
|
||||
`rounded-xl border … dark:bg-white/[0.05]` shell plus the crisp
|
||||
`--coollabs-hairline` ring; in light they are `bg-white` with the ring and
|
||||
@@ -635,7 +677,13 @@ color. Row-hover states keep the lighter `dark:hover:bg-white/[0.025]`.
|
||||
- With the default tile items (not Appearance → Navigation items → Flat), each
|
||||
icon sits in a 30px 3D tile (16px icon, 6px padding, 1px border) in a 36px row.
|
||||
- With tile items, the row has no fill: the current item shows through its
|
||||
accent tile (purple light, yellow dark) and its brighter label.
|
||||
purple accent tile and its brighter label. The dark active tile has a 2px
|
||||
depth edge and no glow, because the row (`overflow: hidden`) clips a glow
|
||||
into a square.
|
||||
- In the collapsed desktop sidebar, Search is a tile like the other items. The
|
||||
row fill is removed in `@layer utilities` (the button has a layered
|
||||
`!important` fill utility that an unlayered rule cannot beat), and the dark
|
||||
tile uses the same colors as the other tiles.
|
||||
- With Flat items, active rows are rounded pills (`rounded-md`) with a solid
|
||||
neutral selected fill (`bg-black/5` light, `bg-white/6` dark) and no accent
|
||||
rail. Hover rows use the same radius. Do not use accent-tinted gradients on nav
|
||||
@@ -662,6 +710,16 @@ in-flow resource heading, or legacy `.navbar-main` tabs to one resource type.
|
||||
Keep route-derived active state in Blade/Livewire. Do not rely only on Alpine
|
||||
state because it can disappear after polling or a Livewire morph.
|
||||
|
||||
From `xl`, the resource menu replaces the main menu in the main sidebar
|
||||
(`x-main-nav-menu`, `registerMainNavContext()`, `components/navbar.blade.php`).
|
||||
Its back row (`‹ Dashboard`, `‹ Servers`, `‹ Service`) shows the menu one depth
|
||||
lower, without a page load. The main menu then starts with a row that opens the
|
||||
deepest resource menu again: the resource name with a right chevron
|
||||
(`adonisjs ›`). Pass the name as `name` to `registerMainNavContext()` or
|
||||
`<x-main-nav-menu :name>`; without a name the row says "Resource menu". Pages
|
||||
with no resource menu keep an invisible row of the same height, so the main
|
||||
menu items do not move between pages.
|
||||
|
||||
The global topbar owns the current resource identity, its compact status
|
||||
badges, configuration warnings (`#configuration-warning-hud-slot`), and the
|
||||
resource actions (`#resource-action-hud-slot`). If a resource is missing from
|
||||
@@ -1169,7 +1227,11 @@ top-anchored overlay:
|
||||
- create rows use a neutral plus tile that only picks up the accent when the
|
||||
row is focused;
|
||||
- type pills and quickcommand chips stay recessed; they tint with the accent
|
||||
only on the focused row;
|
||||
only on the focused row. In dark mode the text of the focused row's chips,
|
||||
its icon tile glyph, and its chevron are white;
|
||||
- in dark mode the shell, header, and results body carry the brand-purple
|
||||
tint from Part 1 (rules after the dark `.view-toggle` rules in
|
||||
`resources/css/app.css`). Light mode stays neutral;
|
||||
- neutral thin scrollbar inside the results body (not brand-colored);
|
||||
- create-resource modals opened from the palette reuse the standard
|
||||
`application-settings-section` layer-card shell.
|
||||
@@ -1186,7 +1248,10 @@ server → destination → project → environment create flow.
|
||||
Current toast behavior:
|
||||
|
||||
- compact layered card, maximum width 26rem;
|
||||
- Reicon status tile for success, info, warning, danger, or default;
|
||||
- Reicon status tile for success, info, warning, danger, or default. The tile
|
||||
(`.toast-icon`) is 3D: a border and a 2px bottom edge in the status color;
|
||||
- a faint edge (`.toast-card`, dark `rgb(255 255 255 / 0.05)`) that blends into
|
||||
the toast background;
|
||||
- title plus optional description;
|
||||
- dismiss and copy-details actions;
|
||||
- normally up to four stacked notifications, without evicting persistent notices;
|
||||
|
||||
@@ -4626,11 +4626,12 @@ function wireNavigate(): string
|
||||
* Registers a resource menu that the main sidebar shows in place of its main menu on wide screens
|
||||
* (see components/navbar.blade.php). The menu teleports itself into #main-nav-context-{depth}.
|
||||
* Back on a menu shows the menu one depth lower, and Back at depth 0 shows the main menu.
|
||||
* The main menu then shows a row (labelled with $name) that opens the deepest resource menu again.
|
||||
*/
|
||||
function registerMainNavContext(int $depth, string $label, string $backTitle): void
|
||||
function registerMainNavContext(int $depth, string $label, string $backTitle, ?string $name = null): void
|
||||
{
|
||||
$levels = request()->attributes->get('mainNavContext', []);
|
||||
$levels[$depth] = ['label' => $label, 'backTitle' => $backTitle];
|
||||
$levels[$depth] = ['label' => $label, 'backTitle' => $backTitle, 'name' => $name];
|
||||
ksort($levels);
|
||||
|
||||
request()->attributes->set('mainNavContext', $levels);
|
||||
|
||||
+58
-1
@@ -5576,6 +5576,12 @@ html:where(:not([data-nav-items="flat"])) .menu-subitem {
|
||||
background-color: oklch(100% 0 0 / 0.07) !important;
|
||||
}
|
||||
|
||||
/* The collapsed desktop rail shows Search as a tile only, without the row fill. */
|
||||
html .app-sidebar nav.sidebar-collapsed .menu-search.menu-item,
|
||||
html .app-sidebar nav.sidebar-collapsed .menu-search.menu-item:hover {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
@media (max-width: 1023.98px) {
|
||||
html:where(:not([data-nav-items="flat"])) nav.sidebar-collapsed .menu-search.menu-item {
|
||||
background-color: var(--color-neutral-100) !important;
|
||||
@@ -5599,7 +5605,7 @@ html.dark:where(:not([data-nav-items="flat"])) :is(.menu-item-active, .menu-subi
|
||||
border-color: #8a4dff !important;
|
||||
background: linear-gradient(180deg, var(--color-brand-hover) 0%, var(--color-brand-press) 100%) !important;
|
||||
--button-depth-color: var(--color-brand-depth) !important;
|
||||
box-shadow: 0 2px 0 var(--color-brand-depth), 0 0 16px rgb(115 23 255 / 0.50) !important;
|
||||
box-shadow: 0 2px 0 var(--color-brand-depth) !important;
|
||||
}
|
||||
|
||||
html.dark:where(:not([data-nav-items="flat"])) :is(.menu-item-active, .menu-subitem-active):active .menu-item-icon:not(.text-error) {
|
||||
@@ -6040,6 +6046,19 @@ html.dark :is(.app-sidebar, .mobile-sidebar-sheet) .menu-search kbd {
|
||||
border-color: rgb(163 145 255 / 0.18) !important;
|
||||
}
|
||||
|
||||
/* The collapsed rail shows Search as a tile like the other items: no row frame (the fill is
|
||||
removed in the utilities layer above), the dark tile. */
|
||||
html.dark .app-sidebar nav.sidebar-collapsed .menu-search {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
html.dark:where(:not([data-nav-items="flat"])) .app-sidebar nav.sidebar-collapsed .menu-search.menu-item .menu-item-icon {
|
||||
border-color: rgb(163 145 255 / 0.13);
|
||||
background: rgb(107 22 237 / 0.07);
|
||||
color: #b9adff;
|
||||
--button-depth-color: rgb(255 255 255 / 0.05);
|
||||
}
|
||||
|
||||
html.dark :is(.app-sidebar, .mobile-sidebar-sheet) nav:not(.sidebar-collapsed) .nav-section {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10.5px;
|
||||
@@ -6118,6 +6137,44 @@ html.dark .view-toggle .bg-coollabs {
|
||||
color: #d9d0ff;
|
||||
}
|
||||
|
||||
/* Toasts: a faint edge that blends into the toast background. */
|
||||
html.dark .toast-card {
|
||||
box-shadow: 0 0 0 1px rgb(255 255 255 / 0.05), var(--shadow-dropdown);
|
||||
}
|
||||
|
||||
/* Toast icon tile: the 3D depth of the sidebar tiles, tinted with the icon color of the toast type. */
|
||||
.toast-icon {
|
||||
border: 1px solid color-mix(in oklab, currentColor 22%, transparent);
|
||||
box-shadow: 0 2px 0 color-mix(in oklab, currentColor 30%, transparent);
|
||||
}
|
||||
|
||||
/* Command palette: the purple brand tint of the dark shell. */
|
||||
html.dark .command-palette {
|
||||
background: color-mix(in oklab, #6b16ed 10%, var(--coollabs-elevated));
|
||||
box-shadow: 0 0 0 1px rgb(163 145 255 / 0.22), var(--shadow-dropdown);
|
||||
}
|
||||
|
||||
html.dark .command-palette-header {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
html.dark .command-palette-header-icon:not(.is-loading) {
|
||||
color: var(--color-brand-light);
|
||||
}
|
||||
|
||||
html.dark .command-palette-input::placeholder {
|
||||
color: oklch(72% 0.05 300);
|
||||
}
|
||||
|
||||
html.dark .command-palette-body {
|
||||
background: color-mix(in oklab, #6b16ed 6%, var(--coollabs-base));
|
||||
box-shadow: 0 0 0 1px rgb(163 145 255 / 0.12);
|
||||
}
|
||||
|
||||
html.dark .command-palette-item:is(:focus, :focus-visible) :is(.command-palette-quickcommand, .command-palette-type-badge, .command-palette-item-chevron, .command-palette-item-icon) {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
html.dark .resource-glyph {
|
||||
border-color: rgb(163 145 255 / 0.20);
|
||||
background: rgb(107 22 237 / 0.10);
|
||||
|
||||
@@ -254,7 +254,7 @@
|
||||
|
||||
@php
|
||||
// The main sidebar shows this menu on wide screens (see components/navbar.blade.php).
|
||||
registerMainNavContext(0, 'Dashboard', 'Back to the main menu');
|
||||
registerMainNavContext(0, 'Dashboard', 'Back to the main menu', $application->name);
|
||||
@endphp
|
||||
|
||||
{{-- Rendered twice: in the page (below xl), and teleported into the main sidebar (xl and up).
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
// One list view (no table / grid choice): nothing to switch to on the list page.
|
||||
$singleList = count($views) === 1;
|
||||
@endphp
|
||||
{{-- Table / grid / canvas switcher. On the list page, table and grid switch the
|
||||
parent's Alpine viewMode; the canvas page shows one "List view" link back
|
||||
{{-- Table / grid / canvas switcher. On the list page, an icon button switches the
|
||||
parent's Alpine viewMode to the other view; the canvas page shows one "List view" link back
|
||||
(?view= makes the list the remembered view again, see RemembersCanvasView).
|
||||
Pages with one list view pass :views="['list' => [...]]". Without a
|
||||
canvasUrl it is only a table / grid switcher. --}}
|
||||
@@ -22,16 +22,13 @@
|
||||
List view
|
||||
</a>
|
||||
@else
|
||||
{{-- Every view stays visible. The current one is the selected pill. --}}
|
||||
{{-- Only the views that are not current show, as icons: the button switches to that view. --}}
|
||||
@unless ($singleList)
|
||||
@foreach ($views as $mode => [$label, $icon])
|
||||
<button type="button" x-on:click="setViewMode(@js($mode))"
|
||||
class="{{ $canvasItem }}"
|
||||
:class="viewMode === @js($mode) ? 'is-selected' : @js($idle)"
|
||||
:aria-pressed="(viewMode === @js($mode)).toString()"
|
||||
title="{{ $label }}">
|
||||
<button type="button" x-on:click="setViewMode(@js($mode))" x-show="viewMode !== @js($mode)" x-cloak
|
||||
class="{{ $canvasItem }} {{ $idle }} w-7.5 justify-center px-0!"
|
||||
title="{{ $label }}" aria-label="{{ $label }}">
|
||||
<x-reicon :name="$icon" class="size-3.5" />
|
||||
{{ $label }}
|
||||
</button>
|
||||
@endforeach
|
||||
@endunless
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
@php
|
||||
// The main sidebar shows this menu on wide screens (see components/navbar.blade.php).
|
||||
registerMainNavContext(0, 'Dashboard', 'Back to the main menu');
|
||||
registerMainNavContext(0, 'Dashboard', 'Back to the main menu', $database->name);
|
||||
@endphp
|
||||
|
||||
{{-- Rendered twice: in the page (below xl), and teleported into the main sidebar (xl and up).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@props(['backLabel', 'backTitle' => 'Back to the main menu', 'depth' => 0, 'navOnly' => false])
|
||||
@props(['backLabel', 'backTitle' => 'Back to the main menu', 'depth' => 0, 'name' => null, 'navOnly' => false])
|
||||
@php
|
||||
// The main sidebar replaces its menu with this one on wide screens (see components/navbar.blade.php).
|
||||
registerMainNavContext($depth, $backLabel, $backTitle);
|
||||
registerMainNavContext($depth, $backLabel, $backTitle, $name);
|
||||
@endphp
|
||||
{{-- A page menu, rendered twice: in the page (below xl, CSS hides it on xl), and teleported into the
|
||||
main sidebar (xl and up). The slot must not contain Livewire components (they would render twice).
|
||||
|
||||
@@ -178,6 +178,7 @@
|
||||
// components/main-nav-menu.blade.php). Each one teleports itself into #main-nav-context-{depth};
|
||||
// only the desktop sidebar has the slots.
|
||||
$navContext = $desktop ? request()->attributes->get('mainNavContext', []) : [];
|
||||
$navContextName = collect($navContext)->pluck('name')->filter()->last() ?? 'Resource menu';
|
||||
@endphp
|
||||
{{-- Wide screens on a resource page: the deepest resource menu replaces the main menu. Back shows the
|
||||
menu one depth lower, and then the main menu (or its submenu), without leaving the page. --}}
|
||||
@@ -255,6 +256,25 @@
|
||||
@endforeach
|
||||
<li x-show="!submenu" @if ($activeSubmenu) x-cloak @endif @class(['contents', 'xl:hidden' => $navContext]) :class="{ 'xl:hidden': navLevel !== -1 }">
|
||||
<ul role="list" class="flex flex-col gap-y-0.5" :class="navSwap && 'nav-swap-' + navSwap">
|
||||
@if ($navContext)
|
||||
{{-- Wide screens: the main menu of a resource page opens the deepest resource menu again.
|
||||
Other pages keep an empty row, so the menu items do not move between pages. --}}
|
||||
<li class="mb-1 hidden xl:block">
|
||||
<button type="button" @click="navLevel = {{ array_key_last($navContext) }}; navSwap = 'forward'"
|
||||
title="Back to the {{ $navContextName }} menu" aria-label="Back to the {{ $navContextName }} menu"
|
||||
class="menu-item menu-back bg-transparent! hover:bg-neutral-100! dark:hover:bg-white/[0.06]!" :class="collapsed && 'lg:justify-center lg:px-0'">
|
||||
<span class="flex-1 truncate pl-6 text-center font-medium text-black dark:text-fg"
|
||||
:class="collapsed && 'lg:hidden'">{{ $navContextName }}</span>
|
||||
<span class="menu-back-icon flex shrink-0 items-center justify-center">
|
||||
<x-reicon name="chevron-down" class="size-3 -rotate-90 text-coollabs dark:text-fg-dim" />
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
@elseif ($desktop)
|
||||
<li class="mb-1 hidden xl:block" aria-hidden="true">
|
||||
<span class="menu-item menu-back invisible"><span class="menu-back-icon"></span><span class="font-medium"> </span></span>
|
||||
</li>
|
||||
@endif
|
||||
@foreach ($navSections as $sectionLabel => $sectionItems)
|
||||
{{-- The first section needs no title. --}}
|
||||
@unless ($loop->first)
|
||||
|
||||
@@ -220,7 +220,7 @@
|
||||
|
||||
@php
|
||||
// The main sidebar shows this menu on wide screens (see components/navbar.blade.php).
|
||||
registerMainNavContext(0, 'Servers', 'Back to the main menu');
|
||||
registerMainNavContext(0, 'Servers', 'Back to the main menu', $server->name);
|
||||
@endphp
|
||||
|
||||
{{-- Rendered twice: in the page (below xl), and teleported into the main sidebar (xl and up).
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
->filter(fn ($items) => $items->isNotEmpty());
|
||||
@endphp
|
||||
|
||||
<x-main-nav-menu back-label="Dashboard" :nav-only="$navOnly"
|
||||
<x-main-nav-menu back-label="Dashboard" :name="$service->name" :nav-only="$navOnly"
|
||||
data-settings-search-items="{{ json_encode(settingsSearchItems($groupedItems, $serviceRouteParameters)) }}">
|
||||
<nav aria-label="Service settings"
|
||||
class="grid grid-cols-2 gap-0.5 border-y border-neutral-200 py-3 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-1 xl:border-y-0 xl:py-0 dark:border-white/[0.06]">
|
||||
|
||||
@@ -13,16 +13,14 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{-- Every view stays visible. The current one is the selected pill. --}}
|
||||
{{-- Only the view that is not current shows, as an icon: the button switches to that view. --}}
|
||||
<div class="view-toggle w-fit">
|
||||
@foreach (['list' => ['List view', 'unordered-list'], 'grid' => ['Grid view', 'grid']] as $mode => [$viewLabel, $icon])
|
||||
<button type="button" @click="viewMode = '{{ $mode }}'; localStorage.setItem('{{ $storageKey }}', '{{ $mode }}')"
|
||||
title="{{ $viewLabel }}"
|
||||
class="relative flex h-7.5 items-center gap-1.5 rounded-md px-2.5 text-[12px] font-medium transition-colors"
|
||||
:class="viewMode === '{{ $mode }}' ? 'is-selected' : 'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:hover:text-white'"
|
||||
:aria-pressed="(viewMode === '{{ $mode }}').toString()">
|
||||
x-show="viewMode !== '{{ $mode }}'" x-cloak
|
||||
title="{{ $viewLabel }}" aria-label="{{ $viewLabel }}"
|
||||
class="relative flex h-7.5 w-7.5 items-center justify-center rounded-md text-neutral-400 transition-colors hover:bg-neutral-100 hover:text-black dark:hover:text-white">
|
||||
<x-reicon :name="$icon" class="size-3.5" />
|
||||
{{ $viewLabel }}
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@@ -103,11 +103,11 @@
|
||||
x-transition:leave-start="translate-y-0 opacity-100"
|
||||
x-transition:leave-end="translate-y-1 opacity-0"
|
||||
@mouseenter="pauseToast(toast)" @mouseleave="resumeToast(toast)"
|
||||
class="surface-popover relative flex w-full items-start rounded-lg group"
|
||||
class="surface-popover toast-card relative flex w-full items-start rounded-lg group"
|
||||
:class="{ 'p-3.5 pr-20': !toast.html, 'p-0': toast.html }">
|
||||
<template x-if="!toast.html">
|
||||
<div class="flex min-w-0 items-start gap-3">
|
||||
<div class="flex size-8 shrink-0 items-center justify-center rounded-lg"
|
||||
<div class="toast-icon flex size-8 shrink-0 items-center justify-center rounded-lg"
|
||||
:class="{
|
||||
'bg-emerald-100 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-400': toast.type === 'success',
|
||||
'bg-coollabs/10 text-coollabs dark:bg-warning/10 dark:text-warning': toast.type === 'info',
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
])->filter();
|
||||
@endphp
|
||||
|
||||
<x-main-nav-menu back-label="Destinations" back-title="Back to Integrations"
|
||||
<x-main-nav-menu back-label="Destinations" back-title="Back to Integrations" :name="$destination->name"
|
||||
data-settings-search-items="{{ json_encode(settingsSearchItems(['Destination' => $destinationMenuItems], $destinationRouteParameters)) }}">
|
||||
<nav aria-label="Destination settings"
|
||||
class="grid grid-cols-2 gap-0.5 border-y border-neutral-200 py-3 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-1 xl:border-y-0 xl:py-0 dark:border-white/[0.06]">
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
@if ($showSettingsSidebar)
|
||||
<section class="application-settings-workspace mt-4 w-full max-w-none lg:mt-0">
|
||||
<div class="grid min-w-0 gap-8 xl:grid-cols-[210px_minmax(0,1fr)] xl:gap-8">
|
||||
<x-main-nav-menu back-label="Sources" back-title="Back to Integrations"
|
||||
<x-main-nav-menu back-label="Sources" back-title="Back to Integrations" :name="$github_app->name ?: 'GitHub App'"
|
||||
data-settings-search-items="{{ json_encode(settingsSearchItems(['GitHub App' => $settingsMenuItems], $githubAppRouteParameters)) }}">
|
||||
<nav aria-label="GitHub App settings"
|
||||
class="grid grid-cols-2 gap-0.5 border-y border-neutral-200 py-3 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-1 xl:border-y-0 xl:py-0 dark:border-white/[0.06]">
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
@if ($showSettingsSidebar)
|
||||
<section class="application-settings-workspace mt-4 w-full max-w-none lg:mt-0">
|
||||
<div class="grid min-w-0 gap-8 xl:grid-cols-[210px_minmax(0,1fr)] xl:gap-8">
|
||||
<x-main-nav-menu back-label="S3 Storage" back-title="Back to Integrations"
|
||||
<x-main-nav-menu back-label="S3 Storage" back-title="Back to Integrations" :name="$storage->name"
|
||||
data-settings-search-items="{{ json_encode(settingsSearchItems(['S3 storage' => $settingsMenuItems], $storageRouteParameters)) }}">
|
||||
<nav aria-label="S3 storage settings"
|
||||
class="grid grid-cols-2 gap-0.5 border-y border-neutral-200 py-3 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-1 xl:border-y-0 xl:py-0 dark:border-white/[0.06]">
|
||||
|
||||
@@ -58,6 +58,7 @@ test('an application page teleports its menu into the main sidebar ', function (
|
||||
->toContain('x-teleport="#main-nav-context-0"')
|
||||
->toContain('application-settings-navigation in-main-nav')
|
||||
->toMatch('#<button type="button" @click="navLevel = -1; navSwap = \'back\'"\s+title="Back to the main menu"#')
|
||||
->toMatch('#@click="navLevel = 0; navSwap = \'forward\'"\s+title="Back to the Navbar Menu App menu"#')
|
||||
->not->toContain('id="main-nav-context-1"')
|
||||
->and(substr_count($html, 'id="main-nav-context-0"'))->toBe(1);
|
||||
});
|
||||
@@ -143,6 +144,7 @@ test('a database backup page keeps the database menu one back away in the main s
|
||||
->toContain('x-teleport="#main-nav-context-1"')
|
||||
->toMatch('#@click="navLevel = -1; navSwap = \'back\'"\s+title="Back to the main menu"#')
|
||||
->toMatch('#@click="navLevel = 0; navSwap = \'back\'"\s+title="Back to the database menu"#')
|
||||
->toMatch('#@click="navLevel = 1; navSwap = \'forward\'"\s+title="Back to the orders-db menu"#')
|
||||
->and(substr_count($html, 'aria-label="Database settings"'))->toBe(1)
|
||||
->and(substr_count($html, 'aria-label="Backup settings"'))->toBe(2);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user