@monkey-mini-app/ui 0.1.0 → 0.1.4
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 +58 -0
- package/dist/globals.css +1 -1
- package/dist/index.js +12 -2
- package/dist/manifest.json +6 -6
- package/dist/runtime.js +9 -0
- package/dist/sdk.js +158 -0
- package/dist/src/blocks/activity-feed.tsx +7 -0
- package/dist/src/blocks/app-shell.tsx +1 -0
- package/dist/src/blocks/attachment-gallery.tsx +7 -0
- package/dist/src/blocks/bar-chart.tsx +15 -1
- package/dist/src/blocks/comment-thread.tsx +7 -0
- package/dist/src/blocks/dashboard-shell.tsx +109 -0
- package/dist/src/blocks/description-list.tsx +7 -0
- package/dist/src/blocks/detail-panel.tsx +1 -0
- package/dist/src/blocks/donut-chart.tsx +19 -2
- package/dist/src/blocks/env-badge.tsx +7 -0
- package/dist/src/blocks/env-table.tsx +316 -0
- package/dist/src/blocks/file-tree.tsx +7 -0
- package/dist/src/blocks/filter-bar.tsx +1 -0
- package/dist/src/blocks/form-sheet.tsx +160 -0
- package/dist/src/blocks/gauge.tsx +29 -6
- package/dist/src/blocks/list-detail.tsx +121 -0
- package/dist/src/blocks/notification-center.tsx +7 -0
- package/dist/src/blocks/page-header.tsx +1 -0
- package/dist/src/blocks/progress-ring.tsx +20 -2
- package/dist/src/blocks/radar-chart.tsx +18 -1
- package/dist/src/blocks/request-inspector.tsx +7 -0
- package/dist/src/blocks/reveal.tsx +56 -0
- package/dist/src/blocks/run-timeline.tsx +7 -0
- package/dist/src/blocks/scrollspy.tsx +7 -0
- package/dist/src/blocks/settings-split.tsx +154 -0
- package/dist/src/blocks/severity-chip.tsx +7 -0
- package/dist/src/blocks/sparkline.tsx +12 -3
- package/dist/src/blocks/stat-card.tsx +1 -0
- package/dist/src/blocks/status-badge.tsx +1 -0
- package/dist/src/blocks/table-page.tsx +83 -0
- package/dist/src/blocks/terminal.tsx +21 -3
- package/dist/src/blocks/test-step-list.tsx +7 -0
- package/dist/src/blocks/trend-card.tsx +7 -0
- package/dist/src/blocks/wizard-shell.tsx +132 -0
- package/dist/src/cdn-modules.d.ts +5 -0
- package/dist/src/components/accordion.tsx +7 -0
- package/dist/src/components/alert-dialog.tsx +7 -0
- package/dist/src/components/alert.tsx +7 -0
- package/dist/src/components/aspect-ratio.tsx +4 -0
- package/dist/src/components/attachment.tsx +4 -0
- package/dist/src/components/avatar.tsx +4 -0
- package/dist/src/components/badge.tsx +7 -0
- package/dist/src/components/breadcrumb.tsx +4 -0
- package/dist/src/components/bubble.tsx +4 -0
- package/dist/src/components/button-group.tsx +6 -0
- package/dist/src/components/button.tsx +7 -0
- package/dist/src/components/calendar.tsx +4 -0
- package/dist/src/components/card.tsx +8 -1
- package/dist/src/components/chart.tsx +7 -0
- package/dist/src/components/checkbox.tsx +5 -0
- package/dist/src/components/collapsible.tsx +4 -0
- package/dist/src/components/combobox.tsx +4 -0
- package/dist/src/components/command.tsx +6 -0
- package/dist/src/components/context-menu.tsx +4 -0
- package/dist/src/components/dialog.tsx +21 -0
- package/dist/src/components/drawer.tsx +4 -0
- package/dist/src/components/dropdown-menu.tsx +7 -0
- package/dist/src/components/empty.tsx +7 -0
- package/dist/src/components/field.tsx +6 -0
- package/dist/src/components/hover-card.tsx +4 -0
- package/dist/src/components/input-group.tsx +4 -0
- package/dist/src/components/input.tsx +7 -0
- package/dist/src/components/item.tsx +4 -0
- package/dist/src/components/kbd.tsx +4 -0
- package/dist/src/components/label.tsx +7 -0
- package/dist/src/components/marker.tsx +4 -0
- package/dist/src/components/menubar.tsx +4 -0
- package/dist/src/components/message-scroller.tsx +4 -0
- package/dist/src/components/message.tsx +4 -0
- package/dist/src/components/native-select.tsx +4 -0
- package/dist/src/components/navigation-menu.tsx +4 -0
- package/dist/src/components/pagination.tsx +4 -0
- package/dist/src/components/popover.tsx +7 -0
- package/dist/src/components/progress.tsx +7 -0
- package/dist/src/components/questionnaire.tsx +4 -0
- package/dist/src/components/radio-group.tsx +4 -0
- package/dist/src/components/resizable.tsx +7 -0
- package/dist/src/components/scroll-area.tsx +4 -0
- package/dist/src/components/select.tsx +7 -0
- package/dist/src/components/separator.tsx +5 -0
- package/dist/src/components/sheet.tsx +22 -0
- package/dist/src/components/sidebar.tsx +4 -0
- package/dist/src/components/skeleton.tsx +7 -0
- package/dist/src/components/slider.tsx +4 -0
- package/dist/src/components/spinner.tsx +6 -0
- package/dist/src/components/switch.tsx +7 -0
- package/dist/src/components/table.tsx +7 -0
- package/dist/src/components/tabs.tsx +7 -0
- package/dist/src/components/textarea.tsx +7 -0
- package/dist/src/components/toast.tsx +7 -0
- package/dist/src/components/toggle-group.tsx +4 -0
- package/dist/src/components/toggle.tsx +4 -0
- package/dist/src/components/tooltip.tsx +7 -0
- package/dist/src/composites/autocomplete.tsx +7 -0
- package/dist/src/composites/cascader.tsx +7 -0
- package/dist/src/composites/color-picker.tsx +7 -0
- package/dist/src/composites/confirm-dialog.tsx +7 -0
- package/dist/src/composites/copyable.tsx +9 -1
- package/dist/src/composites/currency-input.tsx +7 -0
- package/dist/src/composites/date-picker.tsx +7 -0
- package/dist/src/composites/date-range-picker.tsx +4 -0
- package/dist/src/composites/date-time-picker.tsx +7 -0
- package/dist/src/composites/date-time-range-picker.tsx +7 -0
- package/dist/src/composites/duration-input.tsx +7 -0
- package/dist/src/composites/mini-calendar.tsx +7 -0
- package/dist/src/composites/number-field.tsx +7 -0
- package/dist/src/composites/password-field.tsx +7 -0
- package/dist/src/composites/phone-input.tsx +7 -0
- package/dist/src/composites/rating.tsx +7 -0
- package/dist/src/composites/relative-date-picker.tsx +7 -0
- package/dist/src/composites/search-input.tsx +7 -0
- package/dist/src/composites/slider-range.tsx +7 -0
- package/dist/src/composites/tag-input.tsx +7 -0
- package/dist/src/composites/time-picker.tsx +11 -1
- package/dist/src/composites/time-range-picker.tsx +7 -0
- package/dist/src/composites/timezone-select.tsx +7 -0
- package/dist/src/composites/transfer.tsx +7 -0
- package/dist/src/composites/user-picker.tsx +7 -0
- package/dist/src/hooks/use-html-dark.ts +37 -0
- package/dist/src/hooks/use-mobile.ts +3 -0
- package/dist/src/i18n/en.ts +109 -2
- package/dist/src/i18n/zh.ts +99 -2
- package/dist/src/index.ts +147 -134
- package/dist/src/lib/clipboard.ts +10 -0
- package/dist/src/lib/illustrations.tsx +2 -2
- package/dist/src/products/code-block.tsx +35 -18
- package/dist/src/products/code-editor.tsx +156 -24
- package/dist/src/products/commit-graph.tsx +644 -0
- package/dist/src/products/data-grid.tsx +2 -1
- package/dist/src/products/diff-viewer.tsx +104 -8
- package/dist/src/products/event-calendar.tsx +28 -823
- package/dist/src/products/file-dropzone.tsx +7 -0
- package/dist/src/products/full-calendar/animations.ts +38 -0
- package/dist/src/products/full-calendar/calendar-body.tsx +77 -0
- package/dist/src/products/full-calendar/constants.ts +10 -0
- package/dist/src/products/full-calendar/contexts/calendar-context.tsx +255 -0
- package/dist/src/products/full-calendar/contexts/dnd-context.tsx +157 -0
- package/dist/src/products/full-calendar/dialogs/add-edit-event-dialog.tsx +187 -0
- package/dist/src/products/full-calendar/dialogs/delete-event-dialog.tsx +39 -0
- package/dist/src/products/full-calendar/dialogs/event-details-dialog.tsx +100 -0
- package/dist/src/products/full-calendar/dialogs/events-list-dialog.tsx +87 -0
- package/dist/src/products/full-calendar/dnd/draggable-event.tsx +45 -0
- package/dist/src/products/full-calendar/dnd/droppable-area.tsx +45 -0
- package/dist/src/products/full-calendar/dnd/resizable-event.tsx +179 -0
- package/dist/src/products/full-calendar/header/calendar-header.tsx +65 -0
- package/dist/src/products/full-calendar/header/date-navigator.tsx +110 -0
- package/dist/src/products/full-calendar/header/filter.tsx +58 -0
- package/dist/src/products/full-calendar/header/today-button.tsx +45 -0
- package/dist/src/products/full-calendar/header/user-select.tsx +64 -0
- package/dist/src/products/full-calendar/header/view-tabs.tsx +36 -0
- package/dist/src/products/full-calendar/helpers.ts +453 -0
- package/dist/src/products/full-calendar/hooks/use-time-range-create.ts +152 -0
- package/dist/src/products/full-calendar/hooks.ts +70 -0
- package/dist/src/products/full-calendar/interfaces.ts +23 -0
- package/dist/src/products/full-calendar/settings/settings.tsx +80 -0
- package/dist/src/products/full-calendar/shims/avatar-group.tsx +16 -0
- package/dist/src/products/full-calendar/shims/day-picker.tsx +1 -0
- package/dist/src/products/full-calendar/shims/motion.tsx +97 -0
- package/dist/src/products/full-calendar/shims/next-themes.ts +6 -0
- package/dist/src/products/full-calendar/shims/re-resizable.tsx +25 -0
- package/dist/src/products/full-calendar/shims/sonner.ts +4 -0
- package/dist/src/products/full-calendar/types.ts +8 -0
- package/dist/src/products/full-calendar/views/agenda-view/agenda-events.tsx +140 -0
- package/dist/src/products/full-calendar/views/month-view/calendar-month-view.tsx +69 -0
- package/dist/src/products/full-calendar/views/month-view/day-cell.tsx +201 -0
- package/dist/src/products/full-calendar/views/month-view/event-bullet.tsx +40 -0
- package/dist/src/products/full-calendar/views/month-view/month-event-badge.tsx +143 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/calendar-day-view.tsx +295 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/calendar-time-line.tsx +35 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/calendar-week-view.tsx +253 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/day-view-multi-day-events-row.tsx +73 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/event-block.tsx +114 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/render-grouped-events.tsx +49 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/week-view-multi-day-events-row.tsx +143 -0
- package/dist/src/products/full-calendar/views/year-view/calendar-year-view.tsx +161 -0
- package/dist/src/products/gantt.tsx +7 -0
- package/dist/src/products/jira-wiki.tsx +1 -0
- package/dist/src/products/jql-input.tsx +340 -37
- package/dist/src/products/json-viewer.tsx +7 -0
- package/dist/src/products/kanban-issue-panel.tsx +7 -0
- package/dist/src/products/kanban.tsx +1 -0
- package/dist/src/products/log-viewer.tsx +390 -18
- package/dist/src/products/markdown-editor.tsx +21 -6
- package/dist/src/products/markdown.tsx +7 -0
- package/dist/src/products/rich-text-editor.tsx +65 -59
- package/dist/src/products/sortable-list.tsx +7 -0
- package/dist/src/products/stepper.tsx +219 -70
- package/dist/src/products/timeline.tsx +7 -0
- package/dist/src/products/tree-view.tsx +7 -0
- package/dist/src/sdk/app-error-boundary.tsx +240 -0
- package/dist/src/sdk/app-id.ts +7 -0
- package/dist/src/sdk/use-app.ts +126 -0
- package/dist/src/styles/globals.css +7 -7
- package/dist/vendors/lodash.js +20 -0
- package/dist/vendors/motion.js +12 -0
- package/package.json +23 -19
- package/dist/src/products/jql-language.ts +0 -70
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { SettingsIcon } from "lucide-react"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
DropdownMenu,
|
|
5
|
+
DropdownMenuContent,
|
|
6
|
+
DropdownMenuGroup,
|
|
7
|
+
DropdownMenuLabel,
|
|
8
|
+
DropdownMenuRadioGroup,
|
|
9
|
+
DropdownMenuRadioItem,
|
|
10
|
+
DropdownMenuSeparator,
|
|
11
|
+
DropdownMenuTrigger,
|
|
12
|
+
} from "@monkey-mini-app/ui/components/dropdown-menu"
|
|
13
|
+
import { Input } from "@monkey-mini-app/ui/components/input"
|
|
14
|
+
import { Switch } from "@monkey-mini-app/ui/components/switch"
|
|
15
|
+
import { useLabels } from "@monkey-mini-app/ui/i18n/context"
|
|
16
|
+
|
|
17
|
+
import { MAX_SCROLL_HOUR, MIN_SCROLL_HOUR, useCalendar } from "../contexts/calendar-context"
|
|
18
|
+
|
|
19
|
+
export function Settings() {
|
|
20
|
+
const {
|
|
21
|
+
badgeVariant,
|
|
22
|
+
setBadgeVariant,
|
|
23
|
+
use24HourFormat,
|
|
24
|
+
toggleTimeFormat,
|
|
25
|
+
startOfDayHour,
|
|
26
|
+
setStartOfDayHour,
|
|
27
|
+
agendaModeGroupBy,
|
|
28
|
+
setAgendaModeGroupBy,
|
|
29
|
+
} = useCalendar()
|
|
30
|
+
const t = useLabels("eventCalendar")
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<DropdownMenu>
|
|
34
|
+
<DropdownMenuTrigger
|
|
35
|
+
className="inline-flex size-8 items-center justify-center rounded-lg border border-border bg-background"
|
|
36
|
+
aria-label={t.settingsAria}
|
|
37
|
+
>
|
|
38
|
+
<SettingsIcon className="h-4 w-4" />
|
|
39
|
+
</DropdownMenuTrigger>
|
|
40
|
+
<DropdownMenuContent className="w-56">
|
|
41
|
+
<DropdownMenuGroup>
|
|
42
|
+
<DropdownMenuLabel>{t.settings}</DropdownMenuLabel>
|
|
43
|
+
<div className="flex items-center justify-between gap-2 px-1.5 py-1.5 text-sm">
|
|
44
|
+
<span>{t.useDotBadge}</span>
|
|
45
|
+
<Switch
|
|
46
|
+
checked={badgeVariant === "dot"}
|
|
47
|
+
onCheckedChange={(checked) => setBadgeVariant(checked ? "dot" : "colored")}
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
<div className="flex items-center justify-between gap-2 px-1.5 py-1.5 text-sm">
|
|
51
|
+
<span>{t.use24Hour}</span>
|
|
52
|
+
<Switch checked={use24HourFormat} onCheckedChange={() => toggleTimeFormat()} />
|
|
53
|
+
</div>
|
|
54
|
+
<div className="flex items-center justify-between gap-2 px-1.5 py-1.5 text-sm">
|
|
55
|
+
<span>{t.scrollHour}</span>
|
|
56
|
+
<Input
|
|
57
|
+
type="number"
|
|
58
|
+
className="h-7 w-16"
|
|
59
|
+
min={MIN_SCROLL_HOUR}
|
|
60
|
+
max={MAX_SCROLL_HOUR}
|
|
61
|
+
value={startOfDayHour}
|
|
62
|
+
onChange={(e) => setStartOfDayHour(Number(e.target.value))}
|
|
63
|
+
onClick={(e) => e.stopPropagation()}
|
|
64
|
+
onKeyDown={(e) => e.stopPropagation()}
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
</DropdownMenuGroup>
|
|
68
|
+
<DropdownMenuSeparator />
|
|
69
|
+
<DropdownMenuRadioGroup
|
|
70
|
+
value={agendaModeGroupBy}
|
|
71
|
+
onValueChange={(value) => setAgendaModeGroupBy(value as "date" | "color")}
|
|
72
|
+
>
|
|
73
|
+
<DropdownMenuLabel>{t.agendaGroupBy}</DropdownMenuLabel>
|
|
74
|
+
<DropdownMenuRadioItem value="date">{t.groupByDate}</DropdownMenuRadioItem>
|
|
75
|
+
<DropdownMenuRadioItem value="color">{t.groupByColor}</DropdownMenuRadioItem>
|
|
76
|
+
</DropdownMenuRadioGroup>
|
|
77
|
+
</DropdownMenuContent>
|
|
78
|
+
</DropdownMenu>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@monkey-mini-app/ui/lib/utils"
|
|
4
|
+
|
|
5
|
+
export function AvatarGroup({
|
|
6
|
+
children,
|
|
7
|
+
className,
|
|
8
|
+
max = 3,
|
|
9
|
+
}: {
|
|
10
|
+
children: React.ReactNode
|
|
11
|
+
className?: string
|
|
12
|
+
max?: number
|
|
13
|
+
}) {
|
|
14
|
+
const items = React.Children.toArray(children).slice(0, max)
|
|
15
|
+
return <div className={cn("flex items-center -space-x-2", className)}>{items}</div>
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Calendar as DayPicker } from "@monkey-mini-app/ui/components/calendar"
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
function strip(props: Record<string, unknown>) {
|
|
4
|
+
const {
|
|
5
|
+
initial: _i,
|
|
6
|
+
animate: _a,
|
|
7
|
+
exit: _e,
|
|
8
|
+
variants: _v,
|
|
9
|
+
transition: _t,
|
|
10
|
+
whileHover: _h,
|
|
11
|
+
whileTap: _p,
|
|
12
|
+
layout: _l,
|
|
13
|
+
scrollPosition: _s,
|
|
14
|
+
...rest
|
|
15
|
+
} = props
|
|
16
|
+
return rest
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function make(tag: React.ElementType) {
|
|
20
|
+
const El = React.forwardRef(function MotionEl(props: Record<string, unknown>, ref: React.Ref<unknown>) {
|
|
21
|
+
return React.createElement(tag, { ...strip(props), ref })
|
|
22
|
+
})
|
|
23
|
+
El.displayName = `motion.${typeof tag === "string" ? tag : "el"}`
|
|
24
|
+
return El
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* One component per tag, cached.
|
|
29
|
+
*
|
|
30
|
+
* Without the cache `motion.div` returns a NEW component type on every property
|
|
31
|
+
* access, so React sees a different `type` each render and tears the subtree down:
|
|
32
|
+
* DOM refs held across renders (pointer capture, `getBoundingClientRect` during a
|
|
33
|
+
* drag) then point at detached nodes, and every `motion.*` descendant remounts on
|
|
34
|
+
* each state change. Keep this memoised.
|
|
35
|
+
*/
|
|
36
|
+
const byTag = new Map<string, React.ElementType>()
|
|
37
|
+
const byComponent = new Map<React.ElementType, React.ElementType>()
|
|
38
|
+
|
|
39
|
+
function forTag(tag: string): React.ElementType {
|
|
40
|
+
let el = byTag.get(tag)
|
|
41
|
+
if (!el) {
|
|
42
|
+
el = make(tag as React.ElementType)
|
|
43
|
+
byTag.set(tag, el)
|
|
44
|
+
}
|
|
45
|
+
return el
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function forComponent(comp: React.ElementType): React.ElementType {
|
|
49
|
+
let el = byComponent.get(comp)
|
|
50
|
+
if (!el) {
|
|
51
|
+
el = make(comp)
|
|
52
|
+
byComponent.set(comp, el)
|
|
53
|
+
}
|
|
54
|
+
return el
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Props the shim swallows rather than forwarding to the DOM. */
|
|
58
|
+
export type MotionProps = {
|
|
59
|
+
initial?: unknown
|
|
60
|
+
animate?: unknown
|
|
61
|
+
exit?: unknown
|
|
62
|
+
variants?: unknown
|
|
63
|
+
transition?: unknown
|
|
64
|
+
whileHover?: unknown
|
|
65
|
+
whileTap?: unknown
|
|
66
|
+
layout?: unknown
|
|
67
|
+
scrollPosition?: unknown
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* `motion.div` has to type as a component taking **the real element's props** plus the motion
|
|
72
|
+
* ones. Typed as a bare `React.ElementType` (or a `Record<string, unknown>` props bag), every
|
|
73
|
+
* handler at a call site becomes an implicit any — that is how the week view's
|
|
74
|
+
* `onPointerDown={(e) => …}` lost its `e`.
|
|
75
|
+
*/
|
|
76
|
+
export type MotionComponents = {
|
|
77
|
+
[K in keyof React.JSX.IntrinsicElements]: React.ComponentType<
|
|
78
|
+
React.ComponentProps<K> & MotionProps
|
|
79
|
+
>
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const motion = new Proxy(
|
|
83
|
+
{ create: (comp: React.ElementType) => forComponent(comp) },
|
|
84
|
+
{
|
|
85
|
+
get(target, key) {
|
|
86
|
+
if (key === "create") return target.create
|
|
87
|
+
if (typeof key === "string") return forTag(key)
|
|
88
|
+
return undefined
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
) as unknown as { create: (comp: React.ElementType) => React.ElementType } & MotionComponents
|
|
92
|
+
|
|
93
|
+
export function AnimatePresence({ children }: { children?: React.ReactNode; initial?: boolean; mode?: string }) {
|
|
94
|
+
return <>{children}</>
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export type Variants = Record<string, unknown>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ReactNode } from "react"
|
|
2
|
+
|
|
3
|
+
export type ResizeCallback = (
|
|
4
|
+
_event: unknown,
|
|
5
|
+
direction: string,
|
|
6
|
+
ref: { style: { height: string } },
|
|
7
|
+
) => void
|
|
8
|
+
|
|
9
|
+
export function Resizable({
|
|
10
|
+
children,
|
|
11
|
+
className,
|
|
12
|
+
}: {
|
|
13
|
+
children?: ReactNode
|
|
14
|
+
className?: string
|
|
15
|
+
minHeight?: number
|
|
16
|
+
maxHeight?: number
|
|
17
|
+
enable?: unknown
|
|
18
|
+
handleStyles?: unknown
|
|
19
|
+
handleClasses?: unknown
|
|
20
|
+
onResizeStart?: unknown
|
|
21
|
+
onResize?: unknown
|
|
22
|
+
onResizeStop?: unknown
|
|
23
|
+
}) {
|
|
24
|
+
return <div className={className}>{children}</div>
|
|
25
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { format, parseISO } from "date-fns";
|
|
2
|
+
import type { FC } from "react";
|
|
3
|
+
import { Avatar, AvatarFallback, AvatarImage } from "@monkey-mini-app/ui/components/avatar";
|
|
4
|
+
import {
|
|
5
|
+
Command,
|
|
6
|
+
CommandEmpty,
|
|
7
|
+
CommandGroup,
|
|
8
|
+
CommandInput,
|
|
9
|
+
CommandItem,
|
|
10
|
+
CommandList,
|
|
11
|
+
} from "@monkey-mini-app/ui/components/command";
|
|
12
|
+
import { useDateLocale, useLabels } from "@monkey-mini-app/ui/i18n/context";
|
|
13
|
+
import { cn } from "@monkey-mini-app/ui/lib/utils";
|
|
14
|
+
import { useCalendar } from "../../contexts/calendar-context";
|
|
15
|
+
import { EventDetailsDialog } from "../../dialogs/event-details-dialog";
|
|
16
|
+
import {
|
|
17
|
+
formatTime,
|
|
18
|
+
getBgColor,
|
|
19
|
+
getColorClass,
|
|
20
|
+
getEventsForMonth,
|
|
21
|
+
getFirstLetters,
|
|
22
|
+
toCapitalize,
|
|
23
|
+
} from "../../helpers";
|
|
24
|
+
import { EventBullet } from "../month-view/event-bullet";
|
|
25
|
+
|
|
26
|
+
export const AgendaEvents: FC = () => {
|
|
27
|
+
const {
|
|
28
|
+
events,
|
|
29
|
+
use24HourFormat,
|
|
30
|
+
badgeVariant,
|
|
31
|
+
agendaModeGroupBy,
|
|
32
|
+
selectedDate,
|
|
33
|
+
} = useCalendar();
|
|
34
|
+
const t = useLabels("eventCalendar");
|
|
35
|
+
const dateLocale = useDateLocale();
|
|
36
|
+
|
|
37
|
+
const monthEvents = getEventsForMonth(events, selectedDate);
|
|
38
|
+
|
|
39
|
+
const agendaEvents: Record<string, typeof monthEvents> = {};
|
|
40
|
+
for (const event of monthEvents) {
|
|
41
|
+
const key =
|
|
42
|
+
agendaModeGroupBy === "date"
|
|
43
|
+
? format(parseISO(event.startDate), "yyyy-MM-dd")
|
|
44
|
+
: event.color;
|
|
45
|
+
(agendaEvents[key] ??= []).push(event);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const groupedAndSortedEvents = Object.entries(agendaEvents).sort(
|
|
49
|
+
(a, b) => new Date(a[0]).getTime() - new Date(b[0]).getTime(),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Command className="py-4 h-[80vh] bg-transparent">
|
|
54
|
+
<div className="mb-4 mx-4">
|
|
55
|
+
<CommandInput placeholder={t.searchAgenda} />
|
|
56
|
+
</div>
|
|
57
|
+
<CommandList className="max-h-max px-3 border-t">
|
|
58
|
+
{groupedAndSortedEvents.map(([date, groupedEvents]) => (
|
|
59
|
+
<CommandGroup
|
|
60
|
+
key={date}
|
|
61
|
+
heading={
|
|
62
|
+
agendaModeGroupBy === "date"
|
|
63
|
+
? format(parseISO(date), "EEEE, MMMM d, yyyy", { locale: dateLocale })
|
|
64
|
+
: toCapitalize(groupedEvents![0].color)
|
|
65
|
+
}
|
|
66
|
+
>
|
|
67
|
+
{groupedEvents!.map((event) => (
|
|
68
|
+
<CommandItem
|
|
69
|
+
key={event.id}
|
|
70
|
+
className={cn(
|
|
71
|
+
"mb-2 p-4 border rounded-md data-[selected=true]:bg-bg transition-all data-[selected=true]:text-none hover:cursor-pointer",
|
|
72
|
+
{
|
|
73
|
+
[getColorClass(event.color)]: badgeVariant === "colored",
|
|
74
|
+
"hover:bg-zinc-200 dark:hover:bg-gray-900":
|
|
75
|
+
badgeVariant === "dot",
|
|
76
|
+
"hover:opacity-60": badgeVariant === "colored",
|
|
77
|
+
},
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
<EventDetailsDialog event={event}>
|
|
81
|
+
<div className="w-full flex items-center justify-between gap-4">
|
|
82
|
+
<div className="flex items-center gap-2">
|
|
83
|
+
{badgeVariant === "dot" ? (
|
|
84
|
+
<EventBullet color={event.color} />
|
|
85
|
+
) : (
|
|
86
|
+
<Avatar>
|
|
87
|
+
<AvatarImage src="" alt="@shadcn" />
|
|
88
|
+
<AvatarFallback className={getBgColor(event.color)}>
|
|
89
|
+
{getFirstLetters(event.title)}
|
|
90
|
+
</AvatarFallback>
|
|
91
|
+
</Avatar>
|
|
92
|
+
)}
|
|
93
|
+
<div className="flex flex-col">
|
|
94
|
+
<p
|
|
95
|
+
className={cn({
|
|
96
|
+
"font-medium": badgeVariant === "dot",
|
|
97
|
+
"text-foreground": badgeVariant === "dot",
|
|
98
|
+
})}
|
|
99
|
+
>
|
|
100
|
+
{event.title}
|
|
101
|
+
</p>
|
|
102
|
+
<p className="text-muted-foreground text-sm line-clamp-1 text-ellipsis md:text-clip w-1/3">
|
|
103
|
+
{event.description}
|
|
104
|
+
</p>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
<div className="w-40 flex justify-center items-center gap-1">
|
|
108
|
+
{agendaModeGroupBy === "date" ? (
|
|
109
|
+
<>
|
|
110
|
+
<p className="text-sm">
|
|
111
|
+
{formatTime(event.startDate, use24HourFormat)}
|
|
112
|
+
</p>
|
|
113
|
+
<span className="text-muted-foreground">-</span>
|
|
114
|
+
<p className="text-sm">
|
|
115
|
+
{formatTime(event.endDate, use24HourFormat)}
|
|
116
|
+
</p>
|
|
117
|
+
</>
|
|
118
|
+
) : (
|
|
119
|
+
<>
|
|
120
|
+
<p className="text-sm">
|
|
121
|
+
{format(event.startDate, "MM/dd/yyyy")}
|
|
122
|
+
</p>
|
|
123
|
+
<span className="text-sm">at</span>
|
|
124
|
+
<p className="text-sm">
|
|
125
|
+
{formatTime(event.startDate, use24HourFormat)}
|
|
126
|
+
</p>
|
|
127
|
+
</>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</EventDetailsDialog>
|
|
132
|
+
</CommandItem>
|
|
133
|
+
))}
|
|
134
|
+
</CommandGroup>
|
|
135
|
+
))}
|
|
136
|
+
<CommandEmpty>{t.noEvents}</CommandEmpty>
|
|
137
|
+
</CommandList>
|
|
138
|
+
</Command>
|
|
139
|
+
);
|
|
140
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { motion } from "../../shims/motion";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import {
|
|
4
|
+
staggerContainer,
|
|
5
|
+
transition,
|
|
6
|
+
} from "../../animations";
|
|
7
|
+
import { useLabels } from "@monkey-mini-app/ui/i18n/context";
|
|
8
|
+
import { useCalendar } from "../../contexts/calendar-context";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
calculateMonthEventPositions,
|
|
12
|
+
getCalendarCells,
|
|
13
|
+
} from "../../helpers";
|
|
14
|
+
|
|
15
|
+
import type { IEvent } from "../../interfaces";
|
|
16
|
+
import { DayCell } from "./day-cell";
|
|
17
|
+
|
|
18
|
+
interface IProps {
|
|
19
|
+
singleDayEvents: IEvent[];
|
|
20
|
+
multiDayEvents: IEvent[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function CalendarMonthView({ singleDayEvents, multiDayEvents }: IProps) {
|
|
24
|
+
const { selectedDate } = useCalendar();
|
|
25
|
+
const t = useLabels("eventCalendar");
|
|
26
|
+
|
|
27
|
+
const allEvents = [...multiDayEvents, ...singleDayEvents];
|
|
28
|
+
|
|
29
|
+
const cells = useMemo(() => getCalendarCells(selectedDate), [selectedDate]);
|
|
30
|
+
|
|
31
|
+
const eventPositions = useMemo(
|
|
32
|
+
() =>
|
|
33
|
+
calculateMonthEventPositions(
|
|
34
|
+
multiDayEvents,
|
|
35
|
+
singleDayEvents,
|
|
36
|
+
selectedDate,
|
|
37
|
+
),
|
|
38
|
+
[multiDayEvents, singleDayEvents, selectedDate],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<motion.div initial="initial" animate="animate" variants={staggerContainer}>
|
|
43
|
+
<div className="grid grid-cols-7">
|
|
44
|
+
{t.weekdaysSun.map((day, index) => (
|
|
45
|
+
<motion.div
|
|
46
|
+
key={day}
|
|
47
|
+
className="flex items-center justify-center py-2"
|
|
48
|
+
initial={{ opacity: 0, y: -10 }}
|
|
49
|
+
animate={{ opacity: 1, y: 0 }}
|
|
50
|
+
transition={{ delay: index * 0.05, ...transition }}
|
|
51
|
+
>
|
|
52
|
+
<span className="text-xs font-medium text-t-quaternary">{day}</span>
|
|
53
|
+
</motion.div>
|
|
54
|
+
))}
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div className="grid grid-cols-7 overflow-hidden">
|
|
58
|
+
{cells.map((cell) => (
|
|
59
|
+
<DayCell
|
|
60
|
+
key={cell.date.toISOString()}
|
|
61
|
+
cell={cell}
|
|
62
|
+
events={allEvents}
|
|
63
|
+
eventPositions={eventPositions}
|
|
64
|
+
/>
|
|
65
|
+
))}
|
|
66
|
+
</div>
|
|
67
|
+
</motion.div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { isToday, startOfDay, isSunday, isSameMonth } from "date-fns";
|
|
5
|
+
import { motion } from "../../shims/motion";
|
|
6
|
+
import { useMemo, useCallback } from "react";
|
|
7
|
+
|
|
8
|
+
import { cn } from "@monkey-mini-app/ui/lib/utils";
|
|
9
|
+
import { transition } from "../../animations";
|
|
10
|
+
import { EventListDialog } from "../../dialogs/events-list-dialog";
|
|
11
|
+
import { DroppableArea } from "../../dnd/droppable-area";
|
|
12
|
+
import { getMonthCellEvents } from "../../helpers";
|
|
13
|
+
import { useMediaQuery } from "../../hooks";
|
|
14
|
+
import type { ICalendarCell, IEvent } from "../../interfaces";
|
|
15
|
+
import { EventBullet } from "./event-bullet";
|
|
16
|
+
import { MonthEventBadge } from "./month-event-badge";
|
|
17
|
+
import { Button } from "@monkey-mini-app/ui/components/button";
|
|
18
|
+
import { Plus } from "lucide-react";
|
|
19
|
+
import { AddEditEventDialog } from "../../dialogs/add-edit-event-dialog";
|
|
20
|
+
|
|
21
|
+
interface IProps {
|
|
22
|
+
cell: ICalendarCell;
|
|
23
|
+
events: IEvent[];
|
|
24
|
+
eventPositions: Record<string, number>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const dayCellVariants = cva("text-white", {
|
|
28
|
+
variants: {
|
|
29
|
+
color: {
|
|
30
|
+
blue: "bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 dark:hover:bg-blue-400 ",
|
|
31
|
+
green:
|
|
32
|
+
"bg-green-600 dark:bg-green-500 hover:bg-green-700 dark:hover:bg-green-400",
|
|
33
|
+
red: "bg-red-600 dark:bg-red-500 hover:bg-red-700 dark:hover:bg-red-400",
|
|
34
|
+
yellow:
|
|
35
|
+
"bg-yellow-600 dark:bg-yellow-500 hover:bg-yellow-700 dark:hover:bg-yellow-400",
|
|
36
|
+
purple:
|
|
37
|
+
"bg-purple-600 dark:bg-purple-500 hover:bg-purple-700 dark:hover:bg-purple-400",
|
|
38
|
+
orange:
|
|
39
|
+
"bg-orange-600 dark:bg-orange-500 hover:bg-orange-700 dark:hover:bg-orange-400",
|
|
40
|
+
gray: "bg-gray-600 dark:bg-gray-500 hover:bg-gray-700 dark:hover:bg-gray-400",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
defaultVariants: {
|
|
44
|
+
color: "blue",
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const MAX_VISIBLE_EVENTS = 3;
|
|
49
|
+
|
|
50
|
+
export function DayCell({ cell, events, eventPositions }: IProps) {
|
|
51
|
+
const { day, currentMonth, date } = cell;
|
|
52
|
+
const isMobile = useMediaQuery("(max-width: 768px)");
|
|
53
|
+
|
|
54
|
+
// Memoize cellEvents and currentCellMonth for performance
|
|
55
|
+
const { cellEvents, currentCellMonth } = useMemo(() => {
|
|
56
|
+
const cellEvents = getMonthCellEvents(date, events, eventPositions);
|
|
57
|
+
const currentCellMonth = startOfDay(
|
|
58
|
+
new Date(date.getFullYear(), date.getMonth(), 1),
|
|
59
|
+
);
|
|
60
|
+
return { cellEvents, currentCellMonth };
|
|
61
|
+
}, [date, events, eventPositions]);
|
|
62
|
+
|
|
63
|
+
// Memoize event rendering for each position with animation
|
|
64
|
+
const renderEventAtPosition = useCallback(
|
|
65
|
+
(position: number) => {
|
|
66
|
+
const event = cellEvents.find((e) => e.position === position);
|
|
67
|
+
if (!event) {
|
|
68
|
+
return (
|
|
69
|
+
<motion.div
|
|
70
|
+
key={`empty-${position}`}
|
|
71
|
+
className="lg:flex-1"
|
|
72
|
+
initial={false}
|
|
73
|
+
animate={false}
|
|
74
|
+
/>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
const showBullet = isSameMonth(
|
|
78
|
+
new Date(event.startDate),
|
|
79
|
+
currentCellMonth,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<motion.div
|
|
84
|
+
key={`event-${event.id}-${position}`}
|
|
85
|
+
className="lg:flex-1"
|
|
86
|
+
initial={{ opacity: 0, x: -10 }}
|
|
87
|
+
animate={{ opacity: 1, x: 0 }}
|
|
88
|
+
transition={{ delay: position * 0.1, ...transition }}
|
|
89
|
+
>
|
|
90
|
+
{showBullet && (
|
|
91
|
+
<EventBullet className="lg:hidden" color={event.color} />
|
|
92
|
+
)}
|
|
93
|
+
<MonthEventBadge
|
|
94
|
+
className="hidden lg:flex"
|
|
95
|
+
event={event}
|
|
96
|
+
cellDate={startOfDay(date)}
|
|
97
|
+
/>
|
|
98
|
+
</motion.div>
|
|
99
|
+
);
|
|
100
|
+
},
|
|
101
|
+
[cellEvents, currentCellMonth, date],
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const showMoreCount = cellEvents.length - MAX_VISIBLE_EVENTS;
|
|
105
|
+
|
|
106
|
+
const showMobileMore = isMobile && currentMonth && showMoreCount > 0;
|
|
107
|
+
const showDesktopMore = !isMobile && currentMonth && showMoreCount > 0;
|
|
108
|
+
|
|
109
|
+
const cellContent = useMemo(
|
|
110
|
+
() => (
|
|
111
|
+
<motion.div
|
|
112
|
+
className={cn(
|
|
113
|
+
"flex h-full lg:min-h-40 flex-col gap-1 border-l border-t",
|
|
114
|
+
isSunday(date) && "border-l-0",
|
|
115
|
+
)}
|
|
116
|
+
initial={{ opacity: 0, y: 10 }}
|
|
117
|
+
animate={{ opacity: 1, y: 0 }}
|
|
118
|
+
transition={transition}
|
|
119
|
+
>
|
|
120
|
+
<DroppableArea date={date} className="w-full h-full py-2">
|
|
121
|
+
<motion.span
|
|
122
|
+
className={cn(
|
|
123
|
+
"h-6 px-1 text-xs font-semibold lg:px-2",
|
|
124
|
+
!currentMonth && "opacity-20",
|
|
125
|
+
isToday(date) &&
|
|
126
|
+
"flex w-6 translate-x-1 items-center justify-center rounded-full bg-primary px-0 font-bold text-primary-foreground",
|
|
127
|
+
)}
|
|
128
|
+
>
|
|
129
|
+
{day}
|
|
130
|
+
</motion.span>
|
|
131
|
+
|
|
132
|
+
<motion.div
|
|
133
|
+
className={cn(
|
|
134
|
+
"flex h-fit gap-1 px-2 mt-1 lg:h-[94px] lg:flex-col lg:gap-2 lg:px-0",
|
|
135
|
+
!currentMonth && "opacity-50",
|
|
136
|
+
)}
|
|
137
|
+
>
|
|
138
|
+
{cellEvents.length === 0 && !isMobile ? (
|
|
139
|
+
<div className="w-full h-full flex justify-center items-center group">
|
|
140
|
+
<AddEditEventDialog startDate={date}>
|
|
141
|
+
<Button
|
|
142
|
+
variant="ghost"
|
|
143
|
+
className="border opacity-0 group-hover:opacity-100 transition-opacity duration-200"
|
|
144
|
+
>
|
|
145
|
+
<Plus className="h-4 w-4" />
|
|
146
|
+
<span className="max-sm:hidden">Add Event</span>
|
|
147
|
+
</Button>
|
|
148
|
+
</AddEditEventDialog>
|
|
149
|
+
</div>
|
|
150
|
+
) : (
|
|
151
|
+
[0, 1, 2].map(renderEventAtPosition)
|
|
152
|
+
)}
|
|
153
|
+
</motion.div>
|
|
154
|
+
|
|
155
|
+
{showMobileMore && (
|
|
156
|
+
<div className="flex justify-end items-end mx-2">
|
|
157
|
+
<span className="text-[0.6rem] font-semibold text-accent-foreground">
|
|
158
|
+
+{showMoreCount}
|
|
159
|
+
</span>
|
|
160
|
+
</div>
|
|
161
|
+
)}
|
|
162
|
+
|
|
163
|
+
{showDesktopMore && (
|
|
164
|
+
<motion.div
|
|
165
|
+
className={cn(
|
|
166
|
+
"h-4.5 px-1.5 my-2 text-end text-xs font-semibold text-muted-foreground",
|
|
167
|
+
!currentMonth && "opacity-50",
|
|
168
|
+
)}
|
|
169
|
+
initial={{ opacity: 0, y: 5 }}
|
|
170
|
+
animate={{ opacity: 1, y: 0 }}
|
|
171
|
+
transition={{ delay: 0.3, ...transition }}
|
|
172
|
+
>
|
|
173
|
+
<EventListDialog date={date} events={cellEvents} />
|
|
174
|
+
</motion.div>
|
|
175
|
+
)}
|
|
176
|
+
</DroppableArea>
|
|
177
|
+
</motion.div>
|
|
178
|
+
),
|
|
179
|
+
[
|
|
180
|
+
date,
|
|
181
|
+
day,
|
|
182
|
+
currentMonth,
|
|
183
|
+
cellEvents,
|
|
184
|
+
showMobileMore,
|
|
185
|
+
showDesktopMore,
|
|
186
|
+
showMoreCount,
|
|
187
|
+
renderEventAtPosition,
|
|
188
|
+
isMobile,
|
|
189
|
+
],
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
if (isMobile && currentMonth) {
|
|
193
|
+
return (
|
|
194
|
+
<EventListDialog date={date} events={cellEvents}>
|
|
195
|
+
{cellContent}
|
|
196
|
+
</EventListDialog>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return cellContent;
|
|
201
|
+
}
|