diff --git a/packages/docs/src/examples/v-alert/prop-duration.vue b/packages/docs/src/examples/v-alert/prop-duration.vue
new file mode 100644
index 00000000000..4d7ee8f6d50
--- /dev/null
+++ b/packages/docs/src/examples/v-alert/prop-duration.vue
@@ -0,0 +1,25 @@
+
+
+
+ Show alert
+
+
+
+
+
+
+
diff --git a/packages/docs/src/examples/v-treeview/prop-search-auto-expand.vue b/packages/docs/src/examples/v-treeview/prop-search-auto-expand.vue
new file mode 100644
index 00000000000..ff2a9c9e844
--- /dev/null
+++ b/packages/docs/src/examples/v-treeview/prop-search-auto-expand.vue
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/docs/src/pages/en/components/alerts.md b/packages/docs/src/pages/en/components/alerts.md
index 97701b717a5..7959420d393 100644
--- a/packages/docs/src/pages/en/components/alerts.md
+++ b/packages/docs/src/pages/en/components/alerts.md
@@ -131,6 +131,12 @@ The **closable** prop adds a [v-icon](/components/icons) on the far right, after
+#### Duration
+
+The **duration** prop causes the alert to automatically dismiss itself after the specified number of milliseconds. Set to `-1` (the default) to disable auto-dismiss. Combine with **closable** to give users both auto-dismiss and manual control.
+
+
+
The close icon automatically applies a default `aria-label` and is configurable by using the **close-label** prop or changing **close** value in your locale.
::: info
diff --git a/packages/vuetify/src/components/VAlert/VAlert.tsx b/packages/vuetify/src/components/VAlert/VAlert.tsx
index 42672ddc0e0..b30684c40d7 100644
--- a/packages/vuetify/src/components/VAlert/VAlert.tsx
+++ b/packages/vuetify/src/components/VAlert/VAlert.tsx
@@ -25,7 +25,7 @@ import { makeThemeProps, provideTheme } from '@/composables/theme'
import { genOverlays, makeVariantProps, useVariant } from '@/composables/variant'
// Utilities
-import { toRef } from 'vue'
+import { onMounted, onScopeDispose, toRef, watch } from 'vue'
import { genericComponent, propsFactory } from '@/util'
// Types
@@ -57,6 +57,10 @@ export const makeVAlertProps = propsFactory({
type: String,
default: '$vuetify.close',
},
+ duration: {
+ type: [Number, String],
+ default: -1,
+ },
icon: {
type: [Boolean, String, Function, Object] as PropType,
default: null,
@@ -107,6 +111,28 @@ export const VAlert = genericComponent()({
setup (props, { emit, slots }) {
const isActive = useProxiedModel(props, 'modelValue')
+
+ let dismissTimer = -1
+ function clearDismissTimer () {
+ if (dismissTimer !== -1) {
+ window.clearTimeout(dismissTimer)
+ dismissTimer = -1
+ }
+ }
+ function scheduleDismiss () {
+ clearDismissTimer()
+ const ms = Number(props.duration)
+ if (ms > 0 && isActive.value) {
+ dismissTimer = window.setTimeout(() => {
+ isActive.value = false
+ dismissTimer = -1
+ }, ms)
+ }
+ }
+ onMounted(scheduleDismiss)
+ watch(() => [props.duration, isActive.value], scheduleDismiss)
+ onScopeDispose(clearDismissTimer)
+
const icon = toRef(() => {
if (props.icon === false) return undefined
if (!props.type) return props.icon
diff --git a/packages/vuetify/src/components/VTreeview/VTreeview.tsx b/packages/vuetify/src/components/VTreeview/VTreeview.tsx
index c046ccabbc7..2e44d2a249e 100644
--- a/packages/vuetify/src/components/VTreeview/VTreeview.tsx
+++ b/packages/vuetify/src/components/VTreeview/VTreeview.tsx
@@ -106,7 +106,6 @@ export const VTreeview = genericComponent(
const vListRef = ref()
- const opened = computed(() => props.openAll ? openAll(items.value) : props.opened)
const flatItems = computed(() => flatten(items.value))
const search = toRef(() => props.search)
const { filteredItems } = useFilter(props, flatItems, search)
@@ -123,6 +122,28 @@ export const VTreeview = genericComponent(
}))
})
+ const searchOpened = computed(() => {
+ if (!search.value) return null
+ const getPath = vListRef.value?.getPath
+ if (!getPath) return null
+ const ids = new Set()
+ for (const item of filteredItems.value) {
+ const itemVal = props.returnObject ? item.raw : item.props.value
+ for (const id of getPath(itemVal)) {
+ ids.add(toRaw(id))
+ }
+ }
+ return ids.size > 0 ? [...ids] : null
+ })
+
+ const opened = computed(() => {
+ if (props.openAll) return openAll(items.value)
+ if (searchOpened.value) {
+ return [...new Set([...(props.opened ?? []), ...searchOpened.value])]
+ }
+ return props.opened
+ })
+
function getChildren (id: unknown) {
const arr: unknown[] = []
const queue = ((vListRef.value?.children.get(id) ?? []).slice())