mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-04-12 14:40:47 +08:00
wip(calendar)
This commit is contained in:
parent
aaa22493d0
commit
742567b033
@ -397,6 +397,10 @@ export const enComponentRoutes = [
|
||||
path: 'n-skeleton',
|
||||
component: () => import('../../src/skeleton/demos/enUS/index.demo-entry.md')
|
||||
},
|
||||
{
|
||||
path: 'n-calendar',
|
||||
component: () => import('../../src/calendar/demos/enUS/index.demo-entry.md')
|
||||
},
|
||||
// deprecated
|
||||
{
|
||||
path: 'n-nimbus-service-layout',
|
||||
@ -726,6 +730,10 @@ export const zhComponentRoutes = [
|
||||
path: 'n-skeleton',
|
||||
component: () => import('../../src/skeleton/demos/zhCN/index.demo-entry.md')
|
||||
},
|
||||
{
|
||||
path: 'n-calendar',
|
||||
component: () => import('../../src/calendar/demos/zhCN/index.demo-entry.md')
|
||||
},
|
||||
// deprecated
|
||||
{
|
||||
path: 'n-nimbus-service-layout',
|
||||
|
@ -320,6 +320,12 @@ export function createComponentMenuOptions ({ lang, theme, mode }) {
|
||||
en: 'Data Display Components',
|
||||
type: 'group',
|
||||
children: [
|
||||
{
|
||||
en: 'Calendar',
|
||||
zh: '日历',
|
||||
enSuffix: true,
|
||||
path: '/n-calendar'
|
||||
},
|
||||
{
|
||||
en: 'Code',
|
||||
zh: '代码',
|
||||
|
@ -173,7 +173,7 @@ const derived = {
|
||||
actionColor: 'rgb(250, 250, 252)',
|
||||
tableHeaderColor: 'rgb(250, 250, 252)',
|
||||
|
||||
hoverColor: 'rgb(246, 246, 250)',
|
||||
hoverColor: 'rgb(246, 246, 248)',
|
||||
tableColorHover: overlay(base.alphaTablePending),
|
||||
activeColor: overlay(base.alphaActive),
|
||||
|
||||
|
5
src/calendar/demos/enUS/basic.demo.md
Normal file
5
src/calendar/demos/enUS/basic.demo.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Basic
|
||||
|
||||
```html
|
||||
<n-calendar />
|
||||
```
|
9
src/calendar/demos/enUS/index.demo-entry.md
Normal file
9
src/calendar/demos/enUS/index.demo-entry.md
Normal file
@ -0,0 +1,9 @@
|
||||
<!--single-column-->
|
||||
|
||||
# Calendar
|
||||
|
||||
## Demos
|
||||
|
||||
```demo
|
||||
basic
|
||||
```
|
5
src/calendar/demos/zhCN/basic.demo.md
Normal file
5
src/calendar/demos/zhCN/basic.demo.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Basic
|
||||
|
||||
```html
|
||||
<n-calendar />
|
||||
```
|
9
src/calendar/demos/zhCN/index.demo-entry.md
Normal file
9
src/calendar/demos/zhCN/index.demo-entry.md
Normal file
@ -0,0 +1,9 @@
|
||||
<!--single-column-->
|
||||
|
||||
# Calendar
|
||||
|
||||
## Demos
|
||||
|
||||
```demo
|
||||
basic
|
||||
```
|
1
src/calendar/index.ts
Normal file
1
src/calendar/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as NCalendar } from './src/Calendar'
|
186
src/calendar/src/Calendar.tsx
Normal file
186
src/calendar/src/Calendar.tsx
Normal file
@ -0,0 +1,186 @@
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
ExtractPropTypes,
|
||||
h,
|
||||
ref,
|
||||
PropType,
|
||||
CSSProperties
|
||||
} from 'vue'
|
||||
import {
|
||||
getDate,
|
||||
format,
|
||||
getYear,
|
||||
addMonths,
|
||||
startOfDay,
|
||||
startOfMonth
|
||||
} from 'date-fns'
|
||||
import { dateArray } from '../../date-picker/src/utils'
|
||||
import style from './styles/index.cssr'
|
||||
import { useLocale, useTheme } from '../../_mixins'
|
||||
import type { ThemeProps } from '../../_mixins'
|
||||
import { calendarLight } from '../styles'
|
||||
import type { CalendarTheme } from '../styles'
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from '../../_internal/icons'
|
||||
import { NBaseIcon } from '../../_internal'
|
||||
|
||||
const calendarProps = {
|
||||
...(useTheme.props as ThemeProps<CalendarTheme>),
|
||||
isDateDisabled: Function as PropType<(date: number) => boolean | undefined>
|
||||
} as const
|
||||
|
||||
export type CalendarProps = Partial<ExtractPropTypes<typeof calendarProps>>
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Calendar',
|
||||
props: calendarProps,
|
||||
setup (props) {
|
||||
const themeRef = useTheme(
|
||||
'Calendar',
|
||||
'Calendar',
|
||||
style,
|
||||
calendarLight,
|
||||
props
|
||||
)
|
||||
const { locale: localeRef, dateLocale: dateLocaleRef } = useLocale(
|
||||
'DatePicker'
|
||||
)
|
||||
const now = Date.now()
|
||||
// ts => timestamp
|
||||
const monthTsRef = ref(startOfMonth(now).valueOf())
|
||||
const valueRef = ref<number | null>(null)
|
||||
function handlePrevClick (): void {
|
||||
monthTsRef.value = addMonths(monthTsRef.value, -1).valueOf()
|
||||
}
|
||||
function handleNextClick (): void {
|
||||
monthTsRef.value = addMonths(monthTsRef.value, 1).valueOf()
|
||||
}
|
||||
return {
|
||||
locale: localeRef,
|
||||
dateLocale: dateLocaleRef,
|
||||
now,
|
||||
value: ref<number | null>(null),
|
||||
monthTs: monthTsRef,
|
||||
dateItems: computed(() => {
|
||||
return dateArray(
|
||||
monthTsRef.value,
|
||||
valueRef.value,
|
||||
now,
|
||||
localeRef.value.firstDayOfWeek,
|
||||
true
|
||||
)
|
||||
}),
|
||||
handlePrevClick,
|
||||
handleNextClick,
|
||||
cssVars: computed(() => {
|
||||
const {
|
||||
common: { cubicBezierEaseInOut },
|
||||
self: {
|
||||
borderColor,
|
||||
borderRadius,
|
||||
titleFontSize,
|
||||
textColor,
|
||||
titleFontWeight,
|
||||
titleTextColor,
|
||||
dayTextColor,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
dateColorCurrent,
|
||||
dateTextColorCurrent,
|
||||
cellColorHover,
|
||||
cellColorActive
|
||||
}
|
||||
} = themeRef.value
|
||||
return {
|
||||
'--bezier': cubicBezierEaseInOut,
|
||||
'--border-color': borderColor,
|
||||
'--border-radius': borderRadius,
|
||||
'--text-color': textColor,
|
||||
'--title-font-weight': titleFontWeight,
|
||||
'--title-font-size': titleFontSize,
|
||||
'--title-text-color': titleTextColor,
|
||||
'--day-text-color': dayTextColor,
|
||||
'--font-size': fontSize,
|
||||
'--line-height': lineHeight,
|
||||
'--date-color-current': dateColorCurrent,
|
||||
'--date-text-color-current': dateTextColorCurrent,
|
||||
'--cell-color-hover': cellColorHover,
|
||||
'--cell-color-active': cellColorActive
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const {
|
||||
isDateDisabled,
|
||||
monthTs,
|
||||
cssVars,
|
||||
value,
|
||||
dateLocale: { locale },
|
||||
handlePrevClick,
|
||||
handleNextClick
|
||||
} = this
|
||||
const normalizedValue = value && startOfDay(value).valueOf()
|
||||
return (
|
||||
<div class="n-calendar" style={cssVars as CSSProperties}>
|
||||
<div class="n-calendar-header">
|
||||
<NBaseIcon onClick={handlePrevClick} class="n-calendar-prev-btn">
|
||||
{{ default: () => <ChevronLeftIcon /> }}
|
||||
</NBaseIcon>
|
||||
<NBaseIcon onClick={handleNextClick} class="n-calendar-next-btn">
|
||||
{{ default: () => <ChevronRightIcon /> }}
|
||||
</NBaseIcon>
|
||||
{getYear(monthTs)} {format(monthTs, 'MMMM', { locale })}
|
||||
</div>
|
||||
<div
|
||||
class="n-calendar-dates"
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(7, minmax(0, 1fr))'
|
||||
}}
|
||||
>
|
||||
{this.dateItems.map(
|
||||
({ ts, inCurrentMonth, isCurrentDate }, index) => {
|
||||
const disabled = !inCurrentMonth || isDateDisabled?.(ts) === true
|
||||
return (
|
||||
<div
|
||||
key={isCurrentDate ? 'current' : index}
|
||||
class={[
|
||||
'n-calendar-cell',
|
||||
disabled && 'n-calendar-cell--disabled',
|
||||
isCurrentDate && 'n-calendar-cell--current',
|
||||
normalizedValue === startOfDay(ts).valueOf() &&
|
||||
'n-calendar-cell--selected'
|
||||
]}
|
||||
onClick={() => {
|
||||
this.value = ts
|
||||
this.monthTs = startOfMonth(ts).valueOf()
|
||||
}}
|
||||
>
|
||||
<div class="n-calendar-date">
|
||||
{disabled ? (
|
||||
<div class="n-calendar-date__date" key="disabled">
|
||||
{getDate(ts)}
|
||||
</div>
|
||||
) : (
|
||||
<div class="n-calendar-date__date" key="available">
|
||||
{getDate(ts)}
|
||||
</div>
|
||||
)}
|
||||
{index < 7 && (
|
||||
<div class="n-calendar-date__day">
|
||||
{format(ts, 'EEE', {
|
||||
locale
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
131
src/calendar/src/styles/index.cssr.ts
Normal file
131
src/calendar/src/styles/index.cssr.ts
Normal file
@ -0,0 +1,131 @@
|
||||
import { cB, cE, cM, c } from '../../../_utils/cssr'
|
||||
|
||||
// vars:
|
||||
// --bezier
|
||||
// --border-color
|
||||
// --border-radius
|
||||
// --text-color
|
||||
// --title-font-weight
|
||||
// --title-font-size
|
||||
// --title-text-color
|
||||
// --day-text-color
|
||||
// --font-size
|
||||
// --line-height
|
||||
// --date-color-current
|
||||
// --cell-color-hover
|
||||
// --cell-color-active
|
||||
export default cB('calendar', `
|
||||
line-height: var(--line-height);
|
||||
font-size: var(--font-size);
|
||||
color: var(--text-color);
|
||||
height: 720px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`, [
|
||||
cB('calendar-prev-btn', `
|
||||
margin-right: 6px;
|
||||
cursor: pointer;
|
||||
`),
|
||||
cB('calendar-next-btn', `
|
||||
margin-right: 12px;
|
||||
cursor: pointer;
|
||||
`),
|
||||
cB('calendar-header', `
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
font-size: var(--title-font-size);
|
||||
color: var(--title-text-color);
|
||||
font-weight: var(--title-font-weight);
|
||||
padding: 8px 0 18px 0;
|
||||
transition: color .3s var(--bezier);
|
||||
`),
|
||||
cB('calendar-dates', `
|
||||
border-radius: var(--border-radius);
|
||||
flex: 1;
|
||||
border-top: 1px solid;
|
||||
border-left: 1px solid;
|
||||
border-color: var(--border-color);
|
||||
transition: border-color .3s var(--bezier);
|
||||
`),
|
||||
cB('calendar-cell', `
|
||||
box-sizing: border-box;
|
||||
padding: 12px;
|
||||
border-right: 1px solid;
|
||||
border-bottom: 1px solid;
|
||||
border-color: var(--border-color);
|
||||
cursor: pointer;
|
||||
background-clip: padding-box;
|
||||
transition:
|
||||
color .3s var(--bezier),
|
||||
border-color .3s var(--bezier),
|
||||
background-color .3s var(--bezier);
|
||||
`, [
|
||||
c('&:nth-child(7)', `
|
||||
border-top-right-radius: var(--border-radius);
|
||||
`),
|
||||
c('&:nth-last-child(7)', `
|
||||
border-bottom-left-radius: var(--border-radius);
|
||||
`),
|
||||
c('&:last-child', `
|
||||
border-bottom-right-radius: var(--border-radius);
|
||||
`),
|
||||
c('&:hover', `
|
||||
background-color: var(--cell-color-hover);
|
||||
`),
|
||||
cM('selected', `
|
||||
background-color: var(--cell-color-active);
|
||||
`),
|
||||
cB('calendar-date', `
|
||||
transition:
|
||||
color .3s var(--bezier),
|
||||
border-color .3s var(--bezier),
|
||||
background-color .3s var(--bezier);
|
||||
color: var(--text-color);
|
||||
`, [
|
||||
cE('date', `
|
||||
color: var(--text-color);
|
||||
`)
|
||||
]),
|
||||
cM('disabled', [
|
||||
cB('calendar-date', [
|
||||
cE('date', `
|
||||
color: var(--day-text-color);
|
||||
`)
|
||||
])
|
||||
]),
|
||||
cM('current', [
|
||||
cB('calendar-date', [
|
||||
cE('date', `
|
||||
color: var(--date-text-color-current);
|
||||
background-color: var(--date-color-current);
|
||||
`)
|
||||
])
|
||||
]),
|
||||
cB('calendar-date', `
|
||||
position: relative;
|
||||
line-height: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 1em;
|
||||
justify-content: space-between;
|
||||
`, [
|
||||
cE('date', `
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: -0.5em;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
transition:
|
||||
color .3s var(--bezier),
|
||||
background-color .3s var(--bezier);
|
||||
`),
|
||||
cE('day', `
|
||||
color: var(--day-text-color);
|
||||
transition: color .3s var(--bezier);
|
||||
`)
|
||||
])
|
||||
])
|
||||
])
|
3
src/calendar/styles/_common.ts
Normal file
3
src/calendar/styles/_common.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
titleFontSize: '22px'
|
||||
}
|
41
src/calendar/styles/dark.ts
Normal file
41
src/calendar/styles/dark.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { commonDark } from '../../_styles/common'
|
||||
import commonVariables from './_common'
|
||||
import type { CalendarTheme } from './light'
|
||||
import { changeColor } from 'seemly'
|
||||
|
||||
const calendarDark: CalendarTheme = {
|
||||
name: 'Calendar',
|
||||
common: commonDark,
|
||||
self (vars) {
|
||||
const {
|
||||
borderRadius,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
textColor2,
|
||||
textColor1,
|
||||
textColorDisabled,
|
||||
dividerColor,
|
||||
fontWeightStrong,
|
||||
primaryColor,
|
||||
baseColor,
|
||||
hoverColor
|
||||
} = vars
|
||||
return {
|
||||
...commonVariables,
|
||||
borderRadius,
|
||||
borderColor: dividerColor,
|
||||
textColor: textColor2,
|
||||
titleTextColor: textColor1,
|
||||
titleFontWeight: fontWeightStrong,
|
||||
dayTextColor: textColorDisabled,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
dateColorCurrent: primaryColor,
|
||||
dateTextColorCurrent: baseColor,
|
||||
cellColorHover: hoverColor,
|
||||
cellColorActive: changeColor(primaryColor, { alpha: 0.12 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default calendarDark
|
3
src/calendar/styles/index.ts
Normal file
3
src/calendar/styles/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { default as calendarDark } from './dark'
|
||||
export { default as calendarLight } from './light'
|
||||
export type { CalendarTheme, CalendarThemeVars } from './light'
|
47
src/calendar/styles/light.ts
Normal file
47
src/calendar/styles/light.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { commonLight } from '../../_styles/common'
|
||||
import type { ThemeCommonVars } from '../../_styles/common'
|
||||
import commonVariables from './_common'
|
||||
import { Theme } from '../../_mixins'
|
||||
import { changeColor } from 'seemly'
|
||||
|
||||
const self = (vars: ThemeCommonVars) => {
|
||||
const {
|
||||
borderRadius,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
textColor2,
|
||||
textColor1,
|
||||
textColorDisabled,
|
||||
dividerColor,
|
||||
fontWeightStrong,
|
||||
primaryColor,
|
||||
baseColor,
|
||||
hoverColor
|
||||
} = vars
|
||||
return {
|
||||
...commonVariables,
|
||||
borderRadius,
|
||||
borderColor: dividerColor,
|
||||
textColor: textColor2,
|
||||
titleFontWeight: fontWeightStrong,
|
||||
titleTextColor: textColor1,
|
||||
dayTextColor: textColorDisabled,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
dateColorCurrent: primaryColor,
|
||||
dateTextColorCurrent: baseColor,
|
||||
cellColorHover: hoverColor,
|
||||
cellColorActive: changeColor(primaryColor, { alpha: 0.12 })
|
||||
}
|
||||
}
|
||||
|
||||
export type CalendarThemeVars = ReturnType<typeof self>
|
||||
|
||||
const calendarLight: Theme<'Calendar', CalendarThemeVars> = {
|
||||
name: 'Calendar',
|
||||
common: commonLight,
|
||||
self
|
||||
}
|
||||
|
||||
export default calendarLight
|
||||
export type CalendarTheme = typeof calendarLight
|
@ -7,6 +7,7 @@ export * from './back-top'
|
||||
export * from './badge'
|
||||
export * from './breadcrumb'
|
||||
export * from './button'
|
||||
export * from './calendar'
|
||||
export * from './card'
|
||||
export * from './cascader'
|
||||
export * from './checkbox'
|
||||
|
@ -7,6 +7,7 @@ import type { BackTopTheme } from '../../back-top/styles'
|
||||
import type { BadgeTheme } from '../../badge/styles'
|
||||
import type { BreadcrumbTheme } from '../../breadcrumb/styles'
|
||||
import type { ButtonTheme } from '../../button/styles'
|
||||
import type { CalendarTheme } from '../../calendar/styles'
|
||||
import type { CardTheme } from '../../card/styles'
|
||||
import type { CascaderTheme } from '../../cascader/styles'
|
||||
import type { CheckboxTheme } from '../../checkbox/styles'
|
||||
@ -89,6 +90,7 @@ export interface GlobalThemeWithoutCommon {
|
||||
Badge?: BadgeTheme
|
||||
Breadcrumb?: BreadcrumbTheme
|
||||
Button?: ButtonTheme
|
||||
Calendar?: CalendarTheme
|
||||
Card?: CardTheme
|
||||
Cascader?: CascaderTheme
|
||||
Checkbox?: CheckboxTheme
|
||||
|
@ -99,7 +99,8 @@ function dateArray (
|
||||
monthTs: number,
|
||||
valueTs: number | [number, number] | null,
|
||||
currentTs: number,
|
||||
startDay: 0 | 1 | 2 | 3 | 4 | 5 | 6
|
||||
startDay: 0 | 1 | 2 | 3 | 4 | 5 | 6,
|
||||
stripTail: boolean = false
|
||||
): DateItem[] {
|
||||
const displayMonth = getMonth(monthTs)
|
||||
// First day of current month
|
||||
@ -124,7 +125,8 @@ function dateArray (
|
||||
)
|
||||
displayMonthIterator = getTime(addDays(displayMonthIterator, 1))
|
||||
}
|
||||
while (calendarDays.length < 42) {
|
||||
const endIndex = calendarDays.length <= 35 && stripTail ? 35 : 42
|
||||
while (calendarDays.length < endIndex) {
|
||||
calendarDays.push(
|
||||
dateItem(displayMonthIterator, monthTs, valueTs, currentTs)
|
||||
)
|
||||
|
@ -7,6 +7,7 @@ import { backTopDark } from '../back-top/styles'
|
||||
import { badgeDark } from '../badge/styles'
|
||||
import { breadcrumbDark } from '../breadcrumb/styles'
|
||||
import { buttonDark } from '../button/styles'
|
||||
import { calendarDark } from '../calendar/styles'
|
||||
import { cardDark } from '../card/styles'
|
||||
import { cascaderDark } from '../cascader/styles'
|
||||
import { checkboxDark } from '../checkbox/styles'
|
||||
@ -80,6 +81,7 @@ export const darkTheme: BuiltInGlobalTheme = {
|
||||
Badge: badgeDark,
|
||||
Breadcrumb: breadcrumbDark,
|
||||
Button: buttonDark,
|
||||
Calendar: calendarDark,
|
||||
Card: cardDark,
|
||||
Cascader: cascaderDark,
|
||||
Checkbox: checkboxDark,
|
||||
|
@ -9,6 +9,7 @@ import { backTopLight } from '../back-top/styles'
|
||||
import { badgeLight } from '../badge/styles'
|
||||
import { breadcrumbLight } from '../breadcrumb/styles'
|
||||
import { buttonLight } from '../button/styles'
|
||||
import { calendarLight } from '../calendar/styles'
|
||||
import { cardLight } from '../card/styles'
|
||||
import { cascaderLight } from '../cascader/styles'
|
||||
import { checkboxLight } from '../checkbox/styles'
|
||||
@ -82,6 +83,7 @@ export const lightTheme: BuiltInGlobalTheme = {
|
||||
Badge: badgeLight,
|
||||
Breadcrumb: breadcrumbLight,
|
||||
Button: buttonLight,
|
||||
Calendar: calendarLight,
|
||||
Card: cardLight,
|
||||
Cascader: cascaderLight,
|
||||
Checkbox: checkboxLight,
|
||||
|
Loading…
x
Reference in New Issue
Block a user