Skip to content

feat(VSwitch): add 'square' as new inset variant#22881

Merged
J-Sek merged 1 commit into
devfrom
feat/vswitch-square
May 30, 2026
Merged

feat(VSwitch): add 'square' as new inset variant#22881
J-Sek merged 1 commit into
devfrom
feat/vswitch-square

Conversation

@J-Sek
Copy link
Copy Markdown
Contributor

@J-Sek J-Sek commented May 30, 2026

image

Markup:

<template>
  <v-app class="bg-surface">
    <v-defaults-provider :defaults="{ VSwitch: { inset, loading, hideDetails: true, density, trueIcon: icons ? '$complete' : undefined, falseIcon: icons ? '$close' : undefined } }">
      <v-container>
        <v-card min-height="360">
          <v-card-text class="d-flex">
            <div class="pa-2 flex-grow-1">
              <v-theme-provider theme="light">
                <v-sheet>
                  <v-row class="justify-center align-center py-12 border-md" gap="24">
                    <v-switch :model-value="false" />
                    <v-switch :model-value="true" />
                  </v-row>
                </v-sheet>
              </v-theme-provider>
              <v-theme-provider theme="dark">
                <v-sheet>
                  <v-row class="justify-center align-center py-12 border-md" gap="24">
                    <v-switch :model-value="false" />
                    <v-switch :model-value="true" />
                  </v-row>
                </v-sheet>
              </v-theme-provider>
            </div>
            <div class="pa-2 flex-grow-1">
              <v-theme-provider theme="light">
                <v-sheet>
                  <v-row class="justify-center align-center py-12 border-md" gap="24">
                    <v-switch :model-value="false" color="primary" />
                    <v-switch :model-value="true" color="primary" />
                  </v-row>
                </v-sheet>
              </v-theme-provider>
              <v-theme-provider theme="dark">
                <v-sheet>
                  <v-row class="justify-center align-center py-12 border-md" gap="24">
                    <v-switch :model-value="false" color="primary" />
                    <v-switch :model-value="true" color="primary" />
                  </v-row>
                </v-sheet>
              </v-theme-provider>
            </div>
            <div class="pa-2 flex-grow-1">
              <v-theme-provider theme="light">
                <v-sheet>
                  <v-row class="justify-center align-center py-12 border-md" gap="24">
                    <v-switch :model-value="false" color="primary" />
                    <v-switch :model-value="true" color="primary" />
                  </v-row>
                </v-sheet>
              </v-theme-provider>
              <v-theme-provider theme="dark">
                <v-sheet>
                  <v-row class="justify-center align-center py-12 border-md" gap="24">
                    <v-switch :model-value="false" color="primary" />
                    <v-switch :model-value="true" color="primary" />
                  </v-row>
                </v-sheet>
              </v-theme-provider>
            </div>
            <v-defaults-provider :defaults="{ VSwitch: { disabled: true } }">
              <div class="pa-2 flex-grow-1">
                <v-theme-provider theme="light">
                  <v-sheet>
                    <v-row class="justify-center align-center py-12 border-md" gap="24">
                      <v-switch :model-value="false" color="primary" />
                      <v-switch :model-value="true" color="primary" />
                    </v-row>
                  </v-sheet>
                </v-theme-provider>
                <v-theme-provider theme="dark">
                  <v-sheet>
                    <v-row class="justify-center align-center py-12 border-md" gap="24">
                      <v-switch :model-value="false" color="primary" />
                      <v-switch :model-value="true" color="primary" />
                    </v-row>
                  </v-sheet>
                </v-theme-provider>
              </div>
            </v-defaults-provider>
          </v-card-text>
        </v-card>
        <v-card class="mt-4">
          <v-card-text>
            <div class="pa-2 flex-grow-1">
              <v-row>
                <v-col v-for="[color, thumbColor] in colors" :key="color" cols="12" md="4" sm="6">
                  <div class="d-flex align-center ga-3">
                    <v-avatar :color="thumbColor ?? color" size="32" />
                    <v-switch
                      v-model="model"
                      :color="color"
                      :label="color"
                      :thumb-color="thumbColor"
                      :value="color"
                      hide-details
                    />
                  </div>
                </v-col>
              </v-row>
            </div>
          </v-card-text>
        </v-card>
      </v-container>
    </v-defaults-provider>
    <div class="d-flex flex-column ga-2 ma-3 position-absolute bottom-0 right-0">
      <v-btn
        class="align-self-end"
        icon="mdi-palette"
        variant="tonal"
      >
        <v-icon />
        <v-menu
          :close-on-content-click="false"
          activator="parent"
          offset="8"
        >
          <v-sheet
            class="d-flex flex-column ga-3 align-start pa-3 border"
            max-width="500"
            rounded="xl"
          >
            <v-btn
              :text="current.dark ? 'Dark' : 'Light'"
              prepend-icon="mdi-theme-light-dark"
              variant="tonal"
              rounded
              @click="$vuetify.theme.cycle()"
            />
            <span class="text-caption font-weight-medium">Primary</span>
            <v-item-group v-model="primaryColor" class="d-flex ga-2 flex-wrap" mandatory>
              <v-item
                v-for="[key, c] of swatchColors"
                :key="key"
                v-slot="{ isSelected, toggle }"
                :value="key"
              >
                <v-icon-btn
                  :color="c[current.dark ? 'lighten1' : 'darken1']"
                  :icon="isSelected ? '$complete' : ''"
                  @click="toggle"
                />
              </v-item>
            </v-item-group>
            <span class="text-caption font-weight-medium">Surface</span>
            <v-item-group v-model="surfaceColor" class="d-flex ga-2 flex-wrap" mandatory>
              <v-item
                v-for="[key, c] of Object.entries(surfaceColors)"
                :key="key"
                v-slot="{ isSelected, toggle }"
                :value="key"
              >
                <v-icon-btn
                  :color="c.base"
                  :icon="isSelected ? '$complete' : ''"
                  @click="toggle"
                />
              </v-item>
            </v-item-group>
          </v-sheet>
        </v-menu>
      </v-btn>
      <v-btn
        :text="`inset: ${inset || 'OFF'}`"
        variant="tonal"
        @click="inset = insets[(insets.indexOf(inset) + 1) % insets.length]"
      />
      <v-btn
        :text="`loading: ${loading ? 'ON' : 'OFF'}`"
        variant="tonal"
        @click="loading = !loading"
      />
      <v-btn
        :text="`density: ${density}`"
        variant="tonal"
        @click="density = densities[(densities.indexOf(density) + 1) % densities.length]"
      />
      <v-btn
        :text="`icons: ${icons ? 'ON' : 'OFF'}`"
        variant="tonal"
        @click="icons = !icons"
      />
    </div>
  </v-app>
