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,101 +1,101 @@
1
- <template>
2
- <DefinePanel>
3
- <div class="flex w-[var(--ads-steptip-width)] flex-col gap-3 rounded-[var(--ads-tooltip-radius)] bg-[var(--ads-tooltip-bg)] p-4 text-[var(--ads-tooltip-title)] shadow-[var(--ads-shadow-level-1)]">
4
- <div class="flex items-start justify-between gap-3">
5
- <p class="text-base leading-[1.3] font-bold">{{ title }}</p>
6
- <p class="shrink-0 text-xs leading-[1.2] font-medium">{{ step }}/{{ totalSteps }}</p>
7
- </div>
8
- <p class="text-sm leading-[1.3]">{{ description }}</p>
9
- <div class="flex items-center justify-between gap-3">
10
- <button
11
- type="button"
12
- class="text-base text-[var(--ads-steptip-skip)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ads-tooltip-title)]"
13
- @click="emit('skip')"
14
- >
15
- {{ skipLabel }}
16
- </button>
17
- <div class="flex items-center gap-3">
18
- <button
19
- v-if="step > 1"
20
- type="button"
21
- class="text-base font-medium focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ads-text-contrast)]"
22
- @click="emit('back')"
23
- >
24
- {{ backLabel }}
25
- </button>
26
- <button
27
- type="button"
28
- class="text-base font-medium focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ads-text-contrast)]"
29
- @click="step >= totalSteps ? emit('finish') : emit('next')"
30
- >
31
- {{ step >= totalSteps ? finishLabel : nextLabel }}
32
- </button>
33
- </div>
34
- </div>
35
- </div>
36
- </DefinePanel>
37
-
38
- <UTooltip
39
- v-if="hasTrigger"
40
- :delay-duration="delayDuration"
41
- :content="{ side, align: 'center', sideOffset: 8 }"
42
- :ui="tooltipUi"
43
- >
44
- <slot />
45
- <template #content>
46
- <ReusePanel />
47
- </template>
48
- </UTooltip>
49
- <ReusePanel v-else />
50
- </template>
51
-
52
- <script setup lang="ts">
53
- import { computed, useSlots } from "vue";
54
- import { createReusableTemplate } from "@vueuse/core";
55
-
56
- const [DefinePanel, ReusePanel] = createReusableTemplate();
57
-
58
- const props = withDefaults(
59
- defineProps<{
60
- title?: string;
61
- description?: string;
62
- step?: number;
63
- totalSteps?: number;
64
- skipLabel?: string;
65
- backLabel?: string;
66
- nextLabel?: string;
67
- finishLabel?: string;
68
- /** Lado do gatilho onde o passo abre. Só vale com slot. */
69
- side?: "top" | "right" | "bottom" | "left";
70
- delayDuration?: number;
71
- }>(),
72
- {
73
- title: "Título do passo",
74
- description: "Texto que orienta a pessoa neste passo.",
75
- step: 1,
76
- totalSteps: 3,
77
- skipLabel: "pular",
78
- backLabel: "voltar",
79
- nextLabel: "próximo",
80
- finishLabel: "concluir",
81
- side: "bottom",
82
- delayDuration: 150,
83
- },
84
- );
85
-
86
- const emit = defineEmits<{
87
- skip: [];
88
- back: [];
89
- next: [];
90
- finish: [];
91
- }>();
92
-
93
- const slots = useSlots();
94
- const hasTrigger = computed(() => Boolean(slots.default));
95
- const side = computed(() => props.side);
96
-
97
- /** O painel já tem fundo, raio e sombra: o conteúdo do UTooltip só posiciona. */
98
- const tooltipUi = {
99
- content: "z-[70] h-auto rounded-none bg-transparent p-0 shadow-none ring-0",
100
- };
101
- </script>
1
+ <template>
2
+ <DefinePanel>
3
+ <div class="flex w-[var(--ads-steptip-width)] flex-col gap-3 rounded-[var(--ads-tooltip-radius)] bg-[var(--ads-tooltip-bg)] p-4 text-[var(--ads-tooltip-title)] shadow-[var(--ads-shadow-level-1)]">
4
+ <div class="flex items-start justify-between gap-3">
5
+ <p class="text-base leading-[1.3] font-bold">{{ title }}</p>
6
+ <p class="shrink-0 text-xs leading-[1.2] font-medium">{{ step }}/{{ totalSteps }}</p>
7
+ </div>
8
+ <p class="text-sm leading-[1.3]">{{ description }}</p>
9
+ <div class="flex items-center justify-between gap-3">
10
+ <button
11
+ type="button"
12
+ class="text-base text-[var(--ads-steptip-skip)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ads-tooltip-title)]"
13
+ @click="emit('skip')"
14
+ >
15
+ {{ skipLabel }}
16
+ </button>
17
+ <div class="flex items-center gap-3">
18
+ <button
19
+ v-if="step > 1"
20
+ type="button"
21
+ class="text-base font-medium focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ads-text-contrast)]"
22
+ @click="emit('back')"
23
+ >
24
+ {{ backLabel }}
25
+ </button>
26
+ <button
27
+ type="button"
28
+ class="text-base font-medium focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ads-text-contrast)]"
29
+ @click="step >= totalSteps ? emit('finish') : emit('next')"
30
+ >
31
+ {{ step >= totalSteps ? finishLabel : nextLabel }}
32
+ </button>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ </DefinePanel>
37
+
38
+ <UTooltip
39
+ v-if="hasTrigger"
40
+ :delay-duration="delayDuration"
41
+ :content="{ side, align: 'center', sideOffset: 8 }"
42
+ :ui="tooltipUi"
43
+ >
44
+ <slot />
45
+ <template #content>
46
+ <ReusePanel />
47
+ </template>
48
+ </UTooltip>
49
+ <ReusePanel v-else />
50
+ </template>
51
+
52
+ <script setup lang="ts">
53
+ import { computed, useSlots } from "vue";
54
+ import { createReusableTemplate } from "@vueuse/core";
55
+
56
+ const [DefinePanel, ReusePanel] = createReusableTemplate();
57
+
58
+ const props = withDefaults(
59
+ defineProps<{
60
+ title?: string;
61
+ description?: string;
62
+ step?: number;
63
+ totalSteps?: number;
64
+ skipLabel?: string;
65
+ backLabel?: string;
66
+ nextLabel?: string;
67
+ finishLabel?: string;
68
+ /** Lado do gatilho onde o passo abre. Só vale com slot. */
69
+ side?: "top" | "right" | "bottom" | "left";
70
+ delayDuration?: number;
71
+ }>(),
72
+ {
73
+ title: "Título do passo",
74
+ description: "Texto que orienta a pessoa neste passo.",
75
+ step: 1,
76
+ totalSteps: 3,
77
+ skipLabel: "pular",
78
+ backLabel: "voltar",
79
+ nextLabel: "próximo",
80
+ finishLabel: "concluir",
81
+ side: "bottom",
82
+ delayDuration: 150,
83
+ },
84
+ );
85
+
86
+ const emit = defineEmits<{
87
+ skip: [];
88
+ back: [];
89
+ next: [];
90
+ finish: [];
91
+ }>();
92
+
93
+ const slots = useSlots();
94
+ const hasTrigger = computed(() => Boolean(slots.default));
95
+ const side = computed(() => props.side);
96
+
97
+ /** O painel já tem fundo, raio e sombra: o conteúdo do UTooltip só posiciona. */
98
+ const tooltipUi = {
99
+ content: "z-[70] h-auto rounded-none bg-transparent p-0 shadow-none ring-0",
100
+ };
101
+ </script>
@@ -1,59 +1,59 @@
1
- <template>
2
- <label
3
- class="inline-flex items-center"
4
- :class="[hasLabel ? 'gap-[var(--ads-switch-gap)]' : '', isDisabled ? 'cursor-not-allowed' : 'cursor-pointer']"
5
- >
6
- <span class="relative h-[var(--ads-switch-height)] w-[var(--ads-switch-width)] shrink-0">
7
- <input
8
- v-bind="attrs"
9
- type="checkbox"
10
- role="switch"
11
- class="peer absolute inset-0 appearance-none rounded-[var(--ads-switch-radius)] bg-[var(--ads-switch-off)] transition-colors duration-150 ease-out checked:bg-[var(--ads-switch-on)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ads-text-brand)] disabled:cursor-not-allowed disabled:bg-[var(--ads-switch-disabled)] disabled:checked:bg-[var(--ads-switch-disabled-on)]"
12
- :checked="model"
13
- :disabled="isDisabled"
14
- :aria-checked="model"
15
- @change="onChange"
16
- >
17
- <span
18
- class="pointer-events-none absolute top-[var(--ads-switch-inset)] left-0 size-[var(--ads-switch-thumb)] translate-x-[var(--ads-switch-inset)] rounded-full bg-[var(--ads-switch-thumb-color)] transition-transform duration-150 ease-out peer-checked:translate-x-[calc(var(--ads-switch-width)-var(--ads-switch-thumb)-var(--ads-switch-inset))]"
19
- />
20
- </span>
21
- <span
22
- v-if="hasLabel"
23
- class="text-[length:var(--ads-switch-label-size)] font-normal leading-[1.3]"
24
- :class="isDisabled ? 'text-[var(--ads-text-tertiary)]' : 'text-[var(--ads-switch-label)]'"
25
- >
26
- <slot>{{ label }}</slot>
27
- </span>
28
- </label>
29
- </template>
30
-
31
- <script setup lang="ts">
32
- import { computed, useAttrs, useSlots } from "vue";
33
-
34
- defineOptions({ inheritAttrs: false });
35
-
36
- const props = withDefaults(
37
- defineProps<{
38
- label?: string;
39
- disabled?: boolean;
40
- }>(),
41
- {
42
- label: "",
43
- disabled: false,
44
- },
45
- );
46
-
47
- const model = defineModel<boolean>({ default: false });
48
- const attrs = useAttrs();
49
- const slots = useSlots();
50
-
51
- const hasLabel = computed(() => props.label.length > 0 || Boolean(slots.default));
52
- const isDisabled = computed(() => props.disabled);
53
-
54
- const onChange = (event: Event) => {
55
- const input = event.target;
56
- if (!(input instanceof HTMLInputElement) || isDisabled.value) return;
57
- model.value = input.checked;
58
- };
59
- </script>
1
+ <template>
2
+ <label
3
+ class="inline-flex items-center"
4
+ :class="[hasLabel ? 'gap-[var(--ads-switch-gap)]' : '', isDisabled ? 'cursor-not-allowed' : 'cursor-pointer']"
5
+ >
6
+ <span class="relative h-[var(--ads-switch-height)] w-[var(--ads-switch-width)] shrink-0">
7
+ <input
8
+ v-bind="attrs"
9
+ type="checkbox"
10
+ role="switch"
11
+ class="peer absolute inset-0 appearance-none rounded-[var(--ads-switch-radius)] bg-[var(--ads-switch-off)] transition-colors duration-150 ease-out checked:bg-[var(--ads-switch-on)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ads-text-brand)] disabled:cursor-not-allowed disabled:bg-[var(--ads-switch-disabled)] disabled:checked:bg-[var(--ads-switch-disabled-on)]"
12
+ :checked="model"
13
+ :disabled="isDisabled"
14
+ :aria-checked="model"
15
+ @change="onChange"
16
+ >
17
+ <span
18
+ class="pointer-events-none absolute top-[var(--ads-switch-inset)] left-0 size-[var(--ads-switch-thumb)] translate-x-[var(--ads-switch-inset)] rounded-full bg-[var(--ads-switch-thumb-color)] transition-transform duration-150 ease-out peer-checked:translate-x-[calc(var(--ads-switch-width)-var(--ads-switch-thumb)-var(--ads-switch-inset))]"
19
+ />
20
+ </span>
21
+ <span
22
+ v-if="hasLabel"
23
+ class="text-[length:var(--ads-switch-label-size)] font-normal leading-[1.3]"
24
+ :class="isDisabled ? 'text-[var(--ads-text-tertiary)]' : 'text-[var(--ads-switch-label)]'"
25
+ >
26
+ <slot>{{ label }}</slot>
27
+ </span>
28
+ </label>
29
+ </template>
30
+
31
+ <script setup lang="ts">
32
+ import { computed, useAttrs, useSlots } from "vue";
33
+
34
+ defineOptions({ inheritAttrs: false });
35
+
36
+ const props = withDefaults(
37
+ defineProps<{
38
+ label?: string;
39
+ disabled?: boolean;
40
+ }>(),
41
+ {
42
+ label: "",
43
+ disabled: false,
44
+ },
45
+ );
46
+
47
+ const model = defineModel<boolean>({ default: false });
48
+ const attrs = useAttrs();
49
+ const slots = useSlots();
50
+
51
+ const hasLabel = computed(() => props.label.length > 0 || Boolean(slots.default));
52
+ const isDisabled = computed(() => props.disabled);
53
+
54
+ const onChange = (event: Event) => {
55
+ const input = event.target;
56
+ if (!(input instanceof HTMLInputElement) || isDisabled.value) return;
57
+ model.value = input.checked;
58
+ };
59
+ </script>
@@ -1,100 +1,100 @@
1
- <template>
2
- <UTable
3
- :data="data"
4
- :columns="columns"
5
- :loading="loading"
6
- :sticky="sticky"
7
- :ui="tableUi"
8
- >
9
- <template v-if="!$slots.loading" #loading>
10
- <AdsLoading size="sm" :label="loadingLabel" />
11
- </template>
12
-
13
- <template v-if="!$slots.empty" #empty>
14
- <div class="flex flex-col items-center gap-2">
15
- <UIcon
16
- :name="emptyIcon"
17
- class="size-6 text-[var(--ads-border-icon-support)]"
18
- aria-hidden="true"
19
- />
20
- <p class="text-sm text-[var(--ads-text-secondary)]">{{ emptyLabel }}</p>
21
- </div>
22
- </template>
23
-
24
- <template v-for="(_, name) in $slots" #[name]="slotProps">
25
- <slot :name="name" v-bind="slotProps ?? {}" />
26
- </template>
27
- </UTable>
28
- </template>
29
-
30
- <script setup lang="ts" generic="T extends TableData">
31
- import { computed, useSlots } from "vue";
32
- import type { TableColumn, TableData } from "@nuxt/ui";
33
- import AdsLoading from "./AdsLoading.vue";
34
- import {
35
- buildAdsTableUi,
36
- type AdsTableBorders,
37
- type AdsTableDensity,
38
- type AdsTableUi,
39
- } from "../utils/ads-table";
40
-
41
- const props = withDefaults(
42
- defineProps<{
43
- data: T[];
44
- columns: TableColumn<T>[];
45
- /** md: lista e gestão. sm: grade de dados larga ou prévia. */
46
- density?: AdsTableDensity;
47
- /** Zebra só em grade numérica larga. */
48
- striped?: boolean;
49
- /** Borda e raio próprios. Dentro de AdsCard, false. */
50
- framed?: boolean;
51
- /** lines: só horizontal. grid: horizontal e vertical. */
52
- borders?: AdsTableBorders;
53
- /** Degradê por nível. A célula marca `data-depth` de 0 a 2. */
54
- depth?: boolean;
55
- loading?: boolean;
56
- /** Cabeçalho (e rodapé) fixos quando a tabela tem altura máxima. */
57
- sticky?: boolean | "header" | "footer";
58
- emptyLabel?: string;
59
- emptyIcon?: string;
60
- loadingLabel?: string;
61
- /** Ajuste pontual de um slot; soma ao padrão. */
62
- ui?: Partial<AdsTableUi>;
63
- }>(),
64
- {
65
- density: "md",
66
- striped: false,
67
- framed: true,
68
- borders: "lines",
69
- depth: false,
70
- loading: false,
71
- sticky: false,
72
- emptyLabel: "Nenhum registro encontrado",
73
- emptyIcon: "i-lucide-inbox",
74
- loadingLabel: undefined,
75
- ui: undefined,
76
- },
77
- );
78
-
79
- const slots = useSlots();
80
-
81
- const tableUi = computed(() => {
82
- const base = buildAdsTableUi({
83
- density: props.density,
84
- striped: props.striped,
85
- framed: props.framed,
86
- borders: props.borders,
87
- depth: props.depth,
88
- });
89
- if (!slots.expanded) {
90
- // Árvore via getSubRows: o UTable sempre desenha a linha de detalhe da linha aberta.
91
- base.tbody = `${base.tbody} [&>tr[data-slot=tr]:not([data-selectable])]:hidden`;
92
- }
93
- if (!props.ui) return base;
94
- const merged = { ...base };
95
- for (const key of Object.keys(props.ui) as (keyof AdsTableUi)[]) {
96
- merged[key] = `${base[key]} ${props.ui[key] ?? ""}`.trim();
97
- }
98
- return merged;
99
- });
100
- </script>
1
+ <template>
2
+ <UTable
3
+ :data="data"
4
+ :columns="columns"
5
+ :loading="loading"
6
+ :sticky="sticky"
7
+ :ui="tableUi"
8
+ >
9
+ <template v-if="!$slots.loading" #loading>
10
+ <AdsLoading size="sm" :label="loadingLabel" />
11
+ </template>
12
+
13
+ <template v-if="!$slots.empty" #empty>
14
+ <div class="flex flex-col items-center gap-2">
15
+ <UIcon
16
+ :name="emptyIcon"
17
+ class="size-6 text-[var(--ads-border-icon-support)]"
18
+ aria-hidden="true"
19
+ />
20
+ <p class="text-sm text-[var(--ads-text-secondary)]">{{ emptyLabel }}</p>
21
+ </div>
22
+ </template>
23
+
24
+ <template v-for="(_, name) in $slots" #[name]="slotProps">
25
+ <slot :name="name" v-bind="slotProps ?? {}" />
26
+ </template>
27
+ </UTable>
28
+ </template>
29
+
30
+ <script setup lang="ts" generic="T extends TableData">
31
+ import { computed, useSlots } from "vue";
32
+ import type { TableColumn, TableData } from "@nuxt/ui";
33
+ import AdsLoading from "./AdsLoading.vue";
34
+ import {
35
+ buildAdsTableUi,
36
+ type AdsTableBorders,
37
+ type AdsTableDensity,
38
+ type AdsTableUi,
39
+ } from "../utils/ads-table";
40
+
41
+ const props = withDefaults(
42
+ defineProps<{
43
+ data: T[];
44
+ columns: TableColumn<T>[];
45
+ /** md: lista e gestão. sm: grade de dados larga ou prévia. */
46
+ density?: AdsTableDensity;
47
+ /** Zebra só em grade numérica larga. */
48
+ striped?: boolean;
49
+ /** Borda e raio próprios. Dentro de AdsCard, false. */
50
+ framed?: boolean;
51
+ /** lines: só horizontal. grid: horizontal e vertical. */
52
+ borders?: AdsTableBorders;
53
+ /** Degradê por nível. A célula marca `data-depth` de 0 a 2. */
54
+ depth?: boolean;
55
+ loading?: boolean;
56
+ /** Cabeçalho (e rodapé) fixos quando a tabela tem altura máxima. */
57
+ sticky?: boolean | "header" | "footer";
58
+ emptyLabel?: string;
59
+ emptyIcon?: string;
60
+ loadingLabel?: string;
61
+ /** Ajuste pontual de um slot; soma ao padrão. */
62
+ ui?: Partial<AdsTableUi>;
63
+ }>(),
64
+ {
65
+ density: "md",
66
+ striped: false,
67
+ framed: true,
68
+ borders: "lines",
69
+ depth: false,
70
+ loading: false,
71
+ sticky: false,
72
+ emptyLabel: "Nenhum registro encontrado",
73
+ emptyIcon: "i-lucide-inbox",
74
+ loadingLabel: undefined,
75
+ ui: undefined,
76
+ },
77
+ );
78
+
79
+ const slots = useSlots();
80
+
81
+ const tableUi = computed(() => {
82
+ const base = buildAdsTableUi({
83
+ density: props.density,
84
+ striped: props.striped,
85
+ framed: props.framed,
86
+ borders: props.borders,
87
+ depth: props.depth,
88
+ });
89
+ if (!slots.expanded) {
90
+ // Árvore via getSubRows: o UTable sempre desenha a linha de detalhe da linha aberta.
91
+ base.tbody = `${base.tbody} [&>tr[data-slot=tr]:not([data-selectable])]:hidden`;
92
+ }
93
+ if (!props.ui) return base;
94
+ const merged = { ...base };
95
+ for (const key of Object.keys(props.ui) as (keyof AdsTableUi)[]) {
96
+ merged[key] = `${base[key]} ${props.ui[key] ?? ""}`.trim();
97
+ }
98
+ return merged;
99
+ });
100
+ </script>
@@ -1,44 +1,44 @@
1
- <template>
2
- <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
3
- <div v-if="title || description || $slots.title" class="min-w-0">
4
- <slot name="title">
5
- <p class="font-bold text-[var(--ads-text-primary)]">{{ title }}</p>
6
- <p v-if="description" class="mt-1 text-sm text-[var(--ads-text-secondary)]">{{ description }}</p>
7
- </slot>
8
- </div>
9
-
10
- <div class="flex min-w-0 flex-wrap items-center gap-3">
11
- <AdsSearchInput
12
- v-if="searchable"
13
- v-model="search"
14
- size="sm"
15
- class="w-full sm:w-64"
16
- :placeholder="searchPlaceholder"
17
- />
18
- <slot />
19
- </div>
20
- </div>
21
- </template>
22
-
23
- <script setup lang="ts">
24
- import AdsSearchInput from "./AdsSearchInput.vue";
25
-
26
- withDefaults(
27
- defineProps<{
28
- /** Título à esquerda. Sem título, a busca e os filtros ocupam a faixa. */
29
- title?: string;
30
- description?: string;
31
- /** Mostra o AdsSearchInput. O filtro entra no slot padrão, com AdsFilterSelect. */
32
- searchable?: boolean;
33
- searchPlaceholder?: string;
34
- }>(),
35
- {
36
- title: "",
37
- description: "",
38
- searchable: true,
39
- searchPlaceholder: "Pesquisar",
40
- },
41
- );
42
-
43
- const search = defineModel<string>("search", { default: "" });
44
- </script>
1
+ <template>
2
+ <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
3
+ <div v-if="title || description || $slots.title" class="min-w-0">
4
+ <slot name="title">
5
+ <p class="font-bold text-[var(--ads-text-primary)]">{{ title }}</p>
6
+ <p v-if="description" class="mt-1 text-sm text-[var(--ads-text-secondary)]">{{ description }}</p>
7
+ </slot>
8
+ </div>
9
+
10
+ <div class="flex min-w-0 flex-wrap items-center gap-3">
11
+ <AdsSearchInput
12
+ v-if="searchable"
13
+ v-model="search"
14
+ size="sm"
15
+ class="w-full sm:w-64"
16
+ :placeholder="searchPlaceholder"
17
+ />
18
+ <slot />
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <script setup lang="ts">
24
+ import AdsSearchInput from "./AdsSearchInput.vue";
25
+
26
+ withDefaults(
27
+ defineProps<{
28
+ /** Título à esquerda. Sem título, a busca e os filtros ocupam a faixa. */
29
+ title?: string;
30
+ description?: string;
31
+ /** Mostra o AdsSearchInput. O filtro entra no slot padrão, com AdsFilterSelect. */
32
+ searchable?: boolean;
33
+ searchPlaceholder?: string;
34
+ }>(),
35
+ {
36
+ title: "",
37
+ description: "",
38
+ searchable: true,
39
+ searchPlaceholder: "Pesquisar",
40
+ },
41
+ );
42
+
43
+ const search = defineModel<string>("search", { default: "" });
44
+ </script>