Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/features-persist-option.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@vuetify/v0": minor
---

feat(useFeatures): add `persist` option to `createFeaturesPlugin`

Setting `persist: true` saves the set of enabled feature flags to storage and reconciles them against the registered flags on load, so a user's feature toggles survive a page reload. Backed by the existing `createPluginContext` persist/restore hooks and keyed by the plugin namespace.
12 changes: 2 additions & 10 deletions apps/docs/src/components/app/AppBar.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
// Framework
import { Atom, useBreakpoints, useFeatures, useStorage, useTheme } from '@vuetify/v0'
import { Atom, useBreakpoints, useTheme } from '@vuetify/v0'

// Components
import { Discovery } from '@/components/discovery'
Expand All @@ -14,7 +14,7 @@
import { useAuthStore } from '@vuetify/auth'

// Utilities
import { toRef, watch } from 'vue'
import { toRef } from 'vue'
import { useRoute } from 'vue-router'

// Types
Expand All @@ -24,23 +24,15 @@

const auth = useAuthStore()
const navigation = useNavigation()
const storage = useStorage()
const route = useRoute()

const isHomePage = toRef(() => route.path === '/')

const breakpoints = useBreakpoints()
const features = useFeatures()
const search = useSearch()
const settings = useSettings()
const theme = useTheme()

const devmode = features.get('devmode')!

watch(() => devmode.isSelected.value, isSelected => {
storage.set('devmode', isSelected)
})

const darkLogo = 'https://cdn.vuetifyjs.com/docs/images/logos/vzero-logo-dark.svg'
const lightLogo = 'https://cdn.vuetifyjs.com/docs/images/logos/vzero-logo-light.svg'
</script>
Expand Down
7 changes: 1 addition & 6 deletions apps/docs/src/components/app/AppSettingsSheet.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
// Framework
import { Avatar, useFeatures, useRtl, useStack, useStorage } from '@vuetify/v0'
import { Avatar, useFeatures, useRtl, useStack } from '@vuetify/v0'

// Components
import { Discovery } from '@/components/discovery'
Expand All @@ -19,7 +19,6 @@
const auth = useAuthStore()
const features = useFeatures()
const rtl = useRtl()
const storage = useStorage()
const settings = useSettings()
const levelFilter = useLevelFilterContext()
const stack = useStack()
Expand Down Expand Up @@ -61,10 +60,6 @@
el.focus()
})

watch(() => devmode.isSelected.value, isSelected => {
storage.set('devmode', isSelected)
})

function onKeydown (e: KeyboardEvent) {
if (e.key === 'Escape') {
settings.close()
Expand Down
3 changes: 2 additions & 1 deletion apps/docs/src/plugins/zero.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ export default function zero (app: App) {

app.use(
createFeaturesPlugin({
persist: true,
features: {
devmode: {
$value: IN_BROWSER ? localStorage.getItem('v0:devmode') === 'true' : false,
$value: false,
$description: 'Enables development mode with additional logging and warnings',
},
},
Expand Down
20 changes: 20 additions & 0 deletions packages/0/src/composables/useFeatures/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ export interface FeaturePluginOptions extends FeatureContextOptions {
* @remarks Adapters provide dynamic flag values from external services.
*/
adapter?: MaybeArray<FeaturesAdapter>
/**
* Persist enabled feature flags to storage and restore them on load.
*
* @remarks Persists the set of selected flag ids, keyed by the plugin
* namespace. On load the selection is reconciled against the registered
* flags, so a user's toggles survive a page reload.
*
* @default false
*/
persist?: boolean
}

/**
Expand Down Expand Up @@ -216,6 +226,16 @@ export const [createFeaturesContext, createFeaturesPlugin, useFeatures] =
options => createFeatures(options),
{
fallback: () => createFeaturesFallback(),
persist: context => [...context.selectedIds],
restore: (context, saved) => {
if (!isArray(saved)) return

const wanted = new Set(saved)
for (const ticket of context.values()) {
if (wanted.has(ticket.id)) context.select(ticket.id)
else context.unselect(ticket.id)
}
},
setup: (context, app, { adapter }) => {
if (!adapter) return

Expand Down
Loading