hazo_ui 6.14.0 → 6.16.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/CHANGE_LOG.md CHANGED
@@ -5,6 +5,52 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## v6.16.0 (2026-10-15)
9
+
10
+ ### Added — Tailwind v3 preset + responsive AppHeader
11
+
12
+ - **`hazo_ui/tailwind-preset`** — Tailwind CSS v3 preset that maps all
13
+ hazo_theme Tier-2 semantic tokens (background, primary, sidebar, etc.)
14
+ and Tier-3 chrome tokens (navbar, footer, footer-public, footer-app,
15
+ toolbar) to `hsl(var(--token) / <alpha-value>)` color utilities. Also
16
+ provides `borderRadius` mappings for `--radius`. Consumers add
17
+ `presets: [require('hazo_ui/tailwind-preset')]` to their
18
+ `tailwind.config` instead of hand-rolling 100+ color entries.
19
+
20
+ - **Responsive `AppHeader`** — below the `md:` breakpoint the navItems
21
+ slot collapses behind a hamburger button that opens a slide-up drawer
22
+ (vaul). Actions (profile menu, etc.) remain visible at all widths. No
23
+ API change; existing consumers get mobile support for free.
24
+
25
+ ## v6.15.0 (2026-10-13)
26
+
27
+ ### Added — `useHazoEvent` hook
28
+
29
+ - `useHazoEvent(pkg, event, handler)` subscribes to the `hazo:<pkg>:<event>`
30
+ `CustomEvent` dispatched via hazo_core's `dispatchHazoEvent` (requires
31
+ `hazo_core ^2.5.0`). Stable-ref `useEffect` listener (re-subscribing a new
32
+ `handler` on every render never adds a duplicate listener), cleans up on
33
+ unmount. The subscribe-side counterpart of the `hazo:<pkg>:<event>`
34
+ convention — lets a host app react to a hazo_* package's internal state
35
+ change with zero coupling. First consumer: Kinstripe's admin feedback
36
+ badges reacting to hazo_feedback's `status-changed` event.
37
+
38
+ ## v6.14.1 (2026-10-13)
39
+
40
+ ### Fixed — `useFullscreen` silently did nothing when the native API rejected the request
41
+
42
+ - `enter()` only fell back to CSS-driven pseudo-fullscreen when
43
+ `requestFullscreen` was entirely absent (iOS Safari). When the API exists
44
+ but the browser *rejects* the call — most commonly a cross-origin iframe
45
+ embed missing `allow="fullscreen"` (`SecurityError`) — the rejection was
46
+ silently swallowed with no fallback, so the fullscreen button appeared to
47
+ do nothing.
48
+ - `exit()` and `toggle()` had matching bugs once pseudo-fullscreen could be
49
+ reached this way: both branched on `document.fullscreenElement` (always
50
+ `null` in pseudo-fullscreen), so `exit()` could never clear the state and
51
+ `toggle()` would call `enter()` again instead of exiting. Both now branch
52
+ on the hook's own tracked `isFullscreen` state instead.
53
+
8
54
  ## v6.14.0 (2026-10-04)
9
55
 
10
56
  ### Added — German (`de`) catalog and LocalePicker demo
package/dist/index.cjs CHANGED
@@ -877,6 +877,8 @@ var en_default = {
877
877
  "locale_picker.change_language": "Change language",
878
878
  "app_logo.alt_fallback": "Logo",
879
879
  "app_header.aria_label": "Application header",
880
+ "app_header.menu_label": "Menu",
881
+ "app_header.nav_title": "Navigation",
880
882
  "skeleton.loading_content": "Loading content",
881
883
  "spinner.loading": "Loading",
882
884
  "spinner.loading_text": "Loading...",
@@ -9371,32 +9373,27 @@ function useFullscreen(elementRef) {
9371
9373
  try {
9372
9374
  await get_target().requestFullscreen();
9373
9375
  } catch {
9376
+ set_is_fullscreen(true);
9374
9377
  }
9375
9378
  }, [supported, get_target]);
