ads-nuxt 0.1.0 → 0.1.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 (74) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +567 -1
  3. package/app/app.config.ts +25 -25
  4. package/app/assets/ads/checkbox-check.svg +5 -5
  5. package/app/assets/ads/checkbox-minus.svg +5 -5
  6. package/app/assets/brand/farmarcas.svg +6 -6
  7. package/app/assets/brand/radar.svg +9 -9
  8. package/app/assets/css/component-tokens.css +403 -403
  9. package/app/assets/css/main.css +617 -617
  10. package/app/components/AdsBadge.vue +48 -48
  11. package/app/components/AdsBroadcast.vue +45 -45
  12. package/app/components/AdsButton.vue +569 -569
  13. package/app/components/AdsButtonGroup.vue +39 -39
  14. package/app/components/AdsButtonMenu.vue +48 -48
  15. package/app/components/AdsCard.vue +117 -117
  16. package/app/components/AdsCharttip.vue +78 -78
  17. package/app/components/AdsCheckbox.vue +77 -77
  18. package/app/components/AdsDatePicker.vue +473 -473
  19. package/app/components/AdsDropdownMenu.vue +19 -19
  20. package/app/components/AdsEmptyState.vue +58 -58
  21. package/app/components/AdsFarmarcasPill.vue +74 -74
  22. package/app/components/AdsFeatureCard.vue +217 -217
  23. package/app/components/AdsFileCard.vue +101 -101
  24. package/app/components/AdsFilterDropdown.vue +174 -174
  25. package/app/components/AdsFilterOption.vue +72 -72
  26. package/app/components/AdsFilterSelect.vue +202 -202
  27. package/app/components/AdsFooter.vue +25 -25
  28. package/app/components/AdsInput.vue +143 -143
  29. package/app/components/AdsListDropdown.vue +44 -44
  30. package/app/components/AdsLoading.vue +109 -109
  31. package/app/components/AdsMenuBar.vue +103 -103
  32. package/app/components/AdsMetricCard.vue +119 -119
  33. package/app/components/AdsMiniPill.vue +40 -40
  34. package/app/components/AdsModal.vue +157 -157
  35. package/app/components/AdsMonthPicker.vue +80 -80
  36. package/app/components/AdsNavbarApp.vue +67 -67
  37. package/app/components/AdsNavbarDesk.vue +62 -62
  38. package/app/components/AdsNotabilityPill.vue +27 -27
  39. package/app/components/AdsNumberInput.vue +166 -166
  40. package/app/components/AdsPagination.vue +91 -91
  41. package/app/components/AdsProductTag.vue +61 -61
  42. package/app/components/AdsRadio.vue +70 -70
  43. package/app/components/AdsSearchInput.vue +113 -113
  44. package/app/components/AdsSegmentedTabs.vue +28 -28
  45. package/app/components/AdsSideMenu.vue +101 -101
  46. package/app/components/AdsSmalltip.vue +44 -44
  47. package/app/components/AdsSteptip.vue +101 -101
  48. package/app/components/AdsSwitch.vue +59 -59
  49. package/app/components/AdsTable.vue +100 -100
  50. package/app/components/AdsTableToolbar.vue +44 -44
  51. package/app/components/AdsTabs.vue +51 -51
  52. package/app/components/AdsTextarea.vue +109 -109
  53. package/app/components/AdsToast.vue +181 -181
  54. package/app/components/AdsTooltip.vue +66 -66
  55. package/app/components/InvertedTooltip.vue +40 -40
  56. package/app/components/OverlayLoading.vue +173 -173
  57. package/app/components/ProgressBarLoading.vue +256 -256
  58. package/app/composables/useOverlayLoading.ts +66 -66
  59. package/app/locale/pt-br.ts +6 -6
  60. package/app/plugins/ads-toast.client.ts +24 -24
  61. package/app/theme/ads-theme.ts +370 -370
  62. package/app/theme/component-tokens.json +402 -402
  63. package/app/theme/component-tokens.ts +815 -815
  64. package/app/types/overlay-loading.types.ts +26 -26
  65. package/app/utils/ads-filter-select.ts +172 -172
  66. package/app/utils/ads-table.ts +204 -204
  67. package/app/utils/calendar-date.ts +128 -128
  68. package/app/utils/category-role.ts +45 -45
  69. package/app/utils/money.ts +77 -77
  70. package/app/utils/notability.ts +8 -8
  71. package/app/utils/parser.ts +134 -134
  72. package/app/utils/percentage.ts +54 -54
  73. package/nuxt.config.ts +31 -32
  74. package/package.json +48 -46
