diff --git a/packages/frontend/component/src/ui/button/button.tsx b/packages/frontend/component/src/ui/button/button.tsx index 988405c22e..1428c4a4a2 100644 --- a/packages/frontend/component/src/ui/button/button.tsx +++ b/packages/frontend/component/src/ui/button/button.tsx @@ -64,7 +64,8 @@ export interface ButtonProps suffixStyle?: CSSProperties; tooltip?: TooltipProps['content']; - tooltipOptions?: Partial>; + tooltipShortcut?: TooltipProps['shortcut']; + tooltipOptions?: Partial>; } const IconSlot = ({ @@ -113,6 +114,7 @@ export const Button = forwardRef( contentStyle, tooltip, + tooltipShortcut, tooltipOptions, onClick, @@ -129,7 +131,7 @@ export const Button = forwardRef( ); return ( - + + + ))} + + ); +}; + +export const CustomAlign = () => { + const [align, setAlign] = useState('center' as const); + const _ = undefined; + const positions = [ + // [top, left, right, bottom, translateX, translateY] + [0, 0, _, _, _, _], + [0, '50%', _, _, '-50%', _], + [0, _, 0, _, _, _], + ['50%', 0, _, _, _, '-50%'], + ['50%', _, 0, _, _, '-50%'], + [_, 0, _, 0, _, _], + [_, '50%', _, 0, '-50%', _], + [_, _, 0, 0, _, _], + ]; + return ( +
+ +
+ {positions.map(pos => { + const key = pos.join('-'); + const style = { + position: 'absolute', + top: pos[0], + left: pos[1], + right: pos[2], + bottom: pos[3], + transform: `translate(${pos[4] ?? 0}, ${pos[5] ?? 0})`, + } as const; + return ( + + + + ); + })} +
+
+ ); +}; + export const WithCustomContent: StoryFn = args => ( // [T] + * // [⌘ + K] + * // [⌘] [K] + * // [⌘] [K] or [Ctrl] [K] + * ``` + * + * Mapping: + * | Shortcut | macOS | Windows | + * |----------|-------|---------| + * | `$mod` | `⌘` | `Ctrl` | + * | `$alt` | `⌥` | `Alt` | + * | `$shift` | `⇧` | `Shift` | + */ + shortcut?: string | string[]; side?: TooltipContentProps['side']; align?: TooltipContentProps['align']; @@ -21,11 +42,32 @@ export interface TooltipProps { options?: Omit; } +const TooltipShortcut = ({ shortcut }: { shortcut: string | string[] }) => { + const commands = (Array.isArray(shortcut) ? shortcut : [shortcut]) + .map(cmd => cmd.trim()) + .map(cmd => getCommand(cmd)); + + return ( +
+ {commands.map((cmd, index) => ( +
+ {cmd} +
+ ))} +
+ ); +}; + export const Tooltip = ({ children, content, side = 'top', align = 'center', + shortcut, options, rootOptions, portalOptions, @@ -34,6 +76,7 @@ export const Tooltip = ({ return children; } const { className, ...contentOptions } = options || {}; + const { style: contentStyle, ...restContentOptions } = contentOptions; return ( @@ -45,15 +88,31 @@ export const Tooltip = ({ side={side} align={align} sideOffset={5} - style={{ zIndex: 'var(--affine-z-index-popover)' }} - {...contentOptions} + style={{ zIndex: cssVar('zIndexPopover'), ...contentStyle }} + {...restContentOptions} > - {content} - + {shortcut ? ( +
+
{content}
+ +
+ ) : ( + content + )} + + + + +
diff --git a/packages/frontend/component/src/utils/keyboard-mapping.ts b/packages/frontend/component/src/utils/keyboard-mapping.ts new file mode 100644 index 0000000000..70947c335d --- /dev/null +++ b/packages/frontend/component/src/utils/keyboard-mapping.ts @@ -0,0 +1,10 @@ +import { isMacOS } from './platform'; + +const macOS = isMacOS(); + +export const getCommand = (cmd: '$mod' | '$shift' | '$alt' | string) => { + if (cmd === '$mod') return macOS ? '⌘' : 'Ctrl'; + if (cmd === '$alt') return macOS ? '⌥' : 'Alt'; + if (cmd === '$shift') return macOS ? '⇧' : 'Shift'; + return cmd; +}; diff --git a/packages/frontend/component/src/utils/platform.ts b/packages/frontend/component/src/utils/platform.ts new file mode 100644 index 0000000000..4f5aab510a --- /dev/null +++ b/packages/frontend/component/src/utils/platform.ts @@ -0,0 +1,4 @@ +export function isMacOS() { + if (typeof navigator === 'undefined') return false; + return navigator.userAgent.indexOf('Mac') !== -1; +} diff --git a/packages/frontend/core/src/components/app-sidebar/sidebar-header/sidebar-switch.tsx b/packages/frontend/core/src/components/app-sidebar/sidebar-header/sidebar-switch.tsx index 6424dad245..0fbdb44a17 100644 --- a/packages/frontend/core/src/components/app-sidebar/sidebar-header/sidebar-switch.tsx +++ b/packages/frontend/core/src/components/app-sidebar/sidebar-header/sidebar-switch.tsx @@ -18,9 +18,6 @@ export const SidebarSwitch = ({ const tooltipContent = open ? t['com.affine.sidebarSwitch.collapse']() : t['com.affine.sidebarSwitch.expand'](); - // TODO(@CatsJuice): Tooltip shortcut style - const collapseKeyboardShortcuts = - environment.isBrowser && environment.isMacOs ? ' ⌘+/' : ' Ctrl+/'; return (
{ - const t = useI18n(); - return ( - <> - {t['Switch']()} - - {!environment.isServer && environment.isMacOs ? '⌥ + S' : 'Alt + S'} - - - ); -}; + export const EditorModeSwitch = ({ style, docCollection, @@ -106,7 +96,9 @@ export const EditorModeSwitch = ({ return ( } + content={t['Switch']()} + shortcut={['$alt', 'S']} + side="bottom" options={{ hidden: isPublic || trash, }} diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/style.ts b/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/style.ts index 03249c43f4..cb1645684c 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/style.ts +++ b/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/style.ts @@ -1,5 +1,6 @@ import { displayFlex, styled } from '@affine/component'; +// TODO(@CatsJuice): refactor this component export const StyledEditorModeSwitch = styled('div')<{ switchLeft: boolean; showAlone?: boolean; @@ -59,14 +60,3 @@ export const StyledSwitchItem = styled('button')<{ }, }; }); - -export const StyledKeyboardItem = styled('span')(() => { - return { - marginLeft: '10px', - fontSize: 'var(--affine-font-xs)', - paddingLeft: '5px', - paddingRight: '5px', - backgroundColor: 'var(--affine-white-10)', - borderRadius: '4px', - }; -}); diff --git a/packages/frontend/core/src/modules/app-tabs-header/views/app-tabs-header.tsx b/packages/frontend/core/src/modules/app-tabs-header/views/app-tabs-header.tsx index cdbd0f1194..81bd5f6289 100644 --- a/packages/frontend/core/src/modules/app-tabs-header/views/app-tabs-header.tsx +++ b/packages/frontend/core/src/modules/app-tabs-header/views/app-tabs-header.tsx @@ -16,6 +16,7 @@ import { WindowsAppControls } from '@affine/core/components/pure/header/windows- import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import type { AffineDNDData } from '@affine/core/types/dnd'; import { apis, events } from '@affine/electron-api'; +import { useI18n } from '@affine/i18n'; import { CloseIcon, PlusIcon, RightSidebarIcon } from '@blocksuite/icons/rc'; import { useLiveData, @@ -223,6 +224,7 @@ export const AppTabsHeader = ({ className?: string; left?: ReactNode; }) => { + const t = useI18n(); const sidebarWidth = useAtomValue(appSidebarWidthAtom); const sidebarOpen = useAtomValue(appSidebarOpenAtom); const sidebarFloating = useAtomValue(appSidebarFloatingAtom); @@ -365,6 +367,8 @@ export const AppTabsHeader = ({ } diff --git a/packages/frontend/core/src/modules/app-tabs-header/views/styles.css.ts b/packages/frontend/core/src/modules/app-tabs-header/views/styles.css.ts index 93a42e8d40..fa5e41f2bb 100644 --- a/packages/frontend/core/src/modules/app-tabs-header/views/styles.css.ts +++ b/packages/frontend/core/src/modules/app-tabs-header/views/styles.css.ts @@ -36,7 +36,7 @@ export const tabs = style({ display: 'flex', flexDirection: 'row', alignItems: 'center', - padding: '0 8px', + paddingLeft: 8, overflow: 'clip', height: '100%', selectors: { diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index a21ba485ba..2c09a7298e 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -1417,5 +1417,6 @@ "com.affine.ai.template-insert.failed": "Failed to insert template, please try again.", "com.affine.selector-doc.search-placeholder": "Search docs...", "com.affine.selector-collection.search.placeholder": "Search collections...", - "com.affine.selector-tag.search.placeholder": "Search tags..." + "com.affine.selector-tag.search.placeholder": "Search tags...", + "com.affine.multi-tab.new-tab": "New Tab" }