feat: swap date input to use date picker (#6146)

* feat: swap date input to use date picker

* feat: update date picker to analytics branch changes

* feat: polish date picker usage
This commit is contained in:
Truman Gao
2026-05-20 11:50:46 -06:00
committed by GitHub
parent 3eeb549d20
commit d8b1415f9c
6 changed files with 836 additions and 67 deletions
@@ -131,10 +131,9 @@
class="h-10"
/>
<StyledInput
<DatePicker
v-else-if="field.type === 'date'"
v-model="formData[field.name]"
type="date"
wrapper-class="w-full"
/>
@@ -230,6 +229,7 @@ import {
Admonition,
Checkbox,
Combobox,
DatePicker,
defineMessages,
financialMessages,
formFieldLabels,
@@ -95,11 +95,11 @@
<span class="text-red">*</span>
</span>
</label>
<StyledInput
<DatePicker
v-model="formData.dateOfBirth"
type="date"
:max="maxDate"
:max-date="maxDate"
autocomplete="bday"
placeholder="Select date of birth"
wrapper-class="w-full"
/>
</div>
@@ -213,6 +213,7 @@
import {
Chips,
Combobox,
DatePicker,
defineMessages,
formFieldLabels,
formFieldPlaceholders,
+7 -2
View File
@@ -102,7 +102,12 @@
<tr>
<td>Revenue earned on</td>
<td>
<StyledInput id="revenue-date-picker" v-model="rawSelectedDate" type="date" />
<DatePicker
id="revenue-date-picker"
v-model="rawSelectedDate"
show-today
position="above"
/>
<noscript
>(JavaScript must be enabled for the date picker to function, example date:
2024-07-15)
@@ -162,7 +167,7 @@
</template>
<script lang="ts" setup>
import { injectModrinthClient, StyledInput, useFormatDateTime, useFormatMoney } from '@modrinth/ui'
import { DatePicker, injectModrinthClient, useFormatDateTime, useFormatMoney } from '@modrinth/ui'
import { useQuery } from '@tanstack/vue-query'
import dayjs from 'dayjs'
import { computed, ref } from 'vue'
+13 -13
View File
@@ -16,10 +16,10 @@
"
:width="'550px'"
>
<div class="flex flex-col gap-4">
<div class="flex w-full flex-col">
<div class="flex flex-col gap-6">
<div class="flex w-full flex-col gap-2.5">
<label for="pat-name">
<span class="label__title">{{ formatMessage(createModalMessages.nameLabel) }}</span>
<span class="font-semibold">{{ formatMessage(createModalMessages.nameLabel) }}</span>
</label>
<StyledInput
id="pat-name"
@@ -29,20 +29,20 @@
/>
</div>
<div class="flex w-full flex-col">
<div class="flex w-full flex-col gap-2.5">
<label for="pat-scopes">
<span class="label__title">{{ formatMessage(commonMessages.scopesLabel) }}</span>
<span class="font-semibold">{{ formatMessage(commonMessages.scopesLabel) }}</span>
</label>
<div
id="pat-scopes"
class="scope-items mt-2 grid grid-cols-1 gap-x-6 gap-y-4 min-[600px]:grid-cols-2"
class="scope-items grid grid-cols-1 gap-x-6 gap-y-4 min-[600px]:grid-cols-2"
>
<div
v-for="category in scopeCategories"
:key="category.name"
class="flex flex-col gap-2"
class="flex flex-col gap-1.5"
>
<h4 class="m-0 border-b border-divider pb-1 text-base font-bold text-contrast">
<h4 class="m-0 border-b border-divider text-base font-medium text-primary">
{{ category.name }}
</h4>
<div class="flex flex-col gap-2">
@@ -58,15 +58,14 @@
</div>
</div>
<div class="flex w-full flex-col">
<div class="flex w-full flex-col gap-2.5">
<label for="pat-expires">
<span class="label__title">{{ formatMessage(createModalMessages.expiresLabel) }}</span>
<span class="font-semibold">{{ formatMessage(createModalMessages.expiresLabel) }}</span>
</label>
<StyledInput id="pat-expires" v-model="expires" type="date" />
<p></p>
<DatePicker id="pat-expires" show-today v-model="expires" wrapper-class="w-full" />
</div>
<div class="ml-auto flex gap-2">
<div class="ml-auto mt-4 flex gap-2">
<ButtonStyled type="outlined">
<button @click="$refs.patModal.hide()">
<XIcon />
@@ -207,6 +206,7 @@ import {
commonSettingsMessages,
ConfirmModal,
CopyCode,
DatePicker,
defineMessages,
injectModrinthClient,
injectNotificationManager,
File diff suppressed because it is too large Load Diff
@@ -94,6 +94,28 @@ export const Range: Story = {
}),
}
export const SameDayRange: Story = {
render: () => ({
components: { DatePicker },
setup() {
const value = ref(['2026-04-27', '2026-04-27'])
return { value }
},
template: /* html */ `
<div class="flex max-w-sm flex-col gap-2">
<DatePicker
v-model="value"
wrapperClass="w-[350px]"
mode="range"
default-view-date="2026-04-01"
placeholder="Select a date range..."
/>
<p class="text-sm text-secondary">Selected value: {{ value?.join(' to ') || 'None' }}</p>
</div>
`,
}),
}
export const TwoMonthRange: Story = {
render: () => ({
components: { DatePicker },
@@ -125,7 +147,61 @@ export const TwoMonthRange: Story = {
}),
}
export const DraggableRange: Story = {
export const TwoMonthRangeAlignedRight: Story = {
render: () => ({
components: { DatePicker },
setup() {
const value = ref(['2033-11-16', '2033-12-21'])
const datePicker = ref<InstanceType<typeof DatePicker> | null>(null)
onMounted(async () => {
await nextTick()
datePicker.value?.open()
})
return { datePicker, value }
},
template: /* html */ `
<div class="flex h-[460px] max-w-[700px] flex-col gap-2">
<DatePicker
ref="datePicker"
v-model="value"
wrapperClass="w-[350px]"
mode="range"
:show-months="2"
view-date-alignment="right"
placeholder="Select a date range..."
/>
<p class="text-sm text-secondary">Selected value: {{ value?.join(' to ') || 'None' }}</p>
</div>
`,
}),
}
export const TwoMonthCalendarOnlyRange: Story = {
render: () => ({
components: { DatePicker },
setup() {
const value = ref(['2033-11-16', '2033-12-21'])
return { value }
},
template: /* html */ `
<div class="flex max-w-[700px] flex-col gap-2">
<DatePicker
v-model="value"
wrapperClass="w-full"
mode="range"
:show-months="2"
calendar-only
default-view-date="2033-11-01"
/>
<p class="text-sm text-secondary">Selected value: {{ value?.join(' to ') || 'None' }}</p>
</div>
`,
}),
}
export const AdjustableRange: Story = {
render: () => ({
components: { DatePicker },
setup() {
@@ -150,6 +226,9 @@ export const DraggableRange: Story = {
placeholder="Select a date range..."
/>
<p class="text-sm text-secondary">Selected value: {{ value?.join(' to ') || 'None' }}</p>
<p class="text-xs text-secondary">
Drag either endpoint, or click an endpoint, hover to preview, and click another day to move it.
</p>
</div>
`,
}),
@@ -213,6 +292,35 @@ export const OpenCalendar: Story = {
}),
}
export const ForceAbove: Story = {
render: () => ({
components: { DatePicker },
setup() {
const value = ref('2026-06-15')
const datePicker = ref<InstanceType<typeof DatePicker> | null>(null)
onMounted(async () => {
await nextTick()
datePicker.value?.open()
})
return { datePicker, value }
},
template: /* html */ `
<div class="flex h-[420px] max-w-sm flex-col justify-end gap-2 pb-6">
<DatePicker
ref="datePicker"
v-model="value"
wrapperClass="w-[300px]"
default-view-date="2026-06-01"
position="above"
/>
<p class="text-sm text-secondary">Selected value: {{ value || 'None' }}</p>
</div>
`,
}),
}
export const PreserveDay: Story = {
render: () => ({
components: { DatePicker },
@@ -270,9 +378,67 @@ export const ShowToday: Story = {
}),
}
export const InsideClippedContainer: Story = {
render: () => ({
components: { DatePicker },
setup() {
const value = ref('2026-04-27')
const datePicker = ref<InstanceType<typeof DatePicker> | null>(null)
onMounted(async () => {
await nextTick()
datePicker.value?.open()
})
return { datePicker, value }
},
template: /* html */ `
<div class="h-[220px] w-[360px] overflow-hidden rounded-xl border border-solid border-surface-5 bg-surface-2 p-4">
<div class="flex flex-col gap-2">
<DatePicker
ref="datePicker"
v-model="value"
wrapperClass="w-[300px]"
placeholder="Select a date..."
/>
<p class="text-sm text-secondary">Selected value: {{ value || 'None' }}</p>
</div>
</div>
`,
}),
}
export const Disabled: Story = {
args: {
modelValue: '2026-04-27',
disabled: true,
},
}
export const CalendarClass: Story = {
render: () => ({
components: { DatePicker },
setup() {
const value = ref('2026-04-27')
const datePicker = ref<InstanceType<typeof DatePicker> | null>(null)
onMounted(async () => {
await nextTick()
datePicker.value?.open()
})
return { datePicker, value }
},
template: /* html */ `
<div class="flex h-[420px] max-w-sm flex-col gap-2">
<DatePicker
ref="datePicker"
v-model="value"
wrapperClass="w-[300px]"
calendar-class="ring-2 ring-brand"
/>
<p class="text-sm text-secondary">Calendar has a brand-colored ring applied via calendarClass.</p>
</div>
`,
}),
}