Skip to content

Conversation

@johnleider
Copy link
Member

<script lang="ts" setup>
  import { ref, computed } from 'vue'
  import { ExpansionPanel, createDate } from '@vuetify/v0'

  // Date composable
  const { adapter, locale } = createDate({ locale: 'en-US' })

  const now = ref(adapter.date()!)
  const selectedDate = ref(adapter.date('2024-06-15T10:30:00')!)

  // Formatted dates
  const formatted = computed(() => ({
    fullDate: adapter.format(selectedDate.value, 'fullDate'),
    shortDate: adapter.format(selectedDate.value, 'shortDate'),
    monthAndYear: adapter.format(selectedDate.value, 'monthAndYear'),
    weekday: adapter.format(selectedDate.value, 'weekday'),
    fullDateTime: adapter.format(selectedDate.value, 'fullDateTime'),
  }))

  // Navigation
  const boundaries = computed(() => ({
    startOfDay: adapter.toISO(adapter.startOfDay(selectedDate.value)),
    endOfDay: adapter.toISO(adapter.endOfDay(selectedDate.value)),
    startOfWeek: adapter.toISO(adapter.startOfWeek(selectedDate.value)),
    startOfMonth: adapter.toISO(adapter.startOfMonth(selectedDate.value)),
  }))

  // Week array for calendar grid
  const weekArray = computed(() => adapter.getWeekArray(selectedDate.value, 0))
  const weekdays = adapter.getWeekdays(0, 'short')

  // Arithmetic
  function addDays (days: number) {
    selectedDate.value = adapter.addDays(selectedDate.value, days)
  }

  function addMonths (months: number) {
    selectedDate.value = adapter.addMonths(selectedDate.value, months)
  }

  function goToToday () {
    selectedDate.value = adapter.date()!
  }

  // Comparison
  const comparison = computed(() => ({
    isAfterNow: adapter.isAfter(selectedDate.value, now.value),
    isBeforeNow: adapter.isBefore(selectedDate.value, now.value),
    isSameDay: adapter.isSameDay(selectedDate.value, now.value),
    daysFromNow: adapter.getDiff(selectedDate.value, now.value, 'days'),
  }))

  // ExpansionPanel example
  const panels = [
    { id: 'panel-1', title: 'Panel 1', content: 'This is the content for panel 1.' },
    { id: 'panel-2', title: 'Panel 2', content: 'This is the content for panel 2.' },
    { id: 'panel-3', title: 'Panel 3', content: 'This is the content for panel 3.' },
  ]

  const model = ref(['panel-2'])
</script>

