@datametria/vue-components 2.2.0 → 2.3.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 (78) hide show
  1. package/README.md +25 -7
  2. package/dist/index.es.js +3378 -2148
  3. package/dist/index.umd.js +9 -9
  4. package/dist/src/components/DatametriaAutocomplete.vue.d.ts +14 -17
  5. package/dist/src/components/DatametriaBreadcrumb.vue.d.ts +39 -7
  6. package/dist/src/components/DatametriaCheckbox.vue.d.ts +35 -6
  7. package/dist/src/components/DatametriaCheckboxGroup.vue.d.ts +30 -0
  8. package/dist/src/components/DatametriaDataTable.vue.d.ts +64 -0
  9. package/dist/src/components/DatametriaDatePicker.vue.d.ts +15 -37
  10. package/dist/src/components/DatametriaDialog.vue.d.ts +71 -0
  11. package/dist/src/components/DatametriaEmpty.vue.d.ts +30 -0
  12. package/dist/src/components/DatametriaFloatingBar.vue.d.ts +2 -2
  13. package/dist/src/components/DatametriaForm.vue.d.ts +40 -0
  14. package/dist/src/components/DatametriaFormItem.vue.d.ts +28 -0
  15. package/dist/src/components/DatametriaGrid.vue.d.ts +1 -1
  16. package/dist/src/components/DatametriaInput.vue.d.ts +69 -10
  17. package/dist/src/components/DatametriaMenu.vue.d.ts +3 -3
  18. package/dist/src/components/DatametriaNavbar.vue.d.ts +2 -2
  19. package/dist/src/components/DatametriaPagination.vue.d.ts +29 -0
  20. package/dist/src/components/DatametriaPopconfirm.vue.d.ts +43 -0
  21. package/dist/src/components/DatametriaProgress.vue.d.ts +33 -8
  22. package/dist/src/components/DatametriaRadio.vue.d.ts +25 -6
  23. package/dist/src/components/DatametriaRadioGroup.vue.d.ts +29 -0
  24. package/dist/src/components/DatametriaResult.vue.d.ts +30 -0
  25. package/dist/src/components/DatametriaSelect.vue.d.ts +16 -11
  26. package/dist/src/components/DatametriaSidebar.vue.d.ts +3 -3
  27. package/dist/src/components/DatametriaSlider.vue.d.ts +3 -3
  28. package/dist/src/components/DatametriaSortableTable.vue.d.ts +1 -1
  29. package/dist/src/components/DatametriaSteps.vue.d.ts +45 -0
  30. package/dist/src/components/DatametriaSwitch.vue.d.ts +9 -4
  31. package/dist/src/components/DatametriaTabPane.vue.d.ts +28 -0
  32. package/dist/src/components/DatametriaTextarea.vue.d.ts +27 -8
  33. package/dist/src/components/DatametriaTimePicker.vue.d.ts +17 -25
  34. package/dist/src/components/DatametriaToast.vue.d.ts +1 -1
  35. package/dist/src/components/DatametriaTooltip.vue.d.ts +1 -1
  36. package/dist/src/components/DatametriaTree.vue.d.ts +31 -0
  37. package/dist/src/components/DatametriaTreeNode.vue.d.ts +17 -0
  38. package/dist/src/components/DatametriaUpload.vue.d.ts +64 -0
  39. package/dist/src/index.d.ts +14 -0
  40. package/dist/vue-components.css +1 -1
  41. package/package.json +4 -3
  42. package/src/components/DatametriaAutocomplete.vue +155 -260
  43. package/src/components/DatametriaBreadcrumb.vue +66 -80
  44. package/src/components/DatametriaCheckbox.vue +150 -37
  45. package/src/components/DatametriaCheckboxGroup.vue +43 -0
  46. package/src/components/DatametriaDataTable.vue +304 -0
  47. package/src/components/DatametriaDatePicker.vue +238 -614
  48. package/src/components/DatametriaDialog.vue +295 -0
  49. package/src/components/DatametriaDropdown.vue +352 -0
  50. package/src/components/DatametriaEmpty.vue +153 -0
  51. package/src/components/DatametriaForm.vue +160 -0
  52. package/src/components/DatametriaFormItem.vue +181 -0
  53. package/src/components/DatametriaInput.vue +226 -63
  54. package/src/components/DatametriaPagination.vue +373 -0
  55. package/src/components/DatametriaPopconfirm.vue +236 -0
  56. package/src/components/DatametriaProgress.vue +176 -63
  57. package/src/components/DatametriaRadio.vue +83 -72
  58. package/src/components/DatametriaRadioGroup.vue +42 -0
  59. package/src/components/DatametriaResult.vue +133 -0
  60. package/src/components/DatametriaSelect.vue +172 -67
  61. package/src/components/DatametriaSortableTable.vue +35 -4
  62. package/src/components/DatametriaSteps.vue +314 -0
  63. package/src/components/DatametriaSwitch.vue +86 -80
  64. package/src/components/DatametriaTabPane.vue +82 -0
  65. package/src/components/DatametriaTextarea.vue +140 -100
  66. package/src/components/DatametriaTimePicker.vue +231 -214
  67. package/src/components/DatametriaTree.vue +124 -0
  68. package/src/components/DatametriaTreeNode.vue +174 -0
  69. package/src/components/DatametriaUpload.vue +365 -0
  70. package/src/index.ts +25 -11
  71. package/src/components/__tests__/DatametriaAutocomplete.test.ts +0 -180
  72. package/src/components/__tests__/DatametriaBreadcrumb.test.ts +0 -75
  73. package/src/components/__tests__/DatametriaCheckbox.test.ts +0 -47
  74. package/src/components/__tests__/DatametriaDatePicker.test.ts +0 -234
  75. package/src/components/__tests__/DatametriaProgress.test.ts +0 -90
  76. package/src/components/__tests__/DatametriaRadio.test.ts +0 -77
  77. package/src/components/__tests__/DatametriaSwitch.test.ts +0 -64
  78. package/src/components/__tests__/DatametriaTextarea.test.ts +0 -66
