Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce styled tooltips. Refactor to be cross-platform #151

Merged
merged 2 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-tooltip": "^1.1.2",
"@srcbook/shared": "workspace:^",
"@uiw/codemirror-themes": "^4.22.2",
"@uiw/react-codemirror": "^4.22.0",
Expand Down
44 changes: 31 additions & 13 deletions packages/web/src/components/cells/code.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { useEffect, useRef, useState } from 'react';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import Shortcut from '@/components/keyboard-shortcut';
import { useNavigate } from 'react-router-dom';
import { useHotkeys } from 'react-hotkeys-hook';
import CodeMirror, { keymap, Prec } from '@uiw/react-codemirror';
Expand Down Expand Up @@ -201,15 +203,22 @@ export default function CodeCell(props: {
cell.status === 'running' ? 'opacity-100' : '',
)}
>
<Button
variant="icon"
size="icon"
disabled={promptMode !== 'off'}
onClick={() => setPromptMode('idle')}
tabIndex={1}
>
<Sparkles size={16} />
</Button>
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Button
variant="icon"
size="icon"
disabled={promptMode !== 'off'}
onClick={() => setPromptMode('idle')}
tabIndex={1}
>
<Sparkles size={16} />
</Button>
</TooltipTrigger>
<TooltipContent>Edit cell using AI</TooltipContent>
</Tooltip>
</TooltipProvider>
{promptMode === 'idle' && (
<Button
variant="default"
Expand All @@ -233,10 +242,19 @@ export default function CodeCell(props: {
</Button>
)}
{cell.status === 'idle' && (
<Button size="default-with-icon" onClick={runCell} tabIndex={1}>
<Play size={16} />
Run
</Button>
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Button size="default-with-icon" onClick={runCell} tabIndex={1}>
<Play size={16} />
Run
</Button>
</TooltipTrigger>
<TooltipContent>
<Shortcut keys={['mod', 'enter']} /> to run cell
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</>
)}
Expand Down
65 changes: 65 additions & 0 deletions packages/web/src/components/keyboard-shortcut.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react';

type Platform = 'mac' | 'windows' | 'linux' | 'other';
type KeyType = 'mod' | 'alt';

const getPlatform = () => {
const platform = navigator.platform.toLowerCase();
const userAgent = navigator.userAgent.toLowerCase();

if (platform.includes('mac') || userAgent.includes('mac')) {
return 'mac';
} else if (platform.includes('win') || userAgent.includes('win')) {
return 'windows';
} else if (platform.includes('linux') || userAgent.includes('linux')) {
return 'linux';
} else {
return 'other';
}
};

const keyMappings: Record<KeyType, Record<Platform, string>> = {
mod: {
mac: '⌘',
windows: 'Ctrl',
linux: 'Ctrl',
other: 'Ctrl',
},
alt: {
mac: '⌥',
windows: 'Alt',
linux: 'Alt',
other: 'Alt',
},
};

const getPlatformSpecificKey = (keyType: KeyType): string => {
const platform = getPlatform();
return keyMappings[keyType][platform];
};

export default function Shortcut({ keys }: { keys: string[] }) {
// Replace keys that are in the keyMappings with the platform specific key
keys = keys.map((key) => {
if (key === 'mod') {
return getPlatformSpecificKey('mod');
} else if (key === 'alt') {
return getPlatformSpecificKey('alt');
} else {
return key;
}
});
return (
<>
{keys.map((key) => {
return (
<React.Fragment key={key}>
<span className="font-mono bg-background text-foreground border py-[1px] px-1.5 rounded-sm drop-shadow-key mx-0.5">
{key}
</span>
</React.Fragment>
);
})}
</>
);
}
31 changes: 11 additions & 20 deletions packages/web/src/components/keyboard-shortcuts-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import Shortcut from '@/components/keyboard-shortcut';
import {
Dialog,
DialogContent,
Expand All @@ -7,20 +7,11 @@ import {
DialogTitle,
} from '@/components/ui/dialog';

function ShortCut({ keys, description }: { keys: string[]; description: string }) {
function ShortcutRow({ keys, description }: { keys: string[]; description: string }) {
return (
<div className="grid grid-cols-4 w-full py-2">
<p className="col-span-1">
{keys.map((key, i) => {
return (
<React.Fragment key={key}>
<span className="font-mono bg-primary text-primary-foreground py-[1px] px-1.5 rounded shadow-md">
{key}
</span>
{i < keys.length - 1 && <span className="mx-1.5">+</span>}
</React.Fragment>
);
})}
<Shortcut keys={keys} />
</p>
<p className="col-span-3">{description}</p>
</div>
Expand All @@ -42,16 +33,16 @@ export default function KeyboardShortcutsDialog({
<DialogDescription asChild>
<div>
<h5 className="font-semibold pt-4 pb-2">Global</h5>
<ShortCut keys={['?']} description="show this dialog" />
<ShortCut keys={['', 'i']} description="open NPM package install modal" />
<ShortcutRow keys={['?']} description="show this dialog" />
<ShortcutRow keys={['mod', 'i']} description="open NPM package install modal" />
<h5 className="font-semibold pt-6 pb-2">Markdown edit</h5>
<ShortCut keys={['esc']} description="switch back to preview mode" />
<ShortCut keys={['', '↵']} description="switch back to preview mode" />
<ShortcutRow keys={['esc']} description="switch back to preview mode" />
<ShortcutRow keys={['mod', '↵']} description="switch back to preview mode" />
<h5 className="font-semibold pt-6 pb-2">Code cell edit</h5>
<ShortCut keys={['', '↵']} description="run cell" />
<ShortCut keys={['', '/']} description="toggle lines comment" />
<ShortCut keys={['', '↑']} description="move lines up" />
<ShortCut keys={['', '↓']} description="move lines down" />
<ShortcutRow keys={['mod', '↵']} description="run cell" />
<ShortcutRow keys={['mod', '/']} description="toggle lines comment" />
<ShortcutRow keys={['alt', '↑']} description="move lines up" />
<ShortcutRow keys={['alt', '↓']} description="move lines down" />
</div>
</DialogDescription>
</DialogHeader>
Expand Down
29 changes: 29 additions & 0 deletions packages/web/src/components/ui/tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as React from 'react';
import * as TooltipPrimitive from '@radix-ui/react-tooltip';

import { cn } from '@/lib/utils';

const TooltipProvider = TooltipPrimitive.Provider;

const Tooltip = TooltipPrimitive.Root;

const TooltipTrigger = TooltipPrimitive.Trigger;

const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
side="bottom"
className={cn(
'z-50 overflow-hidden rounded-sm bg-muted px-1.5 py-1.5 text-xs font-medium border animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className,
)}
{...props}
/>
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;

export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
3 changes: 3 additions & 0 deletions packages/web/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ module.exports = {
'blue-80': 'hsl(var(--sb-blue-80))',
},
},
dropShadow: {
key: '0 1px 0 hsl(var(--border))', // for keyboard shortcuts. border-border 1px straight down
},
borderRadius: {
sm: '0.1875rem', // 3px
mid: '0.375rem', // 6px
Expand Down
Loading
Loading