hazo_ui 6.13.1 → 6.15.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,69 @@ 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.15.0 (2026-10-13)
9
+
10
+ ### Added — `useHazoEvent` hook
11
+
12
+ - `useHazoEvent(pkg, event, handler)` subscribes to the `hazo:<pkg>:<event>`
13
+ `CustomEvent` dispatched via hazo_core's `dispatchHazoEvent` (requires
14
+ `hazo_core ^2.5.0`). Stable-ref `useEffect` listener (re-subscribing a new
15
+ `handler` on every render never adds a duplicate listener), cleans up on
16
+ unmount. The subscribe-side counterpart of the `hazo:<pkg>:<event>`
17
+ convention — lets a host app react to a hazo_* package's internal state
18
+ change with zero coupling. First consumer: Kinstripe's admin feedback
19
+ badges reacting to hazo_feedback's `status-changed` event.
20
+
21
+ ## v6.14.1 (2026-10-13)
22
+
23
+ ### Fixed — `useFullscreen` silently did nothing when the native API rejected the request
24
+
25
+ - `enter()` only fell back to CSS-driven pseudo-fullscreen when
26
+ `requestFullscreen` was entirely absent (iOS Safari). When the API exists
27
+ but the browser *rejects* the call — most commonly a cross-origin iframe
28
+ embed missing `allow="fullscreen"` (`SecurityError`) — the rejection was
29
+ silently swallowed with no fallback, so the fullscreen button appeared to
30
+ do nothing.
31
+ - `exit()` and `toggle()` had matching bugs once pseudo-fullscreen could be
32
+ reached this way: both branched on `document.fullscreenElement` (always
33
+ `null` in pseudo-fullscreen), so `exit()` could never clear the state and
34
+ `toggle()` would call `enter()` again instead of exiting. Both now branch
35
+ on the hook's own tracked `isFullscreen` state instead.
36
+
37
+ ## v6.14.0 (2026-10-04)
38
+
39
+ ### Added — German (`de`) catalog and LocalePicker demo
40
+
41
+ - New `src/locales/de.json`: German translation of all 219 keys in `en.json`
42
+ (flat dotted keys, identical key set, every `{{placeholder}}` preserved
43
+ verbatim). Consumers register it through hazo_i18n alongside `en`; the package
44
+ does not bundle non-English catalogs into `dist/`.
45
+ - The German catalog was produced with the hazo_i18n pipeline, not by hand:
46
+ `translateMessages()` (export, then Gemini via `hazo_llm_api` per chunk, then
47
+ `importMessages()` with ICU/HTML/expansion validation). 9 chunks, 0 failed,
48
+ 0 rejected. 14 non-blocking length-expansion warnings on very short strings
49
+ (e.g. `Save` to `Speichern`) were accepted; no translation was changed by hand.
50
+ - Rationale: German is the first non-English catalog for hazo_ui, and it exercises
51
+ the hazo_i18n export/LLM/import round trip on a real package catalog.
52
+ - Test-app: new `/locale-picker` page demoing `LocalePicker` (dropdown, compact and
53
+ inline variants; native, native+code and code display; flags) with real locale
54
+ alternates. Alternates point at a new `/set-locale` route that sets the
55
+ `NEXT_LOCALE` cookie and redirects back.
56
+ - Test-app `i18n_runtime.ts` now registers the hazo_ui `en` and `de` catalogs under
57
+ the `hazo_ui` namespace, so hazo_ui components switch language in the test-app
58
+ through `HazoUiI18nProvider`. Previously they stayed English.
59
+
60
+ ### Fixed — dialog re-render loop on open
61
+
62
+ - `HazoUiMultiFilterDialog` and `HazoUiMultiSortDialog` defaulted `initialFilters` /
63
+ `initialSortFields` to a fresh `[]` each render. Their open-time sync effect depends
64
+ on that prop, so every render reset state to a new array and looped until React
65
+ threw `RangeError: Invalid array length`. Defaults now reference a module-level
66
+ empty constant.
67
+ - `filter_dialog_i18n.test.tsx`: the "explicit title wins" assertion queried
68
+ `getByText("Filter")`, which matched the still-mounted trigger button; it now asserts
69
+ no heading named "Filter".
70
+
8
71
  ## v6.13.1 (2026-10-03)
9
72
 