@@ -0,0 +1,153 @@
1
+ <template>
2
+ <div class="datametria-empty" role="status" aria-live="polite">
3
+ <div class="datametria-empty__image">
4
+ <slot name="image">
5
+ <svg
6
+ v-if="imageType === 'no-data'"
7
+ class="datametria-empty__svg"
8
+ viewBox="0 0 200 200"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ >
11
+ <circle cx="100" cy="100" r="80" fill="#f0f0f0" />
12
+ <path d="M70 90 Q100 70 130 90" stroke="#999" stroke-width="3" fill="none" />
13
+ <circle cx="80" cy="85" r="8" fill="#999" />
14
+ <circle cx="120" cy="85" r="8" fill="#999" />
15
+ <path d="M70 130 Q100 150 130 130" stroke="#999" stroke-width="3" fill="none" />
16
+ </svg>
17
+ <svg
18
+ v-else-if="imageType === 'no-results'"
19
+ class="datametria-empty__svg"
20
+ viewBox="0 0 200 200"
21
+ xmlns="http://www.w3.org/2000/svg"
22
+ >
23
+ <circle cx="80" cy="80" r="50" fill="none" stroke="#999" stroke-width="4" />
24
+ <line x1="120" y1="120" x2="160" y2="160" stroke="#999" stroke-width="4" stroke-linecap="round" />
25
+ <line x1="60" y1="60" x2="100" y2="100" stroke="#e74c3c" stroke-width="3" />
26
+ <line x1="100" y1="60" x2="60" y2="100" stroke="#e74c3c" stroke-width="3" />
27
+ </svg>
28
+ <svg
29
+ v-else-if="imageType === 'error'"
30
+ class="datametria-empty__svg"
31
+ viewBox="0 0 200 200"
32
+ xmlns="http://www.w3.org/2000/svg"
33
+ >
34
+ <circle cx="100" cy="100" r="80" fill="#fee" stroke="#e74c3c" stroke-width="3" />
35
+ <line x1="70" y1="70" x2="130" y2="130" stroke="#e74c3c" stroke-width="4" stroke-linecap="round" />
36
+ <line x1="130" y1="70" x2="70" y2="130" stroke="#e74c3c" stroke-width="4" stroke-linecap="round" />
37
+ </svg>
38
+ <img
39
+ v-else-if="image"
40
+ :src="image"
41
+ :alt="imageAlt"
42
+ class="datametria-empty__custom-image"
43
+ />
44
+ </slot>
45
+ </div>
46
+
47
+ <div class="datametria-empty__description">
48
+ <slot name="description">
49
+ <p v-if="description" class="datametria-empty__text">{{ description }}</p>
50
+ </slot>
51
+ </div>
52
+
53
+ <div v-if="$slots.default" class="datametria-empty__actions">
54
+ <slot></slot>
55
+ </div>
56
+ </div>
57
+ </template>
58
+
59
+ <script setup lang="ts">
60
+ import { computed } from 'vue'
61
+
62
+ interface Props {
63
+ description?: string
64
+ image?: string
65
+ imageAlt?: string
66
+ imageType?: 'no-data' | 'no-results' | 'error'
67
+ }
68
+
69
+ const props = withDefaults(defineProps<Props>(), {
70
+ description: '',
71
+ image: '',
72
+ imageAlt: 'Empty state',
73
+ imageType: 'no-data'
74
+ })
75
+
76
+ const imageType = computed(() => {
77
+ if (props.image) return null
78
+ return props.imageType
79
+ })
80
+ </script>
81
+
82
+ <style scoped>
83
+ .datametria-empty {
84
+ display: flex;
85
+ flex-direction: column;
86
+ align-items: center;
87
+ justify-content: center;
88
+ padding: var(--dm-spacing-xl, 48px) var(--dm-spacing-lg, 24px);
89
+ text-align: center;
90
+ min-height: 300px;
91
+ }
92
+
93
+ .datametria-empty__image {
94
+ margin-bottom: var(--dm-spacing-lg, 24px);
95
+ }
96
+
97
+ .datametria-empty__svg {
98
+ width: 200px;
99
+ height: 200px;
100
+ opacity: 0.8;
101
+ }
102
+
103
+ .datametria-empty__custom-image {
104
+ max-width: 200px;
105
+ max-height: 200px;
106
+ object-fit: contain;
107
+ }
108
+
109
+ .datametria-empty__description {
110
+ margin-bottom: var(--dm-spacing-md, 16px);
111
+ }
112
+
113
+ .datametria-empty__text {
114
+ color: var(--dm-color-text-secondary, #666);
115
+ font-size: var(--dm-font-size-base, 14px);
116
+ line-height: 1.6;
117
+ margin: 0;
118
+ max-width: 400px;
119
+ }
120
+
121
+ .datametria-empty__actions {
122
+ margin-top: var(--dm-spacing-md, 16px);
123
+ }
124
+
125
+ @media (prefers-color-scheme: dark) {
126
+ .datametria-empty__text {
127
+ color: var(--dm-color-text-secondary-dark, #999);
128
+ }
129
+
130
+ .datametria-empty__svg circle[fill="#f0f0f0"] {
131
+ fill: #2a2a2a;
132
+ }
133
+
134
+ .datametria-empty__svg circle[fill="#fee"] {
135
+ fill: #3a2020;
136
+ }
137
+ }
138
+
139
+ @media (max-width: 768px) {
140
+ .datametria-empty {
141
+ padding: var(--dm-spacing-lg, 24px) var(--dm-spacing-md, 16px);
142
+ min-height: 250px;
143
+ }
144
+
145
+ .datametria-empty__svg,
146
+ .datametria-empty__custom-image {
147
+ width: 150px;
148
+ height: 150px;
149
+ max-width: 150px;
150
+ max-height: 150px;
151
+ }
152
+ }
153
+ </style>
@@ -0,0 +1,160 @@
1
+ <template>
2
+ <form
3
+ class="datametria-form"
4
+ :class="{
5
+ [`datametria-form--label-${labelPosition}`]: labelPosition,
6
+ 'datametria-form--inline': inline
7
+ }"
8
+ @submit.prevent="handleSubmit"
9
+ >
10
+ <slot></slot>
11
+ </form>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { provide, reactive, ref } from 'vue'
16
+
17
+ interface Props {
18
+ model?: Record<string, any>
19
+ rules?: Record<string, any>
20
+ labelWidth?: string
21
+ labelPosition?: 'left' | 'right' | 'top'
22
+ inline?: boolean
23
+ }
24
+
25
+ const props = withDefaults(defineProps<Props>(), {
26
+ model: () => ({}),
27
+ rules: () => ({}),
28
+ labelPosition: 'right',
29
+ inline: false
30
+ })
31
+
32
+ const emit = defineEmits<{
33
+ validate: [valid: boolean, errors: Record<string, string[]>]
34
+ submit: [model: Record<string, any>]
35
+ }>()
36
+
37
+ const fields = reactive<Map<string, any>>(new Map())
38
+ const errors = ref<Record<string, string[]>>({})
39
+
40
+ const registerField = (field: any) => {
41
+ fields.set(field.prop, field)
42
+ }
43
+
44
+ const unregisterField = (prop: string) => {
45
+ fields.delete(prop)
46
+ }
47
+
48
+ const validate = async (): Promise<boolean> => {
49
+ const validationPromises: Promise<boolean>[] = []
50
+ const newErrors: Record<string, string[]> = {}
51
+
52
+ fields.forEach((field) => {
53
+ validationPromises.push(
54
+ field.validate().then((valid: boolean) => {
55
+ if (!valid && field.errorMessage) {
56
+ newErrors[field.prop] = [field.errorMessage]
57
+ }
58
+ return valid
59
+ })
60
+ )
61
+ })
62
+
63
+ const results = await Promise.all(validationPromises)
64
+ const isValid = results.every((result) => result)
65
+
66
+ errors.value = newErrors
67
+ emit('validate', isValid, newErrors)
68
+
69
+ return isValid
70
+ }
71
+
72
+ const validateField = async (prop: string): Promise<boolean> => {
73
+ const field = fields.get(prop)
74
+ if (!field) return true
75
+
76
+ const valid = await field.validate()
77
+ if (!valid && field.errorMessage) {
78
+ errors.value[prop] = [field.errorMessage]
79
+ } else {
80
+ delete errors.value[prop]
81
+ }
82
+
83
+ return valid
84
+ }
85
+
86
+ const resetFields = () => {
87
+ fields.forEach((field) => {
88
+ field.reset()
89
+ })
90
+ errors.value = {}
91
+ }
92
+
93
+ const clearValidate = (props?: string | string[]) => {
94
+ if (!props) {
95
+ errors.value = {}
96
+ fields.forEach((field) => field.clearValidate())
97
+ return
98
+ }
99
+
100
+ const propsArray = Array.isArray(props) ? props : [props]
101
+ propsArray.forEach((prop) => {
102
+ delete errors.value[prop]
103
+ const field = fields.get(prop)
104
+ if (field) field.clearValidate()
105
+ })
106
+ }
107
+
108
+ const handleSubmit = async () => {
109
+ const valid = await validate()
110
+ if (valid) {
111
+ emit('submit', props.model)
112
+ }
113
+ }
114
+
115
+ provide('datametriaForm', {
116
+ model: props.model,
117
+ rules: props.rules,
118
+ labelWidth: props.labelWidth,
119
+ labelPosition: props.labelPosition,
120
+ registerField,
121
+ unregisterField,
122
+ validateField
123
+ })
124
+
125
+ defineExpose({
126
+ validate,
127
+ validateField,
128
+ resetFields,
129
+ clearValidate
130
+ })
131
+ </script>
132
+
133
+ <style scoped>
134
+ .datametria-form {
135
+ font-size: var(--dm-font-size-base, 14px);
136
+ }
137
+
138
+ .datametria-form--inline {
139
+ display: flex;
140
+ flex-wrap: wrap;
141
+ gap: 16px;
142
+ }
143
+
144
+ .datametria-form--label-left :deep(.datametria-form-item__label) {
145
+ text-align: left;
146
+ }
147
+
148
+ .datametria-form--label-right :deep(.datametria-form-item__label) {
149
+ text-align: right;
150
+ }
151
+
152
+ .datametria-form--label-top :deep(.datametria-form-item) {
153
+ flex-direction: column;
154
+ align-items: flex-start;
155
+ }
156
+
157
+ .datametria-form--label-top :deep(.datametria-form-item__label) {
158
+ margin-bottom: 8px;
159
+ }
160
+ </style>
@@ -0,0 +1,181 @@
1
+ <template>
2
+ <div
3
+ class="datametria-form-item"
4
+ :class="{
5
+ 'is-error': errorMessage,
6
+ 'is-required': isRequired
7
+ }"
8
+ >
9
+ <label
10
+ v-if="label"
11
+ class="datametria-form-item__label"
12
+ :style="labelStyle"
13
+ >
14
+ {{ label }}
15
+ </label>
16
+ <div class="datametria-form-item__content">
17
+ <slot></slot>
18
+ <transition name="fade">
19
+ <div v-if="errorMessage" class="datametria-form-item__error">
20
+ {{ errorMessage }}
21
+ </div>
22
+ </transition>
23
+ </div>
24
+ </div>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import { ref, computed, inject, onMounted, onBeforeUnmount, watch } from 'vue'
29
+ import { useValidation } from '../composables/useValidation'
30
+
31
+ interface Props {
32
+ prop?: string
33
+ label?: string
34
+ rules?: any[]
35
+ required?: boolean
36
+ }
37
+
38
+ const props = defineProps<Props>()
39
+
40
+ const form = inject<any>('datametriaForm', null)
41
+ const errorMessage = ref('')
42
+
43
+ const isRequired = computed(() => {
44
+ if (props.required) return true
45
+ if (!props.rules) return false
46
+ return props.rules.some((rule: any) => rule.required)
47
+ })
48
+
49
+ const labelStyle = computed(() => {
50
+ if (!form?.labelWidth) return {}
51
+ return { width: form.labelWidth }
52
+ })
53
+
54
+ const modelValue = computed(() => {
55
+ if (!form?.model || !props.prop) return undefined
56
+ return form.model[props.prop]
57
+ })
58
+
59
+ const validate = async (): Promise<boolean> => {
60
+ if (!props.prop || !props.rules) return true
61
+
62
+ const validation = useValidation()
63
+ const errors: string[] = []
64
+
65
+ for (const rule of props.rules) {
66
+ if (rule.required && !validation.required(modelValue.value)) {
67
+ errors.push(rule.message || 'Campo obrigatório')
68
+ break
69
+ }
70
+ }
71
+
72
+ if (errors.length > 0) {
73
+ errorMessage.value = errors[0]
74
+ return false
75
+ }
76
+
77
+ errorMessage.value = ''
78
+ return true
79
+ }
80
+
81
+ const reset = () => {
82
+ errorMessage.value = ''
83
+ if (form?.model && props.prop) {
84
+ form.model[props.prop] = undefined
85
+ }
86
+ }
87
+
88
+ const clearValidate = () => {
89
+ errorMessage.value = ''
90
+ }
91
+
92
+ watch(modelValue, () => {
93
+ if (errorMessage.value) {
94
+ validate()
95
+ }
96
+ })
97
+
98
+ onMounted(() => {
99
+ if (form && props.prop) {
100
+ form.registerField({
101
+ prop: props.prop,
102
+ validate,
103
+ reset,
104
+ clearValidate,
105
+ get errorMessage() {
106
+ return errorMessage.value
107
+ }
108
+ })
109
+ }
110
+ })
111
+
112
+ onBeforeUnmount(() => {
113
+ if (form && props.prop) {
114
+ form.unregisterField(props.prop)
115
+ }
116
+ })
117
+
118
+ defineExpose({
119
+ validate,
120
+ reset,
121
+ clearValidate,
122
+ errorMessage
123
+ })
124
+ </script>
125
+
126
+ <style scoped>
127
+ .datametria-form-item {
128
+ display: flex;
129
+ align-items: flex-start;
130
+ margin-bottom: 18px;
131
+ }
132
+
133
+ .datametria-form-item__label {
134
+ flex-shrink: 0;
135
+ padding-right: 12px;
136
+ line-height: 32px;
137
+ color: var(--dm-text-primary, #333);
138
+ font-size: var(--dm-font-size-base, 14px);
139
+ }
140
+
141
+ .datametria-form-item.is-required .datametria-form-item__label::before {
142
+ content: '*';
143
+ color: var(--dm-color-danger, #f56c6c);
144
+ margin-right: 4px;
145
+ }
146
+
147
+ .datametria-form-item__content {
148
+ flex: 1;
149
+ position: relative;
150
+ }
151
+
152
+ .datametria-form-item__error {
153
+ color: var(--dm-color-danger, #f56c6c);
154
+ font-size: 12px;
155
+ line-height: 1;
156
+ padding-top: 4px;
157
+ }
158
+
159
+ .datametria-form-item.is-error :deep(input),
160
+ .datametria-form-item.is-error :deep(textarea),
161
+ .datametria-form-item.is-error :deep(select) {
162
+ border-color: var(--dm-color-danger, #f56c6c);
163
+ }
164
+
165
+ .fade-enter-active,
166
+ .fade-leave-active {
167
+ transition: opacity 0.2s;
168
+ }
169
+
170
+ .fade-enter-from,
171
+ .fade-leave-to {
172
+ opacity: 0;
173
+ }
174
+
175
+ /* Dark mode */
176
+ @media (prefers-color-scheme: dark) {
177
+ .datametria-form-item__label {
178
+ color: var(--dm-text-primary-dark, #e0e0e0);
179
+ }
180
+ }
181
+ </style>