@datametria/vue-components 1.1.2 → 1.2.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 (32) hide show
  1. package/README.md +657 -472
  2. package/dist/index.es.js +1916 -675
  3. package/dist/index.umd.js +74 -1
  4. package/dist/vue-components.css +1 -1
  5. package/package.json +98 -98
  6. package/src/components/DatametriaAlert.vue +137 -123
  7. package/src/components/DatametriaBadge.vue +98 -90
  8. package/src/components/DatametriaButton.vue +165 -157
  9. package/src/components/DatametriaChip.vue +149 -149
  10. package/src/components/DatametriaMenu.vue +620 -0
  11. package/src/components/DatametriaNavbar.vue +252 -227
  12. package/src/components/DatametriaSkeleton.vue +240 -0
  13. package/src/components/DatametriaSlider.vue +408 -0
  14. package/src/components/DatametriaTimePicker.vue +286 -0
  15. package/src/components/DatametriaToast.vue +176 -163
  16. package/src/components/DatametriaTooltip.vue +409 -0
  17. package/src/components/__tests__/DatametriaAlert.test.js +36 -0
  18. package/src/components/__tests__/DatametriaBadge.test.js +30 -0
  19. package/src/components/__tests__/DatametriaButton.test.js +31 -0
  20. package/src/components/__tests__/DatametriaChip.test.js +39 -0
  21. package/src/components/__tests__/DatametriaNavbar.test.js +49 -0
  22. package/src/components/__tests__/DatametriaToast.test.js +49 -0
  23. package/src/composables/useAccessibilityScale.ts +95 -0
  24. package/src/composables/useBreakpoints.ts +83 -0
  25. package/src/composables/useHapticFeedback.ts +440 -0
  26. package/src/composables/useRipple.ts +219 -0
  27. package/src/index.ts +61 -52
  28. package/src/stories/Variants.stories.js +96 -0
  29. package/src/styles/design-tokens.css +623 -31
  30. package/ACCESSIBILITY.md +0 -78
  31. package/DESIGN-SYSTEM.md +0 -70
  32. package/PROGRESS.md +0 -327
