Skip to content

Commit 095a0c1

Browse files
committed
fix(ContentSearch/DashboardSearch): proxy modal props to support fullscreen
1 parent 05c3bb9 commit 095a0c1

File tree

9 files changed

+131
-14
lines changed

9 files changed

+131
-14
lines changed

src/runtime/components/DashboardSearch.vue

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type { ComponentConfig } from '../types/tv'
88
99
type DashboardSearch = ComponentConfig<typeof theme, AppConfig, 'dashboardSearch'>
1010
11-
export interface DashboardSearchProps<T extends CommandPaletteItem = CommandPaletteItem> extends /* @vue-ignore */ Pick<ModalProps, 'title' | 'description' | 'overlay' | 'transition' | 'content' | 'dismissible' | 'fullscreen' | 'modal' | 'portal'> {
11+
export interface DashboardSearchProps<T extends CommandPaletteItem = CommandPaletteItem> extends Pick<ModalProps, 'title' | 'description' | 'overlay' | 'transition' | 'content' | 'dismissible' | 'fullscreen' | 'modal' | 'portal'> {
1212
/**
1313
* The icon displayed in the input.
1414
* @defaultValue appConfig.ui.icons.search
@@ -87,7 +87,8 @@ import UModal from './Modal.vue'
8787
const props = withDefaults(defineProps<DashboardSearchProps>(), {
8888
shortcut: 'meta_k',
8989
colorMode: true,
90-
close: true
90+
close: true,
91+
fullscreen: false
9192
})
9293
const slots = defineSlots<DashboardSearchSlots>()
9394
@@ -104,6 +105,7 @@ const colorMode = useColorMode()
104105
const appConfig = useAppConfig() as DashboardSearch['AppConfig']
105106
106107
const commandPaletteProps = useForwardProps(reactivePick(props, 'icon', 'placeholder', 'autofocus', 'loading', 'loadingIcon', 'close', 'closeIcon'))
108+
const modalProps = useForwardProps(reactivePick(props, 'overlay', 'transition', 'content', 'dismissible', 'fullscreen', 'modal', 'portal'))
107109
108110
const getProxySlots = () => omit(slots, ['content'])
109111
@@ -112,8 +114,9 @@ const fuse = computed(() => defu({}, props.fuse, {
112114
}
113115
}))
114116
115-
// eslint-disable-next-line vue/no-dupe-keys
116-
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.dashboardSearch || {}) })())
117+
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.dashboardSearch || {}) })({
118+
fullscreen: props.fullscreen
119+
}))
117120
118121
const groups = computed(() => {
119122
const groups = []
@@ -180,8 +183,9 @@ defineExpose({
180183
<template>
181184
<UModal
182185
v-model:open="open"
183-
:title="t('dashboardSearch.title')"
184-
:description="t('dashboardSearch.description')"
186+
:title="title || t('dashboardSearch.title')"
187+
:description="description || t('dashboardSearch.description')"
188+
v-bind="modalProps"
185189
:class="ui.modal({ class: [props.ui?.modal, props.class] })"
186190
>
187191
<template #content>

src/runtime/components/content/ContentSearch.vue

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export interface ContentSearchItem extends Omit<LinkProps, 'custom'>, CommandPal
3535
icon?: IconProps['name']
3636
}
3737
38-
export interface ContentSearchProps<T extends ContentSearchLink = ContentSearchLink> extends /* @vue-ignore */ Pick<ModalProps, 'title' | 'description' | 'overlay' | 'transition' | 'content' | 'dismissible' | 'fullscreen' | 'modal' | 'portal'> {
38+
export interface ContentSearchProps<T extends ContentSearchLink = ContentSearchLink> extends Pick<ModalProps, 'title' | 'description' | 'overlay' | 'transition' | 'content' | 'dismissible' | 'fullscreen' | 'modal' | 'portal'> {
3939
/**
4040
* The icon displayed in the input.
4141
* @defaultValue appConfig.ui.icons.search
@@ -120,7 +120,8 @@ import UCommandPalette from '../CommandPalette.vue'
120120
const props = withDefaults(defineProps<ContentSearchProps<T>>(), {
121121
shortcut: 'meta_k',
122122
colorMode: true,
123-
close: true
123+
close: true,
124+
fullscreen: false
124125
})
125126
const slots = defineSlots<ContentSearchSlots>()
126127
@@ -133,6 +134,7 @@ const colorMode = useColorMode()
133134
const appConfig = useAppConfig() as ContentSearch['AppConfig']
134135
135136
const commandPaletteProps = useForwardProps(reactivePick(props, 'icon', 'placeholder', 'autofocus', 'loading', 'loadingIcon', 'close', 'closeIcon'))
137+
const modalProps = useForwardProps(reactivePick(props, 'overlay', 'transition', 'content', 'dismissible', 'fullscreen', 'modal', 'portal'))
136138
137139
const getProxySlots = () => omit(slots, ['content'])
138140
@@ -142,8 +144,9 @@ const fuse = computed(() => defu({}, props.fuse, {
142144
}
143145
}))
144146
145-
// eslint-disable-next-line vue/no-dupe-keys
146-
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.contentSearch || {}) })())
147+
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.contentSearch || {}) })({
148+
fullscreen: props.fullscreen
149+
}))
147150
148151
function mapLinksItems(links: T[]): ContentSearchItem[] {
149152
return links.flatMap(link => [{
@@ -267,8 +270,9 @@ defineExpose({
267270
<template>
268271
<UModal
269272
v-model:open="open"
270-
:title="t('contentSearch.title')"
271-
:description="t('contentSearch.description')"
273+
:title="title || t('contentSearch.title')"
274+
:description="description || t('contentSearch.description')"
275+
v-bind="modalProps"
272276
:class="ui.modal({ class: [props.ui?.modal, props.class] })"
273277
>
274278
<template #content>
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
export default {
22
slots: {
3-
modal: 'sm:max-w-3xl sm:h-[28rem]',
3+
modal: '',
44
input: '[&>input]:text-base/5'
5+
},
6+
variants: {
7+
fullscreen: {
8+
false: {
9+
modal: 'sm:max-w-3xl sm:h-[28rem]'
10+
}
11+
}
512
}
613
}

src/theme/dashboard-search.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
export default {
22
slots: {
3-
modal: 'sm:max-w-3xl sm:h-[28rem]',
3+
modal: '',
44
input: '[&>input]:text-base/5'
5+
},
6+
variants: {
7+
fullscreen: {
8+
false: {
9+
modal: 'sm:max-w-3xl sm:h-[28rem]'
10+
}
11+
}
512
}
613
}

test/components/DashboardSearch.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ describe('DashboardSearch', () => {
4040
['with loading', { props: { ...props, loading: true } }],
4141
['with loadingIcon', { props: { ...props, loading: true, loadingIcon: 'i-lucide-loading' } }],
4242
['without colorMode', { props: { ...props, colorMode: false } }],
43+
['with fullscreen', { props: { ...props, fullscreen: true } }],
4344
['with ui', { props: { ...props, ui: { input: '[&>input]:text-lg' } } }],
4445
['with class', { props: { ...props, class: 'sm:max-w-5xl' } }]
4546
])('renders %s correctly', async (_: string, options: { props?: DashboardSearchProps }) => {

test/components/__snapshots__/DashboardSearch-vue.spec.ts.snap

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,34 @@ exports[`DashboardSearch > renders with class correctly 1`] = `
2828
</div>"
2929
`;
3030

31+
exports[`DashboardSearch > renders with fullscreen correctly 1`] = `
32+
"<div class="fixed inset-0 flex overflow-hidden">
33+
<!--v-if-->
34+
<!--teleport start-->
35+
<div data-state="open" style="pointer-events: auto;" class="fixed inset-0 bg-elevated/75 data-[state=open]:animate-[fade-in_200ms_ease-out] data-[state=closed]:animate-[fade-out_200ms_ease-in]"></div>
36+
<div data-dismissable-layer="" style="pointer-events: auto;" tabindex="-1" class="fixed bg-default divide-y divide-default flex flex-col focus:outline-none data-[state=open]:animate-[scale-in_200ms_ease-out] data-[state=closed]:animate-[scale-out_200ms_ease-in] inset-0" id="" role="dialog" aria-describedby="reka-dialog-description-v-1" aria-labelledby="reka-dialog-title-v-0" data-state="open"><span aria-hidden="true" style="position: absolute; border: 0px; width: 1px; height: 1px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); clip-path: inset(50%); white-space: nowrap; word-wrap: normal; top: -1px; left: -1px;"><h2 id="reka-dialog-title-v-0">dashboardSearch.title</h2><p id="reka-dialog-description-v-1">dashboardSearch.description</p></span>
37+
<div dir="ltr" class="flex flex-col min-h-0 min-w-0 divide-y divide-default">
38+
<div class="relative inline-flex items-center [&amp;>input]:h-12 [&amp;>input]:text-base/5"><input type="text" placeholder="Type a command or search…" class="w-full rounded-md border-0 appearance-none placeholder:text-dimmed focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 transition-colors px-2.5 py-1.5 text-sm gap-1.5 text-highlighted bg-transparent ps-9 pe-9" autocomplete="off" aria-disabled="false" value=""><span class="absolute inset-y-0 start-0 flex items-center ps-2.5"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" viewBox="0 0 16 16" class="shrink-0 text-dimmed size-5"></svg></span><span class="absolute inset-y-0 end-0 flex items-center pe-2.5"><button type="button" aria-label="Close" class="rounded-md font-medium inline-flex items-center disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 text-default hover:bg-elevated active:bg-elevated focus:outline-none focus-visible:bg-elevated hover:disabled:bg-transparent dark:hover:disabled:bg-transparent hover:aria-disabled:bg-transparent dark:hover:aria-disabled:bg-transparent p-1.5"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" viewBox="0 0 16 16" class="shrink-0 size-5"></svg><!--v-if--><!--v-if--></button></span></div>
39+
<div class="relative overflow-hidden flex flex-col" role="listbox" aria-orientation="vertical" aria-multiselectable="false" data-orientation="vertical">
40+
<div role="presentation" class="relative divide-y divide-default scroll-py-1 overflow-y-auto flex-1 focus:outline-none">
41+
<div role="group" aria-labelledby="reka-listbox-group-v-2" class="p-1 isolate">
42+
<div id="reka-listbox-group-v-2" class="p-1.5 text-xs font-semibold text-highlighted">Go to</div><a href="/" role="option" tabindex="-1" data-reka-collection-item="" id="reka-listbox-item-v-3" aria-selected="false" data-state="unchecked" class="group relative w-full flex items-center gap-1.5 p-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75 text-highlighted before:bg-elevated">
43+
<!--v-if--><span class="truncate space-x-1 text-dimmed"><!--v-if--><span class="text-highlighted [&amp;>mark]:text-inverted [&amp;>mark]:bg-primary">Home</span><span class="text-dimmed [&amp;>mark]:text-inverted [&amp;>mark]:bg-primary"></span></span><span class="ms-auto inline-flex gap-1.5 items-center"><!--v-if--><!--v-if--></span>
44+
</a>
45+
</div>
46+
<div role="group" aria-labelledby="reka-listbox-group-v-4" class="p-1 isolate">
47+
<div id="reka-listbox-group-v-4" class="p-1.5 text-xs font-semibold text-highlighted">Theme</div><button type="button" data-reka-collection-item="" id="reka-listbox-item-v-5" role="option" tabindex="-1" aria-selected="false" data-state="unchecked" class="group relative w-full flex items-center gap-1.5 p-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75 text-highlighted before:bg-elevated"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" viewBox="0 0 16 16" class="shrink-0 size-5 text-default"></svg><span class="truncate space-x-1 text-dimmed"><!--v-if--><span class="text-highlighted [&amp;>mark]:text-inverted [&amp;>mark]:bg-primary">System</span><span class="text-dimmed [&amp;>mark]:text-inverted [&amp;>mark]:bg-primary"></span></span><span class="ms-auto inline-flex gap-1.5 items-center"><!--v-if--><!--v-if--></span></button><button type="button" data-reka-collection-item="" id="reka-listbox-item-v-6" role="option" tabindex="-1" aria-selected="false" data-state="unchecked" class="group relative w-full flex items-center gap-1.5 p-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75 text-default data-highlighted:not-data-disabled:text-highlighted data-highlighted:not-data-disabled:before:bg-elevated/50 transition-colors before:transition-colors"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" viewBox="0 0 16 16" class="shrink-0 size-5 text-dimmed group-data-highlighted:not-group-data-disabled:text-default transition-colors"></svg><span class="truncate space-x-1 text-dimmed"><!--v-if--><span class="text-highlighted [&amp;>mark]:text-inverted [&amp;>mark]:bg-primary">Light</span><span class="text-dimmed [&amp;>mark]:text-inverted [&amp;>mark]:bg-primary"></span></span><span class="ms-auto inline-flex gap-1.5 items-center"><!--v-if--><!--v-if--></span></button><button type="button" data-reka-collection-item="" id="reka-listbox-item-v-7" role="option" tabindex="-1" aria-selected="false" data-state="unchecked" class="group relative w-full flex items-center gap-1.5 p-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75 text-default data-highlighted:not-data-disabled:text-highlighted data-highlighted:not-data-disabled:before:bg-elevated/50 transition-colors before:transition-colors"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" viewBox="0 0 16 16" class="shrink-0 size-5 text-dimmed group-data-highlighted:not-group-data-disabled:text-default transition-colors"></svg><span class="truncate space-x-1 text-dimmed"><!--v-if--><span class="text-highlighted [&amp;>mark]:text-inverted [&amp;>mark]:bg-primary">Dark</span><span class="text-dimmed [&amp;>mark]:text-inverted [&amp;>mark]:bg-primary"></span></span><span class="ms-auto inline-flex gap-1.5 items-center"><!--v-if--><!--v-if--></span></button>
48+
</div>
49+
</div>
50+
</div>
51+
<!--v-if-->
52+
<!--v-if-->
53+
</div>
54+
</div>
55+
<!--teleport end-->
56+
</div>"
57+
`;
58+
3159
exports[`DashboardSearch > renders with groups correctly 1`] = `
3260
"<div class="fixed inset-0 flex overflow-hidden">
3361
<!--v-if-->

0 commit comments

Comments
 (0)