@coze-arch/cli 0.0.1-alpha.de5a13 → 0.0.1-alpha.deaedf
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/lib/__templates__/expo/.cozeproj/scripts/server_dev_run.sh +1 -1
- package/lib/__templates__/expo/client/components/Screen.tsx +2 -2
- package/lib/__templates__/expo/client/eslint.config.mjs +7 -0
- package/lib/__templates__/expo/client/metro.config.js +3 -0
- package/lib/__templates__/expo/client/scripts/install-missing-deps.js +10 -10
- package/lib/__templates__/expo/eslint-plugins/forbid-emoji/index.js +9 -0
- package/lib/__templates__/expo/eslint-plugins/forbid-emoji/rule.js +112 -0
- package/lib/__templates__/expo/eslint-plugins/forbid-emoji/tech.md +94 -0
- package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/index.js +9 -0
- package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/rule.js +120 -0
- package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/tech.md +58 -0
- package/lib/__templates__/expo/pnpm-lock.yaml +8 -5
- package/lib/__templates__/expo/server/package.json +1 -1
- package/lib/__templates__/native-static/.coze +11 -0
- package/lib/__templates__/native-static/index.html +33 -0
- package/lib/__templates__/native-static/styles/main.css +136 -0
- package/lib/__templates__/native-static/template.config.js +22 -0
- package/lib/__templates__/nextjs/.babelrc +3 -0
- package/lib/__templates__/nextjs/README.md +5 -0
- package/lib/__templates__/nextjs/eslint.config.mjs +5 -0
- package/lib/__templates__/nextjs/next.config.ts +1 -2
- package/lib/__templates__/nextjs/package.json +3 -6
- package/lib/__templates__/nextjs/pnpm-lock.yaml +1036 -10
- package/lib/__templates__/nextjs/scripts/build.sh +4 -1
- package/lib/__templates__/nextjs/scripts/dev.sh +8 -2
- package/lib/__templates__/nextjs/scripts/start.sh +7 -1
- package/lib/__templates__/nextjs/src/app/layout.tsx +1 -1
- package/lib/__templates__/nextjs/src/app/page.tsx +1 -2
- package/lib/__templates__/nextjs/src/server.ts +35 -0
- package/lib/__templates__/nextjs/tsconfig.json +1 -1
- package/lib/__templates__/nuxt-vue/.coze +12 -0
- package/lib/__templates__/nuxt-vue/README.md +73 -0
- package/lib/__templates__/nuxt-vue/_gitignore +24 -0
- package/lib/__templates__/nuxt-vue/_npmrc +23 -0
- package/lib/__templates__/nuxt-vue/app/app.vue +6 -0
- package/lib/__templates__/nuxt-vue/app/pages/index.vue +23 -0
- package/lib/__templates__/nuxt-vue/assets/css/main.css +24 -0
- package/lib/__templates__/nuxt-vue/nuxt.config.ts +116 -0
- package/lib/__templates__/nuxt-vue/package.json +35 -0
- package/lib/__templates__/nuxt-vue/pnpm-lock.yaml +8759 -0
- package/lib/__templates__/nuxt-vue/postcss.config.mjs +8 -0
- package/lib/__templates__/nuxt-vue/public/favicon.ico +0 -0
- package/lib/__templates__/nuxt-vue/public/robots.txt +2 -0
- package/lib/__templates__/nuxt-vue/scripts/build.sh +14 -0
- package/lib/__templates__/nuxt-vue/scripts/dev.sh +39 -0
- package/lib/__templates__/nuxt-vue/scripts/prepare.sh +14 -0
- package/lib/__templates__/nuxt-vue/scripts/start.sh +21 -0
- package/lib/__templates__/nuxt-vue/server/api/hello.ts +10 -0
- package/lib/__templates__/nuxt-vue/server/middleware/logger.ts +10 -0
- package/lib/__templates__/nuxt-vue/server/routes/health.ts +10 -0
- package/lib/__templates__/nuxt-vue/tailwind.config.js +13 -0
- package/lib/__templates__/nuxt-vue/template.config.js +87 -0
- package/lib/__templates__/nuxt-vue/tsconfig.json +18 -0
- package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +107 -37
- package/lib/__templates__/taro/.cozeproj/scripts/pack.sh +24 -1
- package/lib/__templates__/taro/README.md +138 -62
- package/lib/__templates__/taro/config/index.ts +105 -41
- package/lib/__templates__/taro/config/prod.ts +4 -5
- package/lib/__templates__/taro/eslint.config.mjs +82 -4
- package/lib/__templates__/taro/package.json +23 -7
- package/lib/__templates__/taro/patches/@tarojs__plugin-mini-ci@4.1.9.patch +30 -0
- package/lib/__templates__/taro/pnpm-lock.yaml +1198 -214
- package/lib/__templates__/taro/server/package.json +3 -1
- package/lib/__templates__/taro/src/app.css +140 -47
- package/lib/__templates__/taro/src/app.tsx +9 -0
- package/lib/__templates__/taro/src/components/ui/accordion.tsx +159 -0
- package/lib/__templates__/taro/src/components/ui/alert-dialog.tsx +260 -0
- package/lib/__templates__/taro/src/components/ui/alert.tsx +60 -0
- package/lib/__templates__/taro/src/components/ui/aspect-ratio.tsx +36 -0
- package/lib/__templates__/taro/src/components/ui/avatar.tsx +84 -0
- package/lib/__templates__/taro/src/components/ui/badge.tsx +37 -0
- package/lib/__templates__/taro/src/components/ui/breadcrumb.tsx +117 -0
- package/lib/__templates__/taro/src/components/ui/button-group.tsx +83 -0
- package/lib/__templates__/taro/src/components/ui/button.tsx +67 -0
- package/lib/__templates__/taro/src/components/ui/calendar.tsx +394 -0
- package/lib/__templates__/taro/src/components/ui/card.tsx +108 -0
- package/lib/__templates__/taro/src/components/ui/carousel.tsx +228 -0
- package/lib/__templates__/taro/src/components/ui/checkbox.tsx +58 -0
- package/lib/__templates__/taro/src/components/ui/code-block.tsx +169 -0
- package/lib/__templates__/taro/src/components/ui/collapsible.tsx +71 -0
- package/lib/__templates__/taro/src/components/ui/command.tsx +385 -0
- package/lib/__templates__/taro/src/components/ui/context-menu.tsx +614 -0
- package/lib/__templates__/taro/src/components/ui/dialog.tsx +256 -0
- package/lib/__templates__/taro/src/components/ui/drawer.tsx +192 -0
- package/lib/__templates__/taro/src/components/ui/dropdown-menu.tsx +561 -0
- package/lib/__templates__/taro/src/components/ui/field.tsx +228 -0
- package/lib/__templates__/taro/src/components/ui/hover-card.tsx +282 -0
- package/lib/__templates__/taro/src/components/ui/input-group.tsx +197 -0
- package/lib/__templates__/taro/src/components/ui/input-otp.tsx +136 -0
- package/lib/__templates__/taro/src/components/ui/input.tsx +56 -0
- package/lib/__templates__/taro/src/components/ui/label.tsx +24 -0
- package/lib/__templates__/taro/src/components/ui/menubar.tsx +595 -0
- package/lib/__templates__/taro/src/components/ui/navigation-menu.tsx +264 -0
- package/lib/__templates__/taro/src/components/ui/pagination.tsx +118 -0
- package/lib/__templates__/taro/src/components/ui/popover.tsx +291 -0
- package/lib/__templates__/taro/src/components/ui/portal.tsx +19 -0
- package/lib/__templates__/taro/src/components/ui/progress.tsx +28 -0
- package/lib/__templates__/taro/src/components/ui/radio-group.tsx +64 -0
- package/lib/__templates__/taro/src/components/ui/resizable.tsx +346 -0
- package/lib/__templates__/taro/src/components/ui/scroll-area.tsx +34 -0
- package/lib/__templates__/taro/src/components/ui/select.tsx +438 -0
- package/lib/__templates__/taro/src/components/ui/separator.tsx +30 -0
- package/lib/__templates__/taro/src/components/ui/sheet.tsx +262 -0
- package/lib/__templates__/taro/src/components/ui/skeleton.tsx +17 -0
- package/lib/__templates__/taro/src/components/ui/slider.tsx +203 -0
- package/lib/__templates__/taro/src/components/ui/sonner.tsx +1 -0
- package/lib/__templates__/taro/src/components/ui/switch.tsx +55 -0
- package/lib/__templates__/taro/src/components/ui/table.tsx +142 -0
- package/lib/__templates__/taro/src/components/ui/tabs.tsx +114 -0
- package/lib/__templates__/taro/src/components/ui/textarea.tsx +54 -0
- package/lib/__templates__/taro/src/components/ui/toast.tsx +517 -0
- package/lib/__templates__/taro/src/components/ui/toggle-group.tsx +120 -0
- package/lib/__templates__/taro/src/components/ui/toggle.tsx +77 -0
- package/lib/__templates__/taro/src/components/ui/tooltip.tsx +455 -0
- package/lib/__templates__/taro/src/lib/hooks/use-keyboard-offset.ts +37 -0
- package/lib/__templates__/taro/src/lib/measure.ts +115 -0
- package/lib/__templates__/taro/src/lib/platform.ts +12 -0
- package/lib/__templates__/taro/src/lib/utils.ts +6 -0
- package/lib/__templates__/taro/src/presets/dev-debug.ts +23 -0
- package/lib/__templates__/taro/src/presets/h5-container.tsx +15 -0
- package/lib/__templates__/taro/src/presets/h5-navbar.tsx +238 -0
- package/lib/__templates__/taro/src/presets/h5-styles.ts +220 -0
- package/lib/__templates__/taro/src/presets/index.tsx +18 -0
- package/lib/__templates__/templates.json +43 -0
- package/lib/__templates__/vite/README.md +190 -11
- package/lib/__templates__/vite/_gitignore +1 -0
- package/lib/__templates__/vite/eslint.config.mjs +6 -1
- package/lib/__templates__/vite/package.json +14 -5
- package/lib/__templates__/vite/pnpm-lock.yaml +768 -24
- package/lib/__templates__/vite/scripts/build.sh +4 -1
- package/lib/__templates__/vite/scripts/dev.sh +9 -2
- package/lib/__templates__/vite/scripts/start.sh +9 -3
- package/lib/__templates__/vite/server/routes/index.ts +31 -0
- package/lib/__templates__/vite/server/server.ts +65 -0
- package/lib/__templates__/vite/server/vite.ts +67 -0
- package/lib/__templates__/vite/tsconfig.json +4 -3
- package/lib/__templates__/vite/vite.config.ts +5 -0
- package/lib/cli.js +124 -103
- package/package.json +7 -3
- package/lib/__templates__/taro/src/app.ts +0 -14
- package/lib/__templates__/taro/src/utils/h5-styles.ts +0 -22
- package/lib/__templates__/taro/src/utils/wx-debug.ts +0 -23
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Picker, Text, View } from "@tarojs/components"
|
|
3
|
+
import { ChevronDown, ChevronLeft, ChevronRight } from "lucide-react-taro"
|
|
4
|
+
import {
|
|
5
|
+
addDays,
|
|
6
|
+
addMonths,
|
|
7
|
+
endOfMonth,
|
|
8
|
+
endOfWeek,
|
|
9
|
+
format,
|
|
10
|
+
isAfter,
|
|
11
|
+
isBefore,
|
|
12
|
+
isSameDay,
|
|
13
|
+
isSameMonth,
|
|
14
|
+
startOfMonth,
|
|
15
|
+
startOfWeek,
|
|
16
|
+
subMonths,
|
|
17
|
+
} from "date-fns"
|
|
18
|
+
|
|
19
|
+
import { cn } from "@/lib/utils"
|
|
20
|
+
import { Button } from "@/components/ui/button"
|
|
21
|
+
|
|
22
|
+
type DateRange = { from?: Date; to?: Date }
|
|
23
|
+
|
|
24
|
+
type CommonProps = {
|
|
25
|
+
className?: string
|
|
26
|
+
month?: Date
|
|
27
|
+
defaultMonth?: Date
|
|
28
|
+
onMonthChange?: (month: Date) => void
|
|
29
|
+
showOutsideDays?: boolean
|
|
30
|
+
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6
|
|
31
|
+
disabled?: ((date: Date) => boolean) | Date[]
|
|
32
|
+
captionLayout?: "label" | "dropdown"
|
|
33
|
+
fromYear?: number
|
|
34
|
+
toYear?: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type SingleProps = CommonProps & {
|
|
38
|
+
mode?: "single"
|
|
39
|
+
selected?: Date
|
|
40
|
+
onSelect?: (date: Date | undefined) => void
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type RangeProps = CommonProps & {
|
|
44
|
+
mode: "range"
|
|
45
|
+
selected?: DateRange
|
|
46
|
+
onSelect?: (range: DateRange | undefined) => void
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type CalendarProps = SingleProps | RangeProps
|
|
50
|
+
|
|
51
|
+
function isDateDisabled(date: Date, disabled?: CalendarProps["disabled"]) {
|
|
52
|
+
if (!disabled) return false
|
|
53
|
+
if (Array.isArray(disabled)) return disabled.some((d) => isSameDay(d, date))
|
|
54
|
+
return disabled(date)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isInRange(date: Date, range?: DateRange) {
|
|
58
|
+
if (!range?.from || !range?.to) return false
|
|
59
|
+
return (
|
|
60
|
+
(isAfter(date, range.from) || isSameDay(date, range.from)) &&
|
|
61
|
+
(isBefore(date, range.to) || isSameDay(date, range.to))
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getSingleSelected(props: CalendarProps) {
|
|
66
|
+
return props.mode === "range" ? undefined : props.selected
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getRangeSelected(props: CalendarProps) {
|
|
70
|
+
return props.mode === "range" ? props.selected : undefined
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function Calendar({
|
|
74
|
+
className,
|
|
75
|
+
month,
|
|
76
|
+
defaultMonth,
|
|
77
|
+
onMonthChange,
|
|
78
|
+
showOutsideDays = true,
|
|
79
|
+
weekStartsOn = 0,
|
|
80
|
+
disabled,
|
|
81
|
+
captionLayout = "dropdown",
|
|
82
|
+
fromYear,
|
|
83
|
+
toYear,
|
|
84
|
+
...props
|
|
85
|
+
}: CalendarProps) {
|
|
86
|
+
const singleSelected = getSingleSelected({ month, defaultMonth, onMonthChange, showOutsideDays, weekStartsOn, disabled, className, ...props } as CalendarProps)
|
|
87
|
+
const rangeSelected = getRangeSelected({ month, defaultMonth, onMonthChange, showOutsideDays, weekStartsOn, disabled, className, ...props } as CalendarProps)
|
|
88
|
+
|
|
89
|
+
const initialMonth = React.useMemo(() => {
|
|
90
|
+
if (month) return month
|
|
91
|
+
if (defaultMonth) return defaultMonth
|
|
92
|
+
if (singleSelected) return singleSelected
|
|
93
|
+
if (rangeSelected?.from) return rangeSelected.from
|
|
94
|
+
return new Date()
|
|
95
|
+
}, [defaultMonth, month, rangeSelected?.from, singleSelected])
|
|
96
|
+
|
|
97
|
+
const [uncontrolledMonth, setUncontrolledMonth] = React.useState<Date>(
|
|
98
|
+
initialMonth
|
|
99
|
+
)
|
|
100
|
+
const visibleMonth = month ?? uncontrolledMonth
|
|
101
|
+
|
|
102
|
+
const setMonth = React.useCallback(
|
|
103
|
+
(next: Date) => {
|
|
104
|
+
if (!month) setUncontrolledMonth(next)
|
|
105
|
+
onMonthChange?.(next)
|
|
106
|
+
},
|
|
107
|
+
[month, onMonthChange]
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
const captionHasDropdown = captionLayout === "dropdown"
|
|
111
|
+
const captionHasButtons = true
|
|
112
|
+
|
|
113
|
+
const yearOptions = React.useMemo(() => {
|
|
114
|
+
const baseYear = new Date().getFullYear()
|
|
115
|
+
const visibleYear = visibleMonth.getFullYear()
|
|
116
|
+
const min = fromYear ?? baseYear - 100
|
|
117
|
+
const max = toYear ?? baseYear + 20
|
|
118
|
+
const start = Math.min(min, visibleYear)
|
|
119
|
+
const end = Math.max(max, visibleYear)
|
|
120
|
+
return Array.from({ length: end - start + 1 }, (_, i) => start + i)
|
|
121
|
+
}, [fromYear, toYear, visibleMonth])
|
|
122
|
+
|
|
123
|
+
const monthOptions = React.useMemo(() => {
|
|
124
|
+
return Array.from({ length: 12 }, (_, i) => i + 1)
|
|
125
|
+
}, [])
|
|
126
|
+
|
|
127
|
+
const yearIndex = React.useMemo(() => {
|
|
128
|
+
const y = visibleMonth.getFullYear()
|
|
129
|
+
const idx = yearOptions.indexOf(y)
|
|
130
|
+
return idx >= 0 ? idx : 0
|
|
131
|
+
}, [visibleMonth, yearOptions])
|
|
132
|
+
|
|
133
|
+
const monthIndex = React.useMemo(() => {
|
|
134
|
+
return visibleMonth.getMonth()
|
|
135
|
+
}, [visibleMonth])
|
|
136
|
+
|
|
137
|
+
const setYear = React.useCallback(
|
|
138
|
+
(year: number) => {
|
|
139
|
+
setMonth(new Date(year, visibleMonth.getMonth(), 1))
|
|
140
|
+
},
|
|
141
|
+
[setMonth, visibleMonth]
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
const setMonthOfYear = React.useCallback(
|
|
145
|
+
(monthOfYear: number) => {
|
|
146
|
+
setMonth(new Date(visibleMonth.getFullYear(), monthOfYear - 1, 1))
|
|
147
|
+
},
|
|
148
|
+
[setMonth, visibleMonth]
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
const gridStart = React.useMemo(() => {
|
|
152
|
+
return startOfWeek(startOfMonth(visibleMonth), { weekStartsOn })
|
|
153
|
+
}, [visibleMonth, weekStartsOn])
|
|
154
|
+
|
|
155
|
+
const gridEnd = React.useMemo(() => {
|
|
156
|
+
return endOfWeek(endOfMonth(visibleMonth), { weekStartsOn })
|
|
157
|
+
}, [visibleMonth, weekStartsOn])
|
|
158
|
+
|
|
159
|
+
const weeks = React.useMemo(() => {
|
|
160
|
+
const days: Date[] = []
|
|
161
|
+
for (
|
|
162
|
+
let d = gridStart;
|
|
163
|
+
!isAfter(d, gridEnd);
|
|
164
|
+
d = addDays(d, 1)
|
|
165
|
+
) {
|
|
166
|
+
days.push(d)
|
|
167
|
+
}
|
|
168
|
+
const rows: Date[][] = []
|
|
169
|
+
for (let i = 0; i < days.length; i += 7) rows.push(days.slice(i, i + 7))
|
|
170
|
+
return rows
|
|
171
|
+
}, [gridEnd, gridStart])
|
|
172
|
+
|
|
173
|
+
const weekdays = React.useMemo(() => {
|
|
174
|
+
const labels = ["日", "一", "二", "三", "四", "五", "六"]
|
|
175
|
+
return Array.from({ length: 7 }).map((_, i) => labels[(i + weekStartsOn) % 7])
|
|
176
|
+
}, [weekStartsOn])
|
|
177
|
+
|
|
178
|
+
const handleSelect = React.useCallback(
|
|
179
|
+
(date: Date) => {
|
|
180
|
+
if (isDateDisabled(date, disabled)) return
|
|
181
|
+
if (props.mode === "range") {
|
|
182
|
+
const current = props.selected
|
|
183
|
+
let next: DateRange
|
|
184
|
+
if (!current?.from || (current.from && current.to)) {
|
|
185
|
+
next = { from: date, to: undefined }
|
|
186
|
+
} else if (current.from && !current.to) {
|
|
187
|
+
if (isBefore(date, current.from)) {
|
|
188
|
+
next = { from: date, to: current.from }
|
|
189
|
+
} else {
|
|
190
|
+
next = { from: current.from, to: date }
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
next = { from: date, to: undefined }
|
|
194
|
+
}
|
|
195
|
+
props.onSelect?.(next)
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
props.onSelect?.(date)
|
|
199
|
+
},
|
|
200
|
+
[disabled, props]
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<View
|
|
205
|
+
className={cn(
|
|
206
|
+
"bg-background w-fit rounded-md p-3",
|
|
207
|
+
"flex flex-col gap-3 border-2",
|
|
208
|
+
className
|
|
209
|
+
)}
|
|
210
|
+
>
|
|
211
|
+
<View className="flex items-center justify-between">
|
|
212
|
+
{captionHasButtons ? (
|
|
213
|
+
<Button
|
|
214
|
+
variant="ghost"
|
|
215
|
+
size="icon"
|
|
216
|
+
className="h-8 w-8"
|
|
217
|
+
onClick={() => setMonth(subMonths(visibleMonth, 1))}
|
|
218
|
+
>
|
|
219
|
+
<ChevronLeft size={16} />
|
|
220
|
+
</Button>
|
|
221
|
+
) : (
|
|
222
|
+
<View className="h-8 w-8" />
|
|
223
|
+
)}
|
|
224
|
+
|
|
225
|
+
{captionHasDropdown ? (
|
|
226
|
+
<View className="flex items-center gap-2">
|
|
227
|
+
<Picker
|
|
228
|
+
mode="selector"
|
|
229
|
+
range={yearOptions}
|
|
230
|
+
value={yearIndex}
|
|
231
|
+
onChange={(e) => setYear(yearOptions[Number(e.detail.value)]!)}
|
|
232
|
+
>
|
|
233
|
+
<Button variant="ghost" className="h-8 px-2">
|
|
234
|
+
<Text className="text-sm font-medium">
|
|
235
|
+
{visibleMonth.getFullYear()}
|
|
236
|
+
</Text>
|
|
237
|
+
<ChevronDown size={16} className="opacity-50" />
|
|
238
|
+
</Button>
|
|
239
|
+
</Picker>
|
|
240
|
+
<Picker
|
|
241
|
+
mode="selector"
|
|
242
|
+
range={monthOptions}
|
|
243
|
+
value={monthIndex}
|
|
244
|
+
onChange={(e) =>
|
|
245
|
+
setMonthOfYear(monthOptions[Number(e.detail.value)]!)
|
|
246
|
+
}
|
|
247
|
+
>
|
|
248
|
+
<Button variant="ghost" className="h-8 px-2">
|
|
249
|
+
<Text className="text-sm font-medium">
|
|
250
|
+
{String(visibleMonth.getMonth() + 1).padStart(2, "0")}
|
|
251
|
+
月
|
|
252
|
+
</Text>
|
|
253
|
+
<ChevronDown size={16} className="opacity-50" />
|
|
254
|
+
</Button>
|
|
255
|
+
</Picker>
|
|
256
|
+
</View>
|
|
257
|
+
) : (
|
|
258
|
+
<Text className="text-sm font-medium">
|
|
259
|
+
{format(visibleMonth, "yyyy年MM月")}
|
|
260
|
+
</Text>
|
|
261
|
+
)}
|
|
262
|
+
|
|
263
|
+
{captionHasButtons ? (
|
|
264
|
+
<Button
|
|
265
|
+
variant="ghost"
|
|
266
|
+
size="icon"
|
|
267
|
+
className="h-8 w-8"
|
|
268
|
+
onClick={() => setMonth(addMonths(visibleMonth, 1))}
|
|
269
|
+
>
|
|
270
|
+
<ChevronRight size={16} />
|
|
271
|
+
</Button>
|
|
272
|
+
) : (
|
|
273
|
+
<View className="h-8 w-8" />
|
|
274
|
+
)}
|
|
275
|
+
</View>
|
|
276
|
+
|
|
277
|
+
<View className="flex">
|
|
278
|
+
{weekdays.map((label) => (
|
|
279
|
+
<View key={label} className="flex flex-1 items-center justify-center">
|
|
280
|
+
<Text className="text-muted-foreground text-xs font-normal">
|
|
281
|
+
{label}
|
|
282
|
+
</Text>
|
|
283
|
+
</View>
|
|
284
|
+
))}
|
|
285
|
+
</View>
|
|
286
|
+
|
|
287
|
+
<View className="flex flex-col gap-2">
|
|
288
|
+
{weeks.map((week, rowIndex) => (
|
|
289
|
+
<View key={rowIndex} className="flex">
|
|
290
|
+
{week.map((date) => {
|
|
291
|
+
const outside = !isSameMonth(date, visibleMonth)
|
|
292
|
+
const hidden = outside && !showOutsideDays
|
|
293
|
+
const disabledDay = isDateDisabled(date, disabled)
|
|
294
|
+
const today = isSameDay(date, new Date())
|
|
295
|
+
|
|
296
|
+
const range = rangeSelected
|
|
297
|
+
const selectedSingle = singleSelected
|
|
298
|
+
? isSameDay(date, singleSelected)
|
|
299
|
+
: false
|
|
300
|
+
|
|
301
|
+
const rangeStart = range?.from ? isSameDay(date, range.from) : false
|
|
302
|
+
const rangeEnd = range?.to ? isSameDay(date, range.to) : false
|
|
303
|
+
const rangeMiddle =
|
|
304
|
+
!!range?.from && !!range?.to && isInRange(date, range) && !rangeStart && !rangeEnd
|
|
305
|
+
|
|
306
|
+
return (
|
|
307
|
+
<View
|
|
308
|
+
key={date.toISOString()}
|
|
309
|
+
className={cn("flex flex-1 items-center justify-center", hidden && "invisible")}
|
|
310
|
+
>
|
|
311
|
+
<CalendarDayButton
|
|
312
|
+
date={date}
|
|
313
|
+
outside={outside}
|
|
314
|
+
today={today}
|
|
315
|
+
disabled={disabledDay}
|
|
316
|
+
selectedSingle={selectedSingle}
|
|
317
|
+
rangeStart={rangeStart}
|
|
318
|
+
rangeMiddle={rangeMiddle}
|
|
319
|
+
rangeEnd={rangeEnd}
|
|
320
|
+
onPress={handleSelect}
|
|
321
|
+
/>
|
|
322
|
+
</View>
|
|
323
|
+
)
|
|
324
|
+
})}
|
|
325
|
+
</View>
|
|
326
|
+
))}
|
|
327
|
+
</View>
|
|
328
|
+
</View>
|
|
329
|
+
)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
type CalendarDayButtonProps = {
|
|
333
|
+
date: Date
|
|
334
|
+
outside: boolean
|
|
335
|
+
today: boolean
|
|
336
|
+
disabled: boolean
|
|
337
|
+
selectedSingle: boolean
|
|
338
|
+
rangeStart: boolean
|
|
339
|
+
rangeMiddle: boolean
|
|
340
|
+
rangeEnd: boolean
|
|
341
|
+
onPress: (date: Date) => void
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function CalendarDayButton({
|
|
345
|
+
date,
|
|
346
|
+
outside,
|
|
347
|
+
today,
|
|
348
|
+
disabled,
|
|
349
|
+
selectedSingle,
|
|
350
|
+
rangeStart,
|
|
351
|
+
rangeMiddle,
|
|
352
|
+
rangeEnd,
|
|
353
|
+
onPress,
|
|
354
|
+
}: CalendarDayButtonProps) {
|
|
355
|
+
const base = "h-8 w-8 p-0 flex items-center justify-center rounded-md"
|
|
356
|
+
const outsideClass = outside ? "text-muted-foreground" : ""
|
|
357
|
+
const todayClass = today ? "bg-accent text-accent-foreground" : ""
|
|
358
|
+
const selectedSingleClass = selectedSingle
|
|
359
|
+
? "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground"
|
|
360
|
+
: ""
|
|
361
|
+
const rangeStartClass = rangeStart
|
|
362
|
+
? "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground"
|
|
363
|
+
: ""
|
|
364
|
+
const rangeEndClass = rangeEnd
|
|
365
|
+
? "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground"
|
|
366
|
+
: ""
|
|
367
|
+
const rangeMiddleClass = rangeMiddle
|
|
368
|
+
? "bg-accent text-accent-foreground rounded-none"
|
|
369
|
+
: ""
|
|
370
|
+
const rangeCapClass = rangeStart || rangeEnd ? "rounded-md" : ""
|
|
371
|
+
|
|
372
|
+
return (
|
|
373
|
+
<Button
|
|
374
|
+
variant="ghost"
|
|
375
|
+
size="icon"
|
|
376
|
+
className={cn(
|
|
377
|
+
base,
|
|
378
|
+
outsideClass,
|
|
379
|
+
todayClass,
|
|
380
|
+
selectedSingleClass,
|
|
381
|
+
rangeMiddleClass,
|
|
382
|
+
rangeStartClass,
|
|
383
|
+
rangeEndClass,
|
|
384
|
+
rangeCapClass,
|
|
385
|
+
disabled && "opacity-50 pointer-events-none"
|
|
386
|
+
)}
|
|
387
|
+
onClick={disabled ? undefined : () => onPress(date)}
|
|
388
|
+
>
|
|
389
|
+
<Text className="text-sm">{format(date, "d")}</Text>
|
|
390
|
+
</Button>
|
|
391
|
+
)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export { Calendar, CalendarDayButton }
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { View } from "@tarojs/components"
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
|
|
6
|
+
// 创建一个上下文来跟踪卡片内部的状态
|
|
7
|
+
const CardContext = React.createContext<{ hasHeader: boolean }>({
|
|
8
|
+
hasHeader: false,
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const Card = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof View>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof View>
|
|
14
|
+
>(({ className, children, ...props }, ref) => {
|
|
15
|
+
// 检查子元素中是否有 CardHeader
|
|
16
|
+
const hasHeader = React.Children.toArray(children).some(
|
|
17
|
+
(child) => React.isValidElement(child) && (child.type as any).displayName === "CardHeader"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<CardContext.Provider value={{ hasHeader }}>
|
|
22
|
+
<View
|
|
23
|
+
ref={ref}
|
|
24
|
+
className={cn(
|
|
25
|
+
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
|
26
|
+
className
|
|
27
|
+
)}
|
|
28
|
+
{...props}
|
|
29
|
+
>
|
|
30
|
+
{children}
|
|
31
|
+
</View>
|
|
32
|
+
</CardContext.Provider>
|
|
33
|
+
)
|
|
34
|
+
})
|
|
35
|
+
Card.displayName = "Card"
|
|
36
|
+
|
|
37
|
+
const CardHeader = React.forwardRef<
|
|
38
|
+
React.ElementRef<typeof View>,
|
|
39
|
+
React.ComponentPropsWithoutRef<typeof View>
|
|
40
|
+
>(({ className, ...props }, ref) => (
|
|
41
|
+
<View
|
|
42
|
+
ref={ref}
|
|
43
|
+
className={cn("flex flex-col space-y-2 p-6", className)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
))
|
|
47
|
+
CardHeader.displayName = "CardHeader"
|
|
48
|
+
|
|
49
|
+
const CardTitle = React.forwardRef<
|
|
50
|
+
React.ElementRef<typeof View>,
|
|
51
|
+
React.ComponentPropsWithoutRef<typeof View>
|
|
52
|
+
>(({ className, ...props }, ref) => (
|
|
53
|
+
<View
|
|
54
|
+
ref={ref}
|
|
55
|
+
className={cn(
|
|
56
|
+
"text-2xl font-semibold leading-none tracking-tight",
|
|
57
|
+
className
|
|
58
|
+
)}
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
))
|
|
62
|
+
CardTitle.displayName = "CardTitle"
|
|
63
|
+
|
|
64
|
+
const CardDescription = React.forwardRef<
|
|
65
|
+
React.ElementRef<typeof View>,
|
|
66
|
+
React.ComponentPropsWithoutRef<typeof View>
|
|
67
|
+
>(({ className, ...props }, ref) => (
|
|
68
|
+
<View
|
|
69
|
+
ref={ref}
|
|
70
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
))
|
|
74
|
+
CardDescription.displayName = "CardDescription"
|
|
75
|
+
|
|
76
|
+
const CardContent = React.forwardRef<
|
|
77
|
+
React.ElementRef<typeof View>,
|
|
78
|
+
React.ComponentPropsWithoutRef<typeof View>
|
|
79
|
+
>(({ className, ...props }, ref) => {
|
|
80
|
+
const { hasHeader } = React.useContext(CardContext)
|
|
81
|
+
return (
|
|
82
|
+
<View
|
|
83
|
+
ref={ref}
|
|
84
|
+
className={cn("p-6", hasHeader && "pt-0", className)}
|
|
85
|
+
{...props}
|
|
86
|
+
/>
|
|
87
|
+
)
|
|
88
|
+
})
|
|
89
|
+
CardContent.displayName = "CardContent"
|
|
90
|
+
|
|
91
|
+
const CardFooter = React.forwardRef<
|
|
92
|
+
React.ElementRef<typeof View>,
|
|
93
|
+
React.ComponentPropsWithoutRef<typeof View>
|
|
94
|
+
>(({ className, ...props }, ref) => {
|
|
95
|
+
const { hasHeader } = React.useContext(CardContext)
|
|
96
|
+
// 注意:Footer 通常也跟在 Content 后面,所以这里逻辑可以更精细,
|
|
97
|
+
// 但为了简单通用,如果卡片有 Header,Footer 默认 pt-0 也是合理的。
|
|
98
|
+
return (
|
|
99
|
+
<View
|
|
100
|
+
ref={ref}
|
|
101
|
+
className={cn("flex items-center p-6", hasHeader && "pt-0", className)}
|
|
102
|
+
{...props}
|
|
103
|
+
/>
|
|
104
|
+
)
|
|
105
|
+
})
|
|
106
|
+
CardFooter.displayName = "CardFooter"
|
|
107
|
+
|
|
108
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|