@fayz-ai/ui 0.23.0 → 0.24.1

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.
@@ -7,7 +7,7 @@ import React15__default, { useState, useRef, useEffect, useMemo, useCallback } f
7
7
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
8
  import { cva } from 'class-variance-authority';
9
9
  import * as DialogPrimitive from '@radix-ui/react-dialog';
10
- import { X, ChevronDown, ChevronUp, Check, ArrowLeft, MoreHorizontal, Search, ChevronLeft, ChevronRight, ImageIcon, RefreshCw, Clock, Plus, Download, Calendar, Loader2, CalendarDays, SlidersHorizontal } from 'lucide-react';
10
+ import { X, ChevronDown, ChevronUp, Check, ArrowLeft, MoreHorizontal, Search, ChevronsLeft, ChevronLeft, ChevronRight, ChevronsRight, ImageIcon, RefreshCw, Clock, Plus, Download, Calendar, Loader2, CalendarDays, SlidersHorizontal } from 'lucide-react';
11
11
  import * as SelectPrimitive from '@radix-ui/react-select';
12
12
  import { Toaster } from 'sonner';
13
13
  export { toast } from 'sonner';
@@ -564,6 +564,9 @@ function ChevronPager({ page, pageSize, total, onPageChange, noun }) {
564
564
  const from = total === 0 ? 0 : (page - 1) * pageSize + 1;
565
565
  const to = Math.min(page * pageSize, total);
566
566
  const button = "flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:pointer-events-none disabled:opacity-40";
567
+ const JANELA = 5;
568
+ const inicio = Math.max(1, Math.min(page - Math.floor(JANELA / 2), pages - JANELA + 1));
569
+ const numeros = Array.from({ length: Math.min(JANELA, pages) }, (_, i) => inicio + i);
567
570
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 border-t border-border px-3 py-2", children: [
568
571
  /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
569
572
  from.toLocaleString(),
@@ -574,6 +577,17 @@ function ChevronPager({ page, pageSize, total, onPageChange, noun }) {
574
577
  noun ? ` ${noun}` : ""
575
578
  ] }),
576
579
  pages > 1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
580
+ pages > JANELA && /* @__PURE__ */ jsx(
581
+ "button",
582
+ {
583
+ type: "button",
584
+ "aria-label": "Primeira p\xE1gina",
585
+ className: button,
586
+ disabled: page <= 1,
587
+ onClick: () => onPageChange(1),
588
+ children: /* @__PURE__ */ jsx(ChevronsLeft, { className: "h-4 w-4" })
589
+ }
590
+ ),
577
591
  /* @__PURE__ */ jsx(
578
592
  "button",
579
593
  {
@@ -585,11 +599,18 @@ function ChevronPager({ page, pageSize, total, onPageChange, noun }) {
585
599
  children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
586
600
  }
587
601
  ),
588
- /* @__PURE__ */ jsxs("span", { className: "px-1 text-xs tabular-nums text-muted-foreground", children: [
589
- page.toLocaleString(),
590
- " / ",
591
- pages.toLocaleString()
592
- ] }),
602
+ numeros.map((n) => /* @__PURE__ */ jsx(
603
+ "button",
604
+ {
605
+ type: "button",
606
+ "aria-label": `P\xE1gina ${n}`,
607
+ "aria-current": n === page ? "page" : void 0,
608
+ onClick: () => onPageChange(n),
609
+ className: n === page ? "flex h-7 min-w-7 items-center justify-center rounded-md bg-primary px-1.5 text-xs font-medium tabular-nums text-primary-foreground" : "flex h-7 min-w-7 items-center justify-center rounded-md px-1.5 text-xs tabular-nums text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
610
+ children: n.toLocaleString()
611
+ },
612
+ n
613
+ )),
593
614
  /* @__PURE__ */ jsx(
594
615
  "button",
595
616
  {
@@ -600,6 +621,17 @@ function ChevronPager({ page, pageSize, total, onPageChange, noun }) {
600
621
  onClick: () => onPageChange(page + 1),
601
622
  children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
602
623
  }
624
+ ),
625
+ pages > JANELA && /* @__PURE__ */ jsx(
626
+ "button",
627
+ {
628
+ type: "button",
629
+ "aria-label": "\xDAltima p\xE1gina",
630
+ className: button,
631
+ disabled: page >= pages,
632
+ onClick: () => onPageChange(pages),
633
+ children: /* @__PURE__ */ jsx(ChevronsRight, { className: "h-4 w-4" })
634
+ }
603
635
  )
604
636
  ] })
605
637
  ] });
@@ -1066,6 +1098,18 @@ function FilterChip({ allLabel, options, value, onChange, label, icon: Icon2, cl
1066
1098
  ] });
1067
1099
  }
1068
1100
  var ICON_BTN = "fz-tap text-muted-foreground";
1101
+ function pageNumbers(page, totalPages) {
1102
+ const fixas = /* @__PURE__ */ new Set([1, totalPages, page - 1, page, page + 1]);
1103
+ const visiveis = [...fixas].filter((n) => n >= 1 && n <= totalPages).sort((a, b) => a - b);
1104
+ const saida = [];
1105
+ for (const n of visiveis) {
1106
+ const anterior = saida[saida.length - 1];
1107
+ if (anterior !== void 0 && n - anterior === 2) saida.push(anterior + 1);
1108
+ else if (anterior !== void 0 && n - anterior > 2) saida.push(-1);
1109
+ saida.push(n);
1110
+ }
1111
+ return saida;
1112
+ }
1069
1113
  function ListFilters({ tagFilter, filters, advanced, label, count, wideCount }) {
1070
1114
  const wide = useIsWide();
1071
1115
  const [open, setOpen] = useState(false);
@@ -1124,6 +1168,7 @@ function ListView({
1124
1168
  search: controlledSearch,
1125
1169
  onSearchChange,
1126
1170
  searchDebounce = 300,
1171
+ searchOwnRow,
1127
1172
  tags,
1128
1173
  allTagLabel = "All",
1129
1174
  tagsLabel,
@@ -1142,8 +1187,11 @@ function ListView({
1142
1187
  page = 1,
1143
1188
  pageSize = 25,
1144
1189
  rangeOfLabel = "of",
1190
+ firstPageLabel = "First page",
1145
1191
  prevPageLabel = "Previous page",
1146
1192
  nextPageLabel = "Next page",
1193
+ lastPageLabel = "Last page",
1194
+ pageLabel = "Page",
1147
1195
  onPageChange,
1148
1196
  toolbar,
1149
1197
  filters,
@@ -1171,6 +1219,7 @@ function ListView({
1171
1219
  }, [onSearchChange, searchDebounce]);
1172
1220
  const totalItems = total ?? data.length;
1173
1221
  const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
1222
+ const pageWindow = pageNumbers(page, totalPages);
1174
1223
  const showPagination = onPageChange && totalItems > pageSize;
1175
1224
  const showSearch = !!onSearchChange;
1176
1225
  const showTags = tags && tags.length > 0 && onTagChange;
@@ -1219,7 +1268,17 @@ function ListView({
1219
1268
  actions: selection.barActions
1220
1269
  }
1221
1270
  ) : /* @__PURE__ */ jsxs(Fragment, { children: [
1222
- showSearch && /* @__PURE__ */ jsxs("div", { className: "relative min-w-0 flex-1", children: [
1271
+ showSearch && // `basis-full` e não uma segunda `div`: o contentor é
1272
+ // `flex-wrap`, então uma primeira cria que ocupa a linha
1273
+ // inteira empurra o resto para baixo sozinha. Sem filtros,
1274
+ // a busca volta a partilhar a linha com atualizar/exportar.
1275
+ // `basis-full` SEM `flex-1`, e não os dois: `flex-1` é
1276
+ // `flex: 1 1 0%`, que fixa `flex-basis: 0%` — e o Tailwind
1277
+ // emite `basis-*` ANTES de `flex-*`, então os dois juntos
1278
+ // deixam o `flex-1` ganhar e a busca continua a encolher
1279
+ // até "Busc". Sozinha na linha ela não precisa crescer:
1280
+ // a base de 100% já é a linha inteira.
1281
+ /* @__PURE__ */ jsxs("div", { className: cn("relative min-w-0", searchOwnRow ? "basis-full" : "flex-1"), children: [
1223
1282
  /* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }),
1224
1283
  /* @__PURE__ */ jsx(
1225
1284
  "input",
@@ -1280,8 +1339,8 @@ function ListView({
1280
1339
  getRowId
1281
1340
  }
1282
1341
  ),
1283
- showPagination && /* @__PURE__ */ jsxs("div", { className: "-mt-px flex flex-wrap items-center justify-between gap-2 rounded-b-card border-t border-border px-3 py-2", children: [
1284
- /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
1342
+ showPagination && /* @__PURE__ */ jsxs("div", { className: "-mt-px flex min-h-9 flex-col items-center gap-2 rounded-b-card border-t border-border px-3 py-2 sm:grid sm:grid-cols-[1fr_auto_1fr] sm:gap-3", children: [
1343
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground sm:justify-self-start", children: [
1285
1344
  (page - 1) * pageSize + 1,
1286
1345
  "\u2013",
1287
1346
  Math.min(page * pageSize, totalItems),
@@ -1291,6 +1350,17 @@ function ListView({
1291
1350
  totalItems.toLocaleString()
1292
1351
  ] }),
1293
1352
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
1353
+ totalPages > 1 && /* @__PURE__ */ jsx(
1354
+ "button",
1355
+ {
1356
+ type: "button",
1357
+ onClick: () => onPageChange(1),
1358
+ disabled: page <= 1,
1359
+ "aria-label": firstPageLabel,
1360
+ className: PAGER_BTN,
1361
+ children: /* @__PURE__ */ jsx(ChevronsLeft, { className: "h-4 w-4" })
1362
+ }
1363
+ ),
1294
1364
  /* @__PURE__ */ jsx(
1295
1365
  "button",
1296
1366
  {
@@ -1302,11 +1372,23 @@ function ListView({
1302
1372
  children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
1303
1373
  }
1304
1374
  ),
1305
- /* @__PURE__ */ jsxs("span", { className: "px-1 text-xs tabular-nums text-muted-foreground", children: [
1306
- page,
1307
- " / ",
1308
- totalPages
1309
- ] }),
1375
+ pageWindow.map((n, i) => n === -1 ? (
1376
+ // Sem `onClick` e fora da ordem de tabulação: é um sinal de que
1377
+ // há páginas ali, não um destino. Um "…" clicável promete um
1378
+ // salto que ninguém sabe para onde vai.
1379
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "px-0.5 text-xs text-muted-foreground", children: "\u2026" }, `gap-${i}`)
1380
+ ) : /* @__PURE__ */ jsx(
1381
+ "button",
1382
+ {
1383
+ type: "button",
1384
+ onClick: () => onPageChange(n),
1385
+ "aria-label": `${pageLabel} ${n}`,
1386
+ "aria-current": n === page ? "page" : void 0,
1387
+ className: n === page ? "flex h-7 min-w-7 items-center justify-center rounded-md bg-primary px-1.5 text-xs font-medium tabular-nums text-primary-foreground" : "flex h-7 min-w-7 items-center justify-center rounded-md px-1.5 text-xs tabular-nums text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
1388
+ children: n
1389
+ },
1390
+ n
1391
+ )),
1310
1392
  /* @__PURE__ */ jsx(
1311
1393
  "button",
1312
1394
  {
@@ -1317,6 +1399,17 @@ function ListView({
1317
1399
  className: PAGER_BTN,
1318
1400
  children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
1319
1401
  }
1402
+ ),
1403
+ totalPages > 1 && /* @__PURE__ */ jsx(
1404
+ "button",
1405
+ {
1406
+ type: "button",
1407
+ onClick: () => onPageChange(totalPages),
1408
+ disabled: page >= totalPages,
1409
+ "aria-label": lastPageLabel,
1410
+ className: PAGER_BTN,
1411
+ children: /* @__PURE__ */ jsx(ChevronsRight, { className: "h-4 w-4" })
1412
+ }
1320
1413
  )
1321
1414
  ] })
1322
1415
  ] })
@@ -2141,7 +2234,9 @@ function MultiSelect({
2141
2234
  emptyLabel = "Nada encontrado",
2142
2235
  disabled,
2143
2236
  className,
2144
- searchThreshold = 8
2237
+ searchThreshold = 8,
2238
+ variant = "field",
2239
+ icon: Icon2
2145
2240
  }) {
2146
2241
  const [query, setQuery] = React15.useState("");
2147
2242
  const selected = React15.useMemo(() => new Set(value), [value]);
@@ -2160,15 +2255,22 @@ function MultiSelect({
2160
2255
  {
2161
2256
  type: "button",
2162
2257
  disabled,
2258
+ "aria-label": allLabel,
2163
2259
  className: cn(
2164
- "flex h-9 w-full items-center justify-between gap-2 rounded-input border border-input bg-card px-3 py-1.5 text-xs",
2165
- "shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] transition-colors hover:bg-muted/40",
2166
2260
  "disabled:cursor-not-allowed disabled:opacity-50",
2167
- value.length === 0 && "text-muted-foreground",
2261
+ variant === "chip" ? cn(
2262
+ "inline-flex shrink-0 items-center gap-1 rounded-full border px-2.5 py-1 text-xs font-medium transition-colors",
2263
+ value.length > 0 ? "border-primary/30 bg-primary/10 text-primary hover:bg-primary/15" : "border-transparent bg-muted/70 text-muted-foreground hover:bg-muted hover:text-foreground"
2264
+ ) : cn(
2265
+ "flex h-9 w-full items-center justify-between gap-2 rounded-input border border-input bg-card px-3 py-1.5 text-xs",
2266
+ "shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] transition-colors hover:bg-muted/40",
2267
+ value.length === 0 && "text-muted-foreground"
2268
+ ),
2168
2269
  className
2169
2270
  ),
2170
2271
  children: [
2171
- /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: label }),
2272
+ variant === "chip" && Icon2 && /* @__PURE__ */ jsx(Icon2, { className: "h-3 w-3 shrink-0 opacity-70" }),
2273
+ /* @__PURE__ */ jsx("span", { className: cn("min-w-0 truncate", variant === "chip" && "max-w-[12rem]"), children: label }),
2172
2274
  /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-1", children: [
2173
2275
  value.length > 0 && // Limpar dentro do gatilho: a alternativa é desmarcar cinco itens
2174
2276
  // um a um para voltar a "todos", e é o caminho de volta mais
@@ -2188,43 +2290,64 @@ function MultiSelect({
2188
2290
  children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" })
2189
2291
  }
2190
2292
  ),
2191
- /* @__PURE__ */ jsx(ChevronDown, { className: "h-3.5 w-3.5 shrink-0 opacity-50" })
2293
+ /* @__PURE__ */ jsx(ChevronDown, { className: cn("shrink-0", variant === "chip" ? "h-3 w-3 opacity-60" : "h-3.5 w-3.5 opacity-50") })
2192
2294
  ] })
2193
2295
  ]
2194
2296
  }
2195
2297
  ) }),
2196
- /* @__PURE__ */ jsxs(DropdownContent, { align: "start", className: "w-[--radix-dropdown-menu-trigger-width] p-0", children: [
2197
- options.length >= searchThreshold && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b px-2 py-1.5", children: [
2198
- /* @__PURE__ */ jsx(Search, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
2199
- /* @__PURE__ */ jsx(
2200
- "input",
2201
- {
2202
- value: query,
2203
- onChange: (e) => setQuery(e.target.value),
2204
- placeholder: searchPlaceholder,
2205
- onKeyDown: (e) => e.stopPropagation(),
2206
- className: "w-full bg-transparent text-xs outline-none placeholder:text-muted-foreground"
2207
- }
2208
- )
2209
- ] }),
2210
- /* @__PURE__ */ jsx("div", { className: "max-h-64 overflow-y-auto py-1", children: filtered.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-3 py-4 text-center text-xs text-muted-foreground", children: emptyLabel }) : filtered.map((o) => /* @__PURE__ */ jsxs(
2211
- "button",
2212
- {
2213
- type: "button",
2214
- onClick: (e) => {
2215
- e.preventDefault();
2216
- toggle(o.value);
2217
- },
2218
- className: "flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs transition-colors hover:bg-muted",
2219
- children: [
2220
- /* @__PURE__ */ jsx(Checkbox, { checked: selected.has(o.value), onChange: () => toggle(o.value) }),
2221
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: o.label }),
2222
- o.hint && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: o.hint })
2223
- ]
2224
- },
2225
- o.value
2226
- )) })
2227
- ] })
2298
+ /* @__PURE__ */ jsxs(
2299
+ DropdownContent,
2300
+ {
2301
+ align: "start",
2302
+ className: cn(
2303
+ "p-0",
2304
+ // Em `field` o painel casa com o campo, que é largo. Em `chip` a
2305
+ // largura do gatilho é a do rótulo — herdá-la daria uma lista de
2306
+ // dois dedos. Aqui ele ganha a sua própria medida de leitura.
2307
+ variant === "chip" ? "w-[min(16rem,calc(100vw-1.5rem))]" : "w-[--radix-dropdown-menu-trigger-width]"
2308
+ ),
2309
+ children: [
2310
+ options.length >= searchThreshold && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b px-2 py-1.5", children: [
2311
+ /* @__PURE__ */ jsx(Search, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
2312
+ /* @__PURE__ */ jsx(
2313
+ "input",
2314
+ {
2315
+ value: query,
2316
+ onChange: (e) => setQuery(e.target.value),
2317
+ placeholder: searchPlaceholder,
2318
+ onKeyDown: (e) => e.stopPropagation(),
2319
+ className: "w-full bg-transparent text-xs outline-none placeholder:text-muted-foreground"
2320
+ }
2321
+ )
2322
+ ] }),
2323
+ /* @__PURE__ */ jsx("div", { className: "max-h-64 overflow-y-auto py-1", children: filtered.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-3 py-4 text-center text-xs text-muted-foreground", children: emptyLabel }) : filtered.map((o) => /* @__PURE__ */ jsxs(
2324
+ "button",
2325
+ {
2326
+ type: "button",
2327
+ onClick: (e) => {
2328
+ e.preventDefault();
2329
+ toggle(o.value);
2330
+ },
2331
+ className: "flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs transition-colors hover:bg-muted",
2332
+ children: [
2333
+ /* @__PURE__ */ jsx(Checkbox, { checked: selected.has(o.value), onChange: () => toggle(o.value) }),
2334
+ o.color && /* @__PURE__ */ jsx(
2335
+ "span",
2336
+ {
2337
+ "aria-hidden": true,
2338
+ className: "h-2 w-2 shrink-0 rounded-full ring-1 ring-inset ring-black/10",
2339
+ style: { backgroundColor: o.color }
2340
+ }
2341
+ ),
2342
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: o.label }),
2343
+ o.hint && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: o.hint })
2344
+ ]
2345
+ },
2346
+ o.value
2347
+ )) })
2348
+ ]
2349
+ }
2350
+ )
2228
2351
  ] });
