@motor-hero/ui-kit 0.44.1 → 0.46.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/index.d.ts CHANGED
@@ -44,6 +44,8 @@ export { TableSkeleton } from "./components/table-skeleton";
44
44
  export type { Theme, ThemeProviderProps, ThemeProviderState, } from "./components/theme-provider";
45
45
  export { ThemeProvider, useTheme } from "./components/theme-provider";
46
46
  export { Toaster } from "./components/toaster";
47
+ export type { UpdateBannerProps } from "./components/update-banner";
48
+ export { UpdateBanner } from "./components/update-banner";
47
49
  export type { RenderLink, RenderLinkProps } from "./components/types";
48
50
  export { defaultRenderLink } from "./components/types";
49
51
  export type { AppUser, UserMenuItem, UserMenuProps, } from "./components/user-menu";
@@ -52,6 +54,8 @@ export type { UseCompanySubdomainOptions, UseCompanySubdomainResult, } from "./h
52
54
  export { getSubdomain, useCompanySubdomain, } from "./hooks/use-company-subdomain";
53
55
  export { useDisclosure } from "./hooks/use-disclosure";
54
56
  export { useIsDesktop } from "./hooks/use-is-desktop";
57
+ export type { UseVersionCheckOptions, VersionCheckState } from "./hooks/use-version-check";
58
+ export { useVersionCheck } from "./hooks/use-version-check";
55
59
  export { toast, useCustomToast } from "./hooks/use-toast";
56
60
  export { extractApiError } from "./lib/api-error";
