mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-04 16:09:05 +00:00
fix(web): align segmented controls, vertical nav and button padding with published guidance
- Selected segments get a 3:1 accent border, and dark mode uses an indigo fill to match the rails - Rail and main nav selection use a straight 4x20 inset bar in place of the bent stripe - Rail rows are 32px with 8px side padding, and titles, labels and icons share one line - Rail and dark main nav hover are neutral, so hover no longer reads as selected - Toolbar and small buttons get wider side padding at 1366px and up; narrower windows keep the old padding - Logs mode buttons have accessible names when icon-only, and pin budgets match rendered widths
This commit is contained in:
@@ -263,13 +263,11 @@ export default defineComponent({
|
||||
|
||||
const activePillClass = computed(() => {
|
||||
if (slideActive.value) {
|
||||
return isDark.value
|
||||
? "text-tabs-active-text border-s-2 border-transparent"
|
||||
: "text-accent border-s-2 border-transparent";
|
||||
return isDark.value ? "text-tabs-active-text" : "text-accent";
|
||||
}
|
||||
return isDark.value
|
||||
? "text-tabs-active-text bg-tabs-active-bg border-s-2 border-accent"
|
||||
: "text-accent bg-surface-base border-s-2 border-accent";
|
||||
? "text-tabs-active-text bg-tabs-active-bg"
|
||||
: "text-accent bg-surface-base shadow-xs";
|
||||
});
|
||||
const activeIconClass = computed(() =>
|
||||
isDark.value ? "text-tabs-active-text!" : "text-accent!",
|
||||
@@ -396,10 +394,10 @@ export default defineComponent({
|
||||
// Sit above the rail's sliding pill so icon/label stay readable.
|
||||
slideActive.value ? "z-10" : "",
|
||||
props.locked
|
||||
? "cursor-pointer text-tabs-inactive-text border-s-2 border-transparent bg-transparent hover:bg-tabs-hover-bg"
|
||||
? "cursor-pointer text-tabs-inactive-text bg-transparent hover:bg-tabs-hover-bg dark:hover:bg-tabs-rail-hover-bg"
|
||||
: isActive.value
|
||||
? activePillClass.value
|
||||
: "text-tabs-inactive-text border-s-2 border-transparent bg-transparent hover:bg-tabs-hover-bg",
|
||||
: "text-tabs-inactive-text bg-transparent hover:bg-tabs-hover-bg dark:hover:bg-tabs-rail-hover-bg",
|
||||
isActive.value ? "nav-menu-item--active" : "",
|
||||
props.title === "Functions" ? "menu-link-function" : "",
|
||||
// Reset native <button> chrome so the trigger looks EXACTLY like a link.
|
||||
|
||||
@@ -25,7 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<OTabs
|
||||
:model-value="tabName(section)"
|
||||
orientation="vertical"
|
||||
class="w-full shrink-0 md:w-60"
|
||||
class="w-full shrink-0 px-1 md:w-60"
|
||||
data-test="autoobserve-policy-tabs"
|
||||
@update:model-value="select"
|
||||
>
|
||||
|
||||
@@ -68,7 +68,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</OButton>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto px-1.5 pt-1 pb-3">
|
||||
<div class="flex-1 overflow-y-auto px-1 pt-1 pb-3">
|
||||
<OTabs
|
||||
:model-value="activeKey ?? ''"
|
||||
orientation="vertical"
|
||||
@@ -78,11 +78,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<template v-for="(group, idx) in visibleGroups" :key="group.label || idx">
|
||||
<!-- Section label. Each group after the first gets top spacing so the
|
||||
sub-sections read as separate blocks rather than one merged list. -->
|
||||
<!-- ps-1.5 (on top of the container's px-1.5) puts the section label on
|
||||
<!-- ps-2 (on top of the container's px-1) puts the section label on
|
||||
the same 12px item-label grid line as the tabs below it. -->
|
||||
<div
|
||||
v-if="!collapsed && group.label"
|
||||
class="text-text-secondary py-1 ps-1.5 text-xs font-semibold"
|
||||
class="text-text-secondary py-1 ps-2 text-xs font-semibold"
|
||||
:class="{ 'mt-3': idx > 0 }"
|
||||
>
|
||||
{{ group.label }}
|
||||
|
||||
@@ -74,7 +74,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</div>
|
||||
</div>
|
||||
<!-- Search Input -->
|
||||
<div class="px-1.5 pb-1.5" :class="mobileRail ? 'pt-1' : ''">
|
||||
<div class="px-1 pb-1.5" :class="mobileRail ? 'pt-1' : ''">
|
||||
<OSearchInput
|
||||
v-model="searchQuery"
|
||||
data-test="folder-search"
|
||||
@@ -84,7 +84,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="folders-tabs flex-1 overflow-y-auto px-1.5">
|
||||
<div class="folders-tabs flex-1 overflow-y-auto px-1">
|
||||
<OTabs
|
||||
orientation="vertical"
|
||||
dense
|
||||
|
||||
@@ -29,7 +29,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
:aria-label="ariaLabel"
|
||||
data-test="dbm-metrics-rail"
|
||||
>
|
||||
<div class="flex-1 overflow-y-auto px-1.5 pt-2 pb-3 max-md:pt-0 max-md:pb-0">
|
||||
<div class="flex-1 overflow-y-auto px-1 pt-2 pb-3 max-md:pt-0 max-md:pb-0">
|
||||
<OTabs
|
||||
:model-value="activeKey ?? ''"
|
||||
:orientation="isMobile ? 'horizontal' : 'vertical'"
|
||||
|
||||
@@ -33,7 +33,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</OButton>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-1.5 px-1.5 pb-1.5 max-md:pt-1.5">
|
||||
<div class="flex items-center gap-1.5 px-1 pb-1.5 max-md:pt-1.5">
|
||||
<OSearchInput
|
||||
v-model="search"
|
||||
class="w-full max-md:hidden"
|
||||
@@ -60,7 +60,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="min-h-0 flex-1 overflow-y-auto px-1.5 max-md:hidden">
|
||||
<div class="min-h-0 flex-1 overflow-y-auto px-1 max-md:hidden">
|
||||
<OEmptyState
|
||||
v-if="forbidden"
|
||||
size="inline"
|
||||
|
||||
@@ -27,7 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex shrink-0 flex-col gap-1.5 px-1.5 pt-1 pb-1">
|
||||
<div class="flex shrink-0 flex-col gap-1.5 px-1 pt-1 pb-1">
|
||||
<OSearchInput
|
||||
v-model="query"
|
||||
clearable
|
||||
@@ -58,7 +58,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</OToggleGroup>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto px-1.5 pt-1 pb-3">
|
||||
<div class="flex-1 overflow-y-auto px-1 pt-1 pb-3">
|
||||
<OTabs
|
||||
orientation="vertical"
|
||||
class="w-full"
|
||||
|
||||
@@ -35,7 +35,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-1.5 pb-1.5">
|
||||
<div class="px-1 pb-1.5">
|
||||
<OSearchInput
|
||||
v-model="searchQuery"
|
||||
:placeholder="t('synthetics.environments.searchPlaceholder')"
|
||||
@@ -45,7 +45,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto px-1.5">
|
||||
<div class="flex-1 overflow-y-auto px-1">
|
||||
<OTabs
|
||||
orientation="vertical"
|
||||
dense
|
||||
|
||||
@@ -46,7 +46,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<template v-slot:before>
|
||||
<div class="h-full w-full ps-2.5 pt-2 pb-2.5">
|
||||
<div class="h-full overflow-y-auto">
|
||||
<OTabs v-model="billingtab" orientation="vertical">
|
||||
<OTabs v-model="billingtab" orientation="vertical" class="px-1">
|
||||
<ORouteTab
|
||||
exact
|
||||
name="plans"
|
||||
|
||||
@@ -97,7 +97,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
:model-value="String(currentIndex)"
|
||||
orientation="vertical"
|
||||
dense
|
||||
class="min-h-0 flex-1 overflow-y-auto px-1.5 py-1"
|
||||
class="min-h-0 flex-1 overflow-y-auto px-1 py-1"
|
||||
data-test="ai-queue-workbench-nav"
|
||||
>
|
||||
<OTab
|
||||
|
||||
@@ -386,20 +386,23 @@ const unavailableClasses = computed(() => {
|
||||
});
|
||||
|
||||
const sizeClasses: Record<NonNullable<ButtonProps["size"]>, string> = {
|
||||
xs: "h-7 ps-2.5 pe-2.5 text-xs gap-1.5 rounded-default",
|
||||
xs: "h-7 ps-3 pe-3 max-[85.5rem]:ps-2.5 max-[85.5rem]:pe-2.5 text-xs gap-1.5 rounded-default",
|
||||
// 34px control height — the workhorse compact button that pairs with 34px
|
||||
// inputs in toolbars/headers. (radius 8 = rounded-default.)
|
||||
sm: "h-[2.125rem] ps-3 pe-3 text-sm gap-2 rounded-default",
|
||||
sm: "h-[2.125rem] ps-3.5 pe-3.5 max-[85.5rem]:ps-3 max-[85.5rem]:pe-3 text-sm gap-2 rounded-default",
|
||||
// Every toolbar control shares this box so a bar never mixes heights or padding.
|
||||
"sm-toolbar": "h-8.5 ps-2.5 pe-2.5 text-xs gap-1.5 rounded-default",
|
||||
"xs-grouped": "h-6.5 ps-2 pe-2 text-xs gap-1.5 rounded-default",
|
||||
"sm-toolbar":
|
||||
"h-8.5 ps-4 pe-4 max-[85.5rem]:ps-2.5 max-[85.5rem]:pe-2.5 text-xs gap-1.5 rounded-default",
|
||||
"xs-grouped":
|
||||
"h-6.5 ps-2.5 pe-2.5 max-[85.5rem]:ps-2 max-[85.5rem]:pe-2 text-xs gap-1.5 rounded-default",
|
||||
// Compact labeled size for inline field chips (axis items) — ~28px, matches the dense button size
|
||||
// Extra-compact chip size — 24px height for axis field chips in query builder
|
||||
chip: "h-6 ps-2 pe-1.5 text-xs gap-1 rounded-default leading-none",
|
||||
// Same as chip but with fixed 12px font — for dashboard query builder axis field chips
|
||||
// (needed because the html font-size is 14px, making text-xs = 10.5px instead of 12px)
|
||||
"chip-12": "h-6 ps-2 pe-1.5 !text-xs gap-1 rounded-default leading-none",
|
||||
"sm-action": "h-[2.125rem] ps-3 pe-3 min-w-20 text-sm gap-2 rounded-default",
|
||||
"sm-action":
|
||||
"h-[2.125rem] ps-3.5 pe-3.5 max-[85.5rem]:ps-3 max-[85.5rem]:pe-3 min-w-20 text-sm gap-2 rounded-default",
|
||||
// h-8.5 sorts before h-9 and up, so a caller's own plain height class still overrides md.
|
||||
md: "h-8.5 ps-4 pe-4 text-sm gap-2 rounded-default",
|
||||
cta: "h-10 ps-4 pe-4 text-sm gap-2 rounded-default",
|
||||
|
||||
@@ -357,8 +357,9 @@ onBeforeUnmount(() => {
|
||||
// tinted selected pill + lighter accent in dark, where a white pill would vanish
|
||||
// on the near-black rail.
|
||||
const indicatorClass = computed(() => [
|
||||
"pointer-events-none absolute left-0 top-0 z-0 rounded-surface border-s-2",
|
||||
"bg-surface-base border-accent dark:bg-tabs-active-bg dark:border-accent",
|
||||
"pointer-events-none absolute left-0 top-0 z-0 rounded-surface",
|
||||
"bg-surface-base shadow-xs dark:bg-tabs-active-bg dark:shadow-none",
|
||||
"before:absolute before:top-2 before:h-5 before:start-0 before:w-1 before:rounded-full before:bg-accent",
|
||||
transitionOn.value &&
|
||||
"transition-[transform,width,height] duration-300 ease-out motion-reduce:transition-none",
|
||||
indicatorVisible.value ? "opacity-100" : "opacity-0",
|
||||
|
||||
@@ -183,7 +183,7 @@ onBeforeUnmount(() => {
|
||||
});
|
||||
|
||||
const indicatorClasses = computed(() => [
|
||||
"pointer-events-none absolute left-0 top-0 rounded-default bg-toggle-item-active-bg",
|
||||
"pointer-events-none absolute left-0 top-0 rounded-default bg-toggle-item-active-bg border border-toggle-item-active-border shadow-xs",
|
||||
transitionOn.value &&
|
||||
"transition-[transform,width,height] duration-300 ease-out motion-reduce:transition-none",
|
||||
indicatorVisible.value ? "opacity-100" : "opacity-0",
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { I18nText } from "@/types/i18n";
|
||||
import type { AcceptableValue } from "reka-ui";
|
||||
|
||||
/** Size controls padding and font-size — height is determined by content */
|
||||
export type ToggleGroupItemSize = "md" | "sm" | "xs";
|
||||
export type ToggleGroupItemSize = "md" | "sm" | "toolbar" | "xs";
|
||||
|
||||
export interface ToggleGroupItemProps {
|
||||
/** Unique value for this option — required */
|
||||
|
||||
@@ -76,6 +76,7 @@ const dropIndicatorClass = computed<string>(() => {
|
||||
const sizeClasses: Record<NonNullable<ToggleGroupItemProps["size"]>, string> = {
|
||||
md: "h-9 gap-2 px-3 text-sm max-md:px-2",
|
||||
sm: "h-7 gap-1.5 px-2.5 text-xs max-md:px-1.5",
|
||||
toolbar: "h-7 gap-1.5 px-3 text-xs max-[85.5rem]:px-2.5 max-md:px-1.5",
|
||||
xs: "h-5 gap-2 px-1.5 text-xs max-md:px-1",
|
||||
};
|
||||
|
||||
@@ -83,6 +84,7 @@ const sizeClasses: Record<NonNullable<ToggleGroupItemProps["size"]>, string> = {
|
||||
const iconSize: Record<NonNullable<ToggleGroupItemProps["size"]>, "xs" | "sm" | "md"> = {
|
||||
md: "md",
|
||||
sm: "sm",
|
||||
toolbar: "sm",
|
||||
xs: "xs",
|
||||
};
|
||||
</script>
|
||||
@@ -147,11 +149,12 @@ const iconSize: Record<NonNullable<ToggleGroupItemProps["size"]>, "xs" | "sm" |
|
||||
'cursor-pointer outline-none',
|
||||
// Hover (inactive only) — scoped to data-state=off so hovering the
|
||||
// active item never repaints over the sliding indicator / active fill.
|
||||
'data-[state=off]:hover:bg-toggle-item-hover-bg',
|
||||
'data-[state=off]:hover:bg-toggle-item-hover-bg data-[state=off]:hover:text-text-heading',
|
||||
// Active fill: normally painted per-item. When the group animates the
|
||||
// selection (opt-in), a single sliding indicator paints the fill instead,
|
||||
// so suppress the per-item background and just keep the active text colour.
|
||||
!isAnimated && 'data-[state=on]:bg-toggle-item-active-bg',
|
||||
!isAnimated &&
|
||||
'data-[state=on]:bg-toggle-item-active-bg data-[state=on]:ring-toggle-item-active-border data-[state=on]:shadow-xs data-[state=on]:ring-1 data-[state=on]:ring-inset',
|
||||
'data-[state=on]:text-toggle-item-active-text',
|
||||
// Sit above the sliding indicator so the icon/label stay readable.
|
||||
isAnimated && 'relative z-10',
|
||||
@@ -194,7 +197,7 @@ const iconSize: Record<NonNullable<ToggleGroupItemProps["size"]>, "xs" | "sm" |
|
||||
v-else-if="props.iconRight"
|
||||
:name="props.iconRight"
|
||||
:size="iconSize[props.size]"
|
||||
:class="props.iconRight === 'lock' ? 'text-warning-600' : ''"
|
||||
:class="props.iconRight === 'lock' ? 'text-text-secondary' : ''"
|
||||
/>
|
||||
</ToggleGroupItem>
|
||||
<span v-if="unavailable && props.tooltip" :id="reasonId" aria-hidden="true" class="sr-only">{{
|
||||
|
||||
@@ -225,9 +225,9 @@ describe("OTab", () => {
|
||||
expect(classes).not.toContain("focus-visible:outline-offset-2");
|
||||
});
|
||||
|
||||
it("uses a positive focus outline offset on a vertical tab", () => {
|
||||
it("keeps the focus outline inside the rail inset on a vertical tab", () => {
|
||||
const classes = mountTab({ isVertical: true }).find('[role="tab"]').classes();
|
||||
expect(classes).toContain("focus-visible:outline-offset-2");
|
||||
expect(classes).toContain("focus-visible:outline-offset-0");
|
||||
expect(classes).not.toContain("focus-visible:-outline-offset-2");
|
||||
});
|
||||
|
||||
|
||||
@@ -71,71 +71,65 @@ const baseClasses = computed<string>(() =>
|
||||
// Named group so the grip (and consumer affordances like a rename pencil)
|
||||
// can fade in on tab hover without colliding with consumer `group` usage.
|
||||
"group/otab",
|
||||
"relative items-center gap-1.5",
|
||||
isVertical.value ? "flex justify-start" : "inline-flex justify-center",
|
||||
// Horizontal inset. A vertical (side-rail) tab is a selectable PILL, so the
|
||||
// rail container insets it (px-1.5) to give the pill breathing room from the
|
||||
// rail edges. With that 6px container inset + the tab's own 2px active border
|
||||
// + this ps-1, the label lands on the page-edge grid line (12px) while the
|
||||
// pill never touches the rail edge. Rails add the px-1.5; tabs don't hand-roll
|
||||
// their own padding override.
|
||||
// Reorderable tabs widen the left inset to a 0.75rem gutter that hosts the
|
||||
// hover-revealed drag grip. The gutter is tied to `reorderable` (not
|
||||
// per-press state), so a tab is pixel-identical at rest, on hover, and
|
||||
// while its label is being edited — the grip only ever changes opacity.
|
||||
"relative items-center",
|
||||
isVertical.value ? "flex justify-start gap-2" : "inline-flex justify-center gap-1.5",
|
||||
// The rail's px-1 plus this px-2 puts a vertical tab's icon on the 12px page-edge grid line.
|
||||
// Reorderable tabs keep a fixed 0.75rem gutter for the grip, so hovering or editing never shifts the label.
|
||||
isReorderable.value
|
||||
? "ps-3 pe-2"
|
||||
: isVertical.value
|
||||
? "ps-1 pe-2"
|
||||
? "px-2"
|
||||
: slots.trailing
|
||||
? "ps-2 pe-1"
|
||||
: "px-2",
|
||||
"font-normal text-sm whitespace-nowrap",
|
||||
isVertical.value && isActive.value ? "font-semibold" : "font-normal",
|
||||
"text-sm whitespace-nowrap",
|
||||
isVertical.value ? "rounded-default" : "rounded-t-default",
|
||||
"outline-none transition-[color,background-color,border-color,text-decoration-color,fill,stroke] duration-(--duration-hover-out) hover:duration-(--duration-hover-in)",
|
||||
"select-none",
|
||||
"focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-focus-ring-accent",
|
||||
// The horizontal strip scrolls on x, which clips y as well, so an outside outline would lose its bottom edge.
|
||||
isVertical.value ? "focus-visible:outline-offset-2" : "focus-visible:-outline-offset-2",
|
||||
isVertical.value ? "focus-visible:outline-offset-0" : "focus-visible:-outline-offset-2",
|
||||
].join(" "),
|
||||
);
|
||||
|
||||
const stateClasses = computed<string>(() => {
|
||||
if (props.disable) {
|
||||
return [
|
||||
"text-tabs-disabled-text cursor-not-allowed opacity-60",
|
||||
// Keep the transparent left border so disabled items don't shift width.
|
||||
isVertical.value ? "border-s-2 border-transparent" : "",
|
||||
].join(" ");
|
||||
return "text-tabs-disabled-text cursor-not-allowed opacity-60";
|
||||
}
|
||||
if (isActive.value) {
|
||||
// Horizontal tabs: colored text only. The active underline is a single
|
||||
// shared bar in OTabs that slides between tabs, so each tab keeps a
|
||||
// transparent 2px border (layout parity with inactive) rather than drawing
|
||||
// its own colored one.
|
||||
// Vertical tabs (side rail): tint bg + primary text + a colored left accent
|
||||
// border that reads as the active rail marker.
|
||||
return [
|
||||
"text-tabs-active-text cursor-pointer",
|
||||
isVertical.value
|
||||
? "bg-tabs-active-bg border-s-2 border-tabs-indicator"
|
||||
? "bg-tabs-active-bg before:bg-tabs-indicator before:absolute before:inset-y-1.5 before:start-0 before:w-1 before:rounded-full"
|
||||
: "border-b-2 border-transparent",
|
||||
].join(" ");
|
||||
}
|
||||
return [
|
||||
"text-tabs-inactive-text cursor-pointer",
|
||||
isVertical.value
|
||||
? "enabled:hover:text-tabs-hover-text enabled:hover:bg-tabs-hover-bg"
|
||||
: "enabled:hover:text-tabs-hover-text",
|
||||
// Transparent border (left for vertical, bottom for horizontal) keeps inactive
|
||||
// items the same size as the active one — no layout shift on activation.
|
||||
isVertical.value ? "border-s-2 border-transparent" : "border-b-2 border-transparent",
|
||||
].join(" ");
|
||||
if (isVertical.value) {
|
||||
return "text-text-body cursor-pointer enabled:hover:text-tabs-hover-text enabled:hover:bg-tabs-rail-hover-bg";
|
||||
}
|
||||
return "text-tabs-inactive-text cursor-pointer enabled:hover:text-tabs-hover-text border-b-2 border-transparent";
|
||||
});
|
||||
|
||||
// A muted glyph lets the rail's labels lead; the active tab's icon inherits its accent text.
|
||||
const iconToneClass = computed<string>(() =>
|
||||
isVertical.value && !isActive.value && !props.disable
|
||||
? "text-text-secondary group-hover/otab:text-tabs-hover-text"
|
||||
: "",
|
||||
);
|
||||
|
||||
// One padlock per locked row would make amber the rail's loudest colour.
|
||||
const lockToneClass = computed<string>(() =>
|
||||
isVertical.value ? "text-text-secondary" : "text-warning-600",
|
||||
);
|
||||
|
||||
const heightClasses = computed<string>(() => {
|
||||
if (isVertical.value) {
|
||||
return isDense.value ? "py-1.5" : "py-2";
|
||||
return "py-1.5";
|
||||
}
|
||||
return isDense.value ? "h-8" : "h-10";
|
||||
});
|
||||
@@ -220,11 +214,13 @@ const heightClasses = computed<string>(() => {
|
||||
:name="icon as any"
|
||||
size="sm"
|
||||
class="o-tab__icon shrink-0"
|
||||
:class="iconToneClass"
|
||||
/>
|
||||
<!-- Fallback: Material icon font glyph (legacy underscore names) -->
|
||||
<span
|
||||
v-else-if="icon"
|
||||
class="o-tab__icon material-icons-outlined shrink-0 text-base leading-none"
|
||||
:class="iconToneClass"
|
||||
>{{ icon }}</span
|
||||
>
|
||||
</slot>
|
||||
@@ -240,7 +236,7 @@ const heightClasses = computed<string>(() => {
|
||||
:name="suffixIcon as any"
|
||||
size="xs"
|
||||
class="ms-auto shrink-0"
|
||||
:class="suffixIcon === 'lock' ? 'text-warning-600' : ''"
|
||||
:class="suffixIcon === 'lock' ? lockToneClass : ''"
|
||||
/>
|
||||
</template>
|
||||
<slot v-else />
|
||||
|
||||
@@ -362,7 +362,7 @@ const alignClasses: Record<NonNullable<OTabsProps["align"]>, string> = {
|
||||
<div
|
||||
ref="tablistRef"
|
||||
:class="[
|
||||
'o-tabs relative flex flex-col gap-0.5 p-1',
|
||||
'o-tabs relative flex flex-col gap-0.5 py-1',
|
||||
alignClasses[align],
|
||||
{ 'border-card-glass-border border-b border-solid': bordered },
|
||||
]"
|
||||
|
||||
@@ -389,8 +389,8 @@ describe("the real token sheets", () => {
|
||||
});
|
||||
|
||||
describe.each([
|
||||
["light", 1086, 1074],
|
||||
["dark", 1087, 1075],
|
||||
["light", 1088, 1076],
|
||||
["dark", 1089, 1077],
|
||||
] as const)("%s theme", (theme, total, resolved) => {
|
||||
const scope = THEMES[theme];
|
||||
const names = colorTokens(scope);
|
||||
|
||||
@@ -141,12 +141,13 @@
|
||||
/* ToggleGroup */
|
||||
--color-toggle-track-bg: var(--color-surface-subtle);
|
||||
--color-toggle-item-bg: transparent;
|
||||
--color-toggle-item-text: var(--color-text-heading);
|
||||
--color-toggle-item-hover-bg: var(--color-primary-50);
|
||||
--color-toggle-item-active-bg: var(--color-surface-accent-active);
|
||||
--color-toggle-item-text: var(--color-text-body);
|
||||
--color-toggle-item-hover-bg: var(--color-grey-150);
|
||||
--color-toggle-item-active-bg: var(--color-surface-base);
|
||||
--color-toggle-item-active-text: var(--color-primary-700);
|
||||
--color-toggle-item-active-border: var(--color-primary-600);
|
||||
--color-toggle-item-disabled: var(--color-text-disabled);
|
||||
--color-toggle-border: var(--color-border-default);
|
||||
--color-toggle-border: var(--color-border-subtle);
|
||||
--color-toggle-focus-ring: var(--color-focus-ring-accent);
|
||||
/* Insertion line shown while dragging to reorder (reorderable groups) */
|
||||
--color-toggle-drop-indicator: var(--color-primary-600);
|
||||
@@ -196,6 +197,7 @@
|
||||
--color-tabs-inactive-text: var(--color-text-heading);
|
||||
--color-tabs-hover-text: var(--color-text-heading);
|
||||
--color-tabs-hover-bg: color-mix(in srgb, var(--color-primary-100) 50%, var(--color-primary-50));
|
||||
--color-tabs-rail-hover-bg: var(--color-grey-150);
|
||||
--color-tabs-disabled-text: var(--color-text-disabled);
|
||||
--color-tabs-bar-border: var(--color-border-default);
|
||||
--spacing-tabs-height: var(--spacing-10);
|
||||
@@ -1468,6 +1470,7 @@
|
||||
--color-toggle-item-hover-bg: var(--color-toggle-item-hover-bg);
|
||||
--color-toggle-item-active-bg: var(--color-toggle-item-active-bg);
|
||||
--color-toggle-item-active-text: var(--color-toggle-item-active-text);
|
||||
--color-toggle-item-active-border: var(--color-toggle-item-active-border);
|
||||
--color-toggle-item-disabled: var(--color-toggle-item-disabled);
|
||||
--color-toggle-border: var(--color-toggle-border);
|
||||
--color-toggle-focus-ring: var(--color-toggle-focus-ring);
|
||||
@@ -1511,6 +1514,7 @@
|
||||
--color-tabs-inactive-text: var(--color-tabs-inactive-text);
|
||||
--color-tabs-hover-text: var(--color-tabs-hover-text);
|
||||
--color-tabs-hover-bg: var(--color-tabs-hover-bg);
|
||||
--color-tabs-rail-hover-bg: var(--color-tabs-rail-hover-bg);
|
||||
--color-tabs-disabled-text: var(--color-tabs-disabled-text);
|
||||
--color-tabs-bar-border: var(--color-tabs-bar-border);
|
||||
--spacing-tabs-height: var(--spacing-tabs-height);
|
||||
|
||||
@@ -233,16 +233,18 @@
|
||||
--color-tooltip-arrow: var(--d-overlay);
|
||||
|
||||
/* ToggleGroup */
|
||||
--color-toggle-item-hover-bg: var(--color-primary-950);
|
||||
--color-toggle-item-hover-bg: var(--d-divider);
|
||||
--color-toggle-item-active-bg: var(--color-primary-900);
|
||||
--color-toggle-item-active-text: var(--color-white);
|
||||
--color-toggle-item-active-border: var(--color-primary-400);
|
||||
/* Accent lightens on dark so the insertion line stays visible on the track */
|
||||
--color-toggle-drop-indicator: var(--color-primary-400);
|
||||
|
||||
/* Tabs */
|
||||
--color-tabs-active-bg: var(--color-surface-accent-active);
|
||||
--color-tabs-active-bg: var(--color-primary-900);
|
||||
--color-tabs-active-text: var(--color-white);
|
||||
--color-tabs-hover-bg: var(--color-primary-900);
|
||||
--color-tabs-hover-bg: var(--d-hover);
|
||||
--color-tabs-rail-hover-bg: var(--color-grey-800);
|
||||
--color-tabs-bar-border: var(--color-grey-700);
|
||||
--color-nav-label-accent: var(--color-accent);
|
||||
|
||||
|
||||
@@ -259,8 +259,8 @@ describe("SearchBar — pinned saved views", () => {
|
||||
|
||||
// 93px = 1px border + 58px list trigger + 1px separator + 32px create button + 1px border.
|
||||
it.each([
|
||||
[680, false],
|
||||
[681, true],
|
||||
[426, false],
|
||||
[427, true],
|
||||
])(
|
||||
"budgets the group at its rendered 93px width (toolbar %ipx → shown: %s)",
|
||||
async (width, shown) => {
|
||||
|
||||
@@ -64,8 +64,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
>
|
||||
<OToggleGroupItem
|
||||
data-test="logs-logs-toggle"
|
||||
:aria-label="t('common.search')"
|
||||
value="logs"
|
||||
size="sm"
|
||||
size="toolbar"
|
||||
:tooltip="toolbarToggleIconOnly ? t('common.search') : undefined"
|
||||
>
|
||||
<template #icon-left>
|
||||
@@ -90,7 +91,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
: undefined
|
||||
"
|
||||
value="visualize"
|
||||
size="sm"
|
||||
size="toolbar"
|
||||
>
|
||||
<template #icon-left>
|
||||
<OIcon name="timeline" size="sm" class="shrink-0" />
|
||||
@@ -100,18 +101,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<OToggleGroupItem
|
||||
data-test="logs-build-toggle"
|
||||
:aria-label="t('search.buildQuery')"
|
||||
value="build"
|
||||
size="sm"
|
||||
size="toolbar"
|
||||
:tooltip="toolbarToggleIconOnly ? t('search.buildQuery') : undefined"
|
||||
>
|
||||
<template #icon-left>
|
||||
<OIcon name="build" size="sm" class="shrink-0" />
|
||||
<OIcon name="show-chart" size="sm" class="shrink-0" />
|
||||
</template>
|
||||
<span v-if="!toolbarToggleIconOnly">{{ t("search.buildQuery") }}</span>
|
||||
</OToggleGroupItem>
|
||||
|
||||
<OToggleGroupItem
|
||||
data-test="logs-drilldown-toggle"
|
||||
:aria-label="t('search.drillDown')"
|
||||
:disabled="searchObj.meta.sqlMode"
|
||||
:tooltip="
|
||||
searchObj.meta.sqlMode
|
||||
@@ -121,7 +124,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
: undefined
|
||||
"
|
||||
value="drilldown"
|
||||
size="sm"
|
||||
size="toolbar"
|
||||
>
|
||||
<template #icon-left>
|
||||
<OIcon name="query-stats" size="sm" class="shrink-0" />
|
||||
@@ -131,20 +134,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<OToggleGroupItem
|
||||
data-test="logs-patterns-toggle"
|
||||
:aria-label="t('search.showPatternsLabel')"
|
||||
value="patterns"
|
||||
size="sm"
|
||||
:tooltip="
|
||||
patternsAccess.allowed && toolbarToggleIconOnly
|
||||
? t('search.showPatternsLabel')
|
||||
: undefined
|
||||
"
|
||||
size="toolbar"
|
||||
:tooltip="toolbarToggleIconOnly ? t('search.showPatternsLabel') : undefined"
|
||||
>
|
||||
<template #icon-left>
|
||||
<OIcon name="layers" size="sm" class="shrink-0" />
|
||||
</template>
|
||||
<span v-if="!toolbarToggleIconOnly">{{ t("search.showPatternsLabel") }}</span>
|
||||
<template v-if="!patternsAccess.allowed" #icon-right>
|
||||
<OIcon name="lock" size="xs" class="text-warning-600 shrink-0" />
|
||||
<OIcon name="lock" size="xs" class="text-text-secondary shrink-0" />
|
||||
</template>
|
||||
</OToggleGroupItem>
|
||||
</OToggleGroup>
|
||||
@@ -2424,7 +2424,8 @@ export default defineComponent({
|
||||
// Approximate rendered widths (px) of each pinned control and of the fixed
|
||||
// left-section content, used only to decide how many pinned items fit before
|
||||
// they would clip. Hidden pinned items stay reachable inside the More menu.
|
||||
const PIN_ITEM_WIDTH = { histogram: 72, sqlMode: 72, quickMode: 72, savedViews: 93 };
|
||||
const PIN_BUTTON_WIDTH = computed(() => (windowWidth.value < 1366 ? 72 : 84));
|
||||
const SAVED_VIEWS_PIN_WIDTH = 93;
|
||||
const SYNTAX_GUIDE_LABEL_WIDTH = 108;
|
||||
const SYNTAX_GUIDE_ICON_WIDTH = 40;
|
||||
const PIN_ITEM_GAP = 8;
|
||||
@@ -2434,10 +2435,10 @@ export default defineComponent({
|
||||
const baseReservedWidth = computed(() => {
|
||||
let w = 0;
|
||||
if (toolbarToggleAsDropdown.value) w += 120;
|
||||
else if (toolbarToggleIconOnly.value) w += 230;
|
||||
else w += 440;
|
||||
else if (toolbarToggleIconOnly.value) w += 186;
|
||||
else w += 400;
|
||||
if (!toolbarMoveResetToMenu.value) w += shouldHideToolbarButtonText.value ? 34 : 80;
|
||||
w += 76; // More button (always visible)
|
||||
w += windowWidth.value < 1366 ? 76 : 84; // More button (always visible)
|
||||
w += 24; // inter-item gaps / padding buffer
|
||||
return w;
|
||||
});
|
||||
@@ -2467,10 +2468,10 @@ export default defineComponent({
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (isPinned("histogram")) res.histogram = tryFit(PIN_ITEM_WIDTH.histogram);
|
||||
if (isPinned("sqlMode")) res.sqlMode = tryFit(PIN_ITEM_WIDTH.sqlMode);
|
||||
if (isPinned("quickMode")) res.quickMode = tryFit(PIN_ITEM_WIDTH.quickMode);
|
||||
if (isPinned("savedViews")) res.savedViews = tryFit(PIN_ITEM_WIDTH.savedViews);
|
||||
if (isPinned("histogram")) res.histogram = tryFit(PIN_BUTTON_WIDTH.value);
|
||||
if (isPinned("sqlMode")) res.sqlMode = tryFit(PIN_BUTTON_WIDTH.value);
|
||||
if (isPinned("quickMode")) res.quickMode = tryFit(PIN_BUTTON_WIDTH.value);
|
||||
if (isPinned("savedViews")) res.savedViews = tryFit(SAVED_VIEWS_PIN_WIDTH);
|
||||
if (isPinned("syntaxGuide")) {
|
||||
if (tryFit(SYNTAX_GUIDE_LABEL_WIDTH)) {
|
||||
res.syntaxGuide = true;
|
||||
@@ -2507,7 +2508,7 @@ export default defineComponent({
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{ value: "build", icon: "build", label: t("search.buildQuery"), disabled: false },
|
||||
{ value: "build", icon: "show-chart", label: t("search.buildQuery"), disabled: false },
|
||||
{
|
||||
value: "drilldown",
|
||||
icon: "query-stats",
|
||||
|
||||
@@ -33,7 +33,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<OSplitter class="h-full" v-model="splitterModel" unit="px" disabled :horizontal="isMobile">
|
||||
<template v-slot:before>
|
||||
<div class="functions-tabs w-full">
|
||||
<OTabs v-model="activeTab" :orientation="isMobile ? 'horizontal' : 'vertical'">
|
||||
<OTabs
|
||||
v-model="activeTab"
|
||||
:orientation="isMobile ? 'horizontal' : 'vertical'"
|
||||
:class="isMobile ? undefined : 'px-1'"
|
||||
>
|
||||
<OTab
|
||||
name="generalSettings"
|
||||
icon="settings"
|
||||
|
||||
@@ -118,7 +118,7 @@ const setOpen = (id: string, open: boolean) => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="min-h-0 flex-1 overflow-y-auto px-1.5 pb-3">
|
||||
<div class="min-h-0 flex-1 overflow-y-auto px-1 pb-3">
|
||||
<OTabs
|
||||
:model-value="active"
|
||||
orientation="vertical"
|
||||
|
||||
Reference in New Issue
Block a user