10
73
  ### Fixed — remaining hardcoded English strings now go through i18n
package/dist/index.cjs CHANGED
@@ -1145,10 +1145,11 @@ function FilterFieldItem({
1145
1145
  ] })
1146
1146
  ] });
1147
1147
  }
1148
+ var NO_INITIAL_FILTERS = [];
1148
1149
  function HazoUiMultiFilterDialog({
1149
1150
  availableFields,
1150
1151
  onFilterChange,
1151
- initialFilters = [],
1152
+ initialFilters = NO_INITIAL_FILTERS,
1152
1153
  title: titleProp,
1153
1154
  description: descriptionProp,
1154
1155
  headerBackgroundColor,
@@ -1513,10 +1514,11 @@ function SortableSortFieldItem({
1513
1514
  }
1514
1515
  );
1515
1516
  }
1517
+ var NO_INITIAL_SORT_FIELDS = [];
1516
1518
  function HazoUiMultiSortDialog({
1517
1519
  availableFields,
1518
1520
  onSortChange,
1519
- initialSortFields = [],
1521
+ initialSortFields = NO_INITIAL_SORT_FIELDS,
1520
1522
  title: titleProp,
1521
1523
  description: descriptionProp,
1522
1524
  headerBackgroundColor,
@@ -9369,32 +9371,27 @@ function useFullscreen(elementRef) {
9369
9371
  try {
9370
9372
  await get_target().requestFullscreen();
9371
9373
  } catch {
9374
+ set_is_fullscreen(true);
9372
9375
  }
9373
9376
  }, [supported, get_target]);
9374
9377
  const exit = React27.useCallback(async () => {
9375
9378
  if (typeof document === "undefined") return;
9376
- if (!supported) {
9377
- set_is_fullscreen(false);
9379
+ if (document.fullscreenElement) {
9380
+ try {
9381
+ await document.exitFullscreen();
9382
+ } catch {
9383
+ }
9378
9384
  return;
9379
9385
  }
9380
- if (!document.fullscreenElement) return;
9381
- try {
9382
- await document.exitFullscreen();
9383
- } catch {
9384
- }
9385
- }, [supported]);
9386
+ set_is_fullscreen(false);
9387
+ }, []);
9386
9388
  const toggle = React27.useCallback(async () => {
9387
- if (!supported) {
9388
- if (is_fullscreen) await exit();
9389
- else await enter();
9390
- return;
9391
- }
9392
- if (document.fullscreenElement) {
9389
+ if (is_fullscreen) {
9393
9390
  await exit();
9394
9391
  } else {
9395
9392
  await enter();
9396
9393
  }
9397
- }, [supported, is_fullscreen, enter, exit]);
9394
+ }, [is_fullscreen, enter, exit]);
9398
9395
  return { isFullscreen: is_fullscreen, enter, exit, toggle };
9399
9396
  }
9400
9397
  function use_wake_lock(active) {
@@ -13187,6 +13184,18 @@ function LocalePicker({
13187
13184
  ] });
13188
13185
  }
13189
13186
  LocalePicker.displayName = "LocalePicker";
13187
+ function useHazoEvent(pkg, event, handler) {
13188
+ const handler_ref = React27.useRef(handler);
13189
+ handler_ref.current = handler;
13190
+ React27.useEffect(() => {
13191
+ const event_name = `hazo:${pkg}:${event}`;
13192
+ const listener = (e) => {
13193
+ handler_ref.current(e.detail);
13194
+ };
13195
+ window.addEventListener(event_name, listener);
13196
+ return () => window.removeEventListener(event_name, listener);
13197
+ }, [pkg, event]);
13198
+ }
13190
13199
 
13191
13200
  Object.defineProperty(exports, "rawToast", {
13192
13201
  enumerable: true,
@@ -13425,6 +13434,7 @@ exports.useDebounce = useDebounce;
13425
13434
  exports.useErrorDisplay = useErrorDisplay;
13426
13435
  exports.useEtaProgress = useEtaProgress;
13427
13436
  exports.useFullscreen = useFullscreen;
13437
+ exports.useHazoEvent = useHazoEvent;
13428
13438
  exports.useHazoUiT = useHazoUiT;
13429
13439
  exports.useIsMobile = useIsMobile;
13430
13440
  exports.useLoadingState = useLoadingState;