57
61
  export type { CompanyTheme, CompanyThemeTokens } from "./lib/company-theme";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -259,6 +259,8 @@ function AppShell({
259
259
  headerActions,
260
260
  collapsible = true,
261
261
  defaultCollapsed = false,
262
+ sidebarFooter,
263
+ banner,
262
264
  children
263
265
  }) {
264
266
  const [collapsed, setCollapsed] = useState3(defaultCollapsed);
@@ -286,6 +288,7 @@ function AppShell({
286
288
  children: [
287
289
  /* @__PURE__ */ jsx4("div", { className: "flex h-14 shrink-0 items-center border-b px-4", children: collapsed ? brandCollapsed ?? brand : brand }),
288
290
  /* @__PURE__ */ jsx4("div", { className: "flex-1 overflow-y-auto px-2 py-4", children: nav(collapsed) }),
291
+ sidebarFooter && /* @__PURE__ */ jsx4("div", { className: "border-t px-3 py-2", "data-slot": "sidebar-footer", children: typeof sidebarFooter === "function" ? sidebarFooter(collapsed) : sidebarFooter }),
289
292
  collapsible && /* @__PURE__ */ jsx4("div", { className: "border-t p-2", children: /* @__PURE__ */ jsx4(
290
293
  "button",
291
294
  {
@@ -327,6 +330,7 @@ function AppShell({
327
330
  }
328
331
  ),
329
332
  /* @__PURE__ */ jsxs3("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
333
+ banner,
330
334
  /* @__PURE__ */ jsxs3("header", { className: "flex h-14 shrink-0 items-center gap-2 border-b bg-background px-4", children: [
331
335
  /* @__PURE__ */ jsx4(
332
336
  "button",
@@ -671,30 +675,35 @@ function useInfiniteScrollSentinel({
671
675
  loading,
672
676
  onLoadMore
673
677
  }) {
674
- const listRef = React4.useRef(null);
675
- const sentinelRef = React4.useRef(null);
678
+ const [listNode, setListNode] = React4.useState(null);
679
+ const [sentinelNode, setSentinelNode] = React4.useState(
680
+ null
681
+ );
676
682
  const loadingRef = React4.useRef(loading);
677
683
  const onLoadMoreRef = React4.useRef(onLoadMore);
684
+ const listRef = React4.useCallback((node) => {
685
+ setListNode(node);
686
+ }, []);
687
+ const sentinelRef = React4.useCallback((node) => {
688
+ setSentinelNode(node);
689
+ }, []);
678
690
  React4.useEffect(() => {
679
691
  loadingRef.current = loading;
680
692
  onLoadMoreRef.current = onLoadMore;
681
693
  });
682
694
  React4.useEffect(() => {
683
- if (!enabled) return;
684
- const root = listRef.current;
685
- const sentinel = sentinelRef.current;
686
- if (!root || !sentinel) return;
695
+ if (!enabled || !listNode || !sentinelNode) return;
687
696
  const observer = new IntersectionObserver(
688
697
  (entries) => {
689
698
  if (entries[0]?.isIntersecting && !loadingRef.current) {
690
699
  onLoadMoreRef.current?.();
691
700
  }
692
701
  },
693
- { root, rootMargin: "0px 0px 100px 0px" }
702
+ { root: listNode, rootMargin: "0px 0px 100px 0px" }
694
703
  );
695
- observer.observe(sentinel);
704
+ observer.observe(sentinelNode);
696
705
  return () => observer.disconnect();
697
- }, [enabled]);
706
+ }, [enabled, listNode, sentinelNode]);
698
707
  return { listRef, sentinelRef };
699
708
  }
700
709
 
@@ -725,7 +734,14 @@ function Combobox({
725
734
  const [search, setSearch] = React5.useState("");
726
735
  const isDesktop = useIsDesktop();
727
736
  const isServer = onSearchChange !== void 0;
728
- const selected = selectedOption?.value === value ? selectedOption : options.find((option) => option.value === value) ?? (creatable && value ? { value, label: value } : void 0);
737
+ const labelCache = React5.useRef(/* @__PURE__ */ new Map());
738
+ const fromPage = selectedOption?.value === value ? selectedOption : options.find((option) => option.value === value);
739
+ React5.useEffect(() => {
740
+ for (const option of options) labelCache.current.set(option.value, option);
741
+ if (selectedOption)
742
+ labelCache.current.set(selectedOption.value, selectedOption);
743
+ });
744
+ const selected = fromPage ?? (value ? labelCache.current.get(value) : void 0) ?? (creatable && value ? { value, label: value } : void 0);
729
745
  const handleOpenChange = (next) => {
730
746
  setOpen(next);
731
747
  if (!next) setSearch("");
@@ -1534,7 +1550,7 @@ function HighlightPill({
1534
1550
  }
1535
1551
 
1536
1552
  // src/components/mobile-card-list.tsx
1537
- import { useRef as useRef3 } from "react";
1553
+ import { useRef as useRef4 } from "react";
1538
1554
  import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
1539
1555
  var SCROLL_THRESHOLD = 8;
1540
1556
  function MobileCardList({
@@ -1545,8 +1561,8 @@ function MobileCardList({
1545
1561
  loadingCount = 5,
1546
1562
  className
1547
1563
  }) {
1548
- const start = useRef3(null);
1549
- const scrolled = useRef3(false);
1564
+ const start = useRef4(null);
1565
+ const scrolled = useRef4(false);
1550
1566
  const onTouchStart = (e) => {
1551
1567
  const t = e.touches[0];
1552
1568
  start.current = { x: t.clientX, y: t.clientY };
@@ -1602,7 +1618,7 @@ import * as DropdownMenu2 from "@radix-ui/react-dropdown-menu";
1602
1618
  import { Moon, Sun, Monitor } from "lucide-react";
1603
1619
 
1604
1620
  // src/components/theme-provider.tsx
1605
- import { createContext, useContext, useEffect as useEffect4, useState as useState8 } from "react";
1621
+ import { createContext, useContext, useEffect as useEffect5, useState as useState9 } from "react";
1606
1622
 
1607
1623
  // src/lib/company-theme.ts
1608
1624
  var VAR_MAP = {
@@ -1643,10 +1659,10 @@ function ThemeProvider({
1643
1659
  companyTheme,
1644
1660
  ...props
1645
1661
  }) {
1646
- const [theme, setTheme] = useState8(
1662
+ const [theme, setTheme] = useState9(
1647
1663
  () => localStorage.getItem(storageKey) || defaultTheme
1648
1664
  );
1649
- useEffect4(() => {
1665
+ useEffect5(() => {
1650
1666
  const root = window.document.documentElement;
1651
1667
  const applyResolved = (resolved) => {
1652
1668
  root.classList.remove("light", "dark");
@@ -1769,12 +1785,14 @@ function MultiCombobox({
1769
1785
  const [open, setOpen] = React8.useState(false);
1770
1786
  const isDesktop = useIsDesktop();
1771
1787
  const isServer = onSearchChange !== void 0;
1772
- const seen = /* @__PURE__ */ new Set();
1773
- const selected = [...selectedOptions ?? [], ...options].filter((option) => {
1774
- if (!value.includes(option.value) || seen.has(option.value)) return false;
1775
- seen.add(option.value);
1776
- return true;
1788
+ const labelCache = React8.useRef(/* @__PURE__ */ new Map());
1789
+ const byValue = /* @__PURE__ */ new Map();
1790
+ for (const option of options) byValue.set(option.value, option);
1791
+ for (const option of selectedOptions ?? []) byValue.set(option.value, option);
1792
+ React8.useEffect(() => {
1793
+ for (const [key, option] of byValue) labelCache.current.set(key, option);
1777
1794
  });
1795
+ const selected = value.map((v) => byValue.get(v) ?? labelCache.current.get(v)).filter((option) => option !== void 0);
1778
1796
  const { listRef, sentinelRef } = useInfiniteScrollSentinel({
1779
1797
  enabled: isServer && !!hasMore,
1780
1798
  loading,
@@ -2118,7 +2136,7 @@ function ResponsiveDataView({
2118
2136
  // src/components/row-actions-menu.tsx
2119
2137
  import * as DropdownMenu3 from "@radix-ui/react-dropdown-menu";
2120
2138
  import { ChevronRight as ChevronRight3, MoreVertical } from "lucide-react";
2121
- import { useState as useState11 } from "react";
2139
+ import { useState as useState12 } from "react";
2122
2140
  import { Drawer as DrawerPrimitive6 } from "vaul";
2123
2141
  import { Fragment as Fragment6, jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
2124
2142
  var triggerClass = "inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50";
@@ -2132,7 +2150,7 @@ function RowActionsMenu({
2132
2150
  renderLink = defaultRenderLink
2133
2151
  }) {
2134
2152
  const isDesktop = useIsDesktop();
2135
- const [open, setOpen] = useState11(false);
2153
+ const [open, setOpen] = useState12(false);
2136
2154
  if (actions.length === 0) return null;
2137
2155
  const trigger = /* @__PURE__ */ jsx27(
2138
2156
  "button",
@@ -2472,8 +2490,96 @@ function Toaster(props) {
2472
2490
  );
2473
2491
  }
2474
2492
 
2493
+ // src/components/update-banner.tsx
2494
+ import { RefreshCw } from "lucide-react";
2495
+
2496
+ // src/hooks/use-version-check.ts
2497
+ import { useCallback as useCallback3, useEffect as useEffect7, useState as useState13 } from "react";
2498
+ function useVersionCheck({
2499
+ currentBuild,
2500
+ url = "/version.json",
2501
+ intervalMs = 6e4
2502
+ }) {
2503
+ const [latestBuild, setLatestBuild] = useState13(null);
2504
+ const [staleChecks, setStaleChecks] = useState13(0);
2505
+ useEffect7(() => {
2506
+ if (!currentBuild) return;
2507
+ let cancelled = false;
2508
+ const check = async () => {
2509
+ if (document.visibilityState === "hidden") return;
2510
+ try {
2511
+ const res = await fetch(url, { cache: "no-store" });
2512
+ const contentType = res.headers.get("content-type") ?? "";
2513
+ if (!res.ok || !contentType.includes("application/json")) return;
2514
+ const data = await res.json();
2515
+ if (cancelled || typeof data.buildTime !== "string") return;
2516
+ if (data.buildTime !== currentBuild) {
2517
+ setLatestBuild(data.buildTime);
2518
+ setStaleChecks((n) => n + 1);
2519
+ }
2520
+ } catch (error) {
2521
+ console.error("Version check failed", error);
2522
+ }
2523
+ };
2524
+ check();
2525
+ const interval = setInterval(check, intervalMs);
2526
+ const onVisible = () => {
2527
+ if (document.visibilityState === "visible") check();
2528
+ };
2529
+ document.addEventListener("visibilitychange", onVisible);
2530
+ return () => {
2531
+ cancelled = true;
2532
+ clearInterval(interval);
2533
+ document.removeEventListener("visibilitychange", onVisible);
2534
+ };
2535
+ }, [currentBuild, url, intervalMs]);
2536
+ const reload = useCallback3(() => window.location.reload(), []);
2537
+ return { updateAvailable: latestBuild !== null, staleChecks, latestBuild, reload };
2538
+ }
2539
+
2540
+ // src/components/update-banner.tsx
2541
+ import { jsx as jsx34, jsxs as jsxs28 } from "react/jsx-runtime";
2542
+ function UpdateBanner({
2543
+ title = "Nova vers\xE3o dispon\xEDvel",
2544
+ description = "Atualize para continuar na vers\xE3o mais recente.",
2545
+ actionLabel = "Atualizar",
2546
+ className,
2547
+ ...options
2548
+ }) {
2549
+ const { updateAvailable, reload } = useVersionCheck(options);
2550
+ if (!updateAvailable) return null;
2551
+ return /* @__PURE__ */ jsxs28(
2552
+ "div",
2553
+ {
2554
+ role: "status",
2555
+ "aria-live": "polite",
2556
+ "data-slot": "update-banner",
2557
+ className: cn(
2558
+ "flex flex-wrap items-center justify-between gap-3 border-b border-primary/30 bg-primary px-4 py-2 text-sm text-primary-foreground",
2559
+ className
2560
+ ),
2561
+ children: [
2562
+ /* @__PURE__ */ jsxs28("span", { className: "flex min-w-0 items-center gap-2", children: [
2563
+ /* @__PURE__ */ jsx34(RefreshCw, { className: "h-4 w-4 shrink-0 animate-spin [animation-duration:3s]", "aria-hidden": true }),
2564
+ /* @__PURE__ */ jsx34("span", { className: "font-medium", children: title }),
2565
+ /* @__PURE__ */ jsx34("span", { className: "hidden truncate opacity-80 sm:inline", children: description })
2566
+ ] }),
2567
+ /* @__PURE__ */ jsx34(
2568
+ "button",
2569
+ {
2570
+ type: "button",
2571
+ onClick: reload,
2572
+ className: "shrink-0 rounded-md bg-background px-3 py-1 text-xs font-medium text-foreground transition-colors hover:bg-background/90",
2573
+ children: actionLabel
2574
+ }
2575
+ )
2576
+ ]
2577
+ }
2578
+ );
2579
+ }
2580
+
2475
2581
  // src/hooks/use-company-subdomain.ts
2476
- import { useState as useState12 } from "react";
2582
+ import { useState as useState14 } from "react";
2477
2583
  var ALWAYS_RESERVED = ["www", "localhost"];
2478
2584
  function getSubdomain(hostname, reservedSubdomains = []) {
2479
2585
  const labels = hostname.split(".");
@@ -2506,7 +2612,7 @@ function useCompanySubdomain(opts = {}) {
2506
2612
  overrideStorageKey = "company-subdomain",
2507
2613
  reservedSubdomains = []
2508
2614
  } = opts;
2509
- const [subdomain] = useState12(() => {
2615
+ const [subdomain] = useState14(() => {
2510
2616
  if (enableOverride) {
2511
2617
  const override = resolveOverride(overrideStorageKey);
2512
2618
  if (override) return override;
@@ -2518,20 +2624,20 @@ function useCompanySubdomain(opts = {}) {
2518
2624
  }
2519
2625
 
2520
2626
  // src/hooks/use-disclosure.ts
2521
- import { useCallback as useCallback2, useState as useState13 } from "react";
2627
+ import { useCallback as useCallback4, useState as useState15 } from "react";
2522
2628
  function useDisclosure(initial = false) {
2523
- const [open, setOpen] = useState13(initial);
2524
- const onOpen = useCallback2(() => setOpen(true), []);
2525
- const onClose = useCallback2(() => setOpen(false), []);
2526
- const onToggle = useCallback2(() => setOpen((v) => !v), []);
2629
+ const [open, setOpen] = useState15(initial);
2630
+ const onOpen = useCallback4(() => setOpen(true), []);
2631
+ const onClose = useCallback4(() => setOpen(false), []);
2632
+ const onToggle = useCallback4(() => setOpen((v) => !v), []);
2527
2633
  return { open, onOpen, onClose, onToggle, setOpen };
2528
2634
  }
2529
2635
 
2530
2636
  // src/hooks/use-toast.ts
2531
2637
  import { toast } from "sonner";
2532
- import { useCallback as useCallback3 } from "react";
2638
+ import { useCallback as useCallback5 } from "react";
2533
2639
  function useCustomToast() {
2534
- const showToast = useCallback3(
2640
+ const showToast = useCallback5(
2535
2641
  (title, description, status = "success") => {
2536
2642
  switch (status) {
2537
2643
  case "success":
@@ -2597,6 +2703,7 @@ export {
2597
2703
  TableSkeleton,
2598
2704
  ThemeProvider,
2599
2705
  Toaster,
2706
+ UpdateBanner,
2600
2707
  UserMenu,
2601
2708
  applyCompanyTheme,
2602
2709
  cn,
@@ -2608,6 +2715,7 @@ export {
2608
2715
  useCustomToast,
2609
2716
  useDisclosure,
2610
2717
  useIsDesktop,
2611
- useTheme
2718
+ useTheme,
2719
+ useVersionCheck
2612
2720
  };
2613
2721
  //# sourceMappingURL=index.js.map