refactor(ui): add button shortcut component

pull/21329/head
Shoubhit Dash 2026-04-07 19:34:05 +05:30
parent 30c4ccbfee
commit f714300e9a
4 changed files with 156 additions and 0 deletions

View File

@ -0,0 +1,36 @@
[data-component="button"][data-button-shortcut] {
[data-slot="button-shortcut-label"] {
min-width: 0;
}
[data-slot="button-shortcut-key"] {
display: flex;
align-items: center;
flex-shrink: 0;
}
[data-slot="button-shortcut-key"] [data-component="keybind"] {
box-shadow: none;
background: var(--surface-raised-base);
color: var(--text-weak);
font-family: var(--font-family-sans);
font-weight: var(--font-weight-regular);
}
&[data-size="small"] [data-slot="button-shortcut-key"] [data-component="keybind"],
&[data-size="normal"] [data-slot="button-shortcut-key"] [data-component="keybind"] {
height: 18px;
padding: 0 4px;
border-radius: 3px;
font-size: var(--font-size-small);
line-height: 18px;
}
&[data-size="large"] [data-slot="button-shortcut-key"] [data-component="keybind"] {
height: 20px;
padding: 0 6px;
border-radius: 4px;
font-size: var(--font-size-small);
line-height: 20px;
}
}

View File

@ -0,0 +1,86 @@
// @ts-nocheck
import { ButtonShortcut } from "./button-shortcut"
const docs = `### Overview
Button with a trailing shortcut keycap.
Use this when the action label and shortcut should be taught together at the control level.
### API
- Inherits Button props.
- \`shortcut\`: visible keycap text.
- \`shortcutAria\`: semantic shortcut string for \`aria-keyshortcuts\`.
- \`shortcutClass\`: optional class override for the keycap.
### Variants and states
- Uses the same \`variant\` and \`size\` options as \`Button\`.
- Supports disabled state.
### Accessibility
- Keep the visible shortcut concise.
- Use \`shortcutAria\` for the canonical key sequence when it differs from the visible label.
### Theming/tokens
- Extends \`Button\` and composes \`Keybind\`.
`
export default {
title: "UI/ButtonShortcut",
id: "components-button-shortcut",
component: ButtonShortcut,
tags: ["autodocs"],
parameters: {
docs: {
description: {
component: docs,
},
},
},
args: {
children: "Cancel",
shortcut: "Esc",
shortcutAria: "Escape",
variant: "ghost",
size: "small",
},
argTypes: {
variant: {
control: "select",
options: ["primary", "secondary", "ghost"],
},
size: {
control: "select",
options: ["small", "normal", "large"],
},
},
}
export const Basic = {}
export const Sizes = {
render: () => (
<div style={{ display: "flex", gap: "12px", "align-items": "center" }}>
<ButtonShortcut size="small" variant="ghost" shortcut="Esc" shortcutAria="Escape">
Cancel
</ButtonShortcut>
<ButtonShortcut size="normal" variant="secondary" shortcut="Tab" shortcutAria="Tab">
Focus
</ButtonShortcut>
<ButtonShortcut size="large" variant="primary" shortcut="Enter" shortcutAria="Enter">
Submit
</ButtonShortcut>
</div>
),
}
export const Shell = {
args: {
children: "Cancel",
shortcut: "Esc",
shortcutAria: "Escape",
variant: "ghost",
size: "small",
class: "h-6 gap-2 rounded-[6px] border-none px-0 py-0 pl-3 pr-0.75 text-13-medium text-text-base shadow-none",
},
}

View File

@ -0,0 +1,33 @@
import { type ComponentProps, Show, splitProps } from "solid-js"
import { Button, type ButtonProps } from "./button"
import { Keybind } from "./keybind"
export interface ButtonShortcutProps extends ButtonProps {
shortcut?: string
shortcutAria?: string
shortcutClass?: string
shortcutClassList?: ComponentProps<"span">["classList"]
}
export function ButtonShortcut(props: ButtonShortcutProps) {
const [split, rest] = splitProps(props, [
"children",
"shortcut",
"shortcutAria",
"shortcutClass",
"shortcutClassList",
])
return (
<Button {...rest} aria-keyshortcuts={split.shortcutAria} data-button-shortcut={split.shortcut ? "true" : undefined}>
<span data-slot="button-shortcut-label">{split.children}</span>
<Show when={split.shortcut}>
<span data-slot="button-shortcut-key">
<Keybind class={split.shortcutClass} classList={split.shortcutClassList}>
{split.shortcut}
</Keybind>
</span>
</Show>
</Button>
)
}

View File

@ -12,6 +12,7 @@
@import "../components/avatar.css" layer(components);
@import "../components/basic-tool.css" layer(components);
@import "../components/button.css" layer(components);
@import "../components/button-shortcut.css" layer(components);
@import "../components/card.css" layer(components);
@import "../components/tool-error-card.css" layer(components);
@import "../components/checkbox.css" layer(components);