gi-component 0.0.1

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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +36 -0
  3. package/dist/dist/index.es.js +2241 -0
  4. package/dist/dist/index.es.js.map +1 -0
  5. package/dist/dist/index.umd.js +2 -0
  6. package/dist/dist/index.umd.js.map +1 -0
  7. package/dist/gi.css +1 -0
  8. package/dist/index.d.ts +1 -0
  9. package/package.json +56 -0
  10. package/packages/components/button/index.ts +5 -0
  11. package/packages/components/button/src/button.vue +59 -0
  12. package/packages/components/button/src/type.ts +15 -0
  13. package/packages/components/card/index.ts +5 -0
  14. package/packages/components/card/src/card.vue +166 -0
  15. package/packages/components/card/src/type.ts +12 -0
  16. package/packages/components/dialog/index.ts +6 -0
  17. package/packages/components/dialog/src/dialog.ts +87 -0
  18. package/packages/components/dialog/src/dialog.vue +122 -0
  19. package/packages/components/dialog/src/type.ts +16 -0
  20. package/packages/components/edit-table/index.ts +5 -0
  21. package/packages/components/edit-table/src/edit-table.vue +207 -0
  22. package/packages/components/edit-table/src/type.ts +69 -0
  23. package/packages/components/form/index.ts +5 -0
  24. package/packages/components/form/src/form.vue +465 -0
  25. package/packages/components/form/src/type.ts +98 -0
  26. package/packages/components/grid/index.ts +8 -0
  27. package/packages/components/grid/src/context.ts +30 -0
  28. package/packages/components/grid/src/grid-item.vue +143 -0
  29. package/packages/components/grid/src/grid.vue +151 -0
  30. package/packages/components/grid/src/hook/use-index.ts +63 -0
  31. package/packages/components/grid/src/hook/use-responsive-state.ts +66 -0
  32. package/packages/components/grid/src/hook/use-responsive-value.ts +36 -0
  33. package/packages/components/grid/src/interface.ts +74 -0
  34. package/packages/components/grid/src/type.ts +0 -0
  35. package/packages/components/grid/src/utils/global-config.ts +6 -0
  36. package/packages/components/grid/src/utils/index.ts +73 -0
  37. package/packages/components/grid/src/utils/is.ts +9 -0
  38. package/packages/components/grid/src/utils/responsive-observe.ts +135 -0
  39. package/packages/components/input-group/index.ts +5 -0
  40. package/packages/components/input-group/src/input-group.vue +92 -0
  41. package/packages/components/input-group/src/type.ts +1 -0
  42. package/packages/components/input-search/index.ts +5 -0
  43. package/packages/components/input-search/src/input-search.vue +62 -0
  44. package/packages/components/input-search/src/type.ts +6 -0
  45. package/packages/components/page-layout/index.ts +5 -0
  46. package/packages/components/page-layout/src/page-layout.vue +180 -0
  47. package/packages/components/page-layout/src/split-button.vue +106 -0
  48. package/packages/components/page-layout/src/type.ts +12 -0
  49. package/packages/components/table/index.ts +5 -0
  50. package/packages/components/table/src/TableColumn.vue +49 -0
  51. package/packages/components/table/src/table.vue +85 -0
  52. package/packages/components/table/src/type.ts +22 -0
  53. package/packages/components/tabs/index.ts +5 -0
  54. package/packages/components/tabs/src/tabs.vue +148 -0
  55. package/packages/components/tabs/src/type.ts +15 -0
  56. package/packages/components.d.ts +26 -0
  57. package/packages/hooks/index.ts +1 -0
  58. package/packages/hooks/useBemClass.ts +11 -0
  59. package/packages/index.ts +78 -0
  60. package/packages/styles/index.scss +176 -0
  61. package/packages/styles/var.scss +1 -0
  62. package/packages/utils/createSelectDialog.ts +67 -0
  63. package/packages/utils/index.ts +1 -0