@@ -1,157 +1,165 @@
1
- <template>
2
- <button
3
- :class="buttonClasses"
4
- :disabled="disabled || loading"
5
- :type="type"
6
- :aria-busy="loading"
7
- :aria-disabled="disabled"
8
- @click="$emit('click', $event)"
9
- >
10
- <span v-if="loading" class="spinner" role="status" aria-label="Carregando"></span>
11
- <slot />
12
- </button>
13
- </template>
14
-
15
- <script setup lang="ts">
16
- import { computed } from 'vue'
17
- import { ButtonVariant, ButtonSize } from '../types'
18
-
19
- interface Props {
20
- variant?: ButtonVariant
21
- size?: ButtonSize
22
- disabled?: boolean
23
- loading?: boolean
24
- fullWidth?: boolean
25
- type?: 'button' | 'submit' | 'reset'
26
- }
27
-
28
- const props = withDefaults(defineProps<Props>(), {
29
- variant: ButtonVariant.PRIMARY,
30
- size: ButtonSize.MD,
31
- disabled: false,
32
- loading: false,
33
- fullWidth: false,
34
- type: 'button'
35
- })
36
-
37
- defineEmits<{
38
- click: [event: MouseEvent]
39
- }>()
40
-
41
- const buttonClasses = computed(() => {
42
- return [
43
- 'datametria-button',
44
- `datametria-button--${props.variant}`,
45
- `datametria-button--${props.size}`,
46
- {
47
- 'datametria-button--full-width': props.fullWidth,
48
- 'datametria-button--loading': props.loading,
49
- 'datametria-button--disabled': props.disabled
50
- }
51
- ]
52
- })
53
- </script>
54
-
55
- <style scoped>
56
- .datametria-button {
57
- display: inline-flex;
58
- align-items: center;
59
- justify-content: center;
60
- font-weight: 500;
61
- border-radius: var(--dm-radius);
62
- transition: var(--dm-transition);
63
- cursor: pointer;
64
- border: 1px solid transparent;
65
- font-family: var(--dm-font-sans, -apple-system, sans-serif);
66
- touch-action: manipulation;
67
- -webkit-tap-highlight-color: transparent;
68
- }
69
-
70
- .datametria-button--primary {
71
- background: var(--dm-primary);
72
- color: white;
73
- }
74
-
75
- .datametria-button--primary:hover:not(:disabled) {
76
- background: #005ba3;
77
- }
78
-
79
- .datametria-button--primary:focus-visible {
80
- outline: none;
81
- box-shadow: var(--dm-focus-ring);
82
- }
83
-
84
- .datametria-button--secondary {
85
- background: var(--dm-secondary);
86
- color: white;
87
- }
88
-
89
- .datametria-button--secondary:focus-visible {
90
- outline: none;
91
- box-shadow: 0 0 0 3px rgba(75, 0, 120, 0.1);
92
- }
93
-
94
- .datametria-button--outline {
95
- background: transparent;
96
- border-color: var(--dm-primary);
97
- color: var(--dm-primary);
98
- }
99
-
100
- .datametria-button--outline:focus-visible {
101
- outline: none;
102
- box-shadow: var(--dm-focus-ring);
103
- }
104
-
105
- .datametria-button--ghost {
106
- background: transparent;
107
- color: #0072CE;
108
- }
109
-
110
- .datametria-button--sm {
111
- padding: var(--dm-space-2) var(--dm-space-4);
112
- font-size: var(--dm-text-sm);
113
- min-height: 2rem;
114
- }
115
-
116
- .datametria-button--md {
117
- padding: var(--dm-space-3) calc(var(--dm-space-4) * 1.5);
118
- font-size: var(--dm-text-base);
119
- min-height: 2.5rem;
120
- }
121
-
122
- .datametria-button--lg {
123
- padding: var(--dm-space-4) calc(var(--dm-space-4) * 2);
124
- font-size: var(--dm-text-lg);
125
- min-height: 3rem;
126
- }
127
-
128
- @media (max-width: 640px) {
129
- .datametria-button--sm { min-height: 2.25rem; }
130
- .datametria-button--md { min-height: 2.75rem; }
131
- .datametria-button--lg { min-height: 3.25rem; }
132
- }
133
-
134
- .datametria-button--full-width {
135
- width: 100%;
136
- }
137
-
138
- .datametria-button--disabled,
139
- .datametria-button:disabled {
140
- opacity: 0.5;
141
- cursor: not-allowed;
142
- }
143
-
144
- .spinner {
145
- width: 1rem;
146
- height: 1rem;
147
- border: 2px solid currentColor;
148
- border-top-color: transparent;
149
- border-radius: 50%;
150
- animation: spin 0.6s linear infinite;
151
- margin-right: 0.5rem;
152
- }
153
-
154
- @keyframes spin {
155
- to { transform: rotate(360deg); }
156
- }
157
- </style>
1
+ <template>
2
+ <button
3
+ :class="buttonClasses"
4
+ :disabled="disabled || loading"
5
+ :type="type"
6
+ :aria-busy="loading"
7
+ :aria-disabled="disabled"
8
+ @click="$emit('click', $event)"
9
+ >
10
+ <span v-if="loading" class="spinner" role="status" aria-label="Carregando"></span>
11
+ <slot />
12
+ </button>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ import { computed } from 'vue'
17
+ import { ButtonVariant, ButtonSize } from '../types'
18
+
19
+ interface Props {
20
+ variant?: ButtonVariant
21
+ size?: ButtonSize
22
+ disabled?: boolean
23
+ loading?: boolean
24
+ fullWidth?: boolean
25
+ type?: 'button' | 'submit' | 'reset'
26
+ }
27
+
28
+ const props = withDefaults(defineProps<Props>(), {
29
+ variant: ButtonVariant.PRIMARY,
30
+ size: ButtonSize.MD,
31
+ disabled: false,
32
+ loading: false,
33
+ fullWidth: false,
34
+ type: 'button'
35
+ })
36
+
37
+ // Validação em desenvolvimento
38
+ if (process.env.NODE_ENV === 'development') {
39
+ const validVariants = Object.values(ButtonVariant)
40
+ if (!validVariants.includes(props.variant)) {
41
+ console.warn(`[DatametriaButton] Invalid variant "${props.variant}". Valid options: ${validVariants.join(', ')}`)
42
+ }
43
+ }
44
+
45
+ defineEmits<{
46
+ click: [event: MouseEvent]
47
+ }>()
48
+
49
+ const buttonClasses = computed(() => {
50
+ return [
51
+ 'datametria-button',
52
+ `datametria-button--${props.variant}`,
53
+ `datametria-button--${props.size}`,
54
+ {
55
+ 'datametria-button--full-width': props.fullWidth,
56
+ 'datametria-button--loading': props.loading,
57
+ 'datametria-button--disabled': props.disabled
58
+ }
59
+ ]
60
+ })
61
+ </script>
62
+
63
+ <style scoped>
64
+ .datametria-button {
65
+ display: inline-flex;
66
+ align-items: center;
67
+ justify-content: center;
68
+ font-weight: 500;
69
+ border-radius: var(--dm-radius);
70
+ transition: var(--dm-transition);
71
+ cursor: pointer;
72
+ border: 1px solid transparent;
73
+ font-family: var(--dm-font-sans, -apple-system, sans-serif);
74
+ touch-action: manipulation;
75
+ -webkit-tap-highlight-color: transparent;
76
+ }
77
+
78
+ .datametria-button--primary {
79
+ background: var(--dm-primary);
80
+ color: white;
81
+ }
82
+
83
+ .datametria-button--primary:hover:not(:disabled) {
84
+ background: #005ba3;
85
+ }
86
+
87
+ .datametria-button--primary:focus-visible {
88
+ outline: none;
89
+ box-shadow: var(--dm-focus-ring);
90
+ }
91
+
92
+ .datametria-button--secondary {
93
+ background: var(--dm-secondary);
94
+ color: white;
95
+ }
96
+
97
+ .datametria-button--secondary:focus-visible {
98
+ outline: none;
99
+ box-shadow: 0 0 0 3px rgba(75, 0, 120, 0.1);
100
+ }
101
+
102
+ .datametria-button--outline {
103
+ background: transparent;
104
+ border-color: var(--dm-primary);
105
+ color: var(--dm-primary);
106
+ }
107
+
108
+ .datametria-button--outline:focus-visible {
109
+ outline: none;
110
+ box-shadow: var(--dm-focus-ring);
111
+ }
112
+
113
+ .datametria-button--ghost {
114
+ background: transparent;
115
+ color: #0072CE;
116
+ }
117
+
118
+ .datametria-button--sm {
119
+ padding: var(--dm-space-2) var(--dm-space-4);
120
+ font-size: var(--dm-text-sm);
121
+ min-height: 2rem;
122
+ }
123
+
124
+ .datametria-button--md {
125
+ padding: var(--dm-space-3) calc(var(--dm-space-4) * 1.5);
126
+ font-size: var(--dm-text-base);
127
+ min-height: 2.5rem;
128
+ }
129
+
130
+ .datametria-button--lg {
131
+ padding: var(--dm-space-4) calc(var(--dm-space-4) * 2);
132
+ font-size: var(--dm-text-lg);
133
+ min-height: 3rem;
134
+ }
135
+
136
+ @media (max-width: 640px) {
137
+ .datametria-button--sm { min-height: 2.25rem; }
138
+ .datametria-button--md { min-height: 2.75rem; }
139
+ .datametria-button--lg { min-height: 3.25rem; }
140
+ }
141
+
142
+ .datametria-button--full-width {
143
+ width: 100%;
144
+ }
145
+
146
+ .datametria-button--disabled,
147
+ .datametria-button:disabled {
148
+ opacity: 0.5;
149
+ cursor: not-allowed;
150
+ }
151
+
152
+ .spinner {
153
+ width: 1rem;
154
+ height: 1rem;
155
+ border: 2px solid currentColor;
156
+ border-top-color: transparent;
157
+ border-radius: 50%;
158
+ animation: spin 0.6s linear infinite;
159
+ margin-right: 0.5rem;
160
+ }
161
+
162
+ @keyframes spin {
163
+ to { transform: rotate(360deg); }
164
+ }
165
+ </style>
@@ -1,149 +1,149 @@
1
- <template>
2
- <div
3
- class="dm-chip"
4
- :class="[`dm-chip--${variant}`, { 'dm-chip--clickable': clickable }]"
5
- :role="clickable ? 'button' : undefined"
6
- :tabindex="clickable ? 0 : undefined"
7
- @click="handleClick"
8
- @keydown.enter="handleClick"
9
- @keydown.space.prevent="handleClick"
10
- >
11
- <span v-if="$slots.icon" class="dm-chip__icon">
12
- <slot name="icon"></slot>
13
- </span>
14
- <span class="dm-chip__label">
15
- <slot>{{ label }}</slot>
16
- </span>
17
- <button
18
- v-if="closable"
19
- class="dm-chip__close"
20
- @click.stop="handleClose"
21
- aria-label="Remover"
22
- type="button"
23
- >×</button>
24
- </div>
25
- </template>
26
-
27
- <script setup lang="ts">
28
- interface Props {
29
- label?: string
30
- variant?: 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'default'
31
- closable?: boolean
32
- clickable?: boolean
33
- }
34
-
35
- withDefaults(defineProps<Props>(), {
36
- variant: 'default',
37
- closable: false,
38
- clickable: false
39
- })
40
-
41
- const emit = defineEmits<{
42
- click: []
43
- close: []
44
- }>()
45
-
46
- const handleClick = () => {
47
- emit('click')
48
- }
49
-
50
- const handleClose = () => {
51
- emit('close')
52
- }
53
- </script>
54
-
55
- <style scoped>
56
- .dm-chip {
57
- display: inline-flex;
58
- align-items: center;
59
- gap: var(--dm-space-2);
60
- padding: var(--dm-space-2) var(--dm-space-3);
61
- border-radius: 16px;
62
- font-size: var(--dm-text-sm);
63
- font-weight: 500;
64
- user-select: none;
65
- transition: var(--dm-transition);
66
- }
67
-
68
- .dm-chip--clickable {
69
- cursor: pointer;
70
- }
71
-
72
- .dm-chip--clickable:hover {
73
- opacity: 0.8;
74
- }
75
-
76
- .dm-chip--clickable:focus-visible {
77
- outline: var(--dm-focus-ring);
78
- outline-offset: 2px;
79
- }
80
-
81
- .dm-chip--default {
82
- background: var(--dm-gray-200);
83
- color: var(--dm-gray-900);
84
- }
85
-
86
- .dm-chip--primary {
87
- background: var(--dm-primary-light, rgba(0, 114, 206, 0.1));
88
- color: var(--dm-primary);
89
- }
90
-
91
- .dm-chip--secondary {
92
- background: var(--dm-secondary-light, rgba(75, 0, 120, 0.1));
93
- color: var(--dm-secondary);
94
- }
95
-
96
- .dm-chip--success {
97
- background: var(--dm-success-light, rgba(34, 197, 94, 0.1));
98
- color: var(--dm-success);
99
- }
100
-
101
- .dm-chip--warning {
102
- background: var(--dm-warning-light, rgba(251, 191, 36, 0.2));
103
- color: var(--dm-warning-dark, #92400e);
104
- }
105
-
106
- .dm-chip--error {
107
- background: var(--dm-error-light, rgba(239, 68, 68, 0.1));
108
- color: var(--dm-error);
109
- }
110
-
111
- .dm-chip__icon {
112
- display: flex;
113
- align-items: center;
114
- font-size: 16px;
115
- }
116
-
117
- .dm-chip__label {
118
- line-height: 1.5;
119
- }
120
-
121
- .dm-chip__close {
122
- display: flex;
123
- align-items: center;
124
- justify-content: center;
125
- width: 20px;
126
- height: 20px;
127
- border: none;
128
- background: transparent;
129
- color: inherit;
130
- font-size: 20px;
131
- line-height: 1;
132
- cursor: pointer;
133
- opacity: 0.6;
134
- transition: var(--dm-transition);
135
- padding: 0;
136
- margin: 0;
137
- }
138
-
139
- .dm-chip__close:hover {
140
- opacity: 1;
141
- }
142
-
143
- @media (prefers-color-scheme: dark) {
144
- .dm-chip--default {
145
- background: var(--dm-gray-700);
146
- color: var(--dm-white);
147
- }
148
- }
149
- </style>
1
+ <template>
2
+ <div
3
+ class="dm-chip"
4
+ :class="[`dm-chip--${variant}`, { 'dm-chip--clickable': clickable }]"
5
+ :role="clickable ? 'button' : undefined"
6
+ :tabindex="clickable ? 0 : undefined"
7
+ @click="handleClick"
8
+ @keydown.enter="handleClick"
9
+ @keydown.space.prevent="handleClick"
10
+ >
11
+ <span v-if="$slots.icon" class="dm-chip__icon">
12
+ <slot name="icon"></slot>
13
+ </span>
14
+ <span class="dm-chip__label">
15
+ <slot>{{ label }}</slot>
16
+ </span>
17
+ <button
18
+ v-if="closable"
19
+ class="dm-chip__close"
20
+ @click.stop="handleClose"
21
+ aria-label="Remover"
22
+ type="button"
23
+ >×</button>
24
+ </div>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ interface Props {
29
+ label?: string
30
+ variant?: 'primary' | 'secondary' | 'success' | 'warning' | 'error'
31
+ closable?: boolean
32
+ clickable?: boolean
33
+ }
34
+
35
+ const props = withDefaults(defineProps<Props>(), {
36
+ variant: 'primary',
37
+ closable: false,
38
+ clickable: false
39
+ })
40
+
41
+ // Validação em desenvolvimento
42
+ if (process.env.NODE_ENV === 'development') {
43
+ const validVariants = ['primary', 'secondary', 'success', 'warning', 'error']
44
+ if (!validVariants.includes(props.variant)) {
45
+ console.warn(`[DatametriaChip] Invalid variant "${props.variant}". Valid options: ${validVariants.join(', ')}`)
46
+ }
47
+ }
48
+
49
+ const emit = defineEmits<{
50
+ click: []
51
+ close: []
52
+ }>()
53
+
54
+ const handleClick = () => {
55
+ emit('click')
56
+ }
57
+
58
+ const handleClose = () => {
59
+ emit('close')
60
+ }
61
+ </script>
62
+
63
+ <style scoped>
64
+ .dm-chip {
65
+ display: inline-flex;
66
+ align-items: center;
67
+ gap: var(--dm-space-2);
68
+ padding: var(--dm-space-2) var(--dm-space-3);
69
+ border-radius: 16px;
70
+ font-size: var(--dm-text-sm);
71
+ font-weight: 500;
72
+ user-select: none;
73
+ transition: var(--dm-transition);
74
+ }
75
+
76
+ .dm-chip--clickable {
77
+ cursor: pointer;
78
+ }
79
+
80
+ .dm-chip--clickable:hover {
81
+ opacity: 0.8;
82
+ }
83
+
84
+ .dm-chip--clickable:focus-visible {
85
+ outline: var(--dm-focus-ring);
86
+ outline-offset: 2px;
87
+ }
88
+
89
+
90
+
91
+ .dm-chip--primary {
92
+ background: var(--dm-primary-light, rgba(0, 114, 206, 0.1));
93
+ color: var(--dm-primary);
94
+ }
95
+
96
+ .dm-chip--secondary {
97
+ background: var(--dm-secondary-light, rgba(75, 0, 120, 0.1));
98
+ color: var(--dm-secondary);
99
+ }
100
+
101
+ .dm-chip--success {
102
+ background: var(--dm-success-light, rgba(34, 197, 94, 0.1));
103
+ color: var(--dm-success);
104
+ }
105
+
106
+ .dm-chip--warning {
107
+ background: var(--dm-warning-light, rgba(251, 191, 36, 0.2));
108
+ color: var(--dm-warning-dark, #92400e);
109
+ }
110
+
111
+ .dm-chip--error {
112
+ background: var(--dm-error-light, rgba(239, 68, 68, 0.1));
113
+ color: var(--dm-error);
114
+ }
115
+
116
+ .dm-chip__icon {
117
+ display: flex;
118
+ align-items: center;
119
+ font-size: 16px;
120
+ }
121
+
122
+ .dm-chip__label {
123
+ line-height: 1.5;
124
+ }
125
+
126
+ .dm-chip__close {
127
+ display: flex;
128
+ align-items: center;
129
+ justify-content: center;
130
+ width: 20px;
131
+ height: 20px;
132
+ border: none;
133
+ background: transparent;
134
+ color: inherit;
135
+ font-size: 20px;
136
+ line-height: 1;
137
+ cursor: pointer;
138
+ opacity: 0.6;
139
+ transition: var(--dm-transition);
140
+ padding: 0;
141
+ margin: 0;
142
+ }
143
+
144
+ .dm-chip__close:hover {
145
+ opacity: 1;
146
+ }
147
+
148
+
149
+ </style>