@datametria/vue-components 2.4.0 → 2.4.2

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 (36) hide show
  1. package/README.md +57 -5
  2. package/dist/index.es.js +5812 -2837
  3. package/dist/index.umd.js +670 -10
  4. package/dist/src/components/DatametriaCheckbox.vue.d.ts +2 -0
  5. package/dist/src/components/DatametriaCheckboxGroup.vue.d.ts +6 -0
  6. package/dist/src/components/DatametriaFileUpload.vue.d.ts +5 -0
  7. package/dist/src/components/DatametriaFloatingBar.vue.d.ts +1 -1
  8. package/dist/src/components/DatametriaInput.vue.d.ts +2 -22
  9. package/dist/src/components/DatametriaNavbar.vue.d.ts +1 -1
  10. package/dist/src/components/DatametriaPasswordInput.vue.d.ts +2 -0
  11. package/dist/src/components/DatametriaRadio.vue.d.ts +2 -0
  12. package/dist/src/components/DatametriaRadioGroup.vue.d.ts +6 -0
  13. package/dist/src/components/DatametriaSelect.vue.d.ts +2 -3
  14. package/dist/src/components/DatametriaSidebar.vue.d.ts +1 -1
  15. package/dist/src/components/DatametriaSwitch.vue.d.ts +8 -1
  16. package/dist/src/components/DatametriaTabs.vue.d.ts +2 -2
  17. package/dist/src/components/DatametriaTextarea.vue.d.ts +6 -0
  18. package/dist/src/composables/useAnalytics.d.ts +8 -0
  19. package/dist/src/types/analytics.d.ts +50 -0
  20. package/dist/vue-components.css +1 -1
  21. package/package.json +3 -2
  22. package/src/components/DatametriaButton.vue +196 -195
  23. package/src/components/DatametriaCheckbox.vue +289 -197
  24. package/src/components/DatametriaCheckboxGroup.vue +161 -58
  25. package/src/components/DatametriaFileUpload.vue +493 -414
  26. package/src/components/DatametriaInput.vue +342 -316
  27. package/src/components/DatametriaPasswordInput.vue +433 -444
  28. package/src/components/DatametriaRadio.vue +240 -151
  29. package/src/components/DatametriaRadioGroup.vue +160 -57
  30. package/src/components/DatametriaSelect.vue +409 -313
  31. package/src/components/DatametriaSortableTable.vue +35 -29
  32. package/src/components/DatametriaSwitch.vue +319 -146
  33. package/src/components/DatametriaTabs.vue +2 -2
  34. package/src/components/DatametriaTextarea.vue +285 -213
  35. package/src/composables/useAnalytics.ts +70 -0
  36. package/src/types/analytics.ts +59 -0
@@ -56,7 +56,7 @@ interface Tab {
56
56
  interface Props {
57
57
  tabs: (string | Tab)[]
58
58
  modelValue?: number
59
- variant?: 'default' | 'pills' | 'underline'
59
+ variant?: 'md' | 'pills' | 'underline'
60
60
  orientation?: 'horizontal' | 'vertical'
61
61
  showIndicator?: boolean
62
62
  ariaLabel?: string
@@ -64,7 +64,7 @@ interface Props {
64
64
 
65
65
  const props = withDefaults(defineProps<Props>(), {
66
66
  modelValue: 0,
67
- variant: 'default',
67
+ variant: 'md',
68
68
  orientation: 'horizontal',
69
69
  showIndicator: true,
70
70
  ariaLabel: 'Tabs'
@@ -1,217 +1,289 @@
1
- <template>
2
- <div class="datametria-textarea" :class="textareaClasses">
3
- <textarea
4
- ref="textareaRef"
5
- v-model="currentValue"
6
- class="datametria-textarea__inner"
7
- :placeholder="placeholder"
8
- :disabled="disabled"
9
- :readonly="readonly"
10
- :rows="computedRows"
11
- :maxlength="maxlength"
12
- @input="handleInput"
13
- @focus="handleFocus"
14
- @blur="handleBlur"
15
- />
16
- <div v-if="showWordLimit && maxlength" class="datametria-textarea__count">
17
- {{ currentValue.length }} / {{ maxlength }}
18
- </div>
19
- </div>
20
- </template>
21
-
22
- <script setup lang="ts">
23
- import { ref, computed, watch, nextTick, onMounted } from 'vue'
24
-
25
- interface Props {
26
- modelValue?: string
27
- placeholder?: string
28
- disabled?: boolean
29
- readonly?: boolean
30
- rows?: number
31
- maxlength?: number
32
- showWordLimit?: boolean
33
- autosize?: boolean | { minRows?: number; maxRows?: number }
34
- }
35
-
36
- const props = withDefaults(defineProps<Props>(), {
37
- modelValue: '',
38
- placeholder: '',
39
- disabled: false,
40
- readonly: false,
41
- rows: 3,
42
- showWordLimit: false,
43
- autosize: false
44
- })
45
-
46
- const emit = defineEmits<{
47
- 'update:modelValue': [value: string]
48
- input: [value: string]
49
- focus: [event: FocusEvent]
50
- blur: [event: FocusEvent]
51
- }>()
52
-
53
- const textareaRef = ref<HTMLTextAreaElement>()
54
- const currentValue = ref(props.modelValue)
55
- const isFocused = ref(false)
56
-
57
- const textareaClasses = computed(() => ({
58
- 'datametria-textarea--disabled': props.disabled,
59
- 'datametria-textarea--focused': isFocused.value
60
- }))
61
-
62
- const computedRows = computed(() => {
63
- if (props.autosize) return undefined
64
- return props.rows
65
- })
66
-
67
- const handleInput = (event: Event) => {
68
- const target = event.target as HTMLTextAreaElement
69
- currentValue.value = target.value
70
- emit('update:modelValue', target.value)
71
- emit('input', target.value)
72
-
73
- if (props.autosize) {
74
- nextTick(() => resizeTextarea())
75
- }
76
- }
77
-
78
- const handleFocus = (event: FocusEvent) => {
79
- isFocused.value = true
80
- emit('focus', event)
81
- }
82
-
83
- const handleBlur = (event: FocusEvent) => {
84
- isFocused.value = false
85
- emit('blur', event)
86
- }
87
-
88
- const resizeTextarea = () => {
89
- if (!textareaRef.value || !props.autosize) return
90
-
91
- const textarea = textareaRef.value
92
- textarea.style.height = 'auto'
93
-
94
- let minHeight = 0
95
- let maxHeight = Infinity
96
-
97
- if (typeof props.autosize === 'object') {
98
- if (props.autosize.minRows) {
99
- const lineHeight = parseInt(getComputedStyle(textarea).lineHeight)
100
- minHeight = props.autosize.minRows * lineHeight
101
- }
102
- if (props.autosize.maxRows) {
103
- const lineHeight = parseInt(getComputedStyle(textarea).lineHeight)
104
- maxHeight = props.autosize.maxRows * lineHeight
105
- }
106
- }
107
-
108
- const scrollHeight = textarea.scrollHeight
109
- const height = Math.max(minHeight, Math.min(maxHeight, scrollHeight))
110
- textarea.style.height = `${height}px`
111
- }
112
-
113
- watch(() => props.modelValue, (newValue) => {
114
- if (newValue !== currentValue.value) {
115
- currentValue.value = newValue
116
- if (props.autosize) {
117
- nextTick(() => resizeTextarea())
118
- }
119
- }
120
- })
121
-
122
- onMounted(() => {
123
- if (props.autosize) {
124
- nextTick(() => resizeTextarea())
125
- }
126
- })
127
-
128
- defineExpose({
129
- focus: () => textareaRef.value?.focus(),
130
- blur: () => textareaRef.value?.blur()
131
- })
132
- </script>
133
-
134
- <style scoped>
135
- .datametria-textarea {
136
- position: relative;
137
- display: inline-block;
138
- width: 100%;
139
- vertical-align: bottom;
140
- font-size: 14px;
141
- }
142
-
143
- .datametria-textarea__inner {
144
- display: block;
145
- resize: vertical;
146
- padding: 8px 12px;
147
- line-height: 1.5;
148
- box-sizing: border-box;
149
- width: 100%;
150
- font-size: inherit;
151
- font-family: inherit;
152
- color: var(--datametria-text-color, #303133);
153
- background-color: var(--datametria-bg-color, #ffffff);
154
- border: 1px solid var(--datametria-border-color, #dcdfe6);
155
- border-radius: 4px;
156
- transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
157
- }
158
-
159
- .datametria-textarea__inner:hover {
160
- border-color: var(--datametria-border-color-hover, #c0c4cc);
161
- }
162
-
163
- .datametria-textarea__inner:focus {
164
- outline: none;
165
- border-color: var(--datametria-primary-color, #0072ce);
166
- }
167
-
168
- .datametria-textarea__inner::placeholder {
169
- color: var(--datametria-placeholder-color, #a8abb2);
170
- }
171
-
172
- .datametria-textarea__inner:disabled {
173
- background-color: var(--datametria-disabled-bg-color, #f5f7fa);
174
- border-color: var(--datametria-disabled-border-color, #e4e7ed);
175
- color: var(--datametria-disabled-text-color, #c0c4cc);
176
- cursor: not-allowed;
177
- }
178
-
179
- .datametria-textarea--disabled .datametria-textarea__inner {
180
- background-color: var(--datametria-disabled-bg-color, #f5f7fa);
181
- border-color: var(--datametria-disabled-border-color, #e4e7ed);
182
- color: var(--datametria-disabled-text-color, #c0c4cc);
183
- cursor: not-allowed;
184
- }
185
-
186
- .datametria-textarea__count {
187
- position: absolute;
188
- bottom: 8px;
189
- right: 12px;
190
- font-size: 12px;
191
- color: var(--datametria-info-color, #909399);
192
- background-color: var(--datametria-bg-color, #ffffff);
193
- padding: 0 4px;
194
- }
195
-
196
- .datametria-textarea--focused .datametria-textarea__inner {
197
- border-color: var(--datametria-primary-color, #0072ce);
198
- }
199
-
200
- /* Dark Mode Support - Hybrid Approach */
201
-
202
- /* Fallback automático (sem JS) */
203
- @media (prefers-color-scheme: dark) {
204
- .datametria-textarea {
205
- background: var(--dm-bg-color-dark, #1e1e1e);
206
- color: var(--dm-text-primary-dark, #e0e0e0);
207
- border-color: var(--dm-border-color-dark, #404040);
1
+ <template>
2
+ <div class="datametria-textarea" :class="textareaClasses">
3
+ <label v-if="label" :for="textareaId" class="datametria-textarea__label">
4
+ {{ label }}
5
+ <span v-if="required" class="datametria-textarea__required">*</span>
6
+ </label>
7
+
8
+ <textarea
9
+ :id="textareaId"
10
+ ref="textareaRef"
11
+ v-model="currentValue"
12
+ class="datametria-textarea__inner"
13
+ :placeholder="placeholder"
14
+ :disabled="disabled"
15
+ :readonly="readonly"
16
+ :rows="computedRows"
17
+ :maxlength="maxlength"
18
+ :aria-label="label || placeholder"
19
+ :aria-required="required"
20
+ :aria-invalid="!!errorMessage"
21
+ :aria-describedby="errorMessage ? `${textareaId}-error` : undefined"
22
+ @input="handleInput"
23
+ @focus="handleFocus"
24
+ @blur="handleBlur"
25
+ />
26
+
27
+ <div v-if="showWordLimit && maxlength" class="datametria-textarea__count">
28
+ {{ currentValue.length }} / {{ maxlength }}
29
+ </div>
30
+
31
+ <div
32
+ v-if="errorMessage"
33
+ :id="`${textareaId}-error`"
34
+ role="alert"
35
+ aria-live="polite"
36
+ class="datametria-textarea__error"
37
+ >
38
+ {{ errorMessage }}
39
+ </div>
40
+ </div>
41
+ </template>
42
+
43
+ <script setup lang="ts">
44
+ import { ref, computed, watch, nextTick, onMounted } from 'vue'
45
+ import { useAnalytics } from '@/composables/useAnalytics'
46
+
47
+ interface Props {
48
+ modelValue?: string
49
+ placeholder?: string
50
+ disabled?: boolean
51
+ readonly?: boolean
52
+ rows?: number
53
+ maxlength?: number
54
+ showWordLimit?: boolean
55
+ autosize?: boolean | { minRows?: number; maxRows?: number }
56
+ label?: string
57
+ required?: boolean
58
+ errorMessage?: string
59
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'
60
+ }
61
+
62
+ const props = withDefaults(defineProps<Props>(), {
63
+ modelValue: '',
64
+ placeholder: '',
65
+ disabled: false,
66
+ readonly: false,
67
+ rows: 3,
68
+ showWordLimit: false,
69
+ autosize: false,
70
+ required: false,
71
+ size: 'md'
72
+ })
73
+
74
+ const emit = defineEmits<{
75
+ 'update:modelValue': [value: string]
76
+ input: [value: string]
77
+ focus: [event: FocusEvent]
78
+ blur: [event: FocusEvent]
79
+ }>()
80
+
81
+ const { trackEvent } = useAnalytics()
82
+ const textareaRef = ref<HTMLTextAreaElement>()
83
+ const currentValue = ref(props.modelValue)
84
+ const isFocused = ref(false)
85
+ const textareaId = `datametria-textarea-${Math.random().toString(36).substr(2, 9)}`
86
+
87
+ const textareaClasses = computed(() => ({
88
+ 'datametria-textarea--disabled': props.disabled,
89
+ 'datametria-textarea--focused': isFocused.value,
90
+ 'datametria-textarea--error': !!props.errorMessage,
91
+ [`datametria-textarea--${props.size}`]: true
92
+ }))
93
+
94
+ const computedRows = computed(() => {
95
+ if (props.autosize) return undefined
96
+ return props.rows
97
+ })
98
+
99
+ const handleInput = (event: Event) => {
100
+ const target = event.target as HTMLTextAreaElement
101
+ currentValue.value = target.value
102
+ emit('update:modelValue', target.value)
103
+ emit('input', target.value)
104
+
105
+ if (props.autosize) {
106
+ nextTick(() => resizeTextarea())
107
+ }
108
+ }
109
+
110
+ const handleFocus = (event: FocusEvent) => {
111
+ isFocused.value = true
112
+ trackEvent('textarea_focus', {
113
+ component: 'DatametriaTextarea',
114
+ size: props.size
115
+ })
116
+ emit('focus', event)
117
+ }
118
+
119
+ const handleBlur = (event: FocusEvent) => {
120
+ isFocused.value = false
121
+ emit('blur', event)
122
+ }
123
+
124
+ const resizeTextarea = () => {
125
+ if (!textareaRef.value || !props.autosize) return
126
+
127
+ const textarea = textareaRef.value
128
+ textarea.style.height = 'auto'
129
+
130
+ let minHeight = 0
131
+ let maxHeight = Infinity
132
+
133
+ if (typeof props.autosize === 'object') {
134
+ if (props.autosize.minRows) {
135
+ const lineHeight = parseInt(getComputedStyle(textarea).lineHeight)
136
+ minHeight = props.autosize.minRows * lineHeight
137
+ }
138
+ if (props.autosize.maxRows) {
139
+ const lineHeight = parseInt(getComputedStyle(textarea).lineHeight)
140
+ maxHeight = props.autosize.maxRows * lineHeight
141
+ }
208
142
  }
143
+
144
+ const scrollHeight = textarea.scrollHeight
145
+ const height = Math.max(minHeight, Math.min(maxHeight, scrollHeight))
146
+ textarea.style.height = `${height}px`
147
+ }
148
+
149
+ watch(() => props.modelValue, (newValue) => {
150
+ if (newValue !== currentValue.value) {
151
+ currentValue.value = newValue
152
+ if (props.autosize) {
153
+ nextTick(() => resizeTextarea())
154
+ }
155
+ }
156
+ })
157
+
158
+ onMounted(() => {
159
+ if (props.autosize) {
160
+ nextTick(() => resizeTextarea())
161
+ }
162
+ })
163
+
164
+ defineExpose({
165
+ focus: () => textareaRef.value?.focus(),
166
+ blur: () => textareaRef.value?.blur()
167
+ })
168
+ </script>
169
+
170
+ <style scoped>
171
+ .datametria-textarea {
172
+ position: relative;
173
+ display: inline-flex;
174
+ flex-direction: column;
175
+ width: 100%;
176
+ }
177
+
178
+ .datametria-textarea__label {
179
+ display: block;
180
+ margin-bottom: 4px;
181
+ font-size: 14px;
182
+ font-weight: 500;
183
+ color: var(--color-text-primary, #1f2937);
184
+ }
185
+
186
+ .datametria-textarea__required {
187
+ color: var(--color-error, #ef4444);
188
+ margin-left: 2px;
189
+ }
190
+
191
+ .datametria-textarea__inner {
192
+ display: block;
193
+ resize: vertical;
194
+ box-sizing: border-box;
195
+ width: 100%;
196
+ font-size: 14px;
197
+ font-family: inherit;
198
+ line-height: 1.5;
199
+ color: var(--color-text-primary, #1f2937);
200
+ background-color: var(--color-background, #ffffff);
201
+ border: 1px solid var(--color-border, #d1d5db);
202
+ border-radius: 6px;
203
+ transition: all 0.2s;
204
+ touch-action: manipulation;
205
+ -webkit-tap-highlight-color: transparent;
206
+ }
207
+
208
+ .datametria-textarea__inner:hover:not(:disabled) {
209
+ border-color: var(--color-primary, #3b82f6);
210
+ }
211
+
212
+ .datametria-textarea__inner:focus {
213
+ outline: none;
214
+ border-color: var(--color-primary, #3b82f6);
215
+ box-shadow: 0 0 0 3px var(--color-primary-alpha, rgba(59, 130, 246, 0.1));
216
+ }
217
+
218
+ .datametria-textarea__inner::placeholder {
219
+ color: var(--color-text-secondary, #6b7280);
220
+ }
221
+
222
+ .datametria-textarea__inner:disabled {
223
+ background-color: var(--color-background-muted, #f3f4f6);
224
+ border-color: var(--color-border, #d1d5db);
225
+ color: var(--color-text-secondary, #6b7280);
226
+ cursor: not-allowed;
227
+ opacity: 0.6;
228
+ }
229
+
230
+ .datametria-textarea--error .datametria-textarea__inner {
231
+ border-color: var(--color-error, #ef4444);
232
+ }
233
+
234
+ .datametria-textarea__count {
235
+ position: absolute;
236
+ bottom: 8px;
237
+ right: 12px;
238
+ font-size: 12px;
239
+ color: var(--color-text-secondary, #6b7280);
240
+ background-color: var(--color-background, #ffffff);
241
+ padding: 0 4px;
242
+ }
243
+
244
+ .datametria-textarea__error {
245
+ margin-top: 4px;
246
+ font-size: 12px;
247
+ color: var(--color-error, #ef4444);
248
+ }
249
+
250
+ /* Tamanhos */
251
+ .datametria-textarea--xs .datametria-textarea__inner {
252
+ padding: 2px 6px;
253
+ font-size: 11px;
254
+ min-height: 2rem;
255
+ }
256
+
257
+ .datametria-textarea--sm .datametria-textarea__inner {
258
+ padding: 4px 8px;
259
+ font-size: 12px;
260
+ min-height: 2.25rem;
261
+ }
262
+
263
+ .datametria-textarea--md .datametria-textarea__inner {
264
+ padding: 8px 12px;
265
+ font-size: 14px;
266
+ min-height: 2.75rem;
267
+ }
268
+
269
+ .datametria-textarea--lg .datametria-textarea__inner {
270
+ padding: 12px 16px;
271
+ font-size: 16px;
272
+ min-height: 3.25rem;
273
+ }
274
+
275
+ .datametria-textarea--xl .datametria-textarea__inner {
276
+ padding: 16px 20px;
277
+ font-size: 18px;
278
+ min-height: 3.75rem;
209
279
  }
210
280
 
211
- /* Controle manual via useTheme() */
212
- [data-theme="dark"] .datametria-textarea {
213
- background: var(--dm-bg-color-dark, #1e1e1e);
214
- color: var(--dm-text-primary-dark, #e0e0e0);
215
- border-color: var(--dm-border-color-dark, #404040);
281
+ /* Responsividade mobile */
282
+ @media (max-width: 640px) {
283
+ .datametria-textarea--xs .datametria-textarea__inner { min-height: 2.5rem; }
284
+ .datametria-textarea--sm .datametria-textarea__inner { min-height: 2.75rem; }
285
+ .datametria-textarea--md .datametria-textarea__inner { min-height: 3.25rem; }
286
+ .datametria-textarea--lg .datametria-textarea__inner { min-height: 3.75rem; }
287
+ .datametria-textarea--xl .datametria-textarea__inner { min-height: 4.25rem; }
216
288
  }
217
- </style>
289
+ </style>
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Firebase Analytics Composable
3
+ * @author Vander Loto - CTO DATAMETRIA
4
+ * @date 22/12/2025
5
+ */
6
+
7
+ import { getAnalytics, logEvent, type Analytics } from 'firebase/analytics'
8
+
9
+ let analytics: Analytics | null = null
10
+
11
+ export function useAnalytics() {
12
+ /**
13
+ * Inicializa o Firebase Analytics (lazy loading)
14
+ */
15
+ const initAnalytics = () => {
16
+ if (!analytics && typeof window !== 'undefined') {
17
+ try {
18
+ analytics = getAnalytics()
19
+ } catch (error) {
20
+ console.warn('[Analytics] Firebase não configurado:', error)
21
+ }
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Rastreia evento no Firebase Analytics
27
+ * @param eventName Nome do evento (snake_case)
28
+ * @param params Parâmetros do evento
29
+ */
30
+ const trackEvent = (
31
+ eventName: string,
32
+ params?: Record<string, any>
33
+ ) => {
34
+ // Apenas em produção
35
+ if (process.env.NODE_ENV === 'production') {
36
+ initAnalytics()
37
+
38
+ if (analytics) {
39
+ // Filtrar dados sensíveis
40
+ const sanitizedParams = sanitizeParams(params)
41
+ logEvent(analytics, eventName, sanitizedParams)
42
+ }
43
+ } else {
44
+ // Log em desenvolvimento
45
+ console.log('[Analytics]', eventName, params)
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Remove dados sensíveis dos parâmetros
51
+ */
52
+ const sanitizeParams = (params?: Record<string, any>) => {
53
+ if (!params) return undefined
54
+
55
+ const sensitiveKeys = ['password', 'token', 'secret', 'key', 'email', 'cpf', 'cnpj']
56
+ const sanitized = { ...params }
57
+
58
+ Object.keys(sanitized).forEach(key => {
59
+ if (sensitiveKeys.some(sensitive => key.toLowerCase().includes(sensitive))) {
60
+ delete sanitized[key]
61
+ }
62
+ })
63
+
64
+ return sanitized
65
+ }
66
+
67
+ return {
68
+ trackEvent
69
+ }
70
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Component Size Type (5 sizes)
3
+ * @author Vander Loto - CTO DATAMETRIA
4
+ * @date 22/12/2025
5
+ */
6
+
7
+ export type ComponentSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
8
+
9
+ export const ComponentSizeEnum = {
10
+ XS: 'xs' as ComponentSize,
11
+ SM: 'sm' as ComponentSize,
12
+ MD: 'md' as ComponentSize,
13
+ LG: 'lg' as ComponentSize,
14
+ XL: 'xl' as ComponentSize
15
+ }
16
+
17
+ /**
18
+ * Analytics Event Types
19
+ */
20
+ export interface AnalyticsEvent {
21
+ component: string
22
+ [key: string]: any
23
+ }
24
+
25
+ export interface ButtonClickEvent extends AnalyticsEvent {
26
+ variant?: string
27
+ size?: ComponentSize
28
+ }
29
+
30
+ export interface InputEvent extends AnalyticsEvent {
31
+ type?: string
32
+ size?: ComponentSize
33
+ hasValue?: boolean
34
+ }
35
+
36
+ export interface ModalEvent extends AnalyticsEvent {
37
+ title?: string
38
+ action?: 'open' | 'close' | 'confirm' | 'cancel'
39
+ }
40
+
41
+ export interface TableEvent extends AnalyticsEvent {
42
+ column?: string
43
+ direction?: 'asc' | 'desc'
44
+ filterType?: string
45
+ selectedCount?: number
46
+ page?: number
47
+ pageSize?: number
48
+ }
49
+
50
+ export interface TabEvent extends AnalyticsEvent {
51
+ tabId: string
52
+ tabLabel?: string
53
+ }
54
+
55
+ export interface FormEvent extends AnalyticsEvent {
56
+ formId?: string
57
+ fieldCount?: number
58
+ errorCount?: number
59
+ }