</template>

<script setup>
  import { useTheme } from 'vuetify'
  import { ref, shallowRef, watch } from 'vue'
  import paletteColors from '../src/util/colors'

  const insets = [false, 'tonal', 'material', 'square']
  const inset = ref('square')

  const loading = ref(false)

  const densities = ['default', 'comfortable', 'compact']
  const density = ref('default')

  const icons = ref(true)

  const { themes, current, change } = useTheme()

  change('dark')

  const swatchColors = Object.entries(paletteColors).filter(([key]) => key !== 'shades')

  const primaryColor = shallowRef('deepPurple')
  watch(primaryColor, v => {
    themes.value.light.colors.primary = paletteColors[v].darken1
    themes.value.dark.colors.primary = paletteColors[v].lighten4
    themes.value.dark.colors['on-primary'] = paletteColors[v].darken3
  }, { immediate: true })

  const surfaceColors = {
    slate: { base: '#64748b', lighten: '#e2e8f0', darken: '#0f172a' },
    gray: { base: '#6b7280', lighten: '#e5e7eb', darken: '#111827' },
    zinc: { base: '#71717a', lighten: '#e4e4e7', darken: '#18181b' },
    neutral: { base: '#737373', lighten: '#e5e5e5', darken: '#171717' },
    stone: { base: '#78716c', lighten: '#e7e5e4', darken: '#1c1917' },
  }
  const surfaceColor = shallowRef('stone')
  watch(surfaceColor, v => {
    themes.value.light.colors.surface = surfaceColors[v].lighten
    themes.value.dark.colors.surface = surfaceColors[v].darken
  }, { immediate: true })

  // [color, thumbColor?]
  const colors = [
    ['red'],
    ['red-darken-3'],
    ['indigo'],
    ['indigo-darken-3'],
    ['orange-lighten-3', 'orange-darken-2'],
    ['orange-darken-3'],
    ['#64a'],
    ['#cf3'],
    ['success'],
    ['info'],
    ['warning'],
    ['error'],
  ]

  const model = ref(colors.map(([color]) => color))
</script>

@J-Sek J-Sek self-assigned this May 30, 2026
@J-Sek J-Sek added T: feature A new feature C: VSwitch labels May 30, 2026
@J-Sek J-Sek marked this pull request as ready for review May 30, 2026 22:54
@J-Sek J-Sek force-pushed the feat/vswitch-square branch from 2a132bf to 5f28a02 Compare May 30, 2026 22:55
@J-Sek J-Sek modified the milestones: v4.1.x, v4.1.0 May 30, 2026
@J-Sek J-Sek merged commit f0fb506 into dev May 30, 2026
16 checks passed
@J-Sek J-Sek deleted the feat/vswitch-square branch May 30, 2026 23:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant