Skip to content

Commit

Permalink
feat(Kbd): special keys for macOS and other systems (#2494)
Browse files Browse the repository at this point in the history
Co-authored-by: Benjamin Canac <[email protected]>
  • Loading branch information
hywax and benjamincanac authored Oct 31, 2024
1 parent f26f6c8 commit 332c6c0
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 268 deletions.
3 changes: 2 additions & 1 deletion docs/content/3.components/kbd.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ props:
---
::

You can pass special keys to the `value` prop that goes through the [`useKbd`](https://github.com/nuxt/ui/blob/v3/src/runtime/composables/useKbd.ts) composable. For example, the `meta` key displays as `` on macOS and `Ctrl` on other platforms.
You can pass special keys to the `value` prop that goes through the [`useKbd`](https://github.com/nuxt/ui/blob/v3/src/runtime/composables/useKbd.ts) composable. For example, the `meta` key displays as `` on macOS and `` on other platforms.

::component-code
---
Expand All @@ -40,6 +40,7 @@ props:
items:
value:
- meta
- win
- command
- shift
- ctrl
Expand Down
8 changes: 0 additions & 8 deletions src/runtime/composables/defineShortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import { ref, computed, toValue } from 'vue'
import type { MaybeRef } from 'vue'
import { useEventListener, useActiveElement, useDebounceFn } from '@vueuse/core'
import { useKbd } from '../composables/useKbd'

type Handler = (e?: any) => void

Expand Down Expand Up @@ -67,7 +66,6 @@ export function defineShortcuts(config: MaybeRef<ShortcutsConfig>, options: Shor
}
const debouncedClearChainedInput = useDebounceFn(clearChainedInput, options.chainDelay ?? 800)

const { macOS } = useKbd()
const activeElement = useActiveElement()

const onKeyDown = (e: KeyboardEvent) => {
Expand Down Expand Up @@ -180,12 +178,6 @@ export function defineShortcuts(config: MaybeRef<ShortcutsConfig>, options: Shor
}
shortcut.chained = chained

// Convert Meta to Ctrl for non-MacOS
if (!macOS.value && shortcut.metaKey && !shortcut.ctrlKey) {
shortcut.metaKey = false
shortcut.ctrlKey = true
}

// Retrieve handler function
if (typeof shortcutConfig === 'function') {
shortcut.handler = shortcutConfig
Expand Down
32 changes: 27 additions & 5 deletions src/runtime/composables/useKbd.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { createSharedComposable } from '@vueuse/core'
import { ref, computed, onMounted } from 'vue'

type KbdSpecificMap = {
meta: string
alt: string
ctrl: string
}

export const kbdKeysMap = {
meta: '',
ctrl: '',
alt: '',
win: '⊞',
command: '⌘',
shift: '⇧',
ctrl: '⌃',
option: '⌥',
alt: '⎇',
enter: '↵',
delete: '⌦',
backspace: '⌫',
Expand All @@ -24,23 +31,38 @@ export const kbdKeysMap = {
end: '↘'
}

export const kbdKeysSpecificMap: Record<'macOS' | 'other', KbdSpecificMap> = {
macOS: {
meta: kbdKeysMap.command,
alt: kbdKeysMap.option,
ctrl: '⌃'
},
other: {
meta: kbdKeysMap.win,
alt: 'alt',
ctrl: 'ctrl'
}
}

export type KbdKey = keyof typeof kbdKeysMap
export type KbdKeySpecific = keyof KbdSpecificMap

const _useKbd = () => {
const macOS = computed(() => import.meta.client && navigator && navigator.userAgent && navigator.userAgent.match(/Macintosh;/))

const metaSymbol = ref(' ')

onMounted(() => {
metaSymbol.value = macOS.value ? kbdKeysMap.command : kbdKeysMap.ctrl
metaSymbol.value = getKbdKey('meta')!
})

function getKbdKey(value?: KbdKey | string) {
if (!value) {
return
}
if (value === 'meta') {
return metaSymbol.value

if (['meta', 'alt', 'ctrl'].includes(value)) {
return kbdKeysSpecificMap[macOS.value ? 'macOS' : 'other'][value as KbdKeySpecific]
}

return kbdKeysMap[value as KbdKey] || value.toUpperCase()
Expand Down
162 changes: 81 additions & 81 deletions test/components/__snapshots__/CommandPalette-vue.spec.ts.snap

Large diffs are not rendered by default.

162 changes: 81 additions & 81 deletions test/components/__snapshots__/CommandPalette.spec.ts.snap

Large diffs are not rendered by default.

90 changes: 45 additions & 45 deletions test/components/__snapshots__/DropdownMenu-vue.spec.ts.snap

Large diffs are not rendered by default.

90 changes: 45 additions & 45 deletions test/components/__snapshots__/DropdownMenu.spec.ts.snap

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/components/__snapshots__/Tooltip-vue.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ exports[`Tooltip > renders with kbds correctly 1`] = `
<div data-radix-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;">
<div data-state="instant-open" style="--radix-tooltip-content-transform-origin: var(--radix-popper-transform-origin); --radix-tooltip-content-available-width: var(--radix-popper-available-width); --radix-tooltip-content-available-height: var(--radix-popper-available-height); --radix-tooltip-trigger-width: var(--radix-popper-anchor-width); --radix-tooltip-trigger-height: var(--radix-popper-anchor-height); animation: none;" data-dismissable-layer="" class="flex items-center gap-1 bg-[var(--ui-bg)] text-[var(--ui-text-highlighted)] shadow rounded-[var(--ui-radius)] ring ring-[var(--ui-border)] h-6 px-2 py-1 text-xs select-none data-[state=delayed-open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in]" data-side="bottom" data-align="center"><span class="truncate">Tooltip</span><span class="hidden lg:inline-flex items-center shrink-0 gap-0.5 before:content-['·'] before:me-0.5"><kbd class="inline-flex items-center justify-center px-1 rounded-[var(--ui-radius)] font-medium font-sans bg-[var(--ui-bg)] text-[var(--ui-text-highlighted)] ring ring-inset ring-[var(--ui-border-accented)] h-4 min-w-[16px] text-[10px]">⌃</kbd><kbd class="inline-flex items-center justify-center px-1 rounded-[var(--ui-radius)] font-medium font-sans bg-[var(--ui-bg)] text-[var(--ui-text-highlighted)] ring ring-inset ring-[var(--ui-border-accented)] h-4 min-w-[16px] text-[10px]">K</kbd></span>
<div data-state="instant-open" style="--radix-tooltip-content-transform-origin: var(--radix-popper-transform-origin); --radix-tooltip-content-available-width: var(--radix-popper-available-width); --radix-tooltip-content-available-height: var(--radix-popper-available-height); --radix-tooltip-trigger-width: var(--radix-popper-anchor-width); --radix-tooltip-trigger-height: var(--radix-popper-anchor-height); animation: none;" data-dismissable-layer="" class="flex items-center gap-1 bg-[var(--ui-bg)] text-[var(--ui-text-highlighted)] shadow rounded-[var(--ui-radius)] ring ring-[var(--ui-border)] h-6 px-2 py-1 text-xs select-none data-[state=delayed-open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in]" data-side="bottom" data-align="center"><span class="truncate">Tooltip</span><span class="hidden lg:inline-flex items-center shrink-0 gap-0.5 before:content-['·'] before:me-0.5"><kbd class="inline-flex items-center justify-center px-1 rounded-[var(--ui-radius)] font-medium font-sans bg-[var(--ui-bg)] text-[var(--ui-text-highlighted)] ring ring-inset ring-[var(--ui-border-accented)] h-4 min-w-[16px] text-[10px]">⊞</kbd><kbd class="inline-flex items-center justify-center px-1 rounded-[var(--ui-radius)] font-medium font-sans bg-[var(--ui-bg)] text-[var(--ui-text-highlighted)] ring ring-inset ring-[var(--ui-border-accented)] h-4 min-w-[16px] text-[10px]">K</kbd></span>
<!--v-if--><span style="position: absolute; border: 0px; width: 1px; display: inline-block; height: 1px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; word-wrap: normal;" id="" role="tooltip">Tooltip</span>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion test/components/__snapshots__/Tooltip.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ exports[`Tooltip > renders with kbds correctly 1`] = `
<div data-radix-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;">
<div data-state="instant-open" style="--radix-tooltip-content-transform-origin: var(--radix-popper-transform-origin); --radix-tooltip-content-available-width: var(--radix-popper-available-width); --radix-tooltip-content-available-height: var(--radix-popper-available-height); --radix-tooltip-trigger-width: var(--radix-popper-anchor-width); --radix-tooltip-trigger-height: var(--radix-popper-anchor-height); animation: none;" data-dismissable-layer="" class="flex items-center gap-1 bg-[var(--ui-bg)] text-[var(--ui-text-highlighted)] shadow rounded-[var(--ui-radius)] ring ring-[var(--ui-border)] h-6 px-2 py-1 text-xs select-none data-[state=delayed-open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in]" data-side="bottom" data-align="center"><span class="truncate">Tooltip</span><span class="hidden lg:inline-flex items-center shrink-0 gap-0.5 before:content-['·'] before:me-0.5"><kbd class="inline-flex items-center justify-center px-1 rounded-[var(--ui-radius)] font-medium font-sans bg-[var(--ui-bg)] text-[var(--ui-text-highlighted)] ring ring-inset ring-[var(--ui-border-accented)] h-4 min-w-[16px] text-[10px]">⌃</kbd><kbd class="inline-flex items-center justify-center px-1 rounded-[var(--ui-radius)] font-medium font-sans bg-[var(--ui-bg)] text-[var(--ui-text-highlighted)] ring ring-inset ring-[var(--ui-border-accented)] h-4 min-w-[16px] text-[10px]">K</kbd></span>
<div data-state="instant-open" style="--radix-tooltip-content-transform-origin: var(--radix-popper-transform-origin); --radix-tooltip-content-available-width: var(--radix-popper-available-width); --radix-tooltip-content-available-height: var(--radix-popper-available-height); --radix-tooltip-trigger-width: var(--radix-popper-anchor-width); --radix-tooltip-trigger-height: var(--radix-popper-anchor-height); animation: none;" data-dismissable-layer="" class="flex items-center gap-1 bg-[var(--ui-bg)] text-[var(--ui-text-highlighted)] shadow rounded-[var(--ui-radius)] ring ring-[var(--ui-border)] h-6 px-2 py-1 text-xs select-none data-[state=delayed-open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in]" data-side="bottom" data-align="center"><span class="truncate">Tooltip</span><span class="hidden lg:inline-flex items-center shrink-0 gap-0.5 before:content-['·'] before:me-0.5"><kbd class="inline-flex items-center justify-center px-1 rounded-[var(--ui-radius)] font-medium font-sans bg-[var(--ui-bg)] text-[var(--ui-text-highlighted)] ring ring-inset ring-[var(--ui-border-accented)] h-4 min-w-[16px] text-[10px]">⊞</kbd><kbd class="inline-flex items-center justify-center px-1 rounded-[var(--ui-radius)] font-medium font-sans bg-[var(--ui-bg)] text-[var(--ui-text-highlighted)] ring ring-inset ring-[var(--ui-border-accented)] h-4 min-w-[16px] text-[10px]">K</kbd></span>
<!--v-if--><span style="position: absolute; border: 0px; width: 1px; display: inline-block; height: 1px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; word-wrap: normal;" id="" role="tooltip">Tooltip</span>
</div>
</div>
Expand Down

0 comments on commit 332c6c0

Please sign in to comment.