@voplus/morpho-workspace 11.0.4 → 11.2.0

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.
Files changed (52) hide show
  1. package/es/controls/AssignButton/index.js +1 -5
  2. package/es/controls/AssignButton/index.js.map +1 -1
  3. package/es/modules/calendar/pages/CalendarMonth/index.d.ts +1 -0
  4. package/es/modules/calendar/pages/CalendarMonth/index.js +15 -1
  5. package/es/modules/calendar/pages/CalendarMonth/index.js.map +1 -1
  6. package/es/modules/calendar-board/CalendarBoardRoutes.d.ts +12 -0
  7. package/es/modules/calendar-board/CalendarBoardRoutes.js +28 -0
  8. package/es/modules/calendar-board/CalendarBoardRoutes.js.map +1 -0
  9. package/es/modules/calendar-board/components/CalendarBoardGrid/index.d.ts +45 -0
  10. package/es/modules/calendar-board/components/CalendarBoardGrid/index.js +48 -0
  11. package/es/modules/calendar-board/components/CalendarBoardGrid/index.js.map +1 -0
  12. package/es/modules/calendar-board/components/CalendarEventChip/index.d.ts +22 -0
  13. package/es/modules/calendar-board/components/CalendarEventChip/index.js +20 -0
  14. package/es/modules/calendar-board/components/CalendarEventChip/index.js.map +1 -0
  15. package/es/modules/calendar-board/components/CalendarLegend/index.d.ts +37 -0
  16. package/es/modules/calendar-board/components/CalendarLegend/index.js +81 -0
  17. package/es/modules/calendar-board/components/CalendarLegend/index.js.map +1 -0
  18. package/es/modules/calendar-board/components/CalendarMoreLink/index.d.ts +11 -0
  19. package/es/modules/calendar-board/components/CalendarMoreLink/index.js +12 -0
  20. package/es/modules/calendar-board/components/CalendarMoreLink/index.js.map +1 -0
  21. package/es/modules/calendar-board/components/CalendarSidebar/index.d.ts +27 -0
  22. package/es/modules/calendar-board/components/CalendarSidebar/index.js +43 -0
  23. package/es/modules/calendar-board/components/CalendarSidebar/index.js.map +1 -0
  24. package/es/modules/calendar-board/components/CalendarToolbar/index.d.ts +23 -0
  25. package/es/modules/calendar-board/components/CalendarToolbar/index.js +28 -0
  26. package/es/modules/calendar-board/components/CalendarToolbar/index.js.map +1 -0
  27. package/es/modules/calendar-board/event-meta.d.ts +160 -0
  28. package/es/modules/calendar-board/event-meta.js +209 -0
  29. package/es/modules/calendar-board/event-meta.js.map +1 -0
  30. package/es/modules/calendar-board/pages/CalendarBoard/index.css +262 -0
  31. package/es/modules/calendar-board/pages/CalendarBoard/index.d.ts +4 -0
  32. package/es/modules/calendar-board/pages/CalendarBoard/index.js +181 -0
  33. package/es/modules/calendar-board/pages/CalendarBoard/index.js.map +1 -0
  34. package/es/modules/calendar-board/pages/CalendarBoard/state.d.ts +116 -0
  35. package/es/modules/calendar-board/pages/CalendarBoard/state.js +232 -0
  36. package/es/modules/calendar-board/pages/CalendarBoard/state.js.map +1 -0
  37. package/es/modules/calendar-board/sidebar-data.d.ts +27 -0
  38. package/es/modules/calendar-board/sidebar-data.js +19 -0
  39. package/es/modules/calendar-board/sidebar-data.js.map +1 -0
  40. package/es/modules/tasks/components/SubTaskList/index.js +1 -4
  41. package/es/modules/tasks/components/SubTaskList/index.js.map +1 -1
  42. package/es/modules/tasks/controls/TaskStatusDataIcon/index.js +1 -7
  43. package/es/modules/tasks/controls/TaskStatusDataIcon/index.js.map +1 -1
  44. package/es/modules/work/components/WorksProperties/index.js +1 -8
  45. package/es/modules/work/components/WorksProperties/index.js.map +1 -1
  46. package/es/modules/work/controls/WorkStatusDataIcon/index.js +1 -7
  47. package/es/modules/work/controls/WorkStatusDataIcon/index.js.map +1 -1
  48. package/es/obsolete/space/components/Tasks/index.js +1 -2
  49. package/es/obsolete/space/components/Tasks/index.js.map +1 -1
  50. package/es/routes.js +2 -2
  51. package/es/routes.js.map +1 -1
  52. package/package.json +7 -5