2229
2352
  }
2230
2353
  var INLINE_TOKEN = /(\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g;
@@ -2772,5 +2895,5 @@ function StockEntryForm({
2772
2895
  }
2773
2896
 
2774
2897
  export { Badge, Breadcrumb, BulkActionBar, ChevronPager, CurrencyInput, DatePicker, DateRangeChip, DotBadge, DraftCommitBar, FilterCheckBox, FilterCheckList, FilterCheckRow, FilterChip, FilterSection, Input, ListView, Markdown, MarkdownEditor, MiniCalendar, Modal, ModalBody, ModalClose, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalOverlay, ModalPortal, ModalTitle, ModalTrigger, MultiSelect, PhoneField, PhoneLink, RefreshButton, RowActions, SearchCombobox, SearchField, SearchSelect, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator2 as Separator, StatTiles, StockEntryForm, Tabs, TabsContent, TabsList, TabsTrigger, Thumbnail, TimePicker, ToastProvider, WHATSAPP_BRAND_COLOR, WhatsAppIcon, badgeVariants, formatPhone2 as formatPhone, normalizePhone, renderMarkdown, setRowActionsLabel, toLocalISODate, useRowSelection };
2775
- //# sourceMappingURL=chunk-XEUIYSW2.js.map
2776
- //# sourceMappingURL=chunk-XEUIYSW2.js.map
2898
+ //# sourceMappingURL=chunk-G73HZNFI.js.map
2899
+ //# sourceMappingURL=chunk-G73HZNFI.js.map