@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.
@@ -16,7 +16,15 @@ export interface AppShellProps {
16
16
  headerActions?: ReactNode;
17
17
  collapsible?: boolean;
18
18
  defaultCollapsed?: boolean;
19
+ /**
20
+ * Bottom of the desktop sidebar, above the collapse control — a product
21
+ * credit, a version, a support link. Receives the collapsed state so it
22
+ * can shrink with the sidebar.
23
+ */
24
+ sidebarFooter?: ReactNode | ((collapsed: boolean) => ReactNode);
25
+ /** Above the header, full width of the content column — `UpdateBanner` goes here. */
26
+ banner?: ReactNode;
19
27
  children: ReactNode;
20
28
  }
21
- export declare function AppShell({ brand, brandCollapsed, navItems, activePath, isActive, isAdmin, user, userMenuItems, onLogout, renderLink, headerActions, collapsible, defaultCollapsed, children, }: AppShellProps): import("react").JSX.Element;
29
+ export declare function AppShell({ brand, brandCollapsed, navItems, activePath, isActive, isAdmin, user, userMenuItems, onLogout, renderLink, headerActions, collapsible, defaultCollapsed, sidebarFooter, banner, children, }: AppShellProps): import("react").JSX.Element;
22
30
  //# sourceMappingURL=app-shell.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-shell.d.ts","sourceRoot":"","sources":["../../src/components/app-shell.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAEhD,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,eAAe,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC,OAAO,EAAY,KAAK,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAEvE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,CAAA;IAChB,cAAc,CAAC,EAAE,SAAS,CAAA;IAC1B,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAA;IACzD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,aAAa,CAAC,EAAE,YAAY,EAAE,CAAA;IAC9B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,aAAa,CAAC,EAAE,SAAS,CAAA;IACzB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,cAAc,EACd,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,OAAe,EACf,IAAI,EACJ,aAAa,EACb,QAAQ,EACR,UAA8B,EAC9B,aAAa,EACb,WAAkB,EAClB,gBAAwB,EACxB,QAAQ,GACT,EAAE,aAAa,+BAsFf"}