@@ -0,0 +1,160 @@
1
+ import dayjs from "dayjs";
2
+ import type { ICalendar } from "../../data/calendar";
3
+ /** 日历事件来源(服务端返回的 source 字段,运行时还会被写入 title) */
4
+ export type CalendarBoardSource = {
5
+ id: string;
6
+ type: string;
7
+ nodeType?: string;
8
+ title?: string;
9
+ };
10
+ /**
11
+ * 日历事件:字段与 src/data/calendar 的 ICalendar 兼容。
12
+ *
13
+ * 对 ICalendar 做了两处收窄 / 补充:
14
+ * - `source` 用更完整的 CalendarBoardSource(运行时还带 nodeType / title,ICalendar 只声明了 id 与 type);
15
+ * - 补上 FullCalendar 运行时字段:`start` / `end` 是 FullCalendar 读取的字段,服务端实际会返回;
16
+ * `extendedProps.source` 是 FullCalendar 把非标准字段(source)收拢后的位置,
17
+ * 现有 CalendarMonth 就是通过 `info.event.extendedProps.source` 拿到事件的。
18
+ */
19
+ export type CalendarBoardEvent = Omit<ICalendar, "source"> & {
20
+ source?: CalendarBoardSource;
21
+ start?: string | Date;
22
+ end?: string | Date;
23
+ extendedProps?: {
24
+ source?: CalendarBoardSource;
25
+ };
26
+ };
27
+ /**
28
+ * 分类色板:沿用 calendar-recreation.html 的事件卡原色(蓝 / 红 / 绿 / 橙)。
29
+ *
30
+ * 顺序即取色顺序,见 buildColorMap —— 同一 source.type 永远落在同一颜色上,
31
+ * 与现有 CalendarMonth 用 generateLightColorFromHash(source.type) 的意图一致。
32
+ */
33
+ export declare const CATEGORY_COLORS: readonly ["blue", "red", "green", "orange"];
34
+ export type CategoryColor = (typeof CATEGORY_COLORS)[number];
35
+ /** 事件卡配色:逐字取自 HTML 的 `border border-{c}-400 bg-{c}-50 text-{c}-600` */
36
+ export declare const CHIP_COLOR_CLASS: Record<CategoryColor, string>;
37
+ /** 事件卡内的时间行配色,取自 calendar-day-view.html 的 `text-amber-600` / `text-red-500` */
38
+ export declare const CHIP_TIME_CLASS: Record<CategoryColor, string>;
39
+ /** 图例 / 侧栏分类列表的圆点配色,取自 HTML 的 `bg-blue-600 / bg-red-500 / ...` */
40
+ export declare const DOT_COLOR_CLASS: Record<CategoryColor, string>;
41
+ /**
42
+ * 事件卡外壳:HTML 的 `text-[10px] font-bold uppercase rounded-md px-2 py-0.5 border-l-4 truncate`。
43
+ *
44
+ * 用 overflow-hidden 而不是 truncate:日视图的卡片里还有第二行时间文案,
45
+ * 文字截断交给内层每一行的 truncate。
46
+ */
47
+ export declare const CHIP_BASE_CLASS = "border border-l-4 text-2xs text-font-primary font-bold uppercase rounded-base px-2 py-0.5 cursor-pointer overflow-hidden";
48
+ /** 没有 source.type 时归入的分类名 */
49
+ export declare const DEFAULT_CATEGORY_TYPE = "Other";
50
+ /** 事件的分类名(source.type) */
51
+ export declare const getEventType: (event: CalendarBoardEvent) => string;
52
+ /**
53
+ * 事件的起始时间。
54
+ *
55
+ * ICalendar 声明的是 startDate,FullCalendar 读的是 start,两者都被兼容;
56
+ * 都缺失时退回 dueDate,仍缺失则返回 undefined(该事件不参与侧栏推导)。
57
+ */
58
+ export declare const getEventStart: (event: CalendarBoardEvent) => Date | undefined;
59
+ /** 事件标题:优先 FullCalendar 解析出的 title,退回 source.title */
60
+ export declare const getEventTitle: (event: CalendarBoardEvent) => string;
61
+ /**
62
+ * 按 source.type 去重排序后依次取色。
63
+ *
64
+ * 排序保证映射只取决于「出现了哪些 type」,与事件顺序无关,
65
+ * 因此事件卡、图例、侧栏分类三处拿到的颜色必然一致。
66
+ */
67
+ export declare const buildColorMap: (types: Iterable<string>) => Map<string, CategoryColor>;
68
+ export interface ICategorySummary {
69
+ /** source.type */
70
+ type: string;
71
+ color: CategoryColor;
72
+ count: number;
73
+ /**
74
+ * 该分类下的事件,供图例 hover 时列出。
75
+ *
76
+ * 与 count 同源(都基于传入的全量事件),所以「图例上的数字」和「浮层里的条数」
77
+ * 永远一致;按开始时间升序,方便直接渲染。
78
+ */
79
+ events: CalendarBoardEvent[];
80
+ }
81
+ export interface IEventSummary {
82
+ colorMap: Map<string, CategoryColor>;
83
+ categories: ICategorySummary[];
84
+ }
85
+ /** 一次算出 type → 颜色映射与分类明细(计数 + 成员),供事件卡、图例、侧栏共用 */
86
+ export declare const summarizeEvents: (events: CalendarBoardEvent[]) => IEventSummary;
87
+ /** 取某个事件的颜色;映射缺失时退回色板首色 */
88
+ export declare const resolveColor: (colorMap: Map<string, CategoryColor>, type: string) => CategoryColor;
89
+ /**
90
+ * 本地过滤:搜索关键字 + 被隐藏的分类。
91
+ *
92
+ * 纯前端过滤,不触发重新请求;日历与侧栏 UPCOMING 列表共用同一份结果。
93
+ */
94
+ export declare const filterEvents: (events: CalendarBoardEvent[], options?: {
95
+ search?: string;
96
+ hiddenTypes?: Iterable<string>;
97
+ }) => CalendarBoardEvent[];
98
+ export interface IUpcomingEvent {
99
+ id: string;
100
+ title: string;
101
+ type: string;
102
+ color: CategoryColor;
103
+ /** 形如 `Aug 20 | 4PM`;全天事件只有日期 */
104
+ timeLabel: string;
105
+ /** 剩余天数,形如 `10D` */
106
+ remainingLabel: string;
107
+ source?: CalendarBoardSource;
108
+ }
109
+ /** 侧栏 UPCOMING DEADLINES 的默认条数,与 HTML 稿一致 */
110
+ export declare const UPCOMING_LIMIT = 5;
111
+ /** 相对今天还有几天(今天为 0D) */
112
+ export declare const formatRemaining: (start: Date, from: dayjs.Dayjs) => string;
113
+ /**
114
+ * UPCOMING DEADLINES:今天及以后的事件,按起始时间升序取前 N 条。
115
+ *
116
+ * @param from 以哪一天为「今天」,默认取当前时间;测试注入固定日期用。
117
+ */
118
+ export declare const buildUpcoming: (events: CalendarBoardEvent[], summary: IEventSummary, options?: {
119
+ from?: dayjs.Dayjs;
120
+ limit?: number;
121
+ }) => IUpcomingEvent[];
122
+ /**
123
+ * 写回服务端时的时间格式:**本地时间**,不带时区后缀。
124
+ *
125
+ * 与仓库里其它写时间属性的地方保持一致(见 MeetingHeader 的 StartTime/EndTime)。
126
+ * 注意不要用 toISOString():它产出 UTC 时刻,UTC+8 下把事件拖到某天零点时,
127
+ * 日期部分会退回前一天,服务端按本地时间解析就落到错误的日期上。
128
+ */
129
+ export declare const DATE_TIME_FORMAT = "YYYY-MM-DDTHH:mm:ss.SSS";
130
+ /** 拖拽落点 → 服务端要写的时间字段 */
131
+ export type DropPayload = {
132
+ Start: string;
133
+ End?: string;
134
+ };
135
+ /**
136
+ * 把 FullCalendar 给出的落点转成服务端载荷。
137
+ *
138
+ * @returns 起止时间都拿不到(或非法)时返回 undefined,调用方应当放弃这次拖拽
139
+ */
140
+ export declare const buildDropPayload: (options: {
141
+ start?: Date | string | null;
142
+ end?: Date | string | null;
143
+ }) => DropPayload | undefined;
144
+ /** 按事件 id 在列表里找对应的那条(id 比较规则见 isSameEvent) */
145
+ export declare const findEventById: (events: CalendarBoardEvent[], id?: string) => CalendarBoardEvent | undefined;
146
+ /**
147
+ * 把拖拽结果写回页面自己的事件列表。
148
+ *
149
+ * 必须做这一步:`state.listOptions` 只在挂载时加载一次,而事件数据会交给 FullCalendar
150
+ * 当 eventSources —— 列表里还是旧位置的话,任何一次事件源重置都会把刚拖出去的事件打回原位。
151
+ *
152
+ * ⚠️ 没命中任何一条时必须**原样返回同一个数组**:`.map()` 那种写法即使什么都没改也会造出
153
+ * 新引用,而新引用本身就会触发 FullCalendar 重置事件源,反而把拖拽结果覆盖掉。
154
+ */
155
+ export declare const applyDropToEvents: (events: CalendarBoardEvent[], id: string, drop: {
156
+ start?: Date | string | null;
157
+ end?: Date | string | null;
158
+ }) => CalendarBoardEvent[];
159
+ /** 图例浮层里的日期文案,设计稿是 `Aug 3`;拿不到时间时返回空串 */
160
+ export declare const formatEventDate: (event: CalendarBoardEvent) => string;
@@ -0,0 +1,209 @@
1
+ import dayjs from "dayjs";
2
+ /**
3
+ * 分类色板:沿用 calendar-recreation.html 的事件卡原色(蓝 / 红 / 绿 / 橙)。
4
+ *
5
+ * 顺序即取色顺序,见 buildColorMap —— 同一 source.type 永远落在同一颜色上,
6
+ * 与现有 CalendarMonth 用 generateLightColorFromHash(source.type) 的意图一致。
7
+ */
8
+ export const CATEGORY_COLORS = ["blue", "red", "green", "orange"];
9
+ /** 事件卡配色:逐字取自 HTML 的 `border border-{c}-400 bg-{c}-50 text-{c}-600` */
10
+ export const CHIP_COLOR_CLASS = {
11
+ blue: "border-blue-400 bg-blue-50",
12
+ red: "border-red-400 bg-red-50",
13
+ green: "border-green-400 bg-green-50",
14
+ orange: "border-amber-400 bg-amber-50",
15
+ };
16
+ /** 事件卡内的时间行配色,取自 calendar-day-view.html 的 `text-amber-600` / `text-red-500` */
17
+ export const CHIP_TIME_CLASS = {
18
+ blue: "text-blue-500",
19
+ red: "text-red-500",
20
+ green: "text-green-600",
21
+ orange: "text-amber-600",
22
+ };
23
+ /** 图例 / 侧栏分类列表的圆点配色,取自 HTML 的 `bg-blue-600 / bg-red-500 / ...` */
24
+ export const DOT_COLOR_CLASS = {
25
+ blue: "bg-blue-600",
26
+ red: "bg-red-500",
27
+ green: "bg-green-600",
28
+ orange: "bg-orange-500",
29
+ };
30
+ /**
31
+ * 事件卡外壳:HTML 的 `text-[10px] font-bold uppercase rounded-md px-2 py-0.5 border-l-4 truncate`。
32
+ *
33
+ * 用 overflow-hidden 而不是 truncate:日视图的卡片里还有第二行时间文案,
34
+ * 文字截断交给内层每一行的 truncate。
35
+ */
36
+ export const CHIP_BASE_CLASS = "border border-l-4 text-2xs text-font-primary font-bold uppercase rounded-base px-2 py-0.5 cursor-pointer overflow-hidden";
37
+ /** 没有 source.type 时归入的分类名 */
38
+ export const DEFAULT_CATEGORY_TYPE = "Other";
39
+ /** 事件的分类名(source.type) */
40
+ export const getEventType = (event) => event.source?.type || DEFAULT_CATEGORY_TYPE;
41
+ /**
42
+ * 事件的起始时间。
43
+ *
44
+ * ICalendar 声明的是 startDate,FullCalendar 读的是 start,两者都被兼容;
45
+ * 都缺失时退回 dueDate,仍缺失则返回 undefined(该事件不参与侧栏推导)。
46
+ */
47
+ export const getEventStart = (event) => {
48
+ const source = event;
49
+ const raw = event.start ?? source.startDate ?? source.dueDate;
50
+ if (!raw)
51
+ return undefined;
52
+ const date = dayjs(raw);
53
+ return date.isValid() ? date.toDate() : undefined;
54
+ };
55
+ /** 事件标题:优先 FullCalendar 解析出的 title,退回 source.title */
56
+ export const getEventTitle = (event) => event.title || event.source?.title || "";
57
+ /**
58
+ * 按 source.type 去重排序后依次取色。
59
+ *
60
+ * 排序保证映射只取决于「出现了哪些 type」,与事件顺序无关,
61
+ * 因此事件卡、图例、侧栏分类三处拿到的颜色必然一致。
62
+ */
63
+ export const buildColorMap = (types) => new Map(Array.from(new Set(types))
64
+ .sort()
65
+ .map((type, index) => [type, CATEGORY_COLORS[index % CATEGORY_COLORS.length]]));
66
+ /** 一次算出 type → 颜色映射与分类明细(计数 + 成员),供事件卡、图例、侧栏共用 */
67
+ export const summarizeEvents = (events) => {
68
+ const colorMap = buildColorMap(events.map(getEventType));
69
+ const groups = new Map();
70
+ events.forEach((event) => {
71
+ const type = getEventType(event);
72
+ const group = groups.get(type);
73
+ if (group) {
74
+ group.push(event);
75
+ }
76
+ else {
77
+ groups.set(type, [event]);
78
+ }
79
+ });
80
+ const categories = Array.from(groups, ([type, group]) => ({
81
+ type,
82
+ count: group.length,
83
+ color: colorMap.get(type) ?? CATEGORY_COLORS[0],
84
+ events: sortByStart(group),
85
+ })).sort((a, b) => b.count - a.count || a.type.localeCompare(b.type));
86
+ return { colorMap, categories };
87
+ };
88
+ /** 按开始时间升序;拿不到时间的排在最后(保持稳定,不改变其余相对顺序) */
89
+ const sortByStart = (events) => events
90
+ .map((event, index) => ({ event, index, time: getEventStart(event)?.getTime() ?? Infinity }))
91
+ .sort((a, b) => a.time - b.time || a.index - b.index)
92
+ .map((item) => item.event);
93
+ /** 取某个事件的颜色;映射缺失时退回色板首色 */
94
+ export const resolveColor = (colorMap, type) => colorMap.get(type) ?? CATEGORY_COLORS[0];
95
+ /**
96
+ * 本地过滤:搜索关键字 + 被隐藏的分类。
97
+ *
98
+ * 纯前端过滤,不触发重新请求;日历与侧栏 UPCOMING 列表共用同一份结果。
99
+ */
100
+ export const filterEvents = (events, options) => {
101
+ const keyword = options?.search?.trim().toLowerCase() ?? "";
102
+ const hidden = new Set(options?.hiddenTypes ?? []);
103
+ return events.filter((event) => {
104
+ if (hidden.has(getEventType(event)))
105
+ return false;
106
+ if (!keyword)
107
+ return true;
108
+ return getEventTitle(event).toLowerCase().includes(keyword);
109
+ });
110
+ };
111
+ /** 侧栏 UPCOMING DEADLINES 的默认条数,与 HTML 稿一致 */
112
+ export const UPCOMING_LIMIT = 5;
113
+ /** 相对今天还有几天(今天为 0D) */
114
+ export const formatRemaining = (start, from) => `${Math.max(0, dayjs(start).startOf("day").diff(from.startOf("day"), "day"))}D`;
115
+ /**
116
+ * UPCOMING DEADLINES:今天及以后的事件,按起始时间升序取前 N 条。
117
+ *
118
+ * @param from 以哪一天为「今天」,默认取当前时间;测试注入固定日期用。
119
+ */
120
+ export const buildUpcoming = (events, summary, options) => {
121
+ const from = options?.from ?? dayjs();
122
+ const limit = options?.limit ?? UPCOMING_LIMIT;
123
+ return events
124
+ .map((event) => ({ event, start: getEventStart(event) }))
125
+ .filter((item) => {
126
+ if (!item.start)
127
+ return false;
128
+ return !dayjs(item.start).startOf("day").isBefore(from.startOf("day"));
129
+ })
130
+ .sort((a, b) => a.start.getTime() - b.start.getTime())
131
+ .slice(0, limit)
132
+ .map(({ event, start }) => {
133
+ const type = getEventType(event);
134
+ return {
135
+ id: event.id,
136
+ title: getEventTitle(event),
137
+ type,
138
+ color: resolveColor(summary.colorMap, type),
139
+ timeLabel: event.allDay
140
+ ? dayjs(start).format("MMM D")
141
+ : `${dayjs(start).format("MMM D")} | ${dayjs(start).format("hA")}`,
142
+ remainingLabel: formatRemaining(start, from),
143
+ source: event.source,
144
+ };
145
+ });
146
+ };
147
+ /**
148
+ * 写回服务端时的时间格式:**本地时间**,不带时区后缀。
149
+ *
150
+ * 与仓库里其它写时间属性的地方保持一致(见 MeetingHeader 的 StartTime/EndTime)。
151
+ * 注意不要用 toISOString():它产出 UTC 时刻,UTC+8 下把事件拖到某天零点时,
152
+ * 日期部分会退回前一天,服务端按本地时间解析就落到错误的日期上。
153
+ */
154
+ export const DATE_TIME_FORMAT = "YYYY-MM-DDTHH:mm:ss.SSS";
155
+ /**
156
+ * 把 FullCalendar 给出的落点转成服务端载荷。
157
+ *
158
+ * @returns 起止时间都拿不到(或非法)时返回 undefined,调用方应当放弃这次拖拽
159
+ */
160
+ export const buildDropPayload = (options) => {
161
+ const start = options.start ? dayjs(options.start) : undefined;
162
+ if (!start || !start.isValid())
163
+ return undefined;
164
+ const payload = { Start: start.format(DATE_TIME_FORMAT) };
165
+ const end = options.end ? dayjs(options.end) : undefined;
166
+ if (end && end.isValid())
167
+ payload.End = end.format(DATE_TIME_FORMAT);
168
+ return payload;
169
+ };
170
+ /**
171
+ * 事件 id 比较。
172
+ *
173
+ * FullCalendar 会把事件 id 强转成字符串(其内部 EVENT_NON_DATE_REFINERS 里 id: String),
174
+ * 所以服务端下发的数字 id 到了这边就成了 "123" —— 用 `===` 直接比会永远不相等,
175
+ * 拖拽结果就同步不回列表。这里统一按字符串比。
176
+ */
177
+ const isSameEvent = (item, id) => item.id !== undefined && item.id !== null && String(item.id) === id;
178
+ /** 按事件 id 在列表里找对应的那条(id 比较规则见 isSameEvent) */
179
+ export const findEventById = (events, id) => id ? events.find((item) => isSameEvent(item, id)) : undefined;
180
+ /**
181
+ * 把拖拽结果写回页面自己的事件列表。
182
+ *
183
+ * 必须做这一步:`state.listOptions` 只在挂载时加载一次,而事件数据会交给 FullCalendar
184
+ * 当 eventSources —— 列表里还是旧位置的话,任何一次事件源重置都会把刚拖出去的事件打回原位。
185
+ *
186
+ * ⚠️ 没命中任何一条时必须**原样返回同一个数组**:`.map()` 那种写法即使什么都没改也会造出
187
+ * 新引用,而新引用本身就会触发 FullCalendar 重置事件源,反而把拖拽结果覆盖掉。
188
+ */
189
+ export const applyDropToEvents = (events, id, drop) => {
190
+ const index = events.findIndex((item) => isSameEvent(item, id));
191
+ if (index === -1)
192
+ return events;
193
+ const next = { ...events[index] };
194
+ if (drop.start) {
195
+ next.start = drop.start;
196
+ next.startDate = dayjs(drop.start).toDate();
197
+ }
198
+ if (drop.end)
199
+ next.end = drop.end;
200
+ const result = events.slice();
201
+ result[index] = next;
202
+ return result;
203
+ };
204
+ /** 图例浮层里的日期文案,设计稿是 `Aug 3`;拿不到时间时返回空串 */
205
+ export const formatEventDate = (event) => {
206
+ const start = getEventStart(event);
207
+ return start ? dayjs(start).format("MMM D") : "";
208
+ };
209
+ //# sourceMappingURL=event-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-meta.js","sourceRoot":"","sources":["../../../src/modules/calendar-board/event-meta.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,262 @@
1
+ /*
2
+ * CalendarBoard 的 FullCalendar 皮肤。
3
+ *
4
+ * FullCalendar 的内部 DOM(表格、日期格、时间轴、+N 徽标)由它自己生成,挂不上 Tailwind 类,
5
+ * 因此这里用官方 CSS 变量(--fc-*)为主、少量选择器覆盖为辅。覆盖的选择器都取自
6
+ * FullCalendar 6.1 实际注入的默认样式(见 daygrid / timegrid 的 internal.js)。
7
+ *
8
+ * 颜色一律走 morpho-theme token(var(--color-*) / var(--font-size-*)),不写死色值;
9
+ * 事件卡的分类原色在 event-meta.ts 的 CHIP_COLOR_CLASS 里,不走这里。
10
+ */
11
+
12
+ .calendar-board {
13
+ /* ═══ FullCalendar 官方 CSS 变量:把内置皮肤换成设计稿的灰白体系 ═══ */
14
+ --fc-border-color: var(--color-border);
15
+ --fc-page-bg-color: var(--color-bg-white);
16
+ --fc-neutral-bg-color: var(--color-bg-table);
17
+ --fc-neutral-text-color: var(--color-font-grey);
18
+ --fc-today-bg-color: var(--color-bg-blue-data);
19
+ --fc-highlight-color: var(--color-bg-blue-data);
20
+ --fc-now-indicator-color: var(--color-font-secondary);
21
+ --fc-more-link-bg-color: transparent;
22
+ --fc-more-link-text-color: inherit;
23
+ /* 事件节点本身不画底色 / 边框:描边浅底由 .calendar-event-chip 负责 */
24
+ --fc-event-bg-color: transparent;
25
+ --fc-event-border-color: transparent;
26
+ --fc-event-text-color: inherit;
27
+ --fc-small-font-size: var(--font-size-2xs);
28
+
29
+ /* 最后一个事件卡要给 +N 让出的宽度 */
30
+ --board-more-gutter: 36px;
31
+
32
+ .fc {
33
+ font-size: var(--font-size-xs);
34
+ }
35
+
36
+ /* ═══ 事件节点:剥掉 FullCalendar 自带外观,只剩我们的卡片 ═══ */
37
+ .fc-h-event {
38
+ border: 0;
39
+ background: transparent;
40
+ }
41
+
42
+ .fc-event-main {
43
+ height: 100%;
44
+ padding: 0;
45
+ }
46
+
47
+ /*
48
+ * ═══ 只去掉最外圈的边框(内部网格线全部保留)═══
49
+ *
50
+ * 边缘线一共有三层,来自不同元素,必须分别处理:
51
+ *
52
+ * 1. .fc-scrollgrid 自身 —— 但 FullCalendar 只让它的上 + 左可见
53
+ * (.fc .fc-scrollgrid 把 right/bottom 清零了);
54
+ * 2. .fc-scrollgrid 自己的格子(每个 section 一行、一行一个 th/td)——
55
+ * 右 + 下都在(FullCalendar 只把它们的 left/top 清零);
56
+ * 3. 紧贴外框的内层表格(列头 / 日期网格 / 时间轴)——
57
+ * 它们的最后一列、最后一行同样各画一条边线。
58
+ *
59
+ * 选择器的安全性:每一层每行的**最后一个**格子才是右侧边缘
60
+ * (时间轴与泳道的分隔线在行内前两个格子之间,不受影响);
61
+ * 底边则只取最后一行的格子,并排除 .fc-col-header —— 它的底边是
62
+ * 「列头与日期格之间」的内部线,必须留着。
63
+ */
64
+ .fc-theme-standard .fc-scrollgrid {
65
+ border: 0;
66
+ }
67
+
68
+ /** 右边缘:所有层级的最后一个格子 */
69
+ .fc-scrollgrid tr > *:last-child {
70
+ border-right-width: 0;
71
+ }
72
+
73
+ /** 下边缘:外框自身的最后一行 */
74
+ .fc-scrollgrid > tbody > tr:last-child > * {
75
+ border-bottom-width: 0;
76
+ }
77
+
78
+ /**
79
+ * 下边缘:最底部那张内层表的最后一行。
80
+ *
81
+ * 限定在「外框最后一行格子的内部」,而不是所有内层表 ——
82
+ * 日视图里全天行在外框第 2 行、并不贴底,它的底边是全天区与时间轴之间的
83
+ * 内部分隔线(那条由 .fc-timegrid-divider 负责画),不该被清掉。
84
+ */
85
+ .fc-scrollgrid > tbody > tr:last-child > * table:not(.fc-col-header) tr:last-child > * {
86
+ border-bottom-width: 0;
87
+ }
88
+
89
+ /* ═══ 列头 SUN / MON / ... ═══ */
90
+ .fc-col-header-cell {
91
+ background-color: var(--color-bg-white);
92
+ }
93
+
94
+ .fc-col-header-cell-cushion {
95
+ padding: var(--space-sm) var(--space-md);
96
+ font-size: var(--font-size-2xs);
97
+ font-weight: 600;
98
+ letter-spacing: 0.05em;
99
+ text-transform: uppercase;
100
+ color: var(--color-font-grey);
101
+ text-decoration: none;
102
+ }
103
+
104
+ /* ═══ 月 / 周视图的日期格 ═══ */
105
+ /* FullCalendar 默认 row-reverse(日期数字靠右),设计稿是左上角 */
106
+ .fc .fc-daygrid-day-top {
107
+ flex-direction: row;
108
+ }
109
+
110
+ .fc-daygrid-day-number {
111
+ padding: var(--space-sm) var(--space-md);
112
+ font-weight: 500;
113
+ color: var(--color-font-primary);
114
+ text-decoration: none;
115
+ }
116
+
117
+ .fc-day-today .fc-daygrid-day-number {
118
+ font-weight: 600;
119
+ color: var(--color-font-secondary);
120
+ }
121
+
122
+ .fc-day-other .fc-daygrid-day-number {
123
+ color: var(--color-font-disabled);
124
+ }
125
+
126
+ .fc .fc-daygrid-day-events {
127
+ margin: 0 var(--space-sm) var(--space-sm);
128
+ }
129
+
130
+ .fc-daygrid-event {
131
+ margin-top: var(--space-xs);
132
+ border-radius: 0;
133
+ /* 旧 index.css 同样关掉 nowrap,让长标题能换行 */
134
+ white-space: normal;
135
+ }
136
+
137
+ /* ═══ +N 徽标:外观由 CalendarMoreLink 负责 ═══ */
138
+ .fc-daygrid-more-link {
139
+ margin: 0;
140
+ padding: 0;
141
+ border-radius: 0;
142
+ text-decoration: none;
143
+ }
144
+
145
+ .fc-daygrid-more-link:hover {
146
+ background-color: transparent;
147
+ }
148
+
149
+ /*
150
+ * +N 与最后一个可见事件卡同排。
151
+ *
152
+ * 设计稿就是这么排的:最后一张卡让位收窄、+N 紧跟在它右边,而不是单独占一行。
153
+ * FullCalendar 没有「内联 +N」的选项;搬运 DOM 也不行 —— Preact 重渲染时会把
154
+ * .fc-daygrid-day-bottom 重新插回 .fc-daygrid-day-events,徽标跳回原位
155
+ * (实测:setOption("eventSources") 之后节点身份不变,但父级被还原)。
156
+ * 因此只能用 CSS 叠回去,好处是天然不受重渲染影响。
157
+ *
158
+ * 两步:
159
+ * 1) 让 +N 脱离文档流,钉在最后一张卡那一行的右侧;
160
+ * 2) 让最后一张「可见」卡右侧留出 +N 的位置。
161
+ *
162
+ * 第 1 步用 position: absolute + bottom: 0,而不是「上移一行」的负 margin:
163
+ * 容器底部就是最后一张卡的底部,这样不需要写死卡片高度 —— 卡片样式随时可能调,
164
+ * 任何像素常量都会在别人改 py-/text- 的时候静默失准。容器本身有 position: relative
165
+ * (FullCalendar 的 unbalanced / balanced 两种布局都会给它定位)。
166
+ *
167
+ * 门禁用 .fc-daygrid-more-link 而不是 .fc-daygrid-day-bottom:
168
+ * 后者是**每个格子都会渲染**的空容器,拿它当条件等于恒真,会让所有格子的
169
+ * 最后一张卡都被平白缩窄(这个坑是测试先发现的)。
170
+ *
171
+ * 关于「最后一个可见卡」:被收起的事件仍然留在 DOM 里(带 inline 的
172
+ * visibility: hidden),所以要按可见性过滤。这里刻意拆成两条规则
173
+ * (先给所有可见卡留位,再把后面还有可见卡的取消),避免写 :not(:has(~ …))
174
+ * 这种嵌套形式 —— 部分 CSS 引擎不支持,那会导致 +N 叠在卡片上面而不是并排。
175
+ */
176
+ .fc-daygrid-day-events:has(.fc-daygrid-more-link)
177
+ > .fc-daygrid-event-harness:not([style*="visibility: hidden"]) {
178
+ padding-right: var(--board-more-gutter);
179
+ }
180
+
181
+ .fc-daygrid-day-events:has(.fc-daygrid-more-link)
182
+ > .fc-daygrid-event-harness:not([style*="visibility: hidden"]):has(
183
+ ~ .fc-daygrid-event-harness:not([style*="visibility: hidden"])
184
+ ) {
185
+ padding-right: 0;
186
+ }
187
+
188
+ .fc .fc-daygrid-day-events:has(.fc-daygrid-more-link) > .fc-daygrid-day-bottom {
189
+ position: absolute;
190
+ right: 0;
191
+ bottom: 0;
192
+ display: flex;
193
+ justify-content: flex-end;
194
+ /* 抵消 FullCalendar 给它的行偏移与左右外边距 */
195
+ margin: 0 !important;
196
+ }
197
+
198
+ /* ═══ 日视图:时间轴 ═══ */
199
+ .fc .fc-timegrid-axis-cushion,
200
+ .fc .fc-timegrid-slot-label-cushion {
201
+ padding: 0 var(--space-md) 0 0;
202
+ font-size: var(--font-size-xs);
203
+ color: var(--color-font-grey);
204
+ }
205
+
206
+ .fc .fc-timegrid-axis-cushion {
207
+ max-width: none;
208
+ }
209
+
210
+ .fc-timegrid-event {
211
+ border-radius: var(--radius-base);
212
+ }
213
+
214
+ /* 卡片撑满 FullCalendar 按事件时长算出的定位盒子 */
215
+ .fc-timegrid-event .calendar-event-chip {
216
+ height: 100%;
217
+ }
218
+
219
+ .fc .fc-timegrid-now-indicator-line {
220
+ border-width: 1.5px 0 0;
221
+ }
222
+
223
+ /* ═══ 溢出弹层(moreLinkClick 默认 popover)═══ */
224
+ .fc-more-popover {
225
+ border-radius: var(--radius-base);
226
+ box-shadow: var(--shadow-card);
227
+ }
228
+
229
+ .fc-popover-header {
230
+ background-color: var(--color-bg-table);
231
+ }
232
+
233
+ /* ═══ 滚动条:沿用旧 index.css 的细条写法 ═══ */
234
+ .fc-scroller {
235
+ &::-webkit-scrollbar {
236
+ width: 7px;
237
+ height: 7px;
238
+ }
239
+ &::-webkit-scrollbar-track {
240
+ background-color: transparent;
241
+ border-radius: 2em;
242
+ }
243
+ &::-webkit-scrollbar-thumb {
244
+ background-color: rgba(0, 0, 0, 0.2);
245
+ background-clip: padding-box;
246
+ border-radius: 2em;
247
+ }
248
+ }
249
+ }
250
+
251
+ /**
252
+ * 盖在标题上的透明 DatePicker。
253
+ *
254
+ * 沿用 CalendarMonth 的 .calendar-ant-picker 做法:点标题即可跳到某月 / 某周 / 某天。
255
+ */
256
+ .calendar-board-picker {
257
+ position: absolute;
258
+ inset: 0;
259
+ width: 100%;
260
+ height: 100%;
261
+ opacity: 0;
262
+ }
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ import "./index.css";
3
+ declare const CalendarBoard: () => React.JSX.Element;
4
+ export default CalendarBoard;