add codeblock tooltips and fix spacing

This commit is contained in:
Bilal Godil 2025-10-07 16:13:33 -07:00
parent 017b43fe9b
commit 5547ddb9c3
2 changed files with 71 additions and 55 deletions

View File

@ -497,12 +497,14 @@ function ProductItemRow({
if (isEditing) {
return (
<div className="flex flex-col gap-1 mb-4">
<div className="flex flex-row">
<div className="flex flex-col gap-2 mb-4">
<div className="flex w-full items-center justify-between gap-2">
<Popover open={itemSelectOpen} onOpenChange={setItemSelectOpen}>
<PopoverTrigger>
<div className="text-sm px-2 py-0.5 rounded bg-muted hover:bg-muted/70 cursor-pointer select-none flex items-center gap-1">
{itemId}
<span className="overflow-x-auto max-w-24">
{itemDisplayName}
</span>
<ChevronsUpDown className="h-4 w-4" />
</div>
</PopoverTrigger>
@ -554,16 +556,54 @@ function ProductItemRow({
</div>
</PopoverContent>
</Popover>
<Input
className="ml-auto w-20 text-right tabular-nums mr-2"
inputMode="numeric"
value={quantity}
onChange={(e) => {
const v = e.target.value;
if (v === '' || /^\d*$/.test(v)) setQuantity(v);
if (!readOnly && (v === '' || /^\d*$/.test(v))) updateParent(v);
}}
/>
<div className="flex items-center gap-2">
<Input
className="w-24 text-right tabular-nums"
inputMode="numeric"
value={quantity}
onChange={(e) => {
const v = e.target.value;
if (v === '' || /^\d*$/.test(v)) setQuantity(v);
if (!readOnly && (v === '' || /^\d*$/.test(v))) updateParent(v);
}}
/>
{onRemove && (
<button className="text-muted-foreground hover:text-foreground" onClick={onRemove} aria-label="Remove item">
<X className="h-4 w-4" />
</button>
)}
</div>
</div>
<div className="flex w-full items-center justify-between gap-2">
<div className="flex items-center gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<div className="text-xs px-2 py-0.5 w-fit rounded bg-muted text-muted-foreground cursor-pointer select-none flex items-center gap-1">
{item.expires === 'never' ? 'Never expires' : `${EXPIRES_OPTIONS.find(o => o.value === item.expires)?.label.toLowerCase()}`}
<ChevronsUpDown className="h-4 w-4" />
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="p-2">
<div className="flex flex-col gap-2">
{EXPIRES_OPTIONS.map((option) => (
<DropdownMenuItem key={option.value}>
<Button
key={option.value}
variant="ghost"
size="sm"
className="flex flex-col items-start"
onClick={() => {
onSave(itemId, { ...item, expires: option.value });
}}>
{option.label}
<span className="text-xs text-muted-foreground">{option.description}</span>
</Button>
</DropdownMenuItem>
))}
</div>
</DropdownMenuContent>
</DropdownMenu>
</div>
<IntervalPopover
readOnly={readOnly}
intervalText={repeatText}
@ -583,41 +623,6 @@ function ProductItemRow({
onSave(itemId, updated);
}}
/>
{onRemove && (
<button className="ml-auto" onClick={onRemove} aria-label="Remove item">
<X className="h-4 w-4" />
</button>
)}
</div>
<div className="flex flex-row items-center gap-2">
<span className="text-xs text-muted-foreground">Expires:</span>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<div className="text-xs px-2 py-0.5 w-fit rounded bg-muted text-muted-foreground cursor-pointer select-none flex items-center gap-1">
{item.expires === 'never' ? 'Never expires' : `${EXPIRES_OPTIONS.find(o => o.value === item.expires)?.label.toLowerCase()}`}
<ChevronsUpDown className="h-4 w-4" />
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="p-2">
<div className="flex flex-col gap-2">
{EXPIRES_OPTIONS.map((option) => (
<DropdownMenuItem key={option.value}>
<Button
key={option.value}
variant="ghost"
size="sm"
className="flex flex-col items-start"
onClick={() => {
onSave(itemId, { ...item, expires: option.value });
}}>
{option.label}
<span className="text-xs text-muted-foreground">{option.description}</span>
</Button>
</DropdownMenuItem>
))}
</div>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
);
@ -633,7 +638,7 @@ function ProductItemRow({
<ChevronDown className={cn("h-4 w-4 transition-transform", isOpen ? "rotate-0" : "-rotate-90")} />
</button>
</CollapsibleTrigger >
<div className="text-sm">{itemId}</div>
<div className="text-sm">{itemDisplayName}</div>
<div className="ml-auto w-16 text-right text-sm text-muted-foreground tabular-nums">{prettyPrintWithMagnitudes(item.quantity)}</div>
<div className="ml-2">
<div className="text-xs px-2 py-0.5 rounded bg-muted text-muted-foreground">{shortRepeatText}</div>
@ -667,6 +672,7 @@ function ProductItemRow({
title="Example"
icon="code"
compact
tooltip="Retrieves this item for the active customer and reads the current quantity they hold."
/>
</div>
</div>
@ -966,7 +972,7 @@ function ProductCard({ id, activeType, product, allProducts, existingItems, onSa
<div className="space-y-2">
{itemsList.map(([itemId, item]) => {
const itemMeta = existingItems.find(i => i.id === itemId);
const itemLabel = itemMeta ? (itemMeta.displayName || itemMeta.id) : 'Select item';
const itemLabel = itemMeta ? itemMeta.id : 'Select item';
return (
<ProductItemRow
key={itemId}
@ -1072,6 +1078,7 @@ function ProductCard({ id, activeType, product, allProducts, existingItems, onSa
title="Checkout"
icon="code"
compact
tooltip="Creates a checkout URL for this product and opens it so the customer can finish their purchase."
/>
</div>
)}

View File

@ -1,7 +1,7 @@
'use client';
import { useThemeWatcher } from '@/lib/theme';
import { CopyButton } from "@stackframe/stack-ui";
import { CopyButton, SimpleTooltip } from "@stackframe/stack-ui";
import { Code, Terminal } from "lucide-react";
import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter';
import bash from 'react-syntax-highlighter/dist/esm/languages/prism/bash';
@ -9,21 +9,25 @@ import python from 'react-syntax-highlighter/dist/esm/languages/prism/python';
import tsx from 'react-syntax-highlighter/dist/esm/languages/prism/tsx';
import typescript from 'react-syntax-highlighter/dist/esm/languages/prism/typescript';
import { dark, prism } from 'react-syntax-highlighter/dist/esm/styles/prism';
import type { ReactNode } from 'react';
import { cn } from '@/lib/utils';
Object.entries({ tsx, bash, typescript, python }).forEach(([key, value]) => {
SyntaxHighlighter.registerLanguage(key, value);
});
export function CodeBlock(props: {
type CodeBlockProps = {
language: string,
content: string,
customRender?: React.ReactNode,
customRender?: ReactNode,
title: string,
icon: 'terminal' | 'code',
maxHeight?: number,
compact?: boolean,
}) {
tooltip?: ReactNode,
};
export function CodeBlock(props: CodeBlockProps) {
const { theme, mounted } = useThemeWatcher();
let icon = null;
@ -45,7 +49,12 @@ export function CodeBlock(props: {
{icon}
{props.title}
</h5>
<CopyButton content={props.content} />
<div className="flex items-center gap-2">
{props.tooltip && (
<SimpleTooltip type="info" tooltip={props.tooltip} />
)}
<CopyButton content={props.content} />
</div>
</div>
<div>
{props.customRender ?? <SyntaxHighlighter