Replace text-only tooltips with custom richly styled tooltips (#3436)

* Replace the title attribute with custom FloatingMenu tooltips

* Separate tooltip labels and descriptions into two styled blocks

* Move keyboard shortcut tooltips to a separate section at the bottom

* Update shortcut key styling in tooltips and hints bar

* Fix .to_string()
This commit is contained in:
Keavon Chambers
2025-11-30 13:32:58 -08:00
committed by GitHub
parent 94e5c8fc05
commit e8ebcc2c21
94 changed files with 1323 additions and 580 deletions
@@ -2,21 +2,31 @@
import { getContext } from "svelte";
import type { AppWindowState } from "@graphite/state-providers/app-window";
import type { DialogState } from "@graphite/state-providers/dialog";
import type { TooltipState } from "@graphite/state-providers/tooltip";
import Dialog from "@graphite/components/floating-menus/Dialog.svelte";
import Tooltip from "@graphite/components/floating-menus/Tooltip.svelte";
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
import StatusBar from "@graphite/components/window/status-bar/StatusBar.svelte";
import TitleBar from "@graphite/components/window/title-bar/TitleBar.svelte";
import Workspace from "@graphite/components/window/workspace/Workspace.svelte";
const dialog = getContext<DialogState>("dialog");
const tooltip = getContext<TooltipState>("tooltip");
const appWindow = getContext<AppWindowState>("appWindow");
</script>
<LayoutCol class="main-window" classes={{ "viewport-hole-punch": $appWindow.viewportHolePunch }}>
<TitleBar />
<Workspace />
<StatusBar />
{#if $dialog.visible}
<Dialog />
{/if}
{#if $tooltip.visible}
<Tooltip />
{/if}
</LayoutCol>
<style lang="scss" global>
@@ -11,13 +11,13 @@
const editor = getContext<Editor>("editor");
</script>
<LayoutRow class="window-button linux" tooltip="Minimize" on:click={() => editor.handle.appWindowMinimize()}>
<LayoutRow class="window-button linux" tooltipLabel="Minimize" on:click={() => editor.handle.appWindowMinimize()}>
<IconLabel icon="WindowButtonWinMinimize" />
</LayoutRow>
<LayoutRow class="window-button linux" tooltip={$appWindow.maximized ? "Unmaximize" : "Maximize"} on:click={() => editor.handle.appWindowMaximize()}>
<LayoutRow class="window-button linux" tooltipLabel={$appWindow.maximized ? "Unmaximize" : "Maximize"} on:click={() => editor.handle.appWindowMaximize()}>
<IconLabel icon={$appWindow.maximized ? "WindowButtonWinRestoreDown" : "WindowButtonWinMaximize"} />
</LayoutRow>
<LayoutRow class="window-button linux" tooltip="Close" on:click={() => editor.handle.appWindowClose()}>
<LayoutRow class="window-button linux" tooltipLabel="Close" on:click={() => editor.handle.appWindowClose()}>
<IconLabel icon="WindowButtonWinClose" />
</LayoutRow>
@@ -8,8 +8,6 @@
const fullscreen = getContext<FullscreenState>("fullscreen");
$: requestFullscreenHotkeys = $fullscreen.keyboardLockApiSupported && !$fullscreen.keyboardLocked;
async function handleClick() {
if ($fullscreen.windowFullscreen) fullscreen.exitFullscreen();
else fullscreen.enterFullscreen();
@@ -19,8 +17,9 @@
<LayoutRow
class="window-buttons-web"
on:click={handleClick}
tooltip={($fullscreen.windowFullscreen ? "Exit Fullscreen (F11)" : "Enter Fullscreen (F11)") +
(requestFullscreenHotkeys ? "\n\nThis provides access to hotkeys normally reserved by the browser" : "")}
tooltipLabel={$fullscreen.windowFullscreen ? "Exit Fullscreen" : "Enter Fullscreen"}
tooltipDescription={$fullscreen.keyboardLockApiSupported ? "While fullscreen, keyboard shortcuts normally reserved by the browser become available." : ""}
tooltipShortcut="F11"
>
<IconLabel icon={$fullscreen.windowFullscreen ? "FullscreenExit" : "FullscreenEnter"} />
</LayoutRow>
@@ -11,13 +11,13 @@
const editor = getContext<Editor>("editor");
</script>
<LayoutRow class="window-button windows" tooltip="Minimize" on:click={() => editor.handle.appWindowMinimize()}>
<LayoutRow class="window-button windows" tooltipLabel="Minimize" on:click={() => editor.handle.appWindowMinimize()}>
<IconLabel icon="WindowButtonWinMinimize" />
</LayoutRow>
<LayoutRow class="window-button windows" tooltip={$appWindow.maximized ? "Restore Down" : "Maximize"} on:click={() => editor.handle.appWindowMaximize()}>
<LayoutRow class="window-button windows" tooltipLabel={$appWindow.maximized ? "Restore Down" : "Maximize"} on:click={() => editor.handle.appWindowMaximize()}>
<IconLabel icon={$appWindow.maximized ? "WindowButtonWinRestoreDown" : "WindowButtonWinMaximize"} />
</LayoutRow>
<LayoutRow class="window-button windows" tooltip="Close" on:click={() => editor.handle.appWindowClose()}>
<LayoutRow class="window-button windows" tooltipLabel="Close" on:click={() => editor.handle.appWindowClose()}>
<IconLabel icon="WindowButtonWinClose" />
</LayoutRow>
@@ -38,7 +38,7 @@
export let tabMinWidths = false;
export let tabCloseButtons = false;
export let tabLabels: { name: string; unsaved?: boolean; tooltip?: string }[];
export let tabLabels: { name: string; unsaved?: boolean; tooltipDescription?: string; tooltipShortcut?: string }[];
export let tabActiveIndex: number;
export let panelType: PanelType | undefined = undefined;
export let clickAction: ((index: number) => void) | undefined = undefined;
@@ -118,7 +118,8 @@
<LayoutRow
class="tab"
classes={{ active: tabIndex === tabActiveIndex }}
tooltip={tabLabel.tooltip || undefined}
tooltipLabel={tabLabel.name}
tooltipDescription={tabLabel.tooltipDescription}
on:click={(e) => {
e.stopPropagation();
clickAction?.(tabIndex);
@@ -3,10 +3,8 @@
import type { Editor } from "@graphite/editor";
import type { OpenDocument } from "@graphite/messages";
import type { DialogState } from "@graphite/state-providers/dialog";
import type { PortfolioState } from "@graphite/state-providers/portfolio";
import Dialog from "@graphite/components/floating-menus/Dialog.svelte";
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
import Panel from "@graphite/components/window/workspace/Panel.svelte";
@@ -34,13 +32,12 @@
const unsaved = !doc.details.isSaved;
if (!editor.handle.inDevelopmentMode()) return { name, unsaved };
const tooltip = `Document ID: ${doc.id}`;
return { name, unsaved, tooltip };
const tooltipDescription = `Document ID: ${doc.id}`;
return { name, unsaved, tooltipDescription };
});
const editor = getContext<Editor>("editor");
const portfolio = getContext<PortfolioState>("portfolio");
const dialog = getContext<DialogState>("dialog");
function resizePanel(e: PointerEvent) {
const gutter = (e.target || undefined) as HTMLDivElement | undefined;
@@ -175,9 +172,6 @@
</LayoutCol>
{/if}
</LayoutRow>
{#if $dialog.visible}
<Dialog />
{/if}
</LayoutRow>
<style lang="scss" global>