Skip to content

Commit aa6e555

Browse files
authored
feat: add mask layer for date picker on the side page (#620) (#641)
1 parent 9e59084 commit aa6e555

File tree

7 files changed

+115
-27
lines changed

7 files changed

+115
-27
lines changed

package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,19 @@
3131
"@crowdin/crowdin-api-client": "^1.49.0",
3232
"@emotion/babel-plugin": "^11.13.5",
3333
"@emotion/css": "^11.13.5",
34-
"@rsdoctor/rspack-plugin": "^1.3.11",
35-
"@rspack/cli": "^1.6.4",
36-
"@rspack/core": "^1.6.4",
34+
"@rsdoctor/rspack-plugin": "^1.3.12",
35+
"@rspack/cli": "^1.6.6",
36+
"@rspack/core": "^1.6.6",
3737
"@swc/core": "^1.15.3",
3838
"@swc/jest": "^0.2.39",
39-
"@types/chrome": "0.1.31",
39+
"@types/chrome": "0.1.32",
4040
"@types/decompress": "^4.2.7",
4141
"@types/jest": "^30.0.0",
4242
"@types/node": "^24.10.1",
4343
"@types/punycode": "^2.1.4",
4444
"@vue/babel-plugin-jsx": "^2.0.1",
4545
"babel-loader": "^10.0.0",
46-
"commitlint": "^20.1.0",
46+
"commitlint": "^20.2.0",
4747
"css-loader": "^7.1.2",
4848
"decompress": "^4.2.1",
4949
"husky": "^9.1.7",
@@ -54,7 +54,7 @@
5454
"postcss": "^8.5.6",
5555
"postcss-loader": "^8.2.0",
5656
"postcss-rtlcss": "^5.7.1",
57-
"puppeteer": "^24.31.0",
57+
"puppeteer": "^24.32.0",
5858
"ts-loader": "^9.5.4",
5959
"ts-node": "^10.9.2",
6060
"tsconfig-paths": "^4.2.0",
@@ -63,9 +63,9 @@
6363
"dependencies": {
6464
"@element-plus/icons-vue": "^2.3.2",
6565
"echarts": "^6.0.0",
66-
"element-plus": "2.11.8",
66+
"element-plus": "2.12.0",
6767
"punycode": "^2.3.1",
68-
"vue": "^3.5.24",
68+
"vue": "^3.5.25",
6969
"vue-router": "^4.6.3"
7070
},
7171
"engines": {

src/api/chrome/tab.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77

88
import { handleError } from "./common"
99

10-
export function getTab(id: number): Promise<ChromeTab> {
10+
export function getTab(id: number): Promise<ChromeTab | undefined> {
11+
if (id < 0) {
12+
return Promise.resolve(undefined)
13+
}
1114
return new Promise(resolve => chrome.tabs.get(id, tab => {
1215
handleError("getTab")
1316
resolve(tab)

src/background/track-server/file-tracker.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type Context = {
1212

1313
async function convertContext(tabId: number): Promise<Context | null> {
1414
const tab = await getTab(tabId)
15+
if (!tab) return null
1516
const { active, url } = tab
1617
if (!active || !url) return null
1718
const fileHost = extractFileHost(url)

src/background/whitelist-menu-manager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const menuInitialOptions: ChromeContextMenuCreateProps = {
3333
async function updateContextMenuInner(param: ChromeTab | number | undefined): Promise<void> {
3434
if (typeof param === 'number') {
3535
// If number, get the tabInfo first
36-
const tab: ChromeTab = await getTab(currentActiveId)
36+
const tab = await getTab(currentActiveId)
3737
tab && await updateContextMenuInner(tab)
3838
} else {
3939
const { url } = param || {}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { formatTimeYMD } from '@util/time'
2+
import { type DateCell, useNamespace } from "element-plus"
3+
import { computed, defineComponent } from 'vue'
4+
5+
const DOT_SIZE = '3px'
6+
const DATE_SIZE = '22px'
7+
8+
const Cell = defineComponent<{ cell: DateCell, dataDates: string[] }>(props => {
9+
const text = computed(() => {
10+
const { renderText, text } = props.cell
11+
return renderText ?? text
12+
})
13+
const hasData = computed(() => {
14+
const { date } = props.cell
15+
if (!date) return false
16+
const dateStr = formatTimeYMD(date)
17+
return props.dataDates.includes(dateStr)
18+
})
19+
const ns = useNamespace('date-table-cell')
20+
21+
return () => (
22+
<div class={ns.b()}>
23+
<span
24+
class={ns.e('text')}
25+
style={{ width: DATE_SIZE, height: DATE_SIZE, lineHeight: DATE_SIZE }}
26+
>{text.value}</span>
27+
{hasData.value && <span style={{
28+
position: 'absolute',
29+
width: DOT_SIZE,
30+
height: DOT_SIZE,
31+
background: 'var(--el-color-primary)',
32+
borderRadius: '50%',
33+
bottom: '1px',
34+
left: '50%',
35+
transform: 'translateX(-50%)',
36+
}} />}
37+
</div>
38+
)
39+
}, { props: ['cell', 'dataDates'] })
40+
41+
export default Cell

src/pages/side/components/Search.tsx renamed to src/pages/side/components/Search/index.tsx

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { Search } from "@element-plus/icons-vue"
22
import { css } from '@emotion/css'
33
import { useState } from "@hooks"
44
import Flex from "@pages/components/Flex"
5-
import { getDatePickerIconSlots } from "@pages/element-ui/rtl"
5+
import { getDatePickerIconSlots } from '@pages/element-ui/rtl'
66
import { t } from "@side/locale"
7-
import { ElDatePicker, ElInput, useNamespace } from "element-plus"
8-
import { defineComponent, watch } from "vue"
7+
import { type DateCell, ElDatePicker, ElInput, useNamespace } from "element-plus"
8+
import { defineComponent, h } from "vue"
9+
import Cell from './Cell'
10+
import { useDatePicker } from './useDatePicker'
911

1012
const useCalendarStyle = () => {
1113
const inputNs = useNamespace('input')
@@ -48,13 +50,12 @@ type Props = {
4850
}
4951

5052
const _default = defineComponent<Props>(props => {
51-
const now = Date.now()
52-
5353
const [query, setQuery] = useState(props.defaultQuery)
54-
const [date, setDate] = useState(props.defaultDate)
5554
const handleSearch = () => props.onSearch?.(query.value.trim(), date.value)
56-
57-
watch(date, handleSearch)
55+
const {
56+
date, setDate, dataDates,
57+
disabledDate, onPanelChange,
58+
} = useDatePicker({ onChange: handleSearch })
5859

5960
const [calendarCls, popoverCls] = useCalendarStyle()
6061

@@ -72,15 +73,19 @@ const _default = defineComponent<Props>(props => {
7273
}}
7374
onKeydown={kv => (kv as KeyboardEvent).code === 'Enter' && handleSearch()}
7475
/>
75-
<ElDatePicker
76-
clearable={false}
77-
disabledDate={(date: Date) => date.getTime() > now}
78-
modelValue={date.value}
79-
onUpdate:modelValue={setDate}
80-
class={calendarCls}
81-
popperClass={popoverCls}
82-
v-slots={getDatePickerIconSlots()}
83-
/>
76+
{/* The events of date picker is not compatible with typescript, so use h() to workaround */}
77+
{h(ElDatePicker, {
78+
clearable: false,
79+
disabledDate,
80+
modelValue: date.value,
81+
'onUpdate:modelValue': setDate,
82+
class: calendarCls,
83+
popperClass: popoverCls,
84+
onPanelChange,
85+
}, {
86+
...getDatePickerIconSlots(),
87+
default: (cell: DateCell) => <Cell cell={cell} dataDates={dataDates.value} />,
88+
})}
8489
</Flex>
8590
)
8691
}, { props: ['defaultDate', 'defaultQuery', 'onSearch'] })
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { useRequest } from '@hooks/useRequest'
2+
import { useState } from '@hooks/useState'
3+
import { selectSite } from '@service/stat-service'
4+
import { getMonthTime, MILL_PER_WEEK } from '@util/time'
5+
import { watch } from 'vue'
6+
7+
export const useDatePicker = (options: { onChange: ArgCallback<Date> }) => {
8+
const { onChange } = options
9+
const [date, setDate] = useState(new Date())
10+
11+
watch(date, val => onChange(val))
12+
13+
const { data: dataDates, refresh: refreshDates } = useRequest(async (dateInMonth: Date) => {
14+
const [ms, me] = getMonthTime(dateInMonth)
15+
const start = new Date(ms.getTime() - ms.getDay() * MILL_PER_WEEK)
16+
const end = new Date(me.getTime() + (6 - me.getDay()) * MILL_PER_WEEK)
17+
18+
const stats = await selectSite({ date: [start, end] })
19+
const dateSet = new Set<string>()
20+
stats.forEach(({ date }) => date && dateSet.add(date))
21+
return Array.from(dateSet)
22+
}, { defaultValue: [], defaultParam: [new Date()] })
23+
24+
const onPanelChange = (val: Date | Date[], mode: 'year' | 'month') => {
25+
if (mode !== 'month') return
26+
const date = Array.isArray(val) ? val[0] : val
27+
if (!date) return
28+
refreshDates(date)
29+
}
30+
31+
const disabledDate = (date: Date) => date.getTime() > Date.now()
32+
33+
return {
34+
dataDates, date, setDate,
35+
onPanelChange,
36+
disabledDate,
37+
}
38+
}

0 commit comments

Comments
 (0)