@fayz-ai/ui 0.24.0 → 0.25.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.
@@ -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, ChevronsLeft, ChevronLeft, ChevronRight, ChevronsRight, 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, MicOff, Mic, 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';
@@ -1098,6 +1098,18 @@ function FilterChip({ allLabel, options, value, onChange, label, icon: Icon2, cl
1098
1098
  ] });
1099
1099
  }
1100
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
+ }
1101
1113
  function ListFilters({ tagFilter, filters, advanced, label, count, wideCount }) {
1102
1114
  const wide = useIsWide();
1103
1115
  const [open, setOpen] = useState(false);
@@ -1207,9 +1219,7 @@ function ListView({
1207
1219
  }, [onSearchChange, searchDebounce]);
1208
1220
  const totalItems = total ?? data.length;
1209
1221
  const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
1210
- const PAGE_WINDOW = 5;
1211
- const windowStart = Math.max(1, Math.min(page - Math.floor(PAGE_WINDOW / 2), totalPages - PAGE_WINDOW + 1));
1212
- const pageWindow = Array.from({ length: Math.min(PAGE_WINDOW, totalPages) }, (_, i) => windowStart + i);
1222
+ const pageWindow = pageNumbers(page, totalPages);
1213
1223
  const showPagination = onPageChange && totalItems > pageSize;
1214
1224
  const showSearch = !!onSearchChange;
1215
1225
  const showTags = tags && tags.length > 0 && onTagChange;
@@ -1262,7 +1272,13 @@ function ListView({
1262
1272
  // `flex-wrap`, então uma primeira cria que ocupa a linha
1263
1273
  // inteira empurra o resto para baixo sozinha. Sem filtros,
1264
1274
  // a busca volta a partilhar a linha com atualizar/exportar.
1265
- /* @__PURE__ */ jsxs("div", { className: cn("relative min-w-0 flex-1", searchOwnRow && "basis-full"), children: [
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: [
1266
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" }),
1267
1283
  /* @__PURE__ */ jsx(
1268
1284
  "input",
@@ -1334,7 +1350,7 @@ function ListView({
1334
1350
  totalItems.toLocaleString()
1335
1351
  ] }),
1336
1352
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
1337
- totalPages > PAGE_WINDOW && /* @__PURE__ */ jsx(
1353
+ totalPages > 1 && /* @__PURE__ */ jsx(
1338
1354
  "button",
1339
1355
  {
1340
1356
  type: "button",
@@ -1356,7 +1372,12 @@ function ListView({
1356
1372
  children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
1357
1373
  }
1358
1374
  ),
1359
- pageWindow.map((n) => /* @__PURE__ */ jsx(
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(
1360
1381
  "button",
1361
1382
  {
1362
1383
  type: "button",
@@ -1379,7 +1400,7 @@ function ListView({
1379
1400
  children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
1380
1401
  }
1381
1402
  ),
1382
- totalPages > PAGE_WINDOW && /* @__PURE__ */ jsx(
1403
+ totalPages > 1 && /* @__PURE__ */ jsx(
1383
1404
  "button",
1384
1405
  {
1385
1406
  type: "button",
@@ -2329,6 +2350,79 @@ function MultiSelect({
2329
2350
  )
2330
2351
  ] });
2331
2352
  }
2353
+ function recognitionCtor() {
2354
+ if (typeof window === "undefined") return null;
2355
+ const w = window;
2356
+ return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
2357
+ }
2358
+ function DictateButton({
2359
+ onTranscript,
2360
+ lang,
2361
+ startLabel = "Ditar",
2362
+ stopLabel = "Parar de ditar",
2363
+ className,
2364
+ disabled
2365
+ }) {
2366
+ const [ouvindo, setOuvindo] = React15.useState(false);
2367
+ const [erro, setErro] = React15.useState(null);
2368
+ const ref = React15.useRef(null);
2369
+ const [suportado, setSuportado] = React15.useState(false);
2370
+ React15.useEffect(() => {
2371
+ setSuportado(recognitionCtor() !== null);
2372
+ }, []);
2373
+ React15.useEffect(() => () => {
2374
+ ref.current?.stop();
2375
+ }, []);
2376
+ const alternar = React15.useCallback(() => {
2377
+ if (ouvindo) {
2378
+ ref.current?.stop();
2379
+ return;
2380
+ }
2381
+ const Ctor = recognitionCtor();
2382
+ if (!Ctor) return;
2383
+ const rec = new Ctor();
2384
+ const doPagina = typeof document !== "undefined" ? document.documentElement.lang : "";
2385
+ rec.lang = lang ?? (doPagina || "pt-BR");
2386
+ rec.continuous = true;
2387
+ rec.interimResults = true;
2388
+ rec.onresult = (e) => {
2389
+ let texto = "";
2390
+ for (let i = 0; i < e.results.length; i++) texto += e.results[i][0].transcript;
2391
+ onTranscript(texto);
2392
+ };
2393
+ rec.onerror = (e) => {
2394
+ if (e?.error && e.error !== "no-speech" && e.error !== "aborted") setErro(String(e.error));
2395
+ setOuvindo(false);
2396
+ };
2397
+ rec.onend = () => setOuvindo(false);
2398
+ ref.current = rec;
2399
+ setErro(null);
2400
+ try {
2401
+ rec.start();
2402
+ setOuvindo(true);
2403
+ } catch {
2404
+ setOuvindo(false);
2405
+ }
2406
+ }, [ouvindo, lang, onTranscript]);
2407
+ if (!suportado) return null;
2408
+ return /* @__PURE__ */ jsx(
2409
+ "button",
2410
+ {
2411
+ type: "button",
2412
+ onClick: alternar,
2413
+ disabled,
2414
+ "aria-pressed": ouvindo,
2415
+ "aria-label": ouvindo ? stopLabel : startLabel,
2416
+ title: erro ? `${startLabel} \u2014 ${erro}` : ouvindo ? stopLabel : startLabel,
2417
+ className: cn(
2418
+ "fz-tap inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors",
2419
+ ouvindo ? "animate-pulse bg-destructive/10 text-destructive hover:bg-destructive/15" : "text-muted-foreground hover:bg-muted hover:text-foreground",
2420
+ className
2421
+ ),
2422
+ children: ouvindo ? /* @__PURE__ */ jsx(MicOff, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Mic, { className: "h-3.5 w-3.5" })
2423
+ }
2424
+ );
2425
+ }
2332
2426
  var INLINE_TOKEN = /(\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g;
2333
2427
  function renderInline(text, keyPrefix) {
2334
2428
  return text.split(INLINE_TOKEN).map((part, i) => {
@@ -2873,6 +2967,6 @@ function StockEntryForm({
2873
2967
  ] }) });
2874
2968
  }
2875
2969
 
2876
- 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 };
2877
- //# sourceMappingURL=chunk-NXFVYZY7.js.map
2878
- //# sourceMappingURL=chunk-NXFVYZY7.js.map
2970
+ export { Badge, Breadcrumb, BulkActionBar, ChevronPager, CurrencyInput, DatePicker, DateRangeChip, DictateButton, 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 };
2971
+ //# sourceMappingURL=chunk-OQVFOBKU.js.map
2972
+ //# sourceMappingURL=chunk-OQVFOBKU.js.map