1
+ {"version":3,"file":"app-shell.d.ts","sourceRoot":"","sources":["../../src/components/app-shell.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAEhD,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,eAAe,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC,OAAO,EAAY,KAAK,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAEvE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,CAAA;IAChB,cAAc,CAAC,EAAE,SAAS,CAAA;IAC1B,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAA;IACzD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,aAAa,CAAC,EAAE,YAAY,EAAE,CAAA;IAC9B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,aAAa,CAAC,EAAE,SAAS,CAAA;IACzB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B;;;;OAIG;IACH,aAAa,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,SAAS,EAAE,OAAO,KAAK,SAAS,CAAC,CAAA;IAC/D,qFAAqF;IACrF,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,cAAc,EACd,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,OAAe,EACf,IAAI,EACJ,aAAa,EACb,QAAQ,EACR,UAA8B,EAC9B,aAAa,EACb,WAAkB,EAClB,gBAAwB,EACxB,aAAa,EACb,MAAM,EACN,QAAQ,GACT,EAAE,aAAa,+BA4Ff"}
@@ -1 +1 @@
1
- {"version":3,"file":"combobox.d.ts","sourceRoot":"","sources":["../../src/components/combobox.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;kEAC8D;IAC9D,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IAC5C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;qEACiE;IACjE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;wDACoD;IACpD,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC;;yDAEqD;IACrD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C;mFAC+E;IAC/E,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,EACxB,OAAO,EACP,KAAK,EACL,QAAQ,EACR,WAA4B,EAC5B,iBAA+B,EAC/B,YAA6C,EAC7C,QAAQ,EACR,EAAE,EACF,SAAS,EACT,cAAc,EAAE,WAAW,EAC3B,kBAAkB,EAAE,eAAe,EACnC,cAAc,EACd,UAAU,EACV,OAAO,EACP,OAAO,EACP,cAAc,EACd,SAAS,EACT,iBAAiB,EACjB,YAAY,GACZ,EAAE,aAAa,qBAuKf"}
1
+ {"version":3,"file":"combobox.d.ts","sourceRoot":"","sources":["../../src/components/combobox.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;kEAC8D;IAC9D,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IAC5C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;qEACiE;IACjE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;wDACoD;IACpD,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC;;yDAEqD;IACrD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C;mFAC+E;IAC/E,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,EACxB,OAAO,EACP,KAAK,EACL,QAAQ,EACR,WAA4B,EAC5B,iBAA+B,EAC/B,YAA6C,EAC7C,QAAQ,EACR,EAAE,EACF,SAAS,EACT,cAAc,EAAE,WAAW,EAC3B,kBAAkB,EAAE,eAAe,EACnC,cAAc,EACd,UAAU,EACV,OAAO,EACP,OAAO,EACP,cAAc,EACd,SAAS,EACT,iBAAiB,EACjB,YAAY,GACZ,EAAE,aAAa,qBAsLf"}
@@ -1 +1 @@
1
- {"version":3,"file":"multi-combobox.d.ts","sourceRoot":"","sources":["../../src/components/multi-combobox.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;IACnC,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oCAAoC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAA;IAC3C,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B;qEACiE;IACjE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;+DAC2D;IAC3D,eAAe,CAAC,EAAE,cAAc,EAAE,CAAA;CACnC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,KAAK,EACL,QAAQ,EACR,WAA4B,EAC5B,iBAA+B,EAC/B,YAA6C,EAC7C,QAAQ,EACR,EAAE,EACF,SAAS,EACT,cAAc,EAAE,WAAW,EAC3B,kBAAkB,EAAE,eAAe,EACnC,cAAc,EACd,UAAU,EACV,OAAO,EACP,OAAO,EACP,eAAe,GAChB,EAAE,kBAAkB,qBA6KpB"}
1
+ {"version":3,"file":"multi-combobox.d.ts","sourceRoot":"","sources":["../../src/components/multi-combobox.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;IACnC,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oCAAoC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAA;IAC3C,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B;qEACiE;IACjE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;+DAC2D;IAC3D,eAAe,CAAC,EAAE,cAAc,EAAE,CAAA;CACnC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,KAAK,EACL,QAAQ,EACR,WAA4B,EAC5B,iBAA+B,EAC/B,YAA6C,EAC7C,QAAQ,EACR,EAAE,EACF,SAAS,EACT,cAAc,EAAE,WAAW,EAC3B,kBAAkB,EAAE,eAAe,EACnC,cAAc,EACd,UAAU,EACV,OAAO,EACP,OAAO,EACP,eAAe,GAChB,EAAE,kBAAkB,qBA6LpB"}
@@ -0,0 +1,16 @@
1
+ import { type UseVersionCheckOptions } from "../hooks/use-version-check";
2
+ export interface UpdateBannerProps extends UseVersionCheckOptions {
3
+ title?: string;
4
+ description?: string;
5
+ actionLabel?: string;
6
+ className?: string;
7
+ }
8
+ /**
9
+ * A bar that appears when a newer build is deployed and stays until the
10
+ * person reloads — not a toast: a notice that can be missed or dismissed
11
+ * leaves the old bundle running against a new API. Themed with the product
12
+ * tokens so it matches light and dark alike. Put it in `AppShell`'s
13
+ * `banner` slot (or at the top of any page).
14
+ */
15
+ export declare function UpdateBanner({ title, description, actionLabel, className, ...options }: UpdateBannerProps): import("react").JSX.Element | null;
16
+ //# sourceMappingURL=update-banner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-banner.d.ts","sourceRoot":"","sources":["../../src/components/update-banner.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,sBAAsB,EAAmB,MAAM,4BAA4B,CAAA;AAEzF,MAAM,WAAW,iBAAkB,SAAQ,sBAAsB;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAgC,EAChC,WAA+D,EAC/D,WAAyB,EACzB,SAAS,EACT,GAAG,OAAO,EACX,EAAE,iBAAiB,sCA4BnB"}
@@ -1,16 +1,24 @@
1
- import * as React from "react";
2
1
  /**
3
- * Dispara `onLoadMore` quando o `sentinelRef` entra na viewport do `listRef`
4
- * (raiz = a lista rolável, não a janela — a lista rola por dentro do Popover/
5
- * Drawer). Usa refs "latest" para `loading`/`onLoadMore` e evita reassinar o
6
- * observer a cada render.
2
+ * Dispara `onLoadMore` quando o sentinela entra na viewport da lista rolável
3
+ * (raiz = a lista, não a janela — ela rola por dentro do Popover/Drawer). Usa
4
+ * refs "latest" para `loading`/`onLoadMore` e não reassina o observer a cada
5
+ * render.
6
+ *
7
+ * Os nós entram como **estado** (callback refs), não como `useRef`: lista e
8
+ * sentinela vivem dentro do Portal do Popover/Drawer e só existem no DOM com o
9
+ * dropdown aberto, o que acontece bem depois de `enabled` virar `true`. Com
10
+ * `useRef` + deps `[enabled]` o efeito rodava uma única vez, com o dropdown
11
+ * fechado e os dois refs `null`, e o observer nunca chegava a ser registrado —
12
+ * a paginação por scroll simplesmente não acontecia. Guardar os nós em estado
13
+ * também refaz a assinatura quando o sentinela desmonta e remonta (`hasMore`
14
+ * oscilando), em vez de deixar o observer preso a um nó já destacado.
7
15
  */
8
16
  export declare function useInfiniteScrollSentinel({ enabled, loading, onLoadMore, }: {
9
17
  enabled: boolean;
10
18
  loading?: boolean;
11
19
  onLoadMore?: () => void;
12
20
  }): {
13
- listRef: React.RefObject<HTMLDivElement | null>;
14
- sentinelRef: React.RefObject<HTMLDivElement | null>;
21
+ listRef: (node: HTMLDivElement | null) => void;
22
+ sentinelRef: (node: HTMLDivElement | null) => void;
15
23
  };
16
24
  //# sourceMappingURL=use-infinite-scroll-sentinel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-infinite-scroll-sentinel.d.ts","sourceRoot":"","sources":["../../src/hooks/use-infinite-scroll-sentinel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,OAAO,EACP,OAAO,EACP,UAAU,GACX,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;CACxB;;;EA8BA"}
1
+ {"version":3,"file":"use-infinite-scroll-sentinel.d.ts","sourceRoot":"","sources":["../../src/hooks/use-infinite-scroll-sentinel.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,OAAO,EACP,OAAO,EACP,UAAU,GACX,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;CACxB;oBAU0C,cAAc,GAAG,IAAI;wBAGjB,cAAc,GAAG,IAAI;EAyBnE"}
@@ -0,0 +1,24 @@
1
+ export interface UseVersionCheckOptions {
2
+ /** The build id baked into the running bundle (see `versionFile` in `@motor-hero/ui-kit/vite`). */
3
+ currentBuild: string | undefined;
4
+ /** Where the deployed build id is published. */
5
+ url?: string;
6
+ intervalMs?: number;
7
+ }
8
+ export interface VersionCheckState {
9
+ /** The deployed build differs from the running one. */
10
+ updateAvailable: boolean;
11
+ /** Grows on every poll that still sees a newer build. */
12
+ staleChecks: number;
13
+ latestBuild: string | null;
14
+ reload: () => void;
15
+ }
16
+ /**
17
+ * Polls the deployed `version.json` and compares its `buildTime` with the
18
+ * build id baked into this bundle. Nothing is inferred from a first fetch —
19
+ * a page loaded during a deploy, or a poll that fails while the server
20
+ * restarts, cannot make the running code believe it is current. Polling
21
+ * pauses while the tab is hidden and runs at once when it comes back.
22
+ */
23
+ export declare function useVersionCheck({ currentBuild, url, intervalMs, }: UseVersionCheckOptions): VersionCheckState;
24
+ //# sourceMappingURL=use-version-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-version-check.d.ts","sourceRoot":"","sources":["../../src/hooks/use-version-check.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,sBAAsB;IACrC,mGAAmG;IACnG,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,eAAe,EAAE,OAAO,CAAA;IACxB,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAC9B,YAAY,EACZ,GAAqB,EACrB,UAAmB,GACpB,EAAE,sBAAsB,GAAG,iBAAiB,CAyC5C"}
package/dist/index.cjs CHANGED
@@ -65,6 +65,7 @@ __export(index_exports, {
65
65
  TableSkeleton: () => TableSkeleton,
66
66
  ThemeProvider: () => ThemeProvider,
67
67
  Toaster: () => Toaster,
68
+ UpdateBanner: () => UpdateBanner,
68
69
  UserMenu: () => UserMenu,
69
70
  applyCompanyTheme: () => applyCompanyTheme,
70
71
  cn: () => cn,
@@ -76,7 +77,8 @@ __export(index_exports, {
76
77
  useCustomToast: () => useCustomToast,
77
78
  useDisclosure: () => useDisclosure,
78
79
  useIsDesktop: () => useIsDesktop,
79
- useTheme: () => useTheme
80
+ useTheme: () => useTheme,
81
+ useVersionCheck: () => useVersionCheck
80
82
  });
81
83
  module.exports = __toCommonJS(index_exports);
82
84
 
@@ -341,6 +343,8 @@ function AppShell({
341
343
  headerActions,
342
344
  collapsible = true,
343
345
  defaultCollapsed = false,
346
+ sidebarFooter,
347
+ banner,
344
348
  children
345
349
  }) {
346
350
  const [collapsed, setCollapsed] = (0, import_react3.useState)(defaultCollapsed);
@@ -368,6 +372,7 @@ function AppShell({
368
372
  children: [
369
373
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex h-14 shrink-0 items-center border-b px-4", children: collapsed ? brandCollapsed ?? brand : brand }),
370
374
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex-1 overflow-y-auto px-2 py-4", children: nav(collapsed) }),
375
+ sidebarFooter && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "border-t px-3 py-2", "data-slot": "sidebar-footer", children: typeof sidebarFooter === "function" ? sidebarFooter(collapsed) : sidebarFooter }),
371
376
  collapsible && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "border-t p-2", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
372
377
  "button",
373
378
  {
@@ -409,6 +414,7 @@ function AppShell({
409
414
  }
410
415
  ),
411
416
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
417
+ banner,
412
418
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("header", { className: "flex h-14 shrink-0 items-center gap-2 border-b bg-background px-4", children: [
413
419
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
414
420
  "button",
@@ -749,30 +755,35 @@ function useInfiniteScrollSentinel({
749
755
  loading,
750
756
  onLoadMore
751
757
  }) {
752
- const listRef = React4.useRef(null);
753
- const sentinelRef = React4.useRef(null);
758
+ const [listNode, setListNode] = React4.useState(null);
759
+ const [sentinelNode, setSentinelNode] = React4.useState(
760
+ null
761
+ );
754
762
  const loadingRef = React4.useRef(loading);
755
763
  const onLoadMoreRef = React4.useRef(onLoadMore);
764
+ const listRef = React4.useCallback((node) => {
765
+ setListNode(node);
766
+ }, []);
767
+ const sentinelRef = React4.useCallback((node) => {
768
+ setSentinelNode(node);
769
+ }, []);
756
770
  React4.useEffect(() => {
757
771
  loadingRef.current = loading;
758
772
  onLoadMoreRef.current = onLoadMore;
759
773
  });
760
774
  React4.useEffect(() => {
761
- if (!enabled) return;
762
- const root = listRef.current;
763
- const sentinel = sentinelRef.current;
764
- if (!root || !sentinel) return;
775
+ if (!enabled || !listNode || !sentinelNode) return;
765
776
  const observer = new IntersectionObserver(
766
777
  (entries) => {
767
778
  if (entries[0]?.isIntersecting && !loadingRef.current) {
768
779
  onLoadMoreRef.current?.();
769
780
  }
770
781
  },
771
- { root, rootMargin: "0px 0px 100px 0px" }
782
+ { root: listNode, rootMargin: "0px 0px 100px 0px" }
772
783
  );
773
- observer.observe(sentinel);
784
+ observer.observe(sentinelNode);
774
785
  return () => observer.disconnect();
775
- }, [enabled]);
786
+ }, [enabled, listNode, sentinelNode]);
776
787
  return { listRef, sentinelRef };
777
788
  }
778
789
 
@@ -803,7 +814,14 @@ function Combobox({
803
814
  const [search, setSearch] = React5.useState("");
804
815
  const isDesktop = useIsDesktop();
805
816
  const isServer = onSearchChange !== void 0;
806
- const selected = selectedOption?.value === value ? selectedOption : options.find((option) => option.value === value) ?? (creatable && value ? { value, label: value } : void 0);
817
+ const labelCache = React5.useRef(/* @__PURE__ */ new Map());
818
+ const fromPage = selectedOption?.value === value ? selectedOption : options.find((option) => option.value === value);
819
+ React5.useEffect(() => {
820
+ for (const option of options) labelCache.current.set(option.value, option);
821
+ if (selectedOption)
822
+ labelCache.current.set(selectedOption.value, selectedOption);
823
+ });
824
+ const selected = fromPage ?? (value ? labelCache.current.get(value) : void 0) ?? (creatable && value ? { value, label: value } : void 0);
807
825
  const handleOpenChange = (next) => {
808
826
  setOpen(next);
809
827
  if (!next) setSearch("");
@@ -1847,12 +1865,14 @@ function MultiCombobox({
1847
1865
  const [open, setOpen] = React8.useState(false);
1848
1866
  const isDesktop = useIsDesktop();
1849
1867
  const isServer = onSearchChange !== void 0;
1850
- const seen = /* @__PURE__ */ new Set();
1851
- const selected = [...selectedOptions ?? [], ...options].filter((option) => {
1852
- if (!value.includes(option.value) || seen.has(option.value)) return false;
1853
- seen.add(option.value);
1854
- return true;
1868
+ const labelCache = React8.useRef(/* @__PURE__ */ new Map());
1869
+ const byValue = /* @__PURE__ */ new Map();
1870
+ for (const option of options) byValue.set(option.value, option);
1871
+ for (const option of selectedOptions ?? []) byValue.set(option.value, option);
1872
+ React8.useEffect(() => {
1873
+ for (const [key, option] of byValue) labelCache.current.set(key, option);
1855
1874
  });
1875
+ const selected = value.map((v) => byValue.get(v) ?? labelCache.current.get(v)).filter((option) => option !== void 0);
1856
1876
  const { listRef, sentinelRef } = useInfiniteScrollSentinel({
1857
1877
  enabled: isServer && !!hasMore,
1858
1878
  loading,
@@ -2550,8 +2570,96 @@ function Toaster(props) {
2550
2570
  );
2551
2571
  }
2552
2572
 
2553
- // src/hooks/use-company-subdomain.ts
2573
+ // src/components/update-banner.tsx
2574
+ var import_lucide_react13 = require("lucide-react");
2575
+
2576
+ // src/hooks/use-version-check.ts
2554
2577
  var import_react8 = require("react");
2578
+ function useVersionCheck({
2579
+ currentBuild,
2580
+ url = "/version.json",
2581
+ intervalMs = 6e4
2582
+ }) {
2583
+ const [latestBuild, setLatestBuild] = (0, import_react8.useState)(null);
2584
+ const [staleChecks, setStaleChecks] = (0, import_react8.useState)(0);
2585
+ (0, import_react8.useEffect)(() => {
2586
+ if (!currentBuild) return;
2587
+ let cancelled = false;
2588
+ const check = async () => {
2589
+ if (document.visibilityState === "hidden") return;
2590
+ try {
2591
+ const res = await fetch(url, { cache: "no-store" });
2592
+ const contentType = res.headers.get("content-type") ?? "";
2593
+ if (!res.ok || !contentType.includes("application/json")) return;
2594
+ const data = await res.json();
2595
+ if (cancelled || typeof data.buildTime !== "string") return;
2596
+ if (data.buildTime !== currentBuild) {
2597
+ setLatestBuild(data.buildTime);
2598
+ setStaleChecks((n) => n + 1);
2599
+ }
2600
+ } catch (error) {
2601
+ console.error("Version check failed", error);
2602
+ }
2603
+ };
2604
+ check();
2605
+ const interval = setInterval(check, intervalMs);
2606
+ const onVisible = () => {
2607
+ if (document.visibilityState === "visible") check();
2608
+ };
2609
+ document.addEventListener("visibilitychange", onVisible);
2610
+ return () => {
2611
+ cancelled = true;
2612
+ clearInterval(interval);
2613
+ document.removeEventListener("visibilitychange", onVisible);
2614
+ };
2615
+ }, [currentBuild, url, intervalMs]);
2616
+ const reload = (0, import_react8.useCallback)(() => window.location.reload(), []);
2617
+ return { updateAvailable: latestBuild !== null, staleChecks, latestBuild, reload };
2618
+ }
2619
+
2620
+ // src/components/update-banner.tsx
2621
+ var import_jsx_runtime34 = require("react/jsx-runtime");
2622
+ function UpdateBanner({
2623
+ title = "Nova vers\xE3o dispon\xEDvel",
2624
+ description = "Atualize para continuar na vers\xE3o mais recente.",
2625
+ actionLabel = "Atualizar",
2626
+ className,
2627
+ ...options
2628
+ }) {
2629
+ const { updateAvailable, reload } = useVersionCheck(options);
2630
+ if (!updateAvailable) return null;
2631
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
2632
+ "div",
2633
+ {
2634
+ role: "status",
2635
+ "aria-live": "polite",
2636
+ "data-slot": "update-banner",
2637
+ className: cn(
2638
+ "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",
2639
+ className
2640
+ ),
2641
+ children: [
2642
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: "flex min-w-0 items-center gap-2", children: [
2643
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react13.RefreshCw, { className: "h-4 w-4 shrink-0 animate-spin [animation-duration:3s]", "aria-hidden": true }),
2644
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "font-medium", children: title }),
2645
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "hidden truncate opacity-80 sm:inline", children: description })
2646
+ ] }),
2647
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2648
+ "button",
2649
+ {
2650
+ type: "button",
2651
+ onClick: reload,
2652
+ className: "shrink-0 rounded-md bg-background px-3 py-1 text-xs font-medium text-foreground transition-colors hover:bg-background/90",
2653
+ children: actionLabel
2654
+ }
2655
+ )
2656
+ ]
2657
+ }
2658
+ );
2659
+ }
2660
+
2661
+ // src/hooks/use-company-subdomain.ts
2662
+ var import_react9 = require("react");
2555
2663
  var ALWAYS_RESERVED = ["www", "localhost"];
2556
2664
  function getSubdomain(hostname, reservedSubdomains = []) {
2557
2665
  const labels = hostname.split(".");
@@ -2584,7 +2692,7 @@ function useCompanySubdomain(opts = {}) {
2584
2692
  overrideStorageKey = "company-subdomain",
2585
2693
  reservedSubdomains = []
2586
2694
  } = opts;
2587
- const [subdomain] = (0, import_react8.useState)(() => {
2695
+ const [subdomain] = (0, import_react9.useState)(() => {
2588
2696
  if (enableOverride) {
2589
2697
  const override = resolveOverride(overrideStorageKey);
2590
2698
  if (override) return override;
@@ -2596,20 +2704,20 @@ function useCompanySubdomain(opts = {}) {
2596
2704
  }
2597
2705
 
2598
2706
  // src/hooks/use-disclosure.ts
2599
- var import_react9 = require("react");
2707
+ var import_react10 = require("react");
2600
2708
  function useDisclosure(initial = false) {
2601
- const [open, setOpen] = (0, import_react9.useState)(initial);
2602
- const onOpen = (0, import_react9.useCallback)(() => setOpen(true), []);
2603
- const onClose = (0, import_react9.useCallback)(() => setOpen(false), []);
2604
- const onToggle = (0, import_react9.useCallback)(() => setOpen((v) => !v), []);
2709
+ const [open, setOpen] = (0, import_react10.useState)(initial);
2710
+ const onOpen = (0, import_react10.useCallback)(() => setOpen(true), []);
2711
+ const onClose = (0, import_react10.useCallback)(() => setOpen(false), []);
2712
+ const onToggle = (0, import_react10.useCallback)(() => setOpen((v) => !v), []);
2605
2713
  return { open, onOpen, onClose, onToggle, setOpen };
2606
2714
  }
2607
2715
 
2608
2716
  // src/hooks/use-toast.ts
2609
2717
  var import_sonner2 = require("sonner");
2610
- var import_react10 = require("react");
2718
+ var import_react11 = require("react");
2611
2719
  function useCustomToast() {
2612
- const showToast = (0, import_react10.useCallback)(
2720
+ const showToast = (0, import_react11.useCallback)(
2613
2721
  (title, description, status = "success") => {
2614
2722
  switch (status) {
2615
2723
  case "success":
@@ -2676,6 +2784,7 @@ function extractApiError(err, fallbackMessage = "Ocorreu um erro inesperado.") {
2676
2784
  TableSkeleton,
2677
2785
  ThemeProvider,
2678
2786
  Toaster,
2787
+ UpdateBanner,
2679
2788
  UserMenu,
2680
2789
  applyCompanyTheme,
2681
2790
  cn,
@@ -2687,6 +2796,7 @@ function extractApiError(err, fallbackMessage = "Ocorreu um erro inesperado.") {
2687
2796
  useCustomToast,
2688
2797
  useDisclosure,
2689
2798
  useIsDesktop,
2690
- useTheme
2799
+ useTheme,
2800
+ useVersionCheck
2691
2801
  });
2692
2802
  //# sourceMappingURL=index.cjs.map