@oddle.me/react-calendar-timeline 0.28.1-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.
- package/LICENSE.md +8 -0
- package/README.md +1298 -0
- package/lib/Timeline.css +110 -0
- package/lib/index.js +77 -0
- package/lib/lib/Timeline.js +966 -0
- package/lib/lib/columns/Columns.js +137 -0
- package/lib/lib/default-config.js +73 -0
- package/lib/lib/headers/CustomDateHeader.js +48 -0
- package/lib/lib/headers/CustomHeader.js +276 -0
- package/lib/lib/headers/DateHeader.js +206 -0
- package/lib/lib/headers/HeadersContext.js +94 -0
- package/lib/lib/headers/Interval.js +131 -0
- package/lib/lib/headers/SidebarHeader.js +130 -0
- package/lib/lib/headers/TimelineHeaders.js +180 -0
- package/lib/lib/headers/constants.js +10 -0
- package/lib/lib/interaction/PreventClickOnDrag.js +99 -0
- package/lib/lib/items/Item.js +627 -0
- package/lib/lib/items/Items.js +178 -0
- package/lib/lib/items/defaultItemRenderer.js +40 -0
- package/lib/lib/items/styles.js +65 -0
- package/lib/lib/layout/Sidebar.js +138 -0
- package/lib/lib/markers/MarkerCanvas.js +164 -0
- package/lib/lib/markers/MarkerCanvasContext.js +28 -0
- package/lib/lib/markers/TimelineMarkersContext.js +157 -0
- package/lib/lib/markers/TimelineMarkersRenderer.js +69 -0
- package/lib/lib/markers/implementations/CursorMarker.js +137 -0
- package/lib/lib/markers/implementations/CustomMarker.js +79 -0
- package/lib/lib/markers/implementations/TodayMarker.js +123 -0
- package/lib/lib/markers/implementations/shared.js +51 -0
- package/lib/lib/markers/markerType.js +12 -0
- package/lib/lib/markers/public/CursorMarker.js +97 -0
- package/lib/lib/markers/public/CustomMarker.js +116 -0
- package/lib/lib/markers/public/TimelineMarkers.js +27 -0
- package/lib/lib/markers/public/TodayMarker.js +121 -0
- package/lib/lib/row/GroupRow.js +94 -0
- package/lib/lib/row/GroupRows.js +117 -0
- package/lib/lib/scroll/ScrollElement.js +285 -0
- package/lib/lib/timeline/TimelineStateContext.js +155 -0
- package/lib/lib/utility/calendar.js +672 -0
- package/lib/lib/utility/dom-helpers.js +62 -0
- package/lib/lib/utility/events.js +23 -0
- package/lib/lib/utility/generic.js +44 -0
- package/lib/resize-detector/container.js +36 -0
- package/lib/resize-detector/window.js +25 -0
- package/package.json +162 -0
- package/src/global.d.ts +373 -0
package/src/global.d.ts
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
// Project: https://github.com/namespace-ee/react-calendar-timeline
|
|
2
|
+
// Definitions by: Rajab Shakirov <https://github.com/radziksh>
|
|
3
|
+
// Andrii Los <https://github.com/rip21>
|
|
4
|
+
// Jon Caruana <https://github.com/joncar>
|
|
5
|
+
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
|
6
|
+
// TypeScript Version: 3.5
|
|
7
|
+
|
|
8
|
+
import * as React from 'react';
|
|
9
|
+
import { Moment } from 'moment';
|
|
10
|
+
|
|
11
|
+
declare module '@oddle.me/react-calendar-timeline' {
|
|
12
|
+
type Id = number | string;
|
|
13
|
+
|
|
14
|
+
export interface TimelineGroupBase {
|
|
15
|
+
id: Id;
|
|
16
|
+
title: React.ReactNode;
|
|
17
|
+
rightTitle?: React.ReactNode | undefined;
|
|
18
|
+
height?: number | undefined;
|
|
19
|
+
stackItems?: boolean | undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface TimelineItemBase<DateType> {
|
|
23
|
+
id: Id;
|
|
24
|
+
group: Id;
|
|
25
|
+
title?: React.ReactNode | undefined;
|
|
26
|
+
start_time: DateType;
|
|
27
|
+
end_time: DateType;
|
|
28
|
+
canMove?: boolean | undefined;
|
|
29
|
+
canResize?: boolean | 'left' | 'right' | 'both' | undefined;
|
|
30
|
+
canChangeGroup?: boolean | undefined;
|
|
31
|
+
className?: string | undefined;
|
|
32
|
+
style?: React.CSSProperties | undefined;
|
|
33
|
+
itemProps?: React.HTMLAttributes<HTMLDivElement> | undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type TimelineItem<CustomItemFields, DateType = number> = TimelineItemBase<DateType> & CustomItemFields;
|
|
37
|
+
export type TimelineGroup<CustomGroupFields> = TimelineGroupBase & CustomGroupFields;
|
|
38
|
+
|
|
39
|
+
export interface TimelineContext {
|
|
40
|
+
timelineWidth: number;
|
|
41
|
+
visibleTimeStart: number;
|
|
42
|
+
visibleTimeEnd: number;
|
|
43
|
+
canvasTimeStart: number;
|
|
44
|
+
canvasTimeEnd: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ItemContext {
|
|
48
|
+
dimensions: {
|
|
49
|
+
collisionLeft: number;
|
|
50
|
+
collisionWidth: number;
|
|
51
|
+
height: number;
|
|
52
|
+
isDragging: boolean;
|
|
53
|
+
left: number;
|
|
54
|
+
order: {
|
|
55
|
+
group: {
|
|
56
|
+
id: string;
|
|
57
|
+
};
|
|
58
|
+
index: number;
|
|
59
|
+
};
|
|
60
|
+
originalLeft: number;
|
|
61
|
+
stack: boolean;
|
|
62
|
+
top: number | null;
|
|
63
|
+
width: number;
|
|
64
|
+
};
|
|
65
|
+
useResizeHandle: boolean;
|
|
66
|
+
title: string;
|
|
67
|
+
canMove: boolean;
|
|
68
|
+
canResizeLeft: boolean;
|
|
69
|
+
canResizeRight: boolean;
|
|
70
|
+
selected: boolean;
|
|
71
|
+
dragging: boolean;
|
|
72
|
+
dragStart: {
|
|
73
|
+
x: number;
|
|
74
|
+
y: number;
|
|
75
|
+
};
|
|
76
|
+
dragTime: number;
|
|
77
|
+
dragGroupDelta: number;
|
|
78
|
+
resizing: boolean;
|
|
79
|
+
resizeEdge: 'left' | 'right';
|
|
80
|
+
resizeStart: number;
|
|
81
|
+
resizeTime: number;
|
|
82
|
+
width: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface TimeFormat {
|
|
86
|
+
long: string;
|
|
87
|
+
mediumLong: string;
|
|
88
|
+
medium: string;
|
|
89
|
+
short: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface LabelFormat {
|
|
93
|
+
year: TimeFormat;
|
|
94
|
+
month: TimeFormat;
|
|
95
|
+
week: TimeFormat;
|
|
96
|
+
day: TimeFormat;
|
|
97
|
+
hour: TimeFormat;
|
|
98
|
+
minute: TimeFormat;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface ItemRendererGetItemPropsReturnType {
|
|
102
|
+
key: Id;
|
|
103
|
+
ref: React.Ref<any>;
|
|
104
|
+
className: string;
|
|
105
|
+
onMouseDown: React.MouseEventHandler;
|
|
106
|
+
onMouseUp: React.MouseEventHandler;
|
|
107
|
+
onTouchStart: React.TouchEventHandler;
|
|
108
|
+
onTouchEnd: React.TouchEventHandler;
|
|
109
|
+
onDoubleClick: React.MouseEventHandler;
|
|
110
|
+
onContextMenu: React.ReactEventHandler;
|
|
111
|
+
style: React.CSSProperties;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export type GetItemsProps = Partial<Omit<ItemRendererGetItemPropsReturnType, 'key' | 'ref'>>;
|
|
115
|
+
|
|
116
|
+
export interface ItemRendererGetResizePropsReturnType {
|
|
117
|
+
left?: {
|
|
118
|
+
ref: React.Ref<any>;
|
|
119
|
+
className: string;
|
|
120
|
+
style: React.CSSProperties;
|
|
121
|
+
} | undefined;
|
|
122
|
+
right?: {
|
|
123
|
+
ref: React.Ref<any>;
|
|
124
|
+
className: string;
|
|
125
|
+
style: React.CSSProperties;
|
|
126
|
+
} | undefined;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export type GetResizeProps = {
|
|
130
|
+
leftStyle?: React.CSSProperties | undefined;
|
|
131
|
+
rightStyle?: React.CSSProperties | undefined;
|
|
132
|
+
leftClassName?: string | undefined;
|
|
133
|
+
rightClassName?: string | undefined;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export interface ReactCalendarItemRendererProps<
|
|
137
|
+
CustomItem extends TimelineItemBase<any> = TimelineItemBase<number>
|
|
138
|
+
> {
|
|
139
|
+
item: CustomItem;
|
|
140
|
+
itemContext: ItemContext;
|
|
141
|
+
getItemProps: (
|
|
142
|
+
props: GetItemsProps,
|
|
143
|
+
) => {
|
|
144
|
+
key: Id;
|
|
145
|
+
ref: React.Ref<any>;
|
|
146
|
+
className: string;
|
|
147
|
+
onMouseDown: React.MouseEventHandler;
|
|
148
|
+
onMouseUp: React.MouseEventHandler;
|
|
149
|
+
onTouchStart: React.TouchEventHandler;
|
|
150
|
+
onTouchEnd: React.TouchEventHandler;
|
|
151
|
+
onDoubleClick: React.MouseEventHandler;
|
|
152
|
+
onContextMenu: React.ReactEventHandler;
|
|
153
|
+
style: React.CSSProperties;
|
|
154
|
+
};
|
|
155
|
+
getResizeProps: (propsOverride?: GetResizeProps) => ItemRendererGetResizePropsReturnType;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface ReactCalendarGroupRendererProps<CustomGroup extends TimelineGroupBase = TimelineGroupBase> {
|
|
159
|
+
group: CustomGroup;
|
|
160
|
+
isRightSidebar?: boolean | undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface OnItemDragObjectBase {
|
|
164
|
+
eventType: 'move' | 'resize';
|
|
165
|
+
itemId: Id;
|
|
166
|
+
time: number;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface OnItemDragObjectMove extends OnItemDragObjectBase {
|
|
170
|
+
eventType: 'move';
|
|
171
|
+
newGroupOrder: number;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface OnItemDragObjectResize extends OnItemDragObjectBase {
|
|
175
|
+
eventType: 'resize';
|
|
176
|
+
edge?: 'left' | 'right' | undefined;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface TimelineKeys {
|
|
180
|
+
groupIdKey: string;
|
|
181
|
+
groupTitleKey: string;
|
|
182
|
+
groupRightTitleKey: string;
|
|
183
|
+
itemIdKey: string;
|
|
184
|
+
itemTitleKey: string;
|
|
185
|
+
itemDivTitleKey: string;
|
|
186
|
+
itemGroupKey: string;
|
|
187
|
+
itemTimeStartKey: string;
|
|
188
|
+
itemTimeEndKey: string;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export interface ReactCalendarTimelineProps<
|
|
192
|
+
CustomItem extends TimelineItemBase<any> = TimelineItemBase<number>,
|
|
193
|
+
CustomGroup extends TimelineGroupBase = TimelineGroupBase
|
|
194
|
+
> {
|
|
195
|
+
children?: React.ReactNode;
|
|
196
|
+
groups: CustomGroup[];
|
|
197
|
+
items: CustomItem[];
|
|
198
|
+
keys?: TimelineKeys | undefined;
|
|
199
|
+
defaultTimeStart?: Date | Moment | undefined;
|
|
200
|
+
defaultTimeEnd?: Date | Moment | undefined;
|
|
201
|
+
visibleTimeStart?: Date | Moment | number | undefined;
|
|
202
|
+
visibleTimeEnd?: Date | Moment | number | undefined;
|
|
203
|
+
selected?: Id[] | undefined;
|
|
204
|
+
sidebarWidth?: number | undefined;
|
|
205
|
+
sidebarContent?: React.ReactNode | undefined;
|
|
206
|
+
rightSidebarWidth?: number | undefined;
|
|
207
|
+
rightSidebarContent?: React.ReactNode | undefined;
|
|
208
|
+
dragSnap?: number | undefined;
|
|
209
|
+
minResizeWidth?: number | undefined;
|
|
210
|
+
lineHeight?: number | undefined;
|
|
211
|
+
itemHeightRatio?: number | undefined;
|
|
212
|
+
minZoom?: number | undefined;
|
|
213
|
+
maxZoom?: number | undefined;
|
|
214
|
+
clickTolerance?: number | undefined;
|
|
215
|
+
canMove?: boolean | undefined;
|
|
216
|
+
canChangeGroup?: boolean | undefined;
|
|
217
|
+
canResize?: false | true | 'left' | 'right' | 'both' | undefined;
|
|
218
|
+
useResizeHandle?: boolean | undefined;
|
|
219
|
+
stackItems?: boolean | undefined;
|
|
220
|
+
traditionalZoom?: boolean | undefined;
|
|
221
|
+
itemTouchSendsClick?: boolean | undefined;
|
|
222
|
+
timeSteps?: TimelineTimeSteps | undefined;
|
|
223
|
+
scrollRef?: React.Ref<any> | undefined;
|
|
224
|
+
onItemDrag?(itemDragObject: OnItemDragObjectMove | OnItemDragObjectResize): void;
|
|
225
|
+
onItemMove?(itemId: Id, dragTime: number, newGroupOrder: number): void;
|
|
226
|
+
onItemResize?(itemId: Id, endTimeOrStartTime: number, edge: 'left' | 'right'): void;
|
|
227
|
+
onItemSelect?(itemId: Id, e: any, time: number): void;
|
|
228
|
+
onItemDeselect?(e: React.SyntheticEvent): void;
|
|
229
|
+
onItemClick?(itemId: Id, e: React.SyntheticEvent, time: number): void;
|
|
230
|
+
onItemDoubleClick?(itemId: Id, e: React.SyntheticEvent, time: number): void;
|
|
231
|
+
onItemContextMenu?(itemId: Id, e: React.SyntheticEvent, time: number): void;
|
|
232
|
+
onCanvasClick?(groupId: Id, time: number, e: React.SyntheticEvent): void;
|
|
233
|
+
onCanvasDoubleClick?(groupId: Id, time: number, e: React.SyntheticEvent): void;
|
|
234
|
+
onCanvasContextMenu?(groupId: Id, time: number, e: React.SyntheticEvent): void;
|
|
235
|
+
onZoom?(timelineContext: TimelineContext, unit: Unit): void;
|
|
236
|
+
moveResizeValidator?(
|
|
237
|
+
action: 'move' | 'resize',
|
|
238
|
+
itemId: Id,
|
|
239
|
+
time: number,
|
|
240
|
+
resizeEdge: 'left' | 'right',
|
|
241
|
+
): number;
|
|
242
|
+
onTimeChange?(
|
|
243
|
+
visibleTimeStart: number,
|
|
244
|
+
visibleTimeEnd: number,
|
|
245
|
+
updateScrollCanvas: (start: number, end: number) => void,
|
|
246
|
+
unit: Unit
|
|
247
|
+
): any;
|
|
248
|
+
onBoundsChange?(canvasTimeStart: number, canvasTimeEnd: number): any;
|
|
249
|
+
itemRenderer?: ((props: ReactCalendarItemRendererProps<CustomItem>) => React.ReactNode) | undefined;
|
|
250
|
+
groupRenderer?: ((props: ReactCalendarGroupRendererProps<CustomGroup>) => React.ReactNode) | undefined;
|
|
251
|
+
resizeDetector?: ((containerResizeDetector: any) => void) | undefined;
|
|
252
|
+
verticalLineClassNamesForTime?: ((start: number, end: number) => string[] | undefined) | undefined;
|
|
253
|
+
horizontalLineClassNamesForGroup?: ((group: CustomGroup) => string[]) | undefined;
|
|
254
|
+
buffer?: number | undefined;
|
|
255
|
+
// Fields that are in propTypes but not documented
|
|
256
|
+
headerRef?: React.Ref<any> | undefined;
|
|
257
|
+
className?: string;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export interface TimelineTimeSteps {
|
|
261
|
+
second: number;
|
|
262
|
+
minute: number;
|
|
263
|
+
hour: number;
|
|
264
|
+
day: number;
|
|
265
|
+
month: number;
|
|
266
|
+
year: number;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export class TimelineMarkers extends React.Component {}
|
|
270
|
+
|
|
271
|
+
export interface CustomMarkerChildrenProps {
|
|
272
|
+
styles: React.CSSProperties;
|
|
273
|
+
date: number;
|
|
274
|
+
}
|
|
275
|
+
export interface MarkerProps {
|
|
276
|
+
date: Date | number;
|
|
277
|
+
children?: ((props: CustomMarkerChildrenProps) => React.ReactNode) | undefined;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export class CustomMarker extends React.Component<MarkerProps> {}
|
|
281
|
+
|
|
282
|
+
export interface TodayMarkerProps extends MarkerProps {
|
|
283
|
+
interval?: number | undefined;
|
|
284
|
+
}
|
|
285
|
+
export class TodayMarker extends React.Component<TodayMarkerProps> {}
|
|
286
|
+
|
|
287
|
+
export type CursorMarkerProps = Omit<MarkerProps, 'date'>;
|
|
288
|
+
export class CursorMarker extends React.Component<CursorMarkerProps> {}
|
|
289
|
+
|
|
290
|
+
export interface TimelineHeadersProps {
|
|
291
|
+
children?: React.ReactNode;
|
|
292
|
+
style?: React.CSSProperties | undefined;
|
|
293
|
+
className?: string | undefined;
|
|
294
|
+
calendarHeaderStyle?: React.CSSProperties | undefined;
|
|
295
|
+
calendarHeaderClassName?: string | undefined;
|
|
296
|
+
headerRef?: React.Ref<any> | undefined;
|
|
297
|
+
}
|
|
298
|
+
export class TimelineHeaders extends React.Component<TimelineHeadersProps> {}
|
|
299
|
+
|
|
300
|
+
export type TimelineHeaderProps = TimelineHeadersProps;
|
|
301
|
+
|
|
302
|
+
export interface SidebarHeaderChildrenFnProps<Data> {
|
|
303
|
+
getRootProps: (propsToOverride?: { style: React.CSSProperties }) => { style: React.CSSProperties };
|
|
304
|
+
data: Data;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export interface SidebarHeaderProps<Data> {
|
|
308
|
+
variant?: 'left' | 'right' | undefined;
|
|
309
|
+
headerData?: Data | undefined;
|
|
310
|
+
children: (props: SidebarHeaderChildrenFnProps<Data>) => React.ReactNode;
|
|
311
|
+
}
|
|
312
|
+
export class SidebarHeader<Data = any> extends React.Component<SidebarHeaderProps<Data>> {}
|
|
313
|
+
|
|
314
|
+
export type Unit = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'isoWeek' | 'month' | 'year';
|
|
315
|
+
|
|
316
|
+
export interface IntervalContext {
|
|
317
|
+
interval: { startTime: number; endTime: number; labelWidth: number; left: number };
|
|
318
|
+
intervalText: string;
|
|
319
|
+
}
|
|
320
|
+
export interface GetIntervalProps {
|
|
321
|
+
interval?: Interval | undefined;
|
|
322
|
+
style?: React.CSSProperties | undefined;
|
|
323
|
+
onClick?: React.MouseEventHandler | undefined;
|
|
324
|
+
}
|
|
325
|
+
export interface IntervalRenderer<Data> {
|
|
326
|
+
intervalContext: IntervalContext;
|
|
327
|
+
getIntervalProps: (props?: GetIntervalProps) => Required<GetIntervalProps> & { key: string | number };
|
|
328
|
+
data?: Data | undefined;
|
|
329
|
+
}
|
|
330
|
+
export interface DateHeaderProps<Data> {
|
|
331
|
+
style?: React.CSSProperties | undefined;
|
|
332
|
+
className?: string | undefined;
|
|
333
|
+
unit?: Unit | 'primaryHeader' | undefined;
|
|
334
|
+
labelFormat?: string | (([startTime, endTime]: [Moment, Moment], unit: Unit, labelWidth: number) => string) | undefined;
|
|
335
|
+
intervalRenderer?: ((props?: IntervalRenderer<Data>) => React.ReactNode) | undefined;
|
|
336
|
+
headerData?: Data | undefined;
|
|
337
|
+
children?: ((props: SidebarHeaderChildrenFnProps<Data>) => React.ReactNode) | undefined;
|
|
338
|
+
height?: number | undefined;
|
|
339
|
+
}
|
|
340
|
+
export class DateHeader<Data = any> extends React.Component<DateHeaderProps<Data>> {}
|
|
341
|
+
export interface Interval {
|
|
342
|
+
startTime: Moment;
|
|
343
|
+
endTime: Moment;
|
|
344
|
+
}
|
|
345
|
+
export interface HeaderContext {
|
|
346
|
+
intervals: { startTime: Moment; endTime: Moment }[];
|
|
347
|
+
unit: string;
|
|
348
|
+
}
|
|
349
|
+
export interface CustomHeaderPropsChildrenFnProps<Data> {
|
|
350
|
+
timelineContext: TimelineContext;
|
|
351
|
+
headerContext: HeaderContext;
|
|
352
|
+
getIntervalProps: (props?: GetIntervalProps) => Required<GetIntervalProps> & { key: string | number };
|
|
353
|
+
getRootProps: (propsToOverride?: { style: React.CSSProperties }) => { style: React.CSSProperties };
|
|
354
|
+
showPeriod: (startDate: Moment | number, endDate: Moment | number) => void;
|
|
355
|
+
data: Data;
|
|
356
|
+
}
|
|
357
|
+
export interface CustomHeaderProps<Data> {
|
|
358
|
+
unit?: Unit | undefined;
|
|
359
|
+
headerData?: Data | undefined;
|
|
360
|
+
height?: number | undefined;
|
|
361
|
+
children: (props?: CustomHeaderPropsChildrenFnProps<Data>) => React.ReactNode;
|
|
362
|
+
}
|
|
363
|
+
export class CustomHeader<Data = any> extends React.Component<CustomHeaderProps<Data>> {}
|
|
364
|
+
|
|
365
|
+
export const defaultKeys: TimelineKeys;
|
|
366
|
+
export const defaultTimeSteps: TimelineTimeSteps;
|
|
367
|
+
export const defaultHeaderFormats: LabelFormat;
|
|
368
|
+
|
|
369
|
+
export default class ReactCalendarTimeline<
|
|
370
|
+
CustomItem extends TimelineItemBase<any> = TimelineItemBase<number>,
|
|
371
|
+
CustomGroup extends TimelineGroupBase = TimelineGroupBase
|
|
372
|
+
> extends React.Component<ReactCalendarTimelineProps<CustomItem, CustomGroup>> {}
|
|
373
|
+
}
|