9376
9379
  const exit = React27.useCallback(async () => {
9377
9380
  if (typeof document === "undefined") return;
9378
- if (!supported) {
9379
- set_is_fullscreen(false);
9381
+ if (document.fullscreenElement) {
9382
+ try {
9383
+ await document.exitFullscreen();
9384
+ } catch {
9385
+ }
9380
9386
  return;
9381
9387
  }
9382
- if (!document.fullscreenElement) return;
9383
- try {
9384
- await document.exitFullscreen();
9385
- } catch {
9386
- }
9387
- }, [supported]);
9388
+ set_is_fullscreen(false);
9389
+ }, []);
9388
9390
  const toggle = React27.useCallback(async () => {
9389
- if (!supported) {
9390
- if (is_fullscreen) await exit();
9391
- else await enter();
9392
- return;
9393
- }
9394
- if (document.fullscreenElement) {
9391
+ if (is_fullscreen) {
9395
9392
  await exit();
9396
9393
  } else {
9397
9394
  await enter();
9398
9395
  }
9399
- }, [supported, is_fullscreen, enter, exit]);
9396
+ }, [is_fullscreen, enter, exit]);
9400
9397
  return { isFullscreen: is_fullscreen, enter, exit, toggle };
9401
9398
  }
9402
9399
  function use_wake_lock(active) {
@@ -12532,6 +12529,7 @@ function Logo({
12532
12529
  }
12533
12530
  function AppHeader({ identity, homeHref, navItems, actions, className }) {
12534
12531
  const t = useHazoUiT();
12532
+ const [drawerOpen, setDrawerOpen] = React27__namespace.useState(false);
12535
12533
  return /* @__PURE__ */ jsxRuntime.jsxs(
12536
12534
  "header",
12537
12535
  {
@@ -12551,9 +12549,34 @@ function AppHeader({ identity, homeHref, navItems, actions, className }) {
12551
12549
  href: homeHref
12552
12550
  }
12553
12551
  ),
12554
- navItems
12552
+ navItems && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden md:flex items-center", children: navItems })
12555
12553
  ] }),
12556
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4", children: actions })
12554
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
12555
+ actions,
12556
+ navItems && /* @__PURE__ */ jsxRuntime.jsxs(Drawer, { open: drawerOpen, onOpenChange: setDrawerOpen, children: [
12557
+ /* @__PURE__ */ jsxRuntime.jsx(DrawerTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
12558
+ "button",
12559
+ {
12560
+ className: "md:hidden rounded-md p-1.5 hover:bg-navbar-active/10",
12561
+ "aria-label": t("app_header.menu_label"),
12562
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Menu, { className: "h-5 w-5" })
12563
+ }
12564
+ ) }),
12565
+ /* @__PURE__ */ jsxRuntime.jsxs(DrawerContent, { children: [
12566
+ /* @__PURE__ */ jsxRuntime.jsx(DrawerTitle, { className: "sr-only", children: t("app_header.nav_title") }),
12567
+ /* @__PURE__ */ jsxRuntime.jsx(
12568
+ "div",
12569
+ {
12570
+ className: "p-4 [&_nav]:flex-col [&_nav]:gap-2 [&_nav]:items-stretch",
12571
+ onClick: (e) => {
12572
+ if (e.target.closest("a")) setDrawerOpen(false);
12573
+ },
12574
+ children: navItems
12575
+ }
12576
+ )
12577
+ ] })
12578
+ ] })
12579
+ ] })
12557
12580
  ]
12558
12581
  }
12559
12582
  );
@@ -13189,6 +13212,18 @@ function LocalePicker({
13189
13212
  ] });
13190
13213
  }
13191
13214
  LocalePicker.displayName = "LocalePicker";
13215
+ function useHazoEvent(pkg, event, handler) {
13216
+ const handler_ref = React27.useRef(handler);
13217
+ handler_ref.current = handler;
13218
+ React27.useEffect(() => {
13219
+ const event_name = `hazo:${pkg}:${event}`;
13220
+ const listener = (e) => {
13221
+ handler_ref.current(e.detail);
13222
+ };
13223
+ window.addEventListener(event_name, listener);
13224
+ return () => window.removeEventListener(event_name, listener);
13225
+ }, [pkg, event]);
13226
+ }
13192
13227
 
13193
13228
  Object.defineProperty(exports, "rawToast", {
13194
13229
  enumerable: true,
@@ -13427,6 +13462,7 @@ exports.useDebounce = useDebounce;
13427
13462
  exports.useErrorDisplay = useErrorDisplay;
13428
13463
  exports.useEtaProgress = useEtaProgress;
13429
13464
  exports.useFullscreen = useFullscreen;
13465
+ exports.useHazoEvent = useHazoEvent;
13430
13466
  exports.useHazoUiT = useHazoUiT;
13431
13467
  exports.useIsMobile = useIsMobile;
13432
13468
  exports.useLoadingState = useLoadingState;