@@ -1,91 +1,91 @@
1
- <template>
2
- <nav class="inline-flex items-center gap-[var(--ads-pagination-gap)] py-1" :aria-label="ariaLabel">
3
- <button
4
- type="button"
5
- class="py-0.5 text-sm leading-[1.3] text-[var(--ads-text-secondary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ads-text-brand)] disabled:cursor-not-allowed disabled:text-[var(--ads-text-tertiary)]"
6
- :disabled="page <= 1"
7
- @click="go(page - 1)"
8
- >
9
- {{ prevLabel }}
10
- </button>
11
- <div class="flex items-center gap-[var(--ads-pagination-number-gap)]">
12
- <template v-for="item in items" :key="item.key">
13
- <span v-if="item.kind === 'gap'" class="px-1 text-sm text-ads-neutro-escuro-600" aria-hidden="true">…</span>
14
- <button
15
- v-else
16
- type="button"
17
- class="flex size-[var(--ads-pagination-cell)] items-center justify-center rounded-[var(--ads-radius-xsm)] text-sm leading-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ads-text-brand)]"
18
- :class="item.page === page ? 'bg-[var(--ads-pagination-selected)] text-[var(--ads-text-contrast)]' : 'text-[var(--ads-pagination-number)] hover:bg-ads-neutro-claro-20'"
19
- :aria-current="item.page === page ? 'page' : undefined"
20
- @click="go(item.page)"
21
- >
22
- {{ item.page }}
23
- </button>
24
- </template>
25
- </div>
26
- <button
27
- type="button"
28
- class="py-0.5 text-sm leading-[1.3] text-[var(--ads-text-secondary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ads-text-brand)] disabled:cursor-not-allowed disabled:text-[var(--ads-text-tertiary)]"
29
- :disabled="page >= totalPages"
30
- @click="go(page + 1)"
31
- >
32
- {{ nextLabel }}
33
- </button>
34
- </nav>
35
- </template>
36
-
37
- <script setup lang="ts">
38
- import { computed } from "vue";
39
-
40
- const props = withDefaults(
41
- defineProps<{
42
- page?: number;
43
- totalPages?: number;
44
- prevLabel?: string;
45
- nextLabel?: string;
46
- ariaLabel?: string;
47
- }>(),
48
- {
49
- page: 1,
50
- totalPages: 1,
51
- prevLabel: "Anterior",
52
- nextLabel: "Próximo",
53
- ariaLabel: "Paginação",
54
- },
55
- );
56
-
57
- const emit = defineEmits<{
58
- "update:page": [page: number];
59
- }>();
60
-
61
- type PageItem = { kind: "page"; page: number; key: string } | { kind: "gap"; key: string };
62
-
63
- const items = computed<PageItem[]>(() => {
64
- const total = Math.max(1, props.totalPages);
65
- const current = Math.min(Math.max(1, props.page), total);
66
- if (total <= 7) {
67
- return Array.from({ length: total }, (_, index) => ({
68
- kind: "page" as const,
69
- page: index + 1,
70
- key: `p${index + 1}`,
71
- }));
72
- }
73
- const pages = new Set([1, total, current - 1, current, current + 1]);
74
- const sorted = [...pages].filter((value) => value >= 1 && value <= total).sort((a, b) => a - b);
75
- const result: PageItem[] = [];
76
- sorted.forEach((value, index) => {
77
- const previous = sorted[index - 1];
78
- if (previous !== undefined && value - previous > 1) {
79
- result.push({ kind: "gap", key: `g${previous}` });
80
- }
81
- result.push({ kind: "page", page: value, key: `p${value}` });
82
- });
83
- return result;
84
- });
85
-
86
- const go = (next: number) => {
87
- const total = Math.max(1, props.totalPages);
88
- const value = Math.min(Math.max(1, next), total);
89
- if (value !== props.page) emit("update:page", value);
90
- };
91
- </script>
1
+ <template>
2
+ <nav class="inline-flex items-center gap-[var(--ads-pagination-gap)] py-1" :aria-label="ariaLabel">
3
+ <button
4
+ type="button"
5
+ class="py-0.5 text-sm leading-[1.3] text-[var(--ads-text-secondary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ads-text-brand)] disabled:cursor-not-allowed disabled:text-[var(--ads-text-tertiary)]"
6
+ :disabled="page <= 1"
7
+ @click="go(page - 1)"
8
+ >
9
+ {{ prevLabel }}
10
+ </button>
11
+ <div class="flex items-center gap-[var(--ads-pagination-number-gap)]">
12
+ <template v-for="item in items" :key="item.key">
13
+ <span v-if="item.kind === 'gap'" class="px-1 text-sm text-ads-neutro-escuro-600" aria-hidden="true">…</span>
14
+ <button
15
+ v-else
16
+ type="button"
17
+ class="flex size-[var(--ads-pagination-cell)] items-center justify-center rounded-[var(--ads-radius-xsm)] text-sm leading-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ads-text-brand)]"
18
+ :class="item.page === page ? 'bg-[var(--ads-pagination-selected)] text-[var(--ads-text-contrast)]' : 'text-[var(--ads-pagination-number)] hover:bg-ads-neutro-claro-20'"
19
+ :aria-current="item.page === page ? 'page' : undefined"
20
+ @click="go(item.page)"
21
+ >
22
+ {{ item.page }}
23
+ </button>
24
+ </template>
25
+ </div>
26
+ <button
27
+ type="button"
28
+ class="py-0.5 text-sm leading-[1.3] text-[var(--ads-text-secondary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ads-text-brand)] disabled:cursor-not-allowed disabled:text-[var(--ads-text-tertiary)]"
29
+ :disabled="page >= totalPages"
30
+ @click="go(page + 1)"
31
+ >
32
+ {{ nextLabel }}
33
+ </button>
34
+ </nav>
35
+ </template>
36
+
37
+ <script setup lang="ts">
38
+ import { computed } from "vue";
39
+
40
+ const props = withDefaults(
41
+ defineProps<{
42
+ page?: number;
43
+ totalPages?: number;
44
+ prevLabel?: string;
45
+ nextLabel?: string;
46
+ ariaLabel?: string;
47
+ }>(),
48
+ {
49
+ page: 1,
50
+ totalPages: 1,
51
+ prevLabel: "Anterior",
52
+ nextLabel: "Próximo",
53
+ ariaLabel: "Paginação",
54
+ },
55
+ );
56
+
57
+ const emit = defineEmits<{
58
+ "update:page": [page: number];
59
+ }>();
60
+
61
+ type PageItem = { kind: "page"; page: number; key: string } | { kind: "gap"; key: string };
62
+
63
+ const items = computed<PageItem[]>(() => {
64
+ const total = Math.max(1, props.totalPages);
65
+ const current = Math.min(Math.max(1, props.page), total);
66
+ if (total <= 7) {
67
+ return Array.from({ length: total }, (_, index) => ({
68
+ kind: "page" as const,
69
+ page: index + 1,
70
+ key: `p${index + 1}`,
71
+ }));
72
+ }
73
+ const pages = new Set([1, total, current - 1, current, current + 1]);
74
+ const sorted = [...pages].filter((value) => value >= 1 && value <= total).sort((a, b) => a - b);
75
+ const result: PageItem[] = [];
76
+ sorted.forEach((value, index) => {
77
+ const previous = sorted[index - 1];
78
+ if (previous !== undefined && value - previous > 1) {
79
+ result.push({ kind: "gap", key: `g${previous}` });
80
+ }
81
+ result.push({ kind: "page", page: value, key: `p${value}` });
82
+ });
83
+ return result;
84
+ });
85
+
86
+ const go = (next: number) => {
87
+ const total = Math.max(1, props.totalPages);
88
+ const value = Math.min(Math.max(1, next), total);
89
+ if (value !== props.page) emit("update:page", value);
90
+ };
91
+ </script>
@@ -1,61 +1,61 @@
1
- <template>
2
- <span
3
- class="inline-flex h-[var(--ads-product-tag-height)] items-center rounded-[var(--ads-radius-xsm)] px-1.5 text-[length:var(--ads-product-tag-font-size)] leading-none font-medium text-[var(--ads-text-contrast)]"
4
- :class="surface"
5
- >
6
- {{ label || product }}
7
- </span>
8
- </template>
9
-
10
- <script setup lang="ts">
11
- import { computed } from "vue";
12
-
13
- /** Mapeia `_Tags` do Figma. O fundo usa a cor de vertical/produto já publicada. */
14
- export type AdsProductTagName =
15
- | "marketing"
16
- | "promocoes"
17
- | "vendas"
18
- | "captador"
19
- | "estoque"
20
- | "pricing"
21
- | "crm"
22
- | "compras"
23
- | "gestao"
24
- | "trade"
25
- | "capacitacao"
26
- | "ecommerce"
27
- | "financas"
28
- | "performance"
29
- | "encontro";
30
-
31
- const props = withDefaults(
32
- defineProps<{
33
- product?: AdsProductTagName;
34
- label?: string;
35
- }>(),
36
- {
37
- product: "performance",
38
- label: "",
39
- },
40
- );
41
-
42
- const surfaces: Record<AdsProductTagName, string> = {
43
- marketing: "bg-ads-produtos-radar-3",
44
- promocoes: "bg-ads-produtos-radar-3",
45
- vendas: "bg-ads-produtos-radar-7",
46
- captador: "bg-ads-produtos-radar-10",
47
- estoque: "bg-ads-produtos-radar-11",
48
- pricing: "bg-ads-produtos-radar-7",
49
- crm: "bg-ads-innovation-300",
50
- compras: "bg-ads-produtos-radar-2",
51
- gestao: "bg-ads-produtos-radar-1",
52
- trade: "bg-ads-produtos-radar-2",
53
- capacitacao: "bg-ads-produtos-radar-4",
54
- ecommerce: "bg-ads-produtos-radar-9",
55
- financas: "bg-ads-produtos-radar-6",
56
- performance: "bg-ads-produtos-radar-6",
57
- encontro: "bg-ads-produtos-radar-5",
58
- };
59
-
60
- const surface = computed(() => surfaces[props.product]);
61
- </script>
1
+ <template>
2
+ <span
3
+ class="inline-flex h-[var(--ads-product-tag-height)] items-center rounded-[var(--ads-radius-xsm)] px-1.5 text-[length:var(--ads-product-tag-font-size)] leading-none font-medium text-[var(--ads-text-contrast)]"
4
+ :class="surface"
5
+ >
6
+ {{ label || product }}
7
+ </span>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { computed } from "vue";
12
+
13
+ /** Mapeia `_Tags` do Figma. O fundo usa a cor de vertical/produto já publicada. */
14
+ export type AdsProductTagName =
15
+ | "marketing"
16
+ | "promocoes"
17
+ | "vendas"
18
+ | "captador"
19
+ | "estoque"
20
+ | "pricing"
21
+ | "crm"
22
+ | "compras"
23
+ | "gestao"
24
+ | "trade"
25
+ | "capacitacao"
26
+ | "ecommerce"
27
+ | "financas"
28
+ | "performance"
29
+ | "encontro";
30
+
31
+ const props = withDefaults(
32
+ defineProps<{
33
+ product?: AdsProductTagName;
34
+ label?: string;
35
+ }>(),
36
+ {
37
+ product: "performance",
38
+ label: "",
39
+ },
40
+ );
41
+
42
+ const surfaces: Record<AdsProductTagName, string> = {
43
+ marketing: "bg-ads-produtos-radar-3",
44
+ promocoes: "bg-ads-produtos-radar-3",
45
+ vendas: "bg-ads-produtos-radar-7",
46
+ captador: "bg-ads-produtos-radar-10",
47
+ estoque: "bg-ads-produtos-radar-11",
48
+ pricing: "bg-ads-produtos-radar-7",
49
+ crm: "bg-ads-innovation-300",
50
+ compras: "bg-ads-produtos-radar-2",
51
+ gestao: "bg-ads-produtos-radar-1",
52
+ trade: "bg-ads-produtos-radar-2",
53
+ capacitacao: "bg-ads-produtos-radar-4",
54
+ ecommerce: "bg-ads-produtos-radar-9",
55
+ financas: "bg-ads-produtos-radar-6",
56
+ performance: "bg-ads-produtos-radar-6",
57
+ encontro: "bg-ads-produtos-radar-5",
58
+ };
59
+
60
+ const surface = computed(() => surfaces[props.product]);
61
+ </script>
@@ -1,70 +1,70 @@
1
- <template>
2
- <label
3
- class="inline-flex items-center gap-[var(--ads-radio-gap)]"
4
- :class="isDisabled ? 'cursor-not-allowed' : 'cursor-pointer'"
5
- >
6
- <span class="relative grid size-[var(--ads-radio-size)] shrink-0 place-items-center">
7
- <input
8
- v-bind="attrs"
9
- type="radio"
10
- class="peer absolute inset-0 m-0 size-full appearance-none opacity-0 disabled:cursor-not-allowed"
11
- :class="isDisabled ? 'cursor-not-allowed' : 'cursor-pointer'"
12
- :name="fieldName"
13
- :value="value"
14
- :checked="checked"
15
- :disabled="isDisabled"
16
- @change="onChange"
17
- >
18
- <span
19
- class="pointer-events-none col-start-1 row-start-1 size-[var(--ads-radio-size)] rounded-full border-[length:var(--ads-radio-border-width)] bg-[var(--ads-radio-background)] peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-[var(--ads-text-brand)]"
20
- :class="isDisabled
21
- ? 'border-[var(--ads-radio-disabled)] peer-checked:border-[var(--ads-radio-disabled)] peer-checked:bg-[var(--ads-radio-disabled)]'
22
- : 'border-[var(--ads-radio-border)] peer-checked:border-[var(--ads-radio-border)] peer-checked:bg-[var(--ads-radio-border)]'"
23
- />
24
- <span class="pointer-events-none col-start-1 row-start-1 size-[var(--ads-radio-dot)] rounded-full bg-[var(--ads-radio-dot-color)] opacity-0 peer-checked:opacity-100" />
25
- </span>
26
- <span
27
- v-if="hasLabel"
28
- class="text-[length:var(--ads-radio-label-size)] font-normal leading-[1.3]"
29
- :class="isDisabled ? 'text-[var(--ads-text-tertiary)]' : 'text-[var(--ads-radio-label)]'"
30
- >
31
- <slot>{{ label }}</slot>
32
- </span>
33
- </label>
34
- </template>
35
-
36
- <script setup lang="ts">
37
- import { computed, useAttrs, useId, useSlots } from "vue";
38
-
39
- defineOptions({ inheritAttrs: false });
40
-
41
- const props = withDefaults(
42
- defineProps<{
43
- label?: string;
44
- value?: string | number | boolean;
45
- name?: string;
46
- disabled?: boolean;
47
- }>(),
48
- {
49
- label: "",
50
- value: "on",
51
- name: "",
52
- disabled: false,
53
- },
54
- );
55
-
56
- const model = defineModel<string | number | boolean>({ default: "" });
57
- const attrs = useAttrs();
58
- const slots = useSlots();
59
- const autoName = useId();
60
-
61
- const hasLabel = computed(() => props.label.length > 0 || Boolean(slots.default));
62
- const isDisabled = computed(() => props.disabled);
63
- const fieldName = computed(() => props.name || autoName);
64
- const checked = computed(() => model.value === props.value);
65
-
66
- const onChange = () => {
67
- if (isDisabled.value) return;
68
- model.value = props.value;
69
- };
70
- </script>
1
+ <template>
2
+ <label
3
+ class="inline-flex items-center gap-[var(--ads-radio-gap)]"
4
+ :class="isDisabled ? 'cursor-not-allowed' : 'cursor-pointer'"
5
+ >
6
+ <span class="relative grid size-[var(--ads-radio-size)] shrink-0 place-items-center">
7
+ <input
8
+ v-bind="attrs"
9
+ type="radio"
10
+ class="peer absolute inset-0 m-0 size-full appearance-none opacity-0 disabled:cursor-not-allowed"
11
+ :class="isDisabled ? 'cursor-not-allowed' : 'cursor-pointer'"
12
+ :name="fieldName"
13
+ :value="value"
14
+ :checked="checked"
15
+ :disabled="isDisabled"
16
+ @change="onChange"
17
+ >
18
+ <span
19
+ class="pointer-events-none col-start-1 row-start-1 size-[var(--ads-radio-size)] rounded-full border-[length:var(--ads-radio-border-width)] bg-[var(--ads-radio-background)] peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-[var(--ads-text-brand)]"
20
+ :class="isDisabled
21
+ ? 'border-[var(--ads-radio-disabled)] peer-checked:border-[var(--ads-radio-disabled)] peer-checked:bg-[var(--ads-radio-disabled)]'
22
+ : 'border-[var(--ads-radio-border)] peer-checked:border-[var(--ads-radio-border)] peer-checked:bg-[var(--ads-radio-border)]'"
23
+ />
24
+ <span class="pointer-events-none col-start-1 row-start-1 size-[var(--ads-radio-dot)] rounded-full bg-[var(--ads-radio-dot-color)] opacity-0 peer-checked:opacity-100" />
25
+ </span>
26
+ <span
27
+ v-if="hasLabel"
28
+ class="text-[length:var(--ads-radio-label-size)] font-normal leading-[1.3]"
29
+ :class="isDisabled ? 'text-[var(--ads-text-tertiary)]' : 'text-[var(--ads-radio-label)]'"
30
+ >
31
+ <slot>{{ label }}</slot>
32
+ </span>
33
+ </label>
34
+ </template>
35
+
36
+ <script setup lang="ts">
37
+ import { computed, useAttrs, useId, useSlots } from "vue";
38
+
39
+ defineOptions({ inheritAttrs: false });
40
+
41
+ const props = withDefaults(
42
+ defineProps<{
43
+ label?: string;
44
+ value?: string | number | boolean;
45
+ name?: string;
46
+ disabled?: boolean;
47
+ }>(),
48
+ {
49
+ label: "",
50
+ value: "on",
51
+ name: "",
52
+ disabled: false,
53
+ },
54
+ );
55
+
56
+ const model = defineModel<string | number | boolean>({ default: "" });
57
+ const attrs = useAttrs();
58
+ const slots = useSlots();
59
+ const autoName = useId();
60
+
61
+ const hasLabel = computed(() => props.label.length > 0 || Boolean(slots.default));
62
+ const isDisabled = computed(() => props.disabled);
63
+ const fieldName = computed(() => props.name || autoName);
64
+ const checked = computed(() => model.value === props.value);
65
+
66
+ const onChange = () => {
67
+ if (isDisabled.value) return;
68
+ model.value = props.value;
69
+ };
70
+ </script>