feat(10.1-02): run registered toolbar actions and scope plugin CSS per controller
- ListToolbar renders server-filtered actions after delete as outline buttons, busy during their POST
- ListView posts {} to toolbar/{action}, toasts, reloads the list and refetches the header partial
- Lists load controller assets too, so other controllers' stylesheet links are disabled on every open
- Vite dev server proxies {prefix}/assets to summer serve; dist rebuilt
This commit is contained in:
@@ -1,21 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Check, Search, Trash2 } from '@lucide/vue'
|
||||
import type { ToolbarAction } from '../../api/types'
|
||||
import { t } from '../../app/i18n'
|
||||
import Button from '../ui/Button.vue'
|
||||
|
||||
// List toolbar (design screen 3, D-14): the search input and the toolbar
|
||||
// buttons in declared order. Delete is a disabled outline button without a
|
||||
// selection and a danger outline button next to the "selected" pill with one.
|
||||
defineProps<{
|
||||
showSearch: boolean
|
||||
search: string
|
||||
searchPrompt: string
|
||||
buttons: string[]
|
||||
selectedCount: number
|
||||
selectedLabel: string
|
||||
deleteLabel: string
|
||||
}>()
|
||||
const emit = defineEmits<{ 'update:search': [value: string]; delete: [] }>()
|
||||
// A registered plugin action (D-12) is an outline button labelled from
|
||||
// `actions`, the list the server already filtered to what the admin may run;
|
||||
// it ignores the selection and is disabled and busy while its POST runs.
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
showSearch: boolean
|
||||
search: string
|
||||
searchPrompt: string
|
||||
buttons: string[]
|
||||
selectedCount: number
|
||||
selectedLabel: string
|
||||
deleteLabel: string
|
||||
actions?: ToolbarAction[]
|
||||
busyAction?: string | null
|
||||
}>(),
|
||||
{ actions: () => [], busyAction: null },
|
||||
)
|
||||
const emit = defineEmits<{ 'update:search': [value: string]; delete: []; action: [name: string] }>()
|
||||
|
||||
const labels = computed(() => new Map(props.actions.map((action) => [action.name, action.label])))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -52,6 +64,17 @@ const emit = defineEmits<{ 'update:search': [value: string]; delete: [] }>()
|
||||
>
|
||||
{{ deleteLabel }}
|
||||
</Button>
|
||||
<Button
|
||||
v-else-if="labels.has(button)"
|
||||
variant="outline"
|
||||
size="md"
|
||||
:data-action="button"
|
||||
:disabled="busyAction === button"
|
||||
:aria-busy="busyAction === button ? 'true' : undefined"
|
||||
@click="emit('action', button)"
|
||||
>
|
||||
{{ labels.get(button) }}
|
||||
</Button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user