<template>
  <div class="space-y-8">
    <!-- useDate Demo -->
    <section>
      <h2 class="text-xl font-bold mb-4">useDate (Temporal API)</h2>

      <div class="grid grid-cols-2 gap-4">
        <!-- Formatting -->
        <div class="border border-divider rounded-lg p-4">
          <h3 class="font-semibold mb-2">Formatting</h3>
          <div class="space-y-1 text-sm">
            <p><span class="opacity-60">fullDate:</span> {{ formatted.fullDate }}</p>
            <p><span class="opacity-60">shortDate:</span> {{ formatted.shortDate }}</p>
            <p><span class="opacity-60">monthAndYear:</span> {{ formatted.monthAndYear }}</p>
            <p><span class="opacity-60">weekday:</span> {{ formatted.weekday }}</p>
            <p><span class="opacity-60">fullDateTime:</span> {{ formatted.fullDateTime }}</p>
          </div>
        </div>

        <!-- Boundaries -->
        <div class="border border-divider rounded-lg p-4">
          <h3 class="font-semibold mb-2">Boundaries</h3>
          <div class="space-y-1 text-sm font-mono">
            <p><span class="opacity-60">startOfDay:</span> {{ boundaries.startOfDay }}</p>
            <p><span class="opacity-60">endOfDay:</span> {{ boundaries.endOfDay }}</p>
            <p><span class="opacity-60">startOfWeek:</span> {{ boundaries.startOfWeek }}</p>
            <p><span class="opacity-60">startOfMonth:</span> {{ boundaries.startOfMonth }}</p>
          </div>
        </div>

        <!-- Navigation -->
        <div class="border border-divider rounded-lg p-4">
          <h3 class="font-semibold mb-2">Navigation</h3>
          <div class="flex flex-wrap gap-2">
            <button
              class="px-3 py-1 bg-primary text-on-primary rounded"
              @click="addDays(-7)"
            >
              -7 days
            </button>
            <button
              class="px-3 py-1 bg-primary text-on-primary rounded"
              @click="addDays(-1)"
            >
              -1 day
            </button>
            <button
              class="px-3 py-1 bg-secondary text-on-secondary rounded"
              @click="goToToday"
            >
              Today
            </button>
            <button
              class="px-3 py-1 bg-primary text-on-primary rounded"
              @click="addDays(1)"
            >
              +1 day
            </button>
            <button
              class="px-3 py-1 bg-primary text-on-primary rounded"
              @click="addDays(7)"
            >
              +7 days
            </button>
          </div>
          <div class="flex flex-wrap gap-2 mt-2">
            <button
              class="px-3 py-1 bg-primary text-on-primary rounded"
              @click="addMonths(-1)"
            >
              Prev Month
            </button>
            <button
              class="px-3 py-1 bg-primary text-on-primary rounded"
              @click="addMonths(1)"
            >
              Next Month
            </button>
          </div>
        </div>

        <!-- Comparison -->
        <div class="border border-divider rounded-lg p-4">
          <h3 class="font-semibold mb-2">Comparison with Now</h3>
          <div class="space-y-1 text-sm">
            <p><span class="opacity-60">isAfterNow:</span> {{ comparison.isAfterNow }}</p>
            <p><span class="opacity-60">isBeforeNow:</span> {{ comparison.isBeforeNow }}</p>
            <p><span class="opacity-60">isSameDay:</span> {{ comparison.isSameDay }}</p>
            <p><span class="opacity-60">daysFromNow:</span> {{ comparison.daysFromNow }} days</p>
          </div>
        </div>
      </div>

      <!-- Calendar Grid -->
      <div class="mt-4 border border-divider rounded-lg p-4">
        <h3 class="font-semibold mb-2">
          Calendar: {{ adapter.format(selectedDate, 'monthAndYear') }}
        </h3>
        <div class="grid grid-cols-7 gap-1 text-center text-sm">
          <div
            v-for="day in weekdays"
            :key="day"
            class="font-semibold opacity-60 py-1"
          >
            {{ day }}
          </div>
          <template v-for="(week, wi) in weekArray" :key="wi">
            <div
              v-for="(date, di) in week"
              :key="`${wi}-${di}`"
              class="py-2 rounded cursor-pointer hover:bg-surface-tint"
              :class="{
                'opacity-40': !adapter.isSameMonth(date, selectedDate),
                'bg-primary text-on-primary': adapter.isSameDay(date, selectedDate),
                'ring-1 ring-primary': adapter.isSameDay(date, now),
              }"
              @click="selectedDate = date"
            >
              {{ adapter.getDate(date) }}
            </div>
          </template>
        </div>
      </div>

      <p class="mt-2 text-sm opacity-60">
        Selected: {{ adapter.toISO(selectedDate) }} | Locale: {{ locale }}
      </p>
    </section>

    <hr class="border-divider">

    <!-- ExpansionPanel Demo -->
    <section>
      <h2 class="text-xl font-bold mb-4">ExpansionPanel</h2>

      <ExpansionPanel.Root
        v-model="model"
        class="border border-divider rounded-lg border-solid overflow-hidden divide-y divide-divider"
        multiple
      >
        <ExpansionPanel.Item
          v-for="item in panels"
          :key="item.id"
          :value="item.id"
        >
          <ExpansionPanel.Activator
            v-slot="{ isSelected }"
            class="w-full px-3 py-2 border-none flex items-center gap-3 text-left cursor-pointer bg-surface hover:bg-surface-tint"
          >
            <span
              class="inline-flex items-center justify-center w-5 text-sm text-on-surface opacity-60"
              :class="isSelected ? 'text-primary' : undefined"
            >
              {{ isSelected ? '−' : '+' }}
            </span>
            <span class="flex-1 font-medium text-on-surface text-base">{{ item.title }}</span>
          </ExpansionPanel.Activator>

          <ExpansionPanel.Content class="pa-4">
            {{ item.content }}
          </ExpansionPanel.Content>
        </ExpansionPanel.Item>
      </ExpansionPanel.Root>

      <p class="mt-4 text-sm text-on-surface opacity-60">
        Expanded: {{ model.join(', ') }}
      </p>
    </section>
  </div>
</template>

Copilot AI review requested due to automatic review settings December 15, 2025 20:22

This comment was marked as outdated.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 15, 2025

Open in StackBlitz

commit: 7f42703

@johnleider johnleider added this to the v0.1.0 milestone Jan 1, 2026
- Replace export * with explicit exports in bridge (fixes Volar)
- Add T extends Temporal.PlainDateTime generic constraint
- Cache Intl.DateTimeFormat/NumberFormat instances for performance
- Optimize formatByString with pre-compiled regex
- Document parse() limitation (format param ignored)
- Add tests for createDateFallback and locale synchronization
- Add comprehensive useDate docs with API reference
- Cover installation, usage, format presets/tokens
- Document locale integration and Vuetify 3 bridge
- Add to composables index
Vuetify will adapt to use v0's date system directly.
Remove string handling from parseDayOfWeek - Vuetify will adapt.
- Add function overloads for proper type inference with custom adapters
- Fix memory leak: watchEffect now uses onScopeDispose for cleanup
- Fix SSR safety: Temporal.Now guarded with IN_BROWSER check
- Extract createDateInternal and resolveLocale helpers
- Simplify createDateFallback to non-generic (always PlainDateTime)

