wip(calendar)

This commit is contained in:
07akioni 2021-04-09 12:43:05 +08:00
parent aaa22493d0
commit 742567b033
19 changed files with 466 additions and 3 deletions

View File

@ -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',

View File

@ -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: '代码',

View File

@ -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),

View File

@ -0,0 +1,5 @@
# Basic
```html
<n-calendar />
```

View File

@ -0,0 +1,9 @@
<!--single-column-->
# Calendar
## Demos
```demo
basic
```

View File

@ -0,0 +1,5 @@
# Basic
```html
<n-calendar />
```

View File

@ -0,0 +1,9 @@
<!--single-column-->
# Calendar
## Demos
```demo
basic
```

1
src/calendar/index.ts Normal file
View File

@ -0,0 +1 @@
export { default as NCalendar } from './src/Calendar'

View 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>
)
}
})

View 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);
`)
])
])
])

View File

@ -0,0 +1,3 @@
export default {
titleFontSize: '22px'
}

View 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

View File

@ -0,0 +1,3 @@
export { default as calendarDark } from './dark'
export { default as calendarLight } from './light'
export type { CalendarTheme, CalendarThemeVars } from './light'

View 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

View File

@ -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'

View File

@ -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

View File

@ -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)
)

View File

@ -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,

View File

@ -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,