rl-core-front 0.16.5 → 0.16.6
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/package.json
CHANGED
|
@@ -38,6 +38,18 @@ export interface DatePickerProps {
|
|
|
38
38
|
/** Descreve o campo para quem usa leitor de tela. */
|
|
39
39
|
label?: string;
|
|
40
40
|
placeholder?: string;
|
|
41
|
+
/**
|
|
42
|
+
* O primeiro dia escolhível (`"yyyy-MM-dd"`) — os anteriores ficam apagados
|
|
43
|
+
* no calendário e o digitado é recusado.
|
|
44
|
+
*
|
|
45
|
+
* Existe porque avisar depois não serve para data: o vencimento anterior à
|
|
46
|
+
* compra e o pagamento anterior ao lançamento são erros que a pessoa comete
|
|
47
|
+
* sem perceber, e descobrir no botão Salvar é refazer o caminho. Aqui o dia
|
|
48
|
+
* simplesmente não se deixa escolher.
|
|
49
|
+
*/
|
|
50
|
+
minDay?: string;
|
|
51
|
+
/** O último dia escolhível, pela mesma razão. */
|
|
52
|
+
maxDay?: string;
|
|
41
53
|
}
|
|
42
54
|
|
|
43
55
|
/**
|
|
@@ -69,6 +81,8 @@ export function DatePicker({
|
|
|
69
81
|
disabled,
|
|
70
82
|
label,
|
|
71
83
|
placeholder,
|
|
84
|
+
minDay,
|
|
85
|
+
maxDay,
|
|
72
86
|
}: DatePickerProps): JSX.Element {
|
|
73
87
|
const { t, locale } = useI18n();
|
|
74
88
|
const [open, setOpen] = useState(false);
|
|
@@ -104,8 +118,24 @@ export function DatePicker({
|
|
|
104
118
|
caret.current = null;
|
|
105
119
|
});
|
|
106
120
|
|
|
121
|
+
/**
|
|
122
|
+
* O dia está dentro da faixa permitida?
|
|
123
|
+
*
|
|
124
|
+
* Comparação de texto ISO, e não de `Date`: `2026-09-03 < 2026-09-08` é
|
|
125
|
+
* verdade em ordem alfabética porque o formato é fixo, e assim não há
|
|
126
|
+
* conversão de fuso no meio para transformar o dia 1º no dia 30.
|
|
127
|
+
*/
|
|
128
|
+
const dayAllowed = (iso: string): boolean =>
|
|
129
|
+
(!minDay || iso >= minDay) && (!maxDay || iso <= maxDay);
|
|
130
|
+
|
|
107
131
|
const pickDay = (day: Date): void => {
|
|
108
|
-
|
|
132
|
+
const iso = toIsoDay(day);
|
|
133
|
+
|
|
134
|
+
if (!dayAllowed(iso)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
onChange(iso);
|
|
109
139
|
setTyped(null);
|
|
110
140
|
setOpen(false);
|
|
111
141
|
};
|
|
@@ -129,7 +159,9 @@ export function DatePicker({
|
|
|
129
159
|
(selected ?? today()).getFullYear(),
|
|
130
160
|
);
|
|
131
161
|
|
|
132
|
-
|
|
162
|
+
// Digitar é o outro caminho para o mesmo campo: sem esta guarda, o dia que
|
|
163
|
+
// o calendário recusa entraria pelo teclado.
|
|
164
|
+
if (iso && dayAllowed(iso)) {
|
|
133
165
|
onChange(iso);
|
|
134
166
|
setMonth(fromIsoDay(iso) ?? month);
|
|
135
167
|
}
|
|
@@ -180,6 +212,11 @@ export function DatePicker({
|
|
|
180
212
|
};
|
|
181
213
|
|
|
182
214
|
const dayClass = (day: Date): string => {
|
|
215
|
+
if (!dayAllowed(toIsoDay(day))) {
|
|
216
|
+
// Continua desenhado, e não escondido: o calendário sem os dias 1 a 7
|
|
217
|
+
// pareceria quebrado, e é o cinza que diz "existe, mas não aqui".
|
|
218
|
+
return "text-muted-foreground/30 line-through";
|
|
219
|
+
}
|
|
183
220
|
if (selected && isSameDay(day, selected)) {
|
|
184
221
|
return "bg-primary text-primary-foreground font-semibold";
|
|
185
222
|
}
|
|
@@ -285,9 +322,13 @@ export function DatePicker({
|
|
|
285
322
|
<button
|
|
286
323
|
key={day.toISOString()}
|
|
287
324
|
type="button"
|
|
325
|
+
disabled={!dayAllowed(toIsoDay(day))}
|
|
288
326
|
onClick={() => pickDay(day)}
|
|
289
327
|
className={cn(
|
|
290
|
-
"flex h-8 w-8 items-center justify-center rounded-full text-sm transition-colors
|
|
328
|
+
"flex h-8 w-8 items-center justify-center rounded-full text-sm transition-colors",
|
|
329
|
+
dayAllowed(toIsoDay(day))
|
|
330
|
+
? "hover:bg-accent"
|
|
331
|
+
: "cursor-not-allowed hover:bg-transparent",
|
|
291
332
|
dayClass(day),
|
|
292
333
|
)}
|
|
293
334
|
>
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
DialogHeader,
|
|
20
20
|
DialogTitle,
|
|
21
21
|
Field,
|
|
22
|
+
HoverTip,
|
|
22
23
|
Input,
|
|
23
24
|
Label,
|
|
24
25
|
} from "#core/components/ui";
|
|
@@ -151,23 +152,29 @@ export function RolesPanel(): JSX.Element {
|
|
|
151
152
|
{canManage && (
|
|
152
153
|
<div className="flex">
|
|
153
154
|
{hasPermission("roles:update:any") && (
|
|
154
|
-
<
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
155
|
+
<HoverTip label={t("common.edit")}>
|
|
156
|
+
<Button
|
|
157
|
+
aria-label={t("common.edit")}
|
|
158
|
+
variant="ghost"
|
|
159
|
+
size="icon"
|
|
160
|
+
onClick={() => openEdit(role)}
|
|
161
|
+
>
|
|
162
|
+
<Pencil className="h-4 w-4" />
|
|
163
|
+
</Button>
|
|
164
|
+
</HoverTip>
|
|
161
165
|
)}
|
|
162
166
|
{hasPermission("roles:delete:any") && (
|
|
163
|
-
<
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
167
|
+
<HoverTip label={t("common.delete")}>
|
|
168
|
+
<Button
|
|
169
|
+
aria-label={t("common.delete")}
|
|
170
|
+
variant="ghost"
|
|
171
|
+
size="icon"
|
|
172
|
+
className="text-destructive"
|
|
173
|
+
onClick={() => remove(role)}
|
|
174
|
+
>
|
|
175
|
+
<Trash2 className="h-4 w-4" />
|
|
176
|
+
</Button>
|
|
177
|
+
</HoverTip>
|
|
171
178
|
)}
|
|
172
179
|
</div>
|
|
173
180
|
)}
|