@@ -0,0 +1,465 @@
1
+ <template>
2
+ <el-form
3
+ ref="formRef"
4
+ :class="getClass"
5
+ v-bind="formProps"
6
+ :model="props.modelValue"
7
+ >
8
+ <Grid
9
+ class="w-full"
10
+ :col-gap="12"
11
+ v-bind="props.gridProps"
12
+ :collapsed="collapsed"
13
+ >
14
+ <template v-for="(item, index) in props.columns">
15
+ <GridItem
16
+ v-if="item.type === 'title'"
17
+ :key="`title${index}`"
18
+ :span="100"
19
+ >
20
+ <el-form-item label-width="0">
21
+ <GiCard
22
+ :title="typeof item.label === 'string' ? item.label : ''"
23
+ :header-style="{ padding: 0 }"
24
+ :body-style="{ display: 'none' }"
25
+ ></GiCard>
26
+ </el-form-item>
27
+ </GridItem>
28
+
29
+ <template v-else>
30
+ <GridItem
31
+ v-if="!isHide(item)"
32
+ :key="item.field + index"
33
+ v-bind="item.gridItemProps || props.gridItemProps"
34
+ :span="
35
+ item.span ||
36
+ item.gridItemProps?.span ||
37
+ props?.gridItemProps?.span
38
+ "
39
+ >
40
+ <el-form-item
41
+ :key="item.field + index"
42
+ :prop="item.field"
43
+ :label="item.label"
44
+ :rules="getFormItemRules(item)"
45
+ v-bind="item.formItemProps"
46
+ >
47
+ <template v-if="item?.labelRender" #label>
48
+ <component :is="item.labelRender"></component>
49
+ </template>
50
+ <div v-if="item.type === 'slot'" class="w-full">
51
+ <slot :name="item.field" :item="item"></slot>
52
+ </div>
53
+ <template v-else>
54
+ <div :class="b('form-item__content')">
55
+ <div :class="b('form-item__component')">
56
+ <component
57
+ :is="CompMap[item.type] || item.type"
58
+ :disabled="isDisabled(item)"
59
+ class="w-full"
60
+ v-bind="getComponentBindProps(item)"
61
+ :model-value="
62
+ props.modelValue[item.fieldName || item.field]
63
+ "
64
+ @update:model-value="updateModelValue($event, item)"
65
+ >
66
+ <template
67
+ v-for="(slotValue, slotKey) in item?.slots || {}"
68
+ :key="slotKey"
69
+ #[slotKey]="scope"
70
+ >
71
+ <template v-if="typeof slotValue === 'string'">
72
+ {{ slotValue }}
73
+ </template>
74
+ <template v-else-if="slotValue">
75
+ <component :is="slotValue(scope)"></component>
76
+ </template>
77
+ </template>
78
+ </component>
79
+ <el-text
80
+ v-if="item.tip"
81
+ :class="b('form-item__tip')"
82
+ type="info"
83
+ size="small"
84
+ >
85
+ {{ item.tip }}
86
+ </el-text>
87
+ </div>
88
+ <!-- 额外信息 -->
89
+ <div v-if="item.extra" :class="b('form-item__extra')">
90
+ <template v-if="typeof item.extra === 'string'">
91
+ <el-text type="info" size="small">{{
92
+ item.extra
93
+ }}</el-text>
94
+ </template>
95
+ <template v-else-if="item.extra">
96
+ <component :is="item.extra"></component>
97
+ </template>
98
+ </div>
99
+ </div>
100
+ </template>
101
+ </el-form-item>
102
+ </GridItem>
103
+ </template>
104
+ </template>
105
+
106
+ <GridItem
107
+ v-if="props.search"
108
+ :suffix="props.search"
109
+ :span="props?.gridItemProps?.span"
110
+ >
111
+ <el-space :class="b('form__search-btns')">
112
+ <el-button type="primary" @click="emit('search')">
113
+ {{ searchText }}
114
+ </el-button>
115
+ <el-button @click="emit('reset')"> 重置 </el-button>
116
+ <el-button
117
+ v-if="!props.hideFoldBtn"
118
+ class="form__fold-btn"
119
+ type="primary"
120
+ :icon="collapsed ? ArrowDown : ArrowUp"
121
+ text
122
+ size="small"
123
+ @click="collapsed = !collapsed"
124
+ >
125
+ {{ collapsed ? '展开' : '收起' }}
126
+ </el-button>
127
+ </el-space>
128
+ </GridItem>
129
+ </Grid>
130
+ </el-form>
131
+ </template>
132
+
133
+ <script lang="tsx" setup>
134
+ import type { FormColumnType, FormColumnItem } from './type';
135
+ import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
136
+ import * as El from 'element-plus';
137
+ import type { FormInstance } from 'element-plus';
138
+ import {
139
+ computed,
140
+ ref,
141
+ toRaw,
142
+ watch,
143
+ useAttrs,
144
+ getCurrentInstance,
145
+ onMounted
146
+ } from 'vue';
147
+ import { useBemClass } from '../../../hooks';
148
+ import GiCard from '../../card';
149
+ import { Grid, GridItem } from '../../grid';
150
+ import InputSearch from '../../input-search';
151
+ import type { FormProps } from './type';
152
+
153
+ const props = withDefaults(defineProps<FormProps>(), {
154
+ columns: () => [],
155
+ labelWidth: 'auto',
156
+ scrollToError: true,
157
+ gridItemProps: () => ({ span: { xs: 24, sm: 24, md: 12 } }), // xs, sm, md, lg, xl, xxl
158
+ search: false,
159
+ searchText: '查询',
160
+ hideFoldBtn: false,
161
+ defaultCollapsed: undefined,
162
+ fc: () => ({})
163
+ });
164
+
165
+ const emit = defineEmits<{
166
+ (e: 'update:modelValue', value: any): void;
167
+ (e: 'search'): void;
168
+ (e: 'reset'): void;
169
+ }>();
170
+
171
+ const attrs = useAttrs();
172
+ const { b } = useBemClass();
173
+ const collapsed = ref(props?.defaultCollapsed ?? props.search);
174
+ const instance = getCurrentInstance();
175
+
176
+ const globalConfig = instance?.appContext.config.globalProperties.$config;
177
+ const clearable = globalConfig?.clearable ?? false;
178
+ // 字典数据存储
179
+ const dictData = ref<Record<string, any[]>>({});
180
+
181
+ /** 组件静态配置 */
182
+ const STATIC_PROPS = new Map([
183
+ ['input', { clearable, maxlength: 20 }],
184
+ [
185
+ 'textarea',
186
+ { clearable, type: 'textarea', maxlength: 200, showWordLimit: true }
187
+ ],
188
+ ['input-number', {}],
189
+ ['input-tag', { clearable }],
190
+ ['select', { clearable }],
191
+ ['select-v2', { clearable }],
192
+ ['tree-select', { clearable }],
193
+ ['cascader', { clearable }],
194
+ ['slider', {}],
195
+ ['switch', {}],
196
+ ['rate', {}],
197
+ ['checkbox-group', {}],
198
+ ['checkbox', {}],
199
+ ['radio-group', {}],
200
+ ['radio', {}],
201
+ ['date-picker', { clearable }],
202
+ ['time-picker', { clearable }],
203
+ ['time-select', { clearable }],
204
+ ['color-picker', {}],
205
+ ['transfer', {}],
206
+ ['autocomplete', {}],
207
+ ['upload', {}],
208
+ ['title', {}]
209
+ ]);
210
+
211
+ // 获取字典数据
212
+ const loadDictData = async () => {
213
+ const dictCodes: string[] = [];
214
+ // 收集所有需要的字典编码
215
+ props.columns?.forEach(item => {
216
+ if (item.dictCode) {
217
+ dictCodes.push(item.dictCode);
218
+ }
219
+ });
220
+ if (!dictCodes.length) return;
221
+ if (!globalConfig?.dictRequest) {
222
+ return El.ElMessage.error('请配置全局字典请求方法dictRequest');
223
+ }
224
+ try {
225
+ // 使用Promise.all并行处理所有字典请求
226
+ const dictResponses = await Promise.all(
227
+ dictCodes.map(code =>
228
+ globalConfig
229
+ .dictRequest(code)
230
+ .then((response: any) => ({ code, response }))
231
+ )
232
+ );
233
+ // 处理所有响应结果
234
+ dictResponses.forEach(({ code, response }) => {
235
+ dictData.value[code] = response;
236
+ });
237
+ } catch (error) {
238
+ console.error('获取字典数据失败:', error);
239
+ El.ElMessage.error('获取字典数据失败');
240
+ }
241
+ };
242
+
243
+ // 组件挂载时获取字典数据
244
+ onMounted(() => {
245
+ loadDictData();
246
+ });
247
+
248
+ // 组件的默认props配置
249
+ function getComponentBindProps(item: FormColumnItem) {
250
+ // 获取默认配置
251
+ const defaultProps: any = STATIC_PROPS.get(item.type) || {};
252
+ defaultProps.placeholder = getPlaceholder(item);
253
+ if (item.type === 'date-picker') {
254
+ defaultProps.valueFormat =
255
+ item?.props?.type === 'datetime' ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD';
256
+ }
257
+ // 如果配置了dictCode且存在对应的字典数据,设置options
258
+ if (item.dictCode && dictData.value[item.dictCode]) {
259
+ defaultProps.options = dictData.value[item.dictCode];
260
+ }
261
+ // 合并默认配置和自定义配置
262
+ return { ...defaultProps, ...item.props };
263
+ }
264
+
265
+ const formProps = computed(() => {
266
+ return {
267
+ ...attrs,
268
+ ...props,
269
+ columns: undefined,
270
+ gridProps: undefined,
271
+ gridItemProps: undefined,
272
+ search: undefined,
273
+ searchText: undefined,
274
+ hideFoldBtn: undefined,
275
+ defaultCollapsed: undefined
276
+ };
277
+ });
278
+
279
+ const getClass = computed(() => {
280
+ const arr: string[] = [b('form')];
281
+ if (props.search) {
282
+ arr.push(b('form--search'));
283
+ }
284
+ return arr.join(' ');
285
+ });
286
+
287
+ const CompMap: Record<Exclude<FormColumnType, 'slot'>, any> = {
288
+ input: El.ElInput,
289
+ textarea: El.ElInput,
290
+ 'input-number': El.ElInputNumber,
291
+ 'input-tag': El.ElInputTag,
292
+ 'input-search': InputSearch,
293
+ select: El.ElSelect,
294
+ 'select-v2': El.ElSelectV2,
295
+ 'tree-select': El.ElTreeSelect,
296
+ cascader: El.ElCascader,
297
+ slider: El.ElSlider,
298
+ switch: El.ElSwitch,
299
+ rate: El.ElRate,
300
+ 'checkbox-group': El.ElCheckboxGroup,
301
+ checkbox: El.ElCheckbox,
302
+ 'radio-group': El.ElRadioGroup,
303
+ radio: El.ElRadio,
304
+ 'date-picker': El.ElDatePicker,
305
+ 'time-picker': El.ElTimePicker,
306
+ 'time-select': El.ElTimeSelect,
307
+ 'color-picker': El.ElColorPicker,
308
+ transfer: El.ElTransfer,
309
+ autocomplete: El.ElAutocomplete,
310
+ upload: El.ElUpload,
311
+ title: El.ElAlert
312
+ };
313
+
314
+ const formRef = ref<FormInstance>();
315
+
316
+ /** 获取占位文本 */
317
+ const getPlaceholder = (item: FormColumnItem) => {
318
+ if (!item.type) return undefined;
319
+ if (['input', 'input-number', 'input-tag'].includes(item.type)) {
320
+ return `请输入${item.label}`;
321
+ }
322
+ if (['textarea'].includes(item.type)) {
323
+ return `请填写${item.label}`;
324
+ }
325
+ if (
326
+ [
327
+ 'select',
328
+ 'select-v2',
329
+ 'tree-select',
330
+ 'cascader',
331
+ 'time-select',
332
+ 'input-search'
333
+ ].includes(item.type)
334
+ ) {
335
+ return `请选择${item.label}`;
336
+ }
337
+ if (['date-picker'].includes(item.type)) {
338
+ return `请选择日期`;
339
+ }
340
+ if (['time-picker'].includes(item.type)) {
341
+ return `请选择时间`;
342
+ }
343
+ return undefined;
344
+ };
345
+
346
+ /** 表单项校验规则 */
347
+ function getFormItemRules(item: FormColumnItem) {
348
+ if (item.required) {
349
+ return [
350
+ { required: true, message: `${item.label}为必填项` },
351
+ ...(Array.isArray(item.rules) ? item.rules : [])
352
+ ];
353
+ }
354
+ if (props.fc?.[item.field]?.required) {
355
+ return [
356
+ {
357
+ required: props.fc?.[item.field]?.required,
358
+ message: `${item.label}为必填项`
359
+ },
360
+ ...(Array.isArray(item.rules) ? item.rules : [])
361
+ ];
362
+ }
363
+ return item.rules;
364
+ }
365
+
366
+ /** 隐藏表单项 */
367
+ function isHide(item: FormColumnItem) {
368
+ if (typeof item.hide === 'boolean') return item.hide;
369
+ if (typeof item.hide === 'function') {
370
+ return item.hide(props.modelValue);
371
+ }
372
+ if (props.fc?.[item.field]?.hidden) return true;
373
+ if (item.hide === undefined) return false;
374
+ }
375
+
376
+ /** 禁用表单项 */
377
+ function isDisabled(item: FormColumnItem) {
378
+ if (item?.props?.disabled !== undefined) return item?.props?.disabled;
379
+ if (props.fc?.[item.field]?.edit === false) return true;
380
+ return false;
381
+ }
382
+
383
+ /** 表单数据更新 */
384
+ function updateModelValue(value: any, item: FormColumnItem) {
385
+ emit(
386
+ 'update:modelValue',
387
+ Object.assign(props.modelValue, { [item.field]: value })
388
+ );
389
+ }
390
+
391
+ watch(
392
+ () => props.modelValue,
393
+ () => {
394
+ // eslint-disable-next-line no-console
395
+ console.log('form', toRaw(props.modelValue));
396
+ },
397
+ { deep: true }
398
+ );
399
+
400
+ defineExpose({ formRef });
401
+ </script>
402
+
403
+ <style lang="scss" scoped>
404
+ @use '../../../styles/var.scss' as a;
405
+
406
+ .el-form {
407
+ width: 100%;
408
+ }
409
+
410
+ :deep(.el-form-item) {
411
+ align-items: center;
412
+
413
+ .el-form-item__label {
414
+ height: inherit;
415
+ line-height: inherit;
416
+ }
417
+ }
418
+
419
+ :deep(.hide-label) {
420
+ // 隐藏el-form-item__label才能完整占满插槽宽度
421
+ .el-form-item__label {
422
+ display: none;
423
+ }
424
+ }
425
+
426
+ .#{a.$prefix}-form {
427
+ &-item {
428
+ &__content {
429
+ width: 100%;
430
+ display: flex;
431
+ }
432
+
433
+ &__component {
434
+ flex: 1;
435
+ }
436
+
437
+ &__tip {
438
+ line-height: 1.5;
439
+ color: var(--el-color-info-light-3);
440
+ }
441
+
442
+ &__extra {
443
+ margin-left: 6px;
444
+ }
445
+ }
446
+
447
+ &__search-btns {
448
+ margin-bottom: 8px;
449
+ }
450
+ }
451
+
452
+ .#{a.$prefix}-form--search {
453
+ :deep(.el-form-item) {
454
+ margin-bottom: 8px;
455
+ }
456
+ }
457
+
458
+ :deep(.w-full) {
459
+ width: 100%;
460
+
461
+ .el-date-editor {
462
+ width: 100%;
463
+ }
464
+ }
465
+ </style>
@@ -0,0 +1,98 @@
1
+ import type * as El from 'element-plus';
2
+ import type { FormProps as ElFormProps } from 'element-plus';
3
+ import type { VNode } from 'vue';
4
+ import type { GridItemProps, GridProps } from '../../grid/src/interface';
5
+ import type { InputSearchInstance } from '../../input-search';
6
+
7
+ export type FormColumnType =
8
+ | 'input'
9
+ | 'textarea'
10
+ | 'input-number'
11
+ | 'input-tag'
12
+ | 'input-search'
13
+ | 'select'
14
+ | 'select-v2'
15
+ | 'tree-select'
16
+ | 'cascader'
17
+ | 'slider'
18
+ | 'switch'
19
+ | 'rate'
20
+ | 'checkbox-group'
21
+ | 'checkbox'
22
+ | 'radio-group'
23
+ | 'radio'
24
+ | 'date-picker'
25
+ | 'time-picker'
26
+ | 'time-select'
27
+ | 'color-picker'
28
+ | 'transfer'
29
+ | 'autocomplete'
30
+ | 'upload'
31
+ | 'title'
32
+ | 'slot';
33
+
34
+ export type FormColumnProps = El.InputProps &
35
+ El.InputNumberProps &
36
+ El.InputTagProps &
37
+ El.SelectProps &
38
+ El.SelectV2Props &
39
+ El.TreeInstance['$props'] &
40
+ El.CascaderProps &
41
+ El.SliderProps &
42
+ El.SwitchProps &
43
+ El.RateProps &
44
+ El.CheckboxGroupProps &
45
+ El.CheckboxProps &
46
+ El.RadioGroupProps &
47
+ El.RadioProps &
48
+ El.DatePickerProps &
49
+ El.TimePickerDefaultProps &
50
+ El.TimeSelectProps &
51
+ El.ColorPickerProps &
52
+ El.TransferProps &
53
+ El.AutocompleteProps &
54
+ El.UploadProps &
55
+ InputSearchInstance['$props'];
56
+
57
+ export type FormColumnItemHide<F> = boolean | ((form: F) => boolean);
58
+
59
+ export type FormColumnSlots = El.InputInstance['$slots'] &
60
+ El.InputNumberInstance['$slots'] &
61
+ El.InputTagInstance['$slots'] &
62
+ El.AutocompleteInstance['$slots'] &
63
+ El.CascaderInstance['$slots'] &
64
+ El.DatePickerInstance['$slots'];
65
+
66
+ export interface FormColumnItem<F = any> {
67
+ type: FormColumnType;
68
+ label?: string;
69
+ labelRender?: () => VNode;
70
+ field: string;
71
+ fieldName?: string;
72
+ span?: number;
73
+ props?: FormColumnProps & {
74
+ options?: El.RadioProps & El.CheckboxProps & El.SelectProps;
75
+ };
76
+ formItemProps?: El.FormItemProps;
77
+ gridItemProps?: any;
78
+ required?: boolean;
79
+ rules?: El.FormItemRule[];
80
+ hide?: FormColumnItemHide<F>; // 是否隐藏
81
+ tip?: string;
82
+ dictCode?: string; // 字典编码
83
+ slotName?: string;
84
+ slots?: FormColumnSlots;
85
+ extra?: string | (() => VNode); // 右侧额外内容
86
+ }
87
+
88
+ export interface FormProps extends Partial<ElFormProps> {
89
+ modelValue: any;
90
+ columns?: FormColumnItem[];
91
+ fc?: Record<string, { edit: boolean; hidden?: boolean; required?: boolean }>; // 表单控制
92
+ gridProps?: GridProps; // grid默认配置
93
+ gridItemProps?: GridItemProps; // grid-item默认配置
94
+ search?: boolean;
95
+ searchText?: string;
96
+ hideFoldBtn?: boolean;
97
+ defaultCollapsed?: boolean | undefined;
98
+ }
@@ -0,0 +1,8 @@
1
+ import GridItem from './src/grid-item.vue';
2
+ import Grid from './src/grid.vue';
3
+
4
+ export type GridInstance = InstanceType<typeof Grid>;
5
+ export type GridItemInstance = InstanceType<typeof GridItem>;
6
+ export * from './src/interface';
7
+
8
+ export { Grid, GridItem };
@@ -0,0 +1,30 @@
1
+ import type { InjectionKey } from 'vue';
2
+ import type { GridItemData } from './interface';
3
+
4
+ export type RowContextContext = Readonly<{
5
+ gutter?: [number, number];
6
+ div?: boolean;
7
+ }>;
8
+
9
+ export const RowContextInjectionKey: InjectionKey<RowContextContext> = Symbol(
10
+ 'RowContextInjectionKey'
11
+ );
12
+
13
+ export type GridContext = Readonly<{
14
+ overflow: boolean;
15
+ displayIndexList: number[];
16
+ cols: number;
17
+ colGap: number;
18
+ }>;
19
+
20
+ export const GridContextInjectionKey: InjectionKey<GridContext> = Symbol(
21
+ 'GridContextInjectionKey'
22
+ );
23
+
24
+ export type GridDataCollector = Readonly<{
25
+ collectItemData: (index: number, itemData: GridItemData) => void;
26
+ removeItemData: (index: number) => void;
27
+ }>;
28
+
29
+ export const GridDataCollectorInjectionKey: InjectionKey<GridDataCollector> =
30
+ Symbol('GridDataCollectorInjectionKey');