feat(quick-261022-k9k): admin save shortcuts and Create and new on record forms
- app/hotkeys.ts: dependency-free data-hotkey core (Ctrl/Cmd+S, Ctrl/Cmd+Enter, Ctrl/Cmd+Shift+S, Esc, / and ?), innermost-modal root, capture-phase modifier listener, platform labels and the help-dialog state - Button hotkey prop (data-hotkey, aria-keyshortcuts); shortcuts installed by AppShell - FormView: save keys and tooltips, Create and new on create forms via remountView - phrasebook: form.create_and_new and the shortcuts group (en, pl)
This commit is contained in:
@@ -13,19 +13,27 @@ import { activeEntry } from '../../state/useNavigation'
|
||||
import { settingsEntry, settingsLinkFor, settingsSectionOf } from '../../state/useSettings'
|
||||
import { useSidebar } from '../../state/useSidebar'
|
||||
import { t } from '../../app/i18n'
|
||||
import { closeShortcutHelp, installHotkeys } from '../../app/hotkeys'
|
||||
|
||||
// Navigation shell (design: Navigation shell and Top header; D-06, D-10):
|
||||
// the plugin rail, the section panel (the active plugin's side menu or, on a
|
||||
// settings page, the settings panel of its category; hidden when the
|
||||
// sidebar is collapsed by the viewport or by the admin), and the 64px header
|
||||
// with breadcrumbs and the user menu. The toast queue is mounted here so
|
||||
// toasts survive navigation.
|
||||
// toasts survive navigation. The keyboard shortcuts (app/hotkeys.ts) are
|
||||
// installed here, so only signed-in shell screens answer them.
|
||||
const route = useRoute()
|
||||
const { collapsed, watchViewport } = useSidebar()
|
||||
|
||||
const stopViewport = watchViewport()
|
||||
onBeforeUnmount(stopViewport)
|
||||
|
||||
const uninstallHotkeys = installHotkeys()
|
||||
onBeforeUnmount(() => {
|
||||
uninstallHotkeys()
|
||||
closeShortcutHelp()
|
||||
})
|
||||
|
||||
// "View site" (WinterCMS parity) opens the public site in a new tab: the
|
||||
// admin page's own origin, without the admin path prefix.
|
||||
const siteURL = `${window.location.origin}/`
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, type Component } from 'vue'
|
||||
import { RouterLink, type RouteLocationRaw } from 'vue-router'
|
||||
import { ariaKeyshortcuts } from '../../app/hotkeys'
|
||||
|
||||
// One button for the whole admin (design tokens): primary, outline, ghost and
|
||||
// danger (outline). With `to` it renders a router link styled the same way.
|
||||
// A hotkey prop marks the button for the admin's keyboard shortcuts
|
||||
// (data-hotkey tokens, see app/hotkeys.ts).
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
variant?: 'primary' | 'outline' | 'ghost' | 'danger'
|
||||
@@ -12,8 +15,9 @@ const props = withDefaults(
|
||||
disabled?: boolean
|
||||
icon?: Component
|
||||
to?: RouteLocationRaw
|
||||
hotkey?: string
|
||||
}>(),
|
||||
{ variant: 'outline', size: 'md', type: 'button', disabled: false, icon: undefined, to: undefined },
|
||||
{ variant: 'outline', size: 'md', type: 'button', disabled: false, icon: undefined, to: undefined, hotkey: undefined },
|
||||
)
|
||||
|
||||
const variants: Record<NonNullable<typeof props.variant>, string> = {
|
||||
@@ -23,6 +27,8 @@ const variants: Record<NonNullable<typeof props.variant>, string> = {
|
||||
danger: 'border border-danger bg-surface text-danger hover:bg-danger-soft',
|
||||
}
|
||||
|
||||
const keyshortcuts = computed(() => (props.hotkey ? ariaKeyshortcuts(props.hotkey) : undefined))
|
||||
|
||||
const classes = computed(() => [
|
||||
'inline-flex shrink-0 items-center justify-center gap-2 rounded-control px-4 font-semibold whitespace-nowrap no-underline transition-colors duration-150 ease-out disabled:cursor-not-allowed disabled:opacity-60',
|
||||
props.size === 'sm' ? 'h-[38px]' : 'h-button',
|
||||
@@ -31,11 +37,24 @@ const classes = computed(() => [
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouterLink v-if="to !== undefined" :to="to" :class="classes">
|
||||
<RouterLink
|
||||
v-if="to !== undefined"
|
||||
:to="to"
|
||||
:class="classes"
|
||||
:data-hotkey="hotkey"
|
||||
:aria-keyshortcuts="keyshortcuts"
|
||||
>
|
||||
<component :is="icon" v-if="icon" :size="16" aria-hidden="true" />
|
||||
<slot />
|
||||
</RouterLink>
|
||||
<button v-else :type="type" :disabled="disabled" :class="classes">
|
||||
<button
|
||||
v-else
|
||||
:type="type"
|
||||
:disabled="disabled"
|
||||
:class="classes"
|
||||
:data-hotkey="hotkey"
|
||||
:aria-keyshortcuts="keyshortcuts"
|
||||
>
|
||||
<component :is="icon" v-if="icon" :size="16" aria-hidden="true" />
|
||||
<slot />
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user