Custom adapters now infer types correctly:
  createDate({ adapter: new DateFnsAdapter() }) → DateContext<Date>
- Add YY token to FORMAT_TOKEN_REGEX for 2-digit year formatting
- Add firstDayOfWeek parameter to endOfWeek() for consistency
- Add MAX_CACHE_SIZE limit to format caches to prevent memory leaks
- Add useDate() composable tests for fallback behavior
- Document SSR-safe date() behavior (returns epoch on server)
- Remove stale Vuetify 3 Bridge documentation
- Fix parameter types in docs (number only, not number | string)
- Add 7 missing comparison methods to documentation
- Fix locale type in docs (required → optional)
- Add SSR behavior tests for date() edge cases
- Add cache management tests for formatter reuse
- Fix getDiff to gracefully handle invalid string input (return 0 instead of throwing)
- Fix createDateContext to preserve custom adapter when provided
- Fix createDatePlugin to preserve custom adapter when provided
- Fix typo in comment ("disrectly" -> "directly")
Add missing test coverage identified by inspection:
- getWeek() with firstDayOfWeek and minimalDays parameters
- createDateContext() trinity tuple structure and options
- Document timezone-dependent formatting SSR limitation
- Add SSR test file with IN_BROWSER=false mock to validate epoch fallback
- Add getDiff tests for all unit types: hours, minutes, seconds, weeks, months, years
- Test string comparing parameter and invalid date handling
- Add JSDoc warning to useDate<T> generic overload about unchecked type parameter
- Add inline comments explaining cast safety in overload implementations
- Export DateOptionsBase for custom adapter authors
- Clear Intl caches on locale change to prevent stale formatters in SSR
- Add section comments to adapters barrel file
Add prominent TYPE SAFETY WARNING header to useDate<T>() JSDoc.
Clarify that incorrect usage causes silent runtime failures and
recommend using the type-safe first overload when possible.
Align adapter naming with framework convention (Vuetify0<Feature>Adapter).
Add tests for medium/low severity findings from code inspection:
- Cache eviction logic at MAX_CACHE_SIZE limit
- Component lifecycle cleanup (watchEffect disposal)
- useLocale integration with localeMap
- Cache clearing on locale change
- ZonedDateTime input handling
- formatByString midnight/noon edge cases (12:00 AM/PM)
Replace raw typeof/null checks with isString and isNull utilities
for consistency with codebase patterns.
- Update all references to renamed Vuetify0DateAdapter class
- Replace == null with isNullOrUndefined() utility
- Add type-level regression tests for overload protection
- Fix createDateContext type signature in docs
- Add custom adapter type inference test
- Add SSR tests for useDate/createDateFallback outside component
- Add dynamic locale sync test with useLocale integration
- Add leap year and invalid locale edge case tests
- Add locale switching benchmarks (cache eviction, toggle)
- Enhance SSR/hydration docs with warning callout and code examples
- Add type-level tests verifying overload safety
- Replace getCurrentInstance() with instanceExists() utility
- Minor import ordering cleanup
- Remove overloaded signatures from createDate, createDateContext,
  createDatePlugin, and useDate
- Consolidate 6 option interfaces into 2 (DateOptions, DateContextOptions)
- Change generic pattern to E extends DateContext (matches useBreakpoints)
- Rename localeMap option to locales for clarity
- Inline single-use helper functions (resolveLocale, isIntlLocale)
- Tighten internal variable names (selectedId, initialLocale)
- Fix docs: localeMap → locales (stale API name)
- DateAdapter<T> now defaults to Temporal.PlainDateTime
- isValid/isNull are now type predicates for narrowing
- Generic pattern: Z, E extends DateContext<Z> (matches v0 conventions)
- Add @module JSDoc to adapters barrel file
- Fix onScopeDispose call (remove undocumented parameter)
- Add polyfill installation instructions to use-date.md
- Install @js-temporal/polyfill in apps/docs
- Add createDatePlugin to docs zero.ts plugin
- Update DocsReleases.vue to use useDate for date formatting
- Fix parseISO to handle timezone-aware strings (e.g., "2024-06-15T10:30:00Z")
- Add interactive example with DocsExample component
- Use code groups for package manager installation options
- Link TC39 proposal in description and installation tip
- Update DateAdapter interface with type predicate signatures
- Expand Custom Adapters section with isValid/isNull examples
- Add useDate to shiki-api-transformer for hover popovers
- Ignore @js-temporal/polyfill in knip (peer dependency)
- Rename localeMap to locales in test assertions
- Add DateAdapter type assertion for custom adapter tests
- Update auto-generated type declarations
- Add calendar.vue demonstrating getWeekArray, getWeekdays, navigation
- Handle Feb 2025 edge case (4 weeks) by padding with next month
- Add Examples section to documentation
@johnleider johnleider merged commit 8b39fe8 into master Jan 10, 2026
4 of 5 checks passed
@johnleider johnleider deleted the feat/use-date branch January 10, 2026 02:15
@johnleider johnleider self-assigned this Jan 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant