@datametria/vue-components 2.4.1 → 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 (35) 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 +124 -3
  25. package/src/components/DatametriaFileUpload.vue +493 -414
  26. package/src/components/DatametriaInput.vue +342 -316
  27. package/src/components/DatametriaPasswordInput.vue +433 -446
  28. package/src/components/DatametriaRadio.vue +240 -151
  29. package/src/components/DatametriaRadioGroup.vue +124 -3
  30. package/src/components/DatametriaSelect.vue +409 -313
  31. package/src/components/DatametriaSwitch.vue +319 -146
  32. package/src/components/DatametriaTabs.vue +2 -2
  33. package/src/components/DatametriaTextarea.vue +285 -213
  34. package/src/composables/useAnalytics.ts +70 -0
  35. package/src/types/analytics.ts +59 -0
@@ -1,316 +1,342 @@
1
- <template>
2
- <div class="datametria-input" :class="inputWrapperClasses">
3
- <!-- Label -->
4
- <label v-if="label" class="datametria-input__label">
5
- {{ label }}
6
- <span v-if="required" class="datametria-input__required">*</span>
7
- </label>
8
-
9
- <div v-if="$slots.prepend" class="datametria-input__prepend">
10
- <slot name="prepend" />
11
- </div>
12
-
13
- <div class="datametria-input__inner">
14
- <span v-if="$slots.prefix || prefixIcon" class="datametria-input__prefix">
15
- <slot name="prefix">
16
- <span v-if="prefixIcon">{{ prefixIcon }}</span>
17
- </slot>
18
- </span>
19
-
20
- <input
21
- ref="inputRef"
22
- v-model="inputValue"
23
- :type="type"
24
- :placeholder="placeholder"
25
- :disabled="disabled"
26
- :readonly="readonly"
27
- :maxlength="maxlength"
28
- :class="inputClasses"
29
- @input="handleInput"
30
- @change="handleChange"
31
- @focus="handleFocus"
32
- @blur="handleBlur"
33
- />
34
-
35
- <span v-if="showClear || $slots.suffix || suffixIcon" class="datametria-input__suffix">
36
- <button
37
- v-if="showClear"
38
- class="datametria-input__clear"
39
- @click="handleClear"
40
- type="button"
41
- >
42
- ×
43
- </button>
44
- <slot name="suffix">
45
- <span v-if="suffixIcon">{{ suffixIcon }}</span>
46
- </slot>
47
- </span>
48
- </div>
49
-
50
- <div v-if="$slots.append" class="datametria-input__append">
51
- <slot name="append" />
52
- </div>
53
-
54
- <!-- Error Message -->
55
- <div v-if="errorMessage" class="datametria-input__error">
56
- {{ errorMessage }}
57
- </div>
58
- </div>
59
- </template>
60
-
61
- <script setup lang="ts">
62
- import { ref, computed, watch } from 'vue'
63
-
64
- /**
65
- * DatametriaInput - Input de texto com validação e slots
66
- *
67
- * @component
68
- * @example
69
- * <DatametriaInput v-model="value" placeholder="Digite algo" />
70
- */
71
-
72
- interface Props {
73
- /** Valor do input */
74
- modelValue?: string | number
75
- /** Label do input */
76
- label?: string
77
- /** Campo obrigatório */
78
- required?: boolean
79
- /** Mensagem de erro */
80
- errorMessage?: string
81
- /** Tipo do input */
82
- type?: 'text' | 'password' | 'email' | 'number' | 'tel' | 'url'
83
- /** Placeholder */
84
- placeholder?: string
85
- /** Input desabilitado */
86
- disabled?: boolean
87
- /** Input somente leitura */
88
- readonly?: boolean
89
- /** Botão de limpar */
90
- clearable?: boolean
91
- /** Comprimento máximo */
92
- maxlength?: number
93
- /** Ícone prefixo */
94
- prefixIcon?: string
95
- /** Ícone sufixo */
96
- suffixIcon?: string
97
- /** Tamanho */
98
- size?: 'small' | 'default' | 'large'
99
- }
100
-
101
- const props = withDefaults(defineProps<Props>(), {
102
- modelValue: '',
103
- type: 'text',
104
- placeholder: '',
105
- disabled: false,
106
- readonly: false,
107
- clearable: false,
108
- required: false,
109
- size: 'default'
110
- })
111
-
112
- const emit = defineEmits<{
113
- 'update:modelValue': [value: string | number]
114
- 'input': [value: string | number]
115
- 'change': [value: string | number]
116
- 'focus': [event: FocusEvent]
117
- 'blur': [event: FocusEvent]
118
- 'clear': []
119
- }>()
120
-
121
- const inputRef = ref<HTMLInputElement>()
122
- const isFocused = ref(false)
123
-
124
- const inputValue = computed({
125
- get: () => props.modelValue,
126
- set: (value) => emit('update:modelValue', value)
127
- })
128
-
129
- const showClear = computed(() => {
130
- return props.clearable && !props.disabled && !props.readonly && inputValue.value
131
- })
132
-
133
- const inputWrapperClasses = computed(() => ({
134
- 'datametria-input--disabled': props.disabled,
135
- 'datametria-input--focused': isFocused.value,
136
- [`datametria-input--${props.size}`]: props.size !== 'default'
137
- }))
138
-
139
- const inputClasses = computed(() => ({
140
- 'datametria-input__field': true,
141
- 'datametria-input__field--error': !!props.errorMessage
142
- }))
143
-
144
- const handleInput = (event: Event) => {
145
- const target = event.target as HTMLInputElement
146
- emit('input', target.value)
147
- }
148
-
149
- const handleChange = (event: Event) => {
150
- const target = event.target as HTMLInputElement
151
- emit('change', target.value)
152
- }
153
-
154
- const handleFocus = (event: FocusEvent) => {
155
- isFocused.value = true
156
- emit('focus', event)
157
- }
158
-
159
- const handleBlur = (event: FocusEvent) => {
160
- isFocused.value = false
161
- emit('blur', event)
162
- }
163
-
164
- const handleClear = () => {
165
- emit('update:modelValue', '')
166
- emit('clear')
167
- inputRef.value?.focus()
168
- }
169
-
170
- defineExpose({
171
- focus: () => inputRef.value?.focus(),
172
- blur: () => inputRef.value?.blur()
173
- })
174
- </script>
175
-
176
- <style scoped>
177
- .datametria-input__label {
178
- display: block;
179
- margin-bottom: 4px;
180
- font-size: 14px;
181
- font-weight: 500;
182
- color: var(--dm-text-primary, #1f2937);
183
- }
184
-
185
- .datametria-input__required {
186
- color: var(--dm-error, #ef4444);
187
- margin-left: 2px;
188
- }
189
-
190
- .datametria-input__error {
191
- margin-top: 4px;
192
- font-size: 12px;
193
- color: var(--dm-error, #ef4444);
194
- }
195
-
196
- .datametria-input__field--error {
197
- border-color: var(--dm-error, #ef4444) !important;
198
- }
199
-
200
- .datametria-input {
201
- display: inline-flex;
202
- flex-direction: column;
203
- width: 100%;
204
- font-size: 14px;
205
- }
206
-
207
- .datametria-input__inner {
208
- position: relative;
209
- display: inline-flex;
210
- align-items: center;
211
- width: 100%;
212
- background: var(--dm-bg-color, #ffffff);
213
- border: 1px solid var(--dm-border-color, #d1d5db);
214
- border-radius: 6px;
215
- transition: all 0.2s;
216
- }
217
-
218
- .datametria-input__inner:hover {
219
- border-color: var(--dm-primary, #3b82f6);
220
- }
221
-
222
- .datametria-input--focused .datametria-input__inner {
223
- border-color: var(--dm-primary, #3b82f6);
224
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
225
- }
226
-
227
- .datametria-input--disabled .datametria-input__inner {
228
- background: var(--dm-bg-disabled, #f3f4f6);
229
- cursor: not-allowed;
230
- opacity: 0.6;
231
- }
232
-
233
- .datametria-input__field {
234
- flex: 1;
235
- width: 100%;
236
- padding: 8px 12px;
237
- border: none;
238
- outline: none;
239
- background: transparent;
240
- color: var(--dm-text-primary, #1f2937);
241
- font-size: inherit;
242
- }
243
-
244
- .datametria-input__field:disabled {
245
- cursor: not-allowed;
246
- }
247
-
248
- .datametria-input__prefix,
249
- .datametria-input__suffix {
250
- display: inline-flex;
251
- align-items: center;
252
- padding: 0 8px;
253
- color: var(--dm-text-secondary, #6b7280);
254
- }
255
-
256
- .datametria-input__clear {
257
- background: none;
258
- border: none;
259
- font-size: 18px;
260
- line-height: 1;
261
- color: var(--dm-text-secondary, #6b7280);
262
- cursor: pointer;
263
- padding: 0 4px;
264
- transition: color 0.2s;
265
- }
266
-
267
- .datametria-input__clear:hover {
268
- color: var(--dm-text-primary, #1f2937);
269
- }
270
-
271
- .datametria-input__prepend,
272
- .datametria-input__append {
273
- display: inline-flex;
274
- align-items: center;
275
- padding: 0 12px;
276
- background: var(--dm-bg-secondary, #f9fafb);
277
- border: 1px solid var(--dm-border-color, #d1d5db);
278
- white-space: nowrap;
279
- }
280
-
281
- .datametria-input__prepend {
282
- border-right: none;
283
- border-radius: 6px 0 0 6px;
284
- }
285
-
286
- .datametria-input__append {
287
- border-left: none;
288
- border-radius: 0 6px 6px 0;
289
- }
290
-
291
- .datametria-input--small .datametria-input__field {
292
- padding: 4px 8px;
293
- font-size: 12px;
294
- }
295
-
296
- .datametria-input--large .datametria-input__field {
297
- padding: 12px 16px;
298
- font-size: 16px;
299
- }
300
-
301
- @media (prefers-color-scheme: dark) {
302
- .datametria-input__inner {
303
- background: var(--color-background, #1f2937);
304
- border-color: var(--color-border, #374151);
305
- }
306
-
307
- [data-theme="dark"] .datametria-input__inner {
308
- background: var(--color-background, #1f2937);
309
- border-color: var(--color-border, #374151);
310
- }
311
-
312
- .datametria-input__field {
313
- color: var(--color-text-primary, #f9fafb);
314
- }
315
- }
316
- </style>
1
+ <template>
2
+ <div class="datametria-input" :class="inputWrapperClasses">
3
+ <label v-if="label" :for="inputId" class="datametria-input__label">
4
+ {{ label }}
5
+ <span v-if="required" class="datametria-input__required">*</span>
6
+ </label>
7
+
8
+ <div v-if="$slots.prepend" class="datametria-input__prepend">
9
+ <slot name="prepend" />
10
+ </div>
11
+
12
+ <div class="datametria-input__inner">
13
+ <span v-if="$slots.prefix || prefixIcon" class="datametria-input__prefix">
14
+ <slot name="prefix">
15
+ <span v-if="prefixIcon">{{ prefixIcon }}</span>
16
+ </slot>
17
+ </span>
18
+
19
+ <input
20
+ :id="inputId"
21
+ ref="inputRef"
22
+ v-model="inputValue"
23
+ :type="type"
24
+ :placeholder="placeholder"
25
+ :disabled="disabled"
26
+ :readonly="readonly"
27
+ :maxlength="maxlength"
28
+ :aria-label="label || placeholder"
29
+ :aria-required="required"
30
+ :aria-invalid="!!errorMessage"
31
+ :aria-describedby="errorMessage ? `${inputId}-error` : undefined"
32
+ :class="inputClasses"
33
+ @input="handleInput"
34
+ @change="handleChange"
35
+ @focus="handleFocus"
36
+ @blur="handleBlur"
37
+ />
38
+
39
+ <span v-if="showClear || $slots.suffix || suffixIcon" class="datametria-input__suffix">
40
+ <button
41
+ v-if="showClear"
42
+ class="datametria-input__clear"
43
+ type="button"
44
+ aria-label="Limpar"
45
+ @click="handleClear"
46
+ >
47
+ ×
48
+ </button>
49
+ <slot name="suffix">
50
+ <span v-if="suffixIcon">{{ suffixIcon }}</span>
51
+ </slot>
52
+ </span>
53
+ </div>
54
+
55
+ <div v-if="$slots.append" class="datametria-input__append">
56
+ <slot name="append" />
57
+ </div>
58
+
59
+ <div
60
+ v-if="errorMessage"
61
+ :id="`${inputId}-error`"
62
+ role="alert"
63
+ aria-live="polite"
64
+ class="datametria-input__error"
65
+ >
66
+ {{ errorMessage }}
67
+ </div>
68
+ </div>
69
+ </template>
70
+
71
+ <script setup lang="ts">
72
+ import { ref, computed } from 'vue'
73
+ import { useAnalytics } from '@/composables/useAnalytics'
74
+
75
+ interface Props {
76
+ modelValue?: string | number
77
+ label?: string
78
+ required?: boolean
79
+ errorMessage?: string
80
+ type?: 'text' | 'password' | 'email' | 'number' | 'tel' | 'url'
81
+ placeholder?: string
82
+ disabled?: boolean
83
+ readonly?: boolean
84
+ clearable?: boolean
85
+ maxlength?: number
86
+ prefixIcon?: string
87
+ suffixIcon?: string
88
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'
89
+ }
90
+
91
+ const props = withDefaults(defineProps<Props>(), {
92
+ modelValue: '',
93
+ type: 'text',
94
+ placeholder: '',
95
+ disabled: false,
96
+ readonly: false,
97
+ clearable: false,
98
+ required: false,
99
+ size: 'md'
100
+ })
101
+
102
+ const emit = defineEmits<{
103
+ 'update:modelValue': [value: string | number]
104
+ 'input': [value: string | number]
105
+ 'change': [value: string | number]
106
+ 'focus': [event: FocusEvent]
107
+ 'blur': [event: FocusEvent]
108
+ 'clear': []
109
+ }>()
110
+
111
+ const { trackEvent } = useAnalytics()
112
+ const inputRef = ref<HTMLInputElement>()
113
+ const isFocused = ref(false)
114
+ const inputId = `datametria-input-${Math.random().toString(36).substr(2, 9)}`
115
+
116
+ const inputValue = computed({
117
+ get: () => props.modelValue,
118
+ set: (value) => emit('update:modelValue', value)
119
+ })
120
+
121
+ const showClear = computed(() => {
122
+ return props.clearable && !props.disabled && !props.readonly && inputValue.value
123
+ })
124
+
125
+ const inputWrapperClasses = computed(() => ({
126
+ 'datametria-input--disabled': props.disabled,
127
+ 'datametria-input--focused': isFocused.value,
128
+ 'datametria-input--error': !!props.errorMessage,
129
+ [`datametria-input--${props.size}`]: true
130
+ }))
131
+
132
+ const inputClasses = computed(() => ({
133
+ 'datametria-input__field': true
134
+ }))
135
+
136
+ const handleInput = (event: Event) => {
137
+ const target = event.target as HTMLInputElement
138
+ emit('input', target.value)
139
+ }
140
+
141
+ const handleChange = (event: Event) => {
142
+ const target = event.target as HTMLInputElement
143
+ trackEvent('input_change', {
144
+ component: 'DatametriaInput',
145
+ type: props.type,
146
+ hasValue: !!target.value
147
+ })
148
+ emit('change', target.value)
149
+ }
150
+
151
+ const handleFocus = (event: FocusEvent) => {
152
+ isFocused.value = true
153
+ trackEvent('input_focus', {
154
+ component: 'DatametriaInput',
155
+ type: props.type,
156
+ size: props.size
157
+ })
158
+ emit('focus', event)
159
+ }
160
+
161
+ const handleBlur = (event: FocusEvent) => {
162
+ isFocused.value = false
163
+ emit('blur', event)
164
+ }
165
+
166
+ const handleClear = () => {
167
+ trackEvent('input_clear', {
168
+ component: 'DatametriaInput',
169
+ type: props.type
170
+ })
171
+ emit('update:modelValue', '')
172
+ emit('clear')
173
+ inputRef.value?.focus()
174
+ }
175
+
176
+ defineExpose({
177
+ focus: () => inputRef.value?.focus(),
178
+ blur: () => inputRef.value?.blur()
179
+ })
180
+ </script>
181
+
182
+ <style scoped>
183
+ .datametria-input {
184
+ display: inline-flex;
185
+ flex-direction: column;
186
+ width: 100%;
187
+ }
188
+
189
+ .datametria-input__label {
190
+ display: block;
191
+ margin-bottom: 4px;
192
+ font-size: 14px;
193
+ font-weight: 500;
194
+ color: var(--color-text-primary, #1f2937);
195
+ }
196
+
197
+ .datametria-input__required {
198
+ color: var(--color-error, #ef4444);
199
+ margin-left: 2px;
200
+ }
201
+
202
+ .datametria-input__inner {
203
+ position: relative;
204
+ display: inline-flex;
205
+ align-items: center;
206
+ width: 100%;
207
+ background: var(--color-background, #ffffff);
208
+ border: 1px solid var(--color-border, #d1d5db);
209
+ border-radius: 6px;
210
+ transition: all 0.2s;
211
+ }
212
+
213
+ .datametria-input__inner:hover:not(.datametria-input--disabled .datametria-input__inner) {
214
+ border-color: var(--color-primary, #3b82f6);
215
+ }
216
+
217
+ .datametria-input--focused .datametria-input__inner {
218
+ border-color: var(--color-primary, #3b82f6);
219
+ box-shadow: 0 0 0 3px var(--color-primary-alpha, rgba(59, 130, 246, 0.1));
220
+ }
221
+
222
+ .datametria-input--error .datametria-input__inner {
223
+ border-color: var(--color-error, #ef4444);
224
+ }
225
+
226
+ .datametria-input--disabled .datametria-input__inner {
227
+ background: var(--color-background-muted, #f3f4f6);
228
+ cursor: not-allowed;
229
+ opacity: 0.6;
230
+ }
231
+
232
+ .datametria-input__field {
233
+ flex: 1;
234
+ width: 100%;
235
+ border: none;
236
+ outline: none;
237
+ background: transparent;
238
+ color: var(--color-text-primary, #1f2937);
239
+ font-size: 14px;
240
+ touch-action: manipulation;
241
+ -webkit-tap-highlight-color: transparent;
242
+ }
243
+
244
+ .datametria-input__field:disabled {
245
+ cursor: not-allowed;
246
+ }
247
+
248
+ .datametria-input__field::placeholder {
249
+ color: var(--color-text-secondary, #6b7280);
250
+ }
251
+
252
+ .datametria-input__prefix,
253
+ .datametria-input__suffix {
254
+ display: inline-flex;
255
+ align-items: center;
256
+ padding: 0 8px;
257
+ color: var(--color-text-secondary, #6b7280);
258
+ }
259
+
260
+ .datametria-input__clear {
261
+ background: none;
262
+ border: none;
263
+ font-size: 18px;
264
+ line-height: 1;
265
+ color: var(--color-text-secondary, #6b7280);
266
+ cursor: pointer;
267
+ padding: 0 4px;
268
+ transition: color 0.2s;
269
+ min-width: 44px;
270
+ min-height: 44px;
271
+ }
272
+
273
+ .datametria-input__clear:hover {
274
+ color: var(--color-text-primary, #1f2937);
275
+ }
276
+
277
+ .datametria-input__prepend,
278
+ .datametria-input__append {
279
+ display: inline-flex;
280
+ align-items: center;
281
+ padding: 0 12px;
282
+ background: var(--color-background-alt, #f9fafb);
283
+ border: 1px solid var(--color-border, #d1d5db);
284
+ white-space: nowrap;
285
+ }
286
+
287
+ .datametria-input__prepend {
288
+ border-right: none;
289
+ border-radius: 6px 0 0 6px;
290
+ }
291
+
292
+ .datametria-input__append {
293
+ border-left: none;
294
+ border-radius: 0 6px 6px 0;
295
+ }
296
+
297
+ .datametria-input__error {
298
+ margin-top: 4px;
299
+ font-size: 12px;
300
+ color: var(--color-error, #ef4444);
301
+ }
302
+
303
+ /* Tamanhos */
304
+ .datametria-input--xs .datametria-input__field {
305
+ padding: 2px 6px;
306
+ font-size: 11px;
307
+ min-height: 1.75rem;
308
+ }
309
+
310
+ .datametria-input--sm .datametria-input__field {
311
+ padding: 4px 8px;
312
+ font-size: 12px;
313
+ min-height: 2rem;
314
+ }
315
+
316
+ .datametria-input--md .datametria-input__field {
317
+ padding: 8px 12px;
318
+ font-size: 14px;
319
+ min-height: 2.5rem;
320
+ }
321
+
322
+ .datametria-input--lg .datametria-input__field {
323
+ padding: 12px 16px;
324
+ font-size: 16px;
325
+ min-height: 3rem;
326
+ }
327
+
328
+ .datametria-input--xl .datametria-input__field {
329
+ padding: 16px 20px;
330
+ font-size: 18px;
331
+ min-height: 3.5rem;
332
+ }
333
+
334
+ /* Responsividade mobile */
335
+ @media (max-width: 640px) {
336
+ .datametria-input--xs .datametria-input__field { min-height: 2rem; }
337
+ .datametria-input--sm .datametria-input__field { min-height: 2.25rem; }
338
+ .datametria-input--md .datametria-input__field { min-height: 2.75rem; }
339
+ .datametria-input--lg .datametria-input__field { min-height: 3.25rem; }
340
+ .datametria-input--xl .datametria-input__field { min-height: 3.75rem; }
341
+ }
342
+ </style>