@seedgrid/fe-components 2026.6.7 → 2026.7.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.
- package/dist/ai/seedgrid-components.manifest.json +8003 -696
- package/dist/commons/SgToaster.d.ts +2 -0
- package/dist/commons/SgToaster.d.ts.map +1 -1
- package/dist/commons/SgToaster.js +2 -2
- package/dist/commons/SgToaster.meta.d.ts.map +1 -1
- package/dist/commons/SgToaster.meta.js +2 -1
- package/dist/commons/SgWhistleHost.d.ts +16 -15
- package/dist/commons/SgWhistleHost.d.ts.map +1 -1
- package/dist/commons/SgWhistleHost.js +38 -147
- package/dist/commons/SgWhistleHost.meta.d.ts.map +1 -1
- package/dist/commons/SgWhistleHost.meta.js +17 -15
- package/dist/commons/SgWhistler.d.ts +20 -0
- package/dist/commons/SgWhistler.d.ts.map +1 -0
- package/dist/commons/SgWhistler.js +168 -0
- package/dist/commons/SgWhistler.meta.d.ts +4 -0
- package/dist/commons/SgWhistler.meta.d.ts.map +1 -0
- package/dist/commons/SgWhistler.meta.js +47 -0
- package/dist/commons/sgWhistleHostRegistry.d.ts +7 -0
- package/dist/commons/sgWhistleHostRegistry.d.ts.map +1 -0
- package/dist/commons/sgWhistleHostRegistry.js +42 -0
- package/dist/gadgets/clock/themes/SgClockThemePicker.d.ts.map +1 -1
- package/dist/gadgets/clock/themes/SgClockThemePicker.js +1 -1
- package/dist/i18n/en-US.d.ts.map +1 -1
- package/dist/i18n/en-US.js +2 -0
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +2 -0
- package/dist/i18n/fr.d.ts.map +1 -1
- package/dist/i18n/fr.js +2 -0
- package/dist/i18n/pt-BR.d.ts.map +1 -1
- package/dist/i18n/pt-BR.js +2 -0
- package/dist/i18n/pt-PT.d.ts.map +1 -1
- package/dist/i18n/pt-PT.js +2 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/inputs/SgCombobox.d.ts.map +1 -1
- package/dist/inputs/SgCombobox.js +6 -2
- package/dist/inputs/SgInputPassword.d.ts +1 -0
- package/dist/inputs/SgInputPassword.d.ts.map +1 -1
- package/dist/inputs/SgInputTextSearch.d.ts +11 -0
- package/dist/inputs/SgInputTextSearch.d.ts.map +1 -0
- package/dist/inputs/SgInputTextSearch.js +47 -0
- package/dist/inputs/SgInputTextSearch.meta.d.ts +4 -0
- package/dist/inputs/SgInputTextSearch.meta.d.ts.map +1 -0
- package/dist/inputs/SgInputTextSearch.meta.js +50 -0
- package/dist/inputs/SgMultiSelect.d.ts +23 -0
- package/dist/inputs/SgMultiSelect.d.ts.map +1 -0
- package/dist/inputs/SgMultiSelect.js +117 -0
- package/dist/inputs/SgMultiSelect.meta.d.ts +4 -0
- package/dist/inputs/SgMultiSelect.meta.d.ts.map +1 -0
- package/dist/inputs/SgMultiSelect.meta.js +56 -0
- package/dist/inputs/SgMultiSelectChips.d.ts +27 -0
- package/dist/inputs/SgMultiSelectChips.d.ts.map +1 -0
- package/dist/inputs/SgMultiSelectChips.js +127 -0
- package/dist/inputs/SgMultiSelectChips.meta.d.ts +4 -0
- package/dist/inputs/SgMultiSelectChips.meta.d.ts.map +1 -0
- package/dist/inputs/SgMultiSelectChips.meta.js +55 -0
- package/dist/inputs/SgMultiSelectDropdown.d.ts +28 -0
- package/dist/inputs/SgMultiSelectDropdown.d.ts.map +1 -0
- package/dist/inputs/SgMultiSelectDropdown.js +41 -0
- package/dist/inputs/SgPeriodSelector.d.ts +103 -0
- package/dist/inputs/SgPeriodSelector.d.ts.map +1 -0
- package/dist/inputs/SgPeriodSelector.js +368 -0
- package/dist/inputs/SgPeriodSelector.meta.d.ts +4 -0
- package/dist/inputs/SgPeriodSelector.meta.d.ts.map +1 -0
- package/dist/inputs/SgPeriodSelector.meta.js +55 -0
- package/dist/inputs/useSgMultiSelect.d.ts +48 -0
- package/dist/inputs/useSgMultiSelect.d.ts.map +1 -0
- package/dist/inputs/useSgMultiSelect.js +202 -0
- package/dist/layout/SgMenu.d.ts.map +1 -1
- package/dist/layout/SgMenu.js +1 -1
- package/dist/sandbox.cjs +53 -53
- package/package.json +3 -3
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Controller } from "react-hook-form";
|
|
5
|
+
import { resolveFieldError } from "../rhf";
|
|
6
|
+
import { cn } from "../layout/sgDocking";
|
|
7
|
+
import { SgCombobox } from "./SgCombobox";
|
|
8
|
+
import { SgInputDate } from "./SgInputDate";
|
|
9
|
+
/**
|
|
10
|
+
* Presets temporais pre-definidos para filtros de relatorios/dashboards.
|
|
11
|
+
* O valor de cada membro e igual ao seu nome (string) para trafegar bem em
|
|
12
|
+
* fontes de dados, JSON e no id de item do SgCombobox.
|
|
13
|
+
*/
|
|
14
|
+
export var PeriodPreset;
|
|
15
|
+
(function (PeriodPreset) {
|
|
16
|
+
PeriodPreset["ALL"] = "ALL";
|
|
17
|
+
PeriodPreset["CUSTOM"] = "CUSTOM";
|
|
18
|
+
PeriodPreset["TODAY"] = "TODAY";
|
|
19
|
+
PeriodPreset["YESTERDAY"] = "YESTERDAY";
|
|
20
|
+
PeriodPreset["TOMORROW"] = "TOMORROW";
|
|
21
|
+
PeriodPreset["THIS_WEEK"] = "THIS_WEEK";
|
|
22
|
+
PeriodPreset["THIS_QUINZENA"] = "THIS_QUINZENA";
|
|
23
|
+
PeriodPreset["THIS_MONTH"] = "THIS_MONTH";
|
|
24
|
+
PeriodPreset["THIS_QUARTER"] = "THIS_QUARTER";
|
|
25
|
+
PeriodPreset["THIS_SEMESTER"] = "THIS_SEMESTER";
|
|
26
|
+
PeriodPreset["THIS_YEAR"] = "THIS_YEAR";
|
|
27
|
+
PeriodPreset["LAST_WEEK"] = "LAST_WEEK";
|
|
28
|
+
PeriodPreset["LAST_QUINZENA"] = "LAST_QUINZENA";
|
|
29
|
+
PeriodPreset["LAST_MONTH"] = "LAST_MONTH";
|
|
30
|
+
PeriodPreset["LAST_QUARTER"] = "LAST_QUARTER";
|
|
31
|
+
PeriodPreset["LAST_SEMESTER"] = "LAST_SEMESTER";
|
|
32
|
+
PeriodPreset["LAST_YEAR"] = "LAST_YEAR";
|
|
33
|
+
PeriodPreset["NEXT_WEEK"] = "NEXT_WEEK";
|
|
34
|
+
PeriodPreset["NEXT_QUINZENA"] = "NEXT_QUINZENA";
|
|
35
|
+
PeriodPreset["NEXT_MONTH"] = "NEXT_MONTH";
|
|
36
|
+
PeriodPreset["NEXT_QUARTER"] = "NEXT_QUARTER";
|
|
37
|
+
PeriodPreset["NEXT_SEMESTER"] = "NEXT_SEMESTER";
|
|
38
|
+
PeriodPreset["NEXT_YEAR"] = "NEXT_YEAR";
|
|
39
|
+
})(PeriodPreset || (PeriodPreset = {}));
|
|
40
|
+
const PERIOD_PRESET_LABELS = {
|
|
41
|
+
[PeriodPreset.ALL]: { "pt-BR": "Todos", "en-US": "All", es: "Todos", "pt-PT": "Todos", fr: "Tous" },
|
|
42
|
+
[PeriodPreset.CUSTOM]: { "pt-BR": "Periodo", "en-US": "Custom period", es: "Periodo", "pt-PT": "Periodo", fr: "Periode" },
|
|
43
|
+
[PeriodPreset.TODAY]: { "pt-BR": "Hoje", "en-US": "Today", es: "Hoy", "pt-PT": "Hoje", fr: "Aujourd'hui" },
|
|
44
|
+
[PeriodPreset.YESTERDAY]: { "pt-BR": "Ontem", "en-US": "Yesterday", es: "Ayer", "pt-PT": "Ontem", fr: "Hier" },
|
|
45
|
+
[PeriodPreset.TOMORROW]: { "pt-BR": "Amanha", "en-US": "Tomorrow", es: "Manana", "pt-PT": "Amanha", fr: "Demain" },
|
|
46
|
+
[PeriodPreset.THIS_WEEK]: { "pt-BR": "Esta semana", "en-US": "This week", es: "Esta semana", "pt-PT": "Esta semana", fr: "Cette semaine" },
|
|
47
|
+
[PeriodPreset.THIS_QUINZENA]: { "pt-BR": "Esta quinzena", "en-US": "This fortnight", es: "Esta quincena", "pt-PT": "Esta quinzena", fr: "Cette quinzaine" },
|
|
48
|
+
[PeriodPreset.THIS_MONTH]: { "pt-BR": "Este mes", "en-US": "This month", es: "Este mes", "pt-PT": "Este mes", fr: "Ce mois" },
|
|
49
|
+
[PeriodPreset.THIS_QUARTER]: { "pt-BR": "Este trimestre", "en-US": "This quarter", es: "Este trimestre", "pt-PT": "Este trimestre", fr: "Ce trimestre" },
|
|
50
|
+
[PeriodPreset.THIS_SEMESTER]: { "pt-BR": "Este semestre", "en-US": "This semester", es: "Este semestre", "pt-PT": "Este semestre", fr: "Ce semestre" },
|
|
51
|
+
[PeriodPreset.THIS_YEAR]: { "pt-BR": "Este ano", "en-US": "This year", es: "Este ano", "pt-PT": "Este ano", fr: "Cette annee" },
|
|
52
|
+
[PeriodPreset.LAST_WEEK]: { "pt-BR": "Semana passada", "en-US": "Last week", es: "Semana pasada", "pt-PT": "Semana passada", fr: "Semaine derniere" },
|
|
53
|
+
[PeriodPreset.LAST_QUINZENA]: { "pt-BR": "Quinzena passada", "en-US": "Last fortnight", es: "Quincena pasada", "pt-PT": "Quinzena passada", fr: "Quinzaine derniere" },
|
|
54
|
+
[PeriodPreset.LAST_MONTH]: { "pt-BR": "Mes passado", "en-US": "Last month", es: "Mes pasado", "pt-PT": "Mes passado", fr: "Mois dernier" },
|
|
55
|
+
[PeriodPreset.LAST_QUARTER]: { "pt-BR": "Trimestre passado", "en-US": "Last quarter", es: "Trimestre pasado", "pt-PT": "Trimestre passado", fr: "Trimestre dernier" },
|
|
56
|
+
[PeriodPreset.LAST_SEMESTER]: { "pt-BR": "Semestre passado", "en-US": "Last semester", es: "Semestre pasado", "pt-PT": "Semestre passado", fr: "Semestre dernier" },
|
|
57
|
+
[PeriodPreset.LAST_YEAR]: { "pt-BR": "Ano passado", "en-US": "Last year", es: "Ano pasado", "pt-PT": "Ano passado", fr: "Annee derniere" },
|
|
58
|
+
[PeriodPreset.NEXT_WEEK]: { "pt-BR": "Proxima semana", "en-US": "Next week", es: "Proxima semana", "pt-PT": "Proxima semana", fr: "Semaine prochaine" },
|
|
59
|
+
[PeriodPreset.NEXT_QUINZENA]: { "pt-BR": "Proxima quinzena", "en-US": "Next fortnight", es: "Proxima quincena", "pt-PT": "Proxima quinzena", fr: "Quinzaine prochaine" },
|
|
60
|
+
[PeriodPreset.NEXT_MONTH]: { "pt-BR": "Proximo mes", "en-US": "Next month", es: "Proximo mes", "pt-PT": "Proximo mes", fr: "Mois prochain" },
|
|
61
|
+
[PeriodPreset.NEXT_QUARTER]: { "pt-BR": "Proximo trimestre", "en-US": "Next quarter", es: "Proximo trimestre", "pt-PT": "Proximo trimestre", fr: "Trimestre prochain" },
|
|
62
|
+
[PeriodPreset.NEXT_SEMESTER]: { "pt-BR": "Proximo semestre", "en-US": "Next semester", es: "Proximo semestre", "pt-PT": "Proximo semestre", fr: "Semestre prochain" },
|
|
63
|
+
[PeriodPreset.NEXT_YEAR]: { "pt-BR": "Proximo ano", "en-US": "Next year", es: "Proximo ano", "pt-PT": "Proximo ano", fr: "Annee prochaine" }
|
|
64
|
+
};
|
|
65
|
+
const GROUP_LABELS = {
|
|
66
|
+
current: { "pt-BR": "Atuais", "en-US": "Current", es: "Actuales", "pt-PT": "Atuais", fr: "Actuels" },
|
|
67
|
+
future: { "pt-BR": "Futuros", "en-US": "Future", es: "Futuros", "pt-PT": "Futuros", fr: "Futurs" },
|
|
68
|
+
past: { "pt-BR": "Passados", "en-US": "Past", es: "Pasados", "pt-PT": "Passados", fr: "Passes" }
|
|
69
|
+
};
|
|
70
|
+
function pickLocale(map, locale) {
|
|
71
|
+
return map[locale] ?? map["pt-BR"] ?? Object.values(map)[0] ?? "";
|
|
72
|
+
}
|
|
73
|
+
export function periodPresetLabel(preset, locale = "pt-BR") {
|
|
74
|
+
return pickLocale(PERIOD_PRESET_LABELS[preset], locale);
|
|
75
|
+
}
|
|
76
|
+
/* ------------------------------------------------------------------ */
|
|
77
|
+
/* PeriodResolver — logica pura, sem React */
|
|
78
|
+
/* ------------------------------------------------------------------ */
|
|
79
|
+
function startOfDay(d) {
|
|
80
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
|
|
81
|
+
}
|
|
82
|
+
function endOfDay(d) {
|
|
83
|
+
// Fim = ultimo instante do dia (23:59:59.999), inclusivo.
|
|
84
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999);
|
|
85
|
+
}
|
|
86
|
+
function lastDayOfMonth(year, month) {
|
|
87
|
+
return new Date(year, month + 1, 0).getDate();
|
|
88
|
+
}
|
|
89
|
+
function dayBounds(ref) {
|
|
90
|
+
return { start: startOfDay(ref), end: endOfDay(ref) };
|
|
91
|
+
}
|
|
92
|
+
/** Semana de calendario com inicio na SEGUNDA-FEIRA (documentado). */
|
|
93
|
+
function weekBounds(ref) {
|
|
94
|
+
const day = ref.getDay(); // 0=domingo .. 6=sabado
|
|
95
|
+
const daysSinceMonday = (day + 6) % 7;
|
|
96
|
+
const start = new Date(ref.getFullYear(), ref.getMonth(), ref.getDate() - daysSinceMonday);
|
|
97
|
+
const end = new Date(start.getFullYear(), start.getMonth(), start.getDate() + 6);
|
|
98
|
+
return { start: startOfDay(start), end: endOfDay(end) };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Quinzena de CALENDARIO (nao 15 dias corridos):
|
|
102
|
+
* dia 1..15 (primeira quinzena) e dia 16..ultimo dia do mes (segunda quinzena).
|
|
103
|
+
*/
|
|
104
|
+
function quinzenaBounds(ref) {
|
|
105
|
+
const year = ref.getFullYear();
|
|
106
|
+
const month = ref.getMonth();
|
|
107
|
+
if (ref.getDate() <= 15) {
|
|
108
|
+
return { start: startOfDay(new Date(year, month, 1)), end: endOfDay(new Date(year, month, 15)) };
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
start: startOfDay(new Date(year, month, 16)),
|
|
112
|
+
end: endOfDay(new Date(year, month, lastDayOfMonth(year, month)))
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** Desloca `ref` por `delta` quinzenas de calendario, podendo cruzar meses. */
|
|
116
|
+
function shiftQuinzena(ref, delta) {
|
|
117
|
+
const half = ref.getDate() <= 15 ? 0 : 1;
|
|
118
|
+
const monthIndex = ref.getFullYear() * 12 + ref.getMonth();
|
|
119
|
+
const absHalf = monthIndex * 2 + half + delta;
|
|
120
|
+
const newMonthIndex = Math.floor(absHalf / 2);
|
|
121
|
+
const newHalf = absHalf - newMonthIndex * 2;
|
|
122
|
+
const newYear = Math.floor(newMonthIndex / 12);
|
|
123
|
+
const newMonth = newMonthIndex - newYear * 12;
|
|
124
|
+
// Dia representativo dentro da quinzena alvo (5 -> 1a metade, 20 -> 2a metade).
|
|
125
|
+
return new Date(newYear, newMonth, newHalf === 0 ? 5 : 20);
|
|
126
|
+
}
|
|
127
|
+
function monthBounds(ref) {
|
|
128
|
+
const year = ref.getFullYear();
|
|
129
|
+
const month = ref.getMonth();
|
|
130
|
+
return {
|
|
131
|
+
start: startOfDay(new Date(year, month, 1)),
|
|
132
|
+
end: endOfDay(new Date(year, month, lastDayOfMonth(year, month)))
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/** Trimestre de calendario: Jan-Mar, Abr-Jun, Jul-Set, Out-Dez. */
|
|
136
|
+
function quarterBounds(ref) {
|
|
137
|
+
const year = ref.getFullYear();
|
|
138
|
+
const startMonth = Math.floor(ref.getMonth() / 3) * 3;
|
|
139
|
+
const endMonth = startMonth + 2;
|
|
140
|
+
return {
|
|
141
|
+
start: startOfDay(new Date(year, startMonth, 1)),
|
|
142
|
+
end: endOfDay(new Date(year, endMonth, lastDayOfMonth(year, endMonth)))
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/** Semestre de calendario: Jan-Jun ou Jul-Dez. */
|
|
146
|
+
function semesterBounds(ref) {
|
|
147
|
+
const year = ref.getFullYear();
|
|
148
|
+
const startMonth = ref.getMonth() < 6 ? 0 : 6;
|
|
149
|
+
const endMonth = startMonth + 5;
|
|
150
|
+
return {
|
|
151
|
+
start: startOfDay(new Date(year, startMonth, 1)),
|
|
152
|
+
end: endOfDay(new Date(year, endMonth, lastDayOfMonth(year, endMonth)))
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function yearBounds(ref) {
|
|
156
|
+
const year = ref.getFullYear();
|
|
157
|
+
return { start: startOfDay(new Date(year, 0, 1)), end: endOfDay(new Date(year, 11, 31)) };
|
|
158
|
+
}
|
|
159
|
+
function addDays(ref, days) {
|
|
160
|
+
return new Date(ref.getFullYear(), ref.getMonth(), ref.getDate() + days);
|
|
161
|
+
}
|
|
162
|
+
function addMonths(ref, months) {
|
|
163
|
+
// Usa dia 15 para evitar overflow (ex.: 31 -> mes sem dia 31).
|
|
164
|
+
return new Date(ref.getFullYear(), ref.getMonth() + months, 15);
|
|
165
|
+
}
|
|
166
|
+
function boundsFor(preset, ref) {
|
|
167
|
+
switch (preset) {
|
|
168
|
+
case PeriodPreset.ALL:
|
|
169
|
+
case PeriodPreset.CUSTOM:
|
|
170
|
+
return null;
|
|
171
|
+
case PeriodPreset.TODAY:
|
|
172
|
+
return dayBounds(ref);
|
|
173
|
+
case PeriodPreset.YESTERDAY:
|
|
174
|
+
return dayBounds(addDays(ref, -1));
|
|
175
|
+
case PeriodPreset.TOMORROW:
|
|
176
|
+
return dayBounds(addDays(ref, 1));
|
|
177
|
+
case PeriodPreset.THIS_WEEK:
|
|
178
|
+
return weekBounds(ref);
|
|
179
|
+
case PeriodPreset.LAST_WEEK:
|
|
180
|
+
return weekBounds(addDays(ref, -7));
|
|
181
|
+
case PeriodPreset.NEXT_WEEK:
|
|
182
|
+
return weekBounds(addDays(ref, 7));
|
|
183
|
+
case PeriodPreset.THIS_QUINZENA:
|
|
184
|
+
return quinzenaBounds(ref);
|
|
185
|
+
case PeriodPreset.LAST_QUINZENA:
|
|
186
|
+
return quinzenaBounds(shiftQuinzena(ref, -1));
|
|
187
|
+
case PeriodPreset.NEXT_QUINZENA:
|
|
188
|
+
return quinzenaBounds(shiftQuinzena(ref, 1));
|
|
189
|
+
case PeriodPreset.THIS_MONTH:
|
|
190
|
+
return monthBounds(ref);
|
|
191
|
+
case PeriodPreset.LAST_MONTH:
|
|
192
|
+
return monthBounds(addMonths(ref, -1));
|
|
193
|
+
case PeriodPreset.NEXT_MONTH:
|
|
194
|
+
return monthBounds(addMonths(ref, 1));
|
|
195
|
+
case PeriodPreset.THIS_QUARTER:
|
|
196
|
+
return quarterBounds(ref);
|
|
197
|
+
case PeriodPreset.LAST_QUARTER:
|
|
198
|
+
return quarterBounds(addMonths(ref, -3));
|
|
199
|
+
case PeriodPreset.NEXT_QUARTER:
|
|
200
|
+
return quarterBounds(addMonths(ref, 3));
|
|
201
|
+
case PeriodPreset.THIS_SEMESTER:
|
|
202
|
+
return semesterBounds(ref);
|
|
203
|
+
case PeriodPreset.LAST_SEMESTER:
|
|
204
|
+
return semesterBounds(addMonths(ref, -6));
|
|
205
|
+
case PeriodPreset.NEXT_SEMESTER:
|
|
206
|
+
return semesterBounds(addMonths(ref, 6));
|
|
207
|
+
case PeriodPreset.THIS_YEAR:
|
|
208
|
+
return yearBounds(ref);
|
|
209
|
+
case PeriodPreset.LAST_YEAR:
|
|
210
|
+
return yearBounds(new Date(ref.getFullYear() - 1, ref.getMonth(), 15));
|
|
211
|
+
case PeriodPreset.NEXT_YEAR:
|
|
212
|
+
return yearBounds(new Date(ref.getFullYear() + 1, ref.getMonth(), 15));
|
|
213
|
+
default:
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Resolve um preset temporal para um intervalo real de datas.
|
|
219
|
+
*
|
|
220
|
+
* Convencoes:
|
|
221
|
+
* - Semana inicia na SEGUNDA-FEIRA.
|
|
222
|
+
* - Quinzena e de calendario (dia 1..15 e 16..fim do mes), nao 15 dias corridos.
|
|
223
|
+
* - startDate = 00:00:00.000 local; endDate = 23:59:59.999 local (inclusivo).
|
|
224
|
+
* - ALL/CUSTOM retornam datas nulas.
|
|
225
|
+
* - minDate/maxDate (options) fazem clamp do intervalo resolvido.
|
|
226
|
+
*/
|
|
227
|
+
export function resolvePeriod(preset, ref = new Date(), options = {}) {
|
|
228
|
+
const locale = options.locale ?? "pt-BR";
|
|
229
|
+
const label = periodPresetLabel(preset, locale);
|
|
230
|
+
const bounds = boundsFor(preset, ref);
|
|
231
|
+
if (!bounds) {
|
|
232
|
+
return { preset, label, startDate: null, endDate: null };
|
|
233
|
+
}
|
|
234
|
+
let { start, end } = bounds;
|
|
235
|
+
if (options.minDate && start.getTime() < options.minDate.getTime()) {
|
|
236
|
+
start = new Date(options.minDate.getTime());
|
|
237
|
+
}
|
|
238
|
+
if (options.maxDate && end.getTime() > options.maxDate.getTime()) {
|
|
239
|
+
end = new Date(options.maxDate.getTime());
|
|
240
|
+
}
|
|
241
|
+
return { preset, label, startDate: start, endDate: end };
|
|
242
|
+
}
|
|
243
|
+
/* ------------------------------------------------------------------ */
|
|
244
|
+
/* Montagem das opcoes da UI a partir dos flags permit* */
|
|
245
|
+
/* ------------------------------------------------------------------ */
|
|
246
|
+
const CURRENT_PRESETS = [
|
|
247
|
+
PeriodPreset.THIS_YEAR,
|
|
248
|
+
PeriodPreset.THIS_SEMESTER,
|
|
249
|
+
PeriodPreset.THIS_QUARTER,
|
|
250
|
+
PeriodPreset.THIS_MONTH,
|
|
251
|
+
PeriodPreset.THIS_QUINZENA,
|
|
252
|
+
PeriodPreset.THIS_WEEK,
|
|
253
|
+
PeriodPreset.TODAY,
|
|
254
|
+
PeriodPreset.YESTERDAY
|
|
255
|
+
];
|
|
256
|
+
const FUTURE_PRESETS = [
|
|
257
|
+
PeriodPreset.TOMORROW,
|
|
258
|
+
PeriodPreset.NEXT_WEEK,
|
|
259
|
+
PeriodPreset.NEXT_QUINZENA,
|
|
260
|
+
PeriodPreset.NEXT_MONTH,
|
|
261
|
+
PeriodPreset.NEXT_QUARTER,
|
|
262
|
+
PeriodPreset.NEXT_SEMESTER,
|
|
263
|
+
PeriodPreset.NEXT_YEAR
|
|
264
|
+
];
|
|
265
|
+
const PAST_PRESETS = [
|
|
266
|
+
PeriodPreset.LAST_WEEK,
|
|
267
|
+
PeriodPreset.LAST_QUINZENA,
|
|
268
|
+
PeriodPreset.LAST_MONTH,
|
|
269
|
+
PeriodPreset.LAST_QUARTER,
|
|
270
|
+
PeriodPreset.LAST_SEMESTER,
|
|
271
|
+
PeriodPreset.LAST_YEAR
|
|
272
|
+
];
|
|
273
|
+
export function buildPeriodOptions(config, locale = "pt-BR") {
|
|
274
|
+
const options = [];
|
|
275
|
+
const push = (preset, group) => {
|
|
276
|
+
options.push({ id: preset, label: periodPresetLabel(preset, locale), group });
|
|
277
|
+
};
|
|
278
|
+
if (config.permitAll)
|
|
279
|
+
push(PeriodPreset.ALL, "");
|
|
280
|
+
if (config.allowCustomPeriod)
|
|
281
|
+
push(PeriodPreset.CUSTOM, "");
|
|
282
|
+
if (config.permitCurrent) {
|
|
283
|
+
const g = pickLocale(GROUP_LABELS.current, locale);
|
|
284
|
+
CURRENT_PRESETS.forEach((p) => push(p, g));
|
|
285
|
+
}
|
|
286
|
+
if (config.permitFuture) {
|
|
287
|
+
const g = pickLocale(GROUP_LABELS.future, locale);
|
|
288
|
+
FUTURE_PRESETS.forEach((p) => push(p, g));
|
|
289
|
+
}
|
|
290
|
+
if (config.permitPast) {
|
|
291
|
+
const g = pickLocale(GROUP_LABELS.past, locale);
|
|
292
|
+
PAST_PRESETS.forEach((p) => push(p, g));
|
|
293
|
+
}
|
|
294
|
+
return options;
|
|
295
|
+
}
|
|
296
|
+
/* ------------------------------------------------------------------ */
|
|
297
|
+
/* Componente */
|
|
298
|
+
/* ------------------------------------------------------------------ */
|
|
299
|
+
function toIsoDate(d) {
|
|
300
|
+
if (!d)
|
|
301
|
+
return "";
|
|
302
|
+
const year = d.getFullYear();
|
|
303
|
+
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
304
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
305
|
+
return `${year}-${month}-${day}`;
|
|
306
|
+
}
|
|
307
|
+
function parseIsoDate(value) {
|
|
308
|
+
if (!value)
|
|
309
|
+
return null;
|
|
310
|
+
const [y, m, d] = value.split("-").map((part) => Number(part));
|
|
311
|
+
if (!y || !m || !d)
|
|
312
|
+
return null;
|
|
313
|
+
return new Date(y, m - 1, d);
|
|
314
|
+
}
|
|
315
|
+
function SgPeriodSelectorBase(props) {
|
|
316
|
+
const { id = "sg-period-selector", label, value, onChange, permitPast = true, permitCurrent = true, permitFuture = true, permitAll = true, allowCustomPeriod = true, locale = "pt-BR", disabled, required, minDate, maxDate, referenceDate, className, error } = props;
|
|
317
|
+
const isControlled = value !== undefined;
|
|
318
|
+
const [internalPreset, setInternalPreset] = React.useState(value ?? null);
|
|
319
|
+
const selectedPreset = isControlled ? value ?? null : internalPreset;
|
|
320
|
+
const [customStart, setCustomStart] = React.useState(null);
|
|
321
|
+
const [customEnd, setCustomEnd] = React.useState(null);
|
|
322
|
+
const options = React.useMemo(() => buildPeriodOptions({ permitAll, allowCustomPeriod, permitCurrent, permitFuture, permitPast }, locale), [permitAll, allowCustomPeriod, permitCurrent, permitFuture, permitPast, locale]);
|
|
323
|
+
const resolveOptions = React.useMemo(() => ({ minDate, maxDate, locale }), [minDate, maxDate, locale]);
|
|
324
|
+
const emitCustom = React.useCallback((start, end) => {
|
|
325
|
+
onChange?.({
|
|
326
|
+
preset: PeriodPreset.CUSTOM,
|
|
327
|
+
label: periodPresetLabel(PeriodPreset.CUSTOM, locale),
|
|
328
|
+
startDate: start ? new Date(start.getFullYear(), start.getMonth(), start.getDate(), 0, 0, 0, 0) : null,
|
|
329
|
+
endDate: end ? new Date(end.getFullYear(), end.getMonth(), end.getDate(), 23, 59, 59, 999) : null
|
|
330
|
+
});
|
|
331
|
+
}, [locale, onChange]);
|
|
332
|
+
const handleValueChange = React.useCallback((raw) => {
|
|
333
|
+
if (raw == null || raw === "") {
|
|
334
|
+
if (!isControlled)
|
|
335
|
+
setInternalPreset(null);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const preset = String(raw);
|
|
339
|
+
if (!isControlled)
|
|
340
|
+
setInternalPreset(preset);
|
|
341
|
+
if (preset === PeriodPreset.CUSTOM) {
|
|
342
|
+
// Emite CUSTOM com o que houver preenchido no mini range (pode ser nulo).
|
|
343
|
+
emitCustom(customStart, customEnd);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
onChange?.(resolvePeriod(preset, referenceDate ?? new Date(), resolveOptions));
|
|
347
|
+
}, [customEnd, customStart, emitCustom, isControlled, onChange, referenceDate, resolveOptions]);
|
|
348
|
+
const showCustomRange = allowCustomPeriod && selectedPreset === PeriodPreset.CUSTOM;
|
|
349
|
+
return (_jsxs("div", { className: cn("w-full max-w-[18rem]", className), children: [_jsx(SgCombobox, { id: id, label: label, source: options, value: selectedPreset ?? null, grouped: true, openOnFocus: true, enabled: disabled ? false : undefined, required: required, error: error, renderGroupHeader: (group) => group ? (_jsx("div", { className: "mt-2 border-b border-border pb-1.5 text-center text-[11px] font-bold uppercase tracking-wider text-muted-foreground", children: group })) : null, onValueChange: handleValueChange }), showCustomRange ? (_jsxs("div", { className: "mt-2 grid grid-cols-1 gap-2", children: [_jsx(SgInputDate, { id: `${id}-custom-start`, inputProps: { value: toIsoDate(customStart) }, minDate: minDate, maxDate: maxDate, enabled: disabled ? false : undefined, onChange: (next) => {
|
|
350
|
+
const parsed = parseIsoDate(next);
|
|
351
|
+
setCustomStart(parsed);
|
|
352
|
+
emitCustom(parsed, customEnd);
|
|
353
|
+
} }), _jsx(SgInputDate, { id: `${id}-custom-end`, inputProps: { value: toIsoDate(customEnd) }, minDate: minDate, maxDate: maxDate, enabled: disabled ? false : undefined, onChange: (next) => {
|
|
354
|
+
const parsed = parseIsoDate(next);
|
|
355
|
+
setCustomEnd(parsed);
|
|
356
|
+
emitCustom(customStart, parsed);
|
|
357
|
+
} })] })) : null] }));
|
|
358
|
+
}
|
|
359
|
+
export function SgPeriodSelector(props) {
|
|
360
|
+
const { control, name, rules, ...rest } = props;
|
|
361
|
+
if (control && name) {
|
|
362
|
+
return (_jsx(Controller, { name: name, control: control, rules: rules, render: ({ field, fieldState }) => (_jsx(SgPeriodSelectorBase, { ...rest, error: resolveFieldError(rest.error, fieldState.error?.message), value: field.value ?? undefined, onChange: (period) => {
|
|
363
|
+
field.onChange(period.preset);
|
|
364
|
+
rest.onChange?.(period);
|
|
365
|
+
} })) }));
|
|
366
|
+
}
|
|
367
|
+
return _jsx(SgPeriodSelectorBase, { ...rest });
|
|
368
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SgPeriodSelector.meta.d.ts","sourceRoot":"","sources":["../../src/inputs/SgPeriodSelector.meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE9D,eAAO,MAAM,MAAM,EAAE,QAmCpB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,WAmBrB,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export const sgMeta = {
|
|
2
|
+
version: "0.1",
|
|
3
|
+
componentId: "form.input.periodSelector",
|
|
4
|
+
package: "@seedgrid/fe-components",
|
|
5
|
+
exportName: "SgPeriodSelector",
|
|
6
|
+
slug: "sg-period-selector",
|
|
7
|
+
displayName: "SgPeriodSelector",
|
|
8
|
+
category: "input",
|
|
9
|
+
subcategory: "period",
|
|
10
|
+
description: "Seletor de periodos pre-definidos (Este mes, Ultimo trimestre, etc.) que resolve a regra temporal para um intervalo real de datas.",
|
|
11
|
+
tags: ["form", "period", "date-range", "filter", "report", "rhf"],
|
|
12
|
+
capabilities: ["single-selection", "grouped-results", "date-resolution", "custom-range"],
|
|
13
|
+
fieldSemantics: ["dateRange", "periodFilter", "structuredChoice"],
|
|
14
|
+
props: [
|
|
15
|
+
{ name: "id", type: "string", description: "Identificador unico do campo.", semanticRole: "data", bindable: false },
|
|
16
|
+
{ name: "label", type: "string", description: "Rotulo exibido ao usuario.", semanticRole: "label", bindable: true },
|
|
17
|
+
{ name: "value", type: "PeriodPreset", description: "Preset selecionado (modo controlado).", semanticRole: "value", bindable: true },
|
|
18
|
+
{ name: "onChange", type: "(period: ResolvedPeriod) => void", description: "Callback com o preset e intervalo resolvido.", semanticRole: "event", bindable: false },
|
|
19
|
+
{ name: "permitPast", type: "boolean", default: true, description: "Habilita o grupo de periodos passados.", semanticRole: "behavior", bindable: true },
|
|
20
|
+
{ name: "permitCurrent", type: "boolean", default: true, description: "Habilita o grupo de periodos atuais.", semanticRole: "behavior", bindable: true },
|
|
21
|
+
{ name: "permitFuture", type: "boolean", default: true, description: "Habilita o grupo de periodos futuros.", semanticRole: "behavior", bindable: true },
|
|
22
|
+
{ name: "permitAll", type: "boolean", default: true, description: "Habilita a opcao Todos (sem limites de data).", semanticRole: "behavior", bindable: true },
|
|
23
|
+
{ name: "allowCustomPeriod", type: "boolean", default: true, description: "Habilita a opcao Periodo (intervalo customizado).", semanticRole: "behavior", bindable: true },
|
|
24
|
+
{ name: "locale", type: "string", default: "pt-BR", description: "Locale usado nos labels e formatacao.", semanticRole: "behavior", bindable: true },
|
|
25
|
+
{ name: "minDate", type: "Date", description: "Limite inferior aplicado como clamp no intervalo.", semanticRole: "behavior", bindable: true },
|
|
26
|
+
{ name: "maxDate", type: "Date", description: "Limite superior aplicado como clamp no intervalo.", semanticRole: "behavior", bindable: true }
|
|
27
|
+
],
|
|
28
|
+
states: ["default", "focused", "open", "disabled", "error", "custom-range"],
|
|
29
|
+
showcase: { route: "/components/sg-period-selector", hasPlayground: false, hasPropsTable: true },
|
|
30
|
+
sdui: {
|
|
31
|
+
rendererType: "field.periodSelector",
|
|
32
|
+
acceptsDataBinding: true,
|
|
33
|
+
defaultProps: { permitPast: true, permitCurrent: true, permitFuture: true, permitAll: true, allowCustomPeriod: true, locale: "pt-BR" }
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
export const aiHints = {
|
|
37
|
+
version: "0.1",
|
|
38
|
+
preferredUseCases: [
|
|
39
|
+
"Filtros de periodo em relatorios, dashboards e telas financeiras.",
|
|
40
|
+
"Escolha rapida de intervalos recorrentes (Este mes, Ultimo trimestre, Este ano).",
|
|
41
|
+
"Substituir dois date pickers quando a maioria dos filtros e por regra temporal."
|
|
42
|
+
],
|
|
43
|
+
avoidUseCases: [
|
|
44
|
+
"Selecao de uma unica data especifica; prefira SgInputDate.",
|
|
45
|
+
"Intervalos totalmente arbitrarios sem presets; use um date range dedicado.",
|
|
46
|
+
"Selecao de horario/hora do dia."
|
|
47
|
+
],
|
|
48
|
+
synonyms: ["period selector", "date range preset", "seletor de periodo", "filtro de periodo", "period filter"],
|
|
49
|
+
relatedEntityFields: ["periodo", "dataInicio", "dataFim", "competencia", "vencimento"],
|
|
50
|
+
compositionHints: [
|
|
51
|
+
"Combinar com SgDatatable e graficos como filtro superior.",
|
|
52
|
+
"Usar dentro de SgToolBar ou SgPanel de filtros."
|
|
53
|
+
],
|
|
54
|
+
rankingSignals: { freeText: 0, structuredChoice: 0.85, date: 0.95, number: 0, denseLayout: 0.7 }
|
|
55
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type SgMultiSelectOptionValue = string | number;
|
|
3
|
+
export type SgMultiSelectOption = {
|
|
4
|
+
label: string;
|
|
5
|
+
value: SgMultiSelectOptionValue;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare function valueEquals(left: SgMultiSelectOptionValue, right: SgMultiSelectOptionValue): boolean;
|
|
9
|
+
export type UseSgMultiSelectParams = {
|
|
10
|
+
options: SgMultiSelectOption[];
|
|
11
|
+
value?: SgMultiSelectOptionValue[];
|
|
12
|
+
onChange?: (value: SgMultiSelectOptionValue[]) => void;
|
|
13
|
+
searchable?: boolean;
|
|
14
|
+
maxSelected?: number;
|
|
15
|
+
isDisabled: boolean;
|
|
16
|
+
borderRadius?: number | string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Logica compartilhada entre SgMultiSelect e SgMultiSelectChips: estado de
|
|
20
|
+
* abertura, selecao, busca, posicionamento do dropdown via portal e navegacao
|
|
21
|
+
* por teclado. Os componentes apenas renderizam o trigger e reaproveitam o
|
|
22
|
+
* dropdown a partir do que este hook expoe.
|
|
23
|
+
*/
|
|
24
|
+
export declare function useSgMultiSelect(params: UseSgMultiSelectParams): {
|
|
25
|
+
readonly wrapperRef: React.RefObject<HTMLDivElement | null>;
|
|
26
|
+
readonly inputRef: React.RefObject<HTMLInputElement | null>;
|
|
27
|
+
readonly dropdownRef: React.RefObject<HTMLDivElement | null>;
|
|
28
|
+
readonly searchRef: React.RefObject<HTMLInputElement | null>;
|
|
29
|
+
readonly ignoreBlurRef: React.RefObject<boolean>;
|
|
30
|
+
readonly open: boolean;
|
|
31
|
+
readonly activeIndex: number;
|
|
32
|
+
readonly query: string;
|
|
33
|
+
readonly dropdownStyle: React.CSSProperties;
|
|
34
|
+
readonly resolvedValue: SgMultiSelectOptionValue[];
|
|
35
|
+
readonly selectedLabels: string[];
|
|
36
|
+
readonly selectedOptions: SgMultiSelectOption[];
|
|
37
|
+
readonly visibleOptions: SgMultiSelectOption[];
|
|
38
|
+
readonly setQuery: React.Dispatch<React.SetStateAction<string>>;
|
|
39
|
+
readonly setActiveIndex: React.Dispatch<React.SetStateAction<number>>;
|
|
40
|
+
readonly isSelected: (optionValue: SgMultiSelectOptionValue) => boolean;
|
|
41
|
+
readonly toggleOption: (option: SgMultiSelectOption) => void;
|
|
42
|
+
readonly removeValue: (optionValue: SgMultiSelectOptionValue) => void;
|
|
43
|
+
readonly clearSelection: () => void;
|
|
44
|
+
readonly closeDropdown: () => void;
|
|
45
|
+
readonly openDropdown: () => void;
|
|
46
|
+
readonly handleListKeyDown: (event: React.KeyboardEvent) => void;
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=useSgMultiSelect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSgMultiSelect.d.ts","sourceRoot":"","sources":["../../src/inputs/useSgMultiSelect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvD,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,wBAAwB,CAAC;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,wBAAgB,WAAW,CAAC,IAAI,EAAE,wBAAwB,EAAE,KAAK,EAAE,wBAAwB,WAE1F;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,KAAK,CAAC,EAAE,wBAAwB,EAAE,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,EAAE,KAAK,IAAI,CAAC;IACvD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB;;;;;;;;;;;;;;;;uCAyB7C,wBAAwB;oCA6C7B,mBAAmB;wCAgBd,wBAAwB;;;;wCA4E9B,KAAK,CAAC,aAAa;EAiE9B"}
|