hazo_ui 6.9.0 → 6.11.1

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,152 @@ 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.11.1 (2026-08-25)
9
+
10
+ ### Added — `data-lane-key` on `HazoUiKanban` swimlane hooks
11
+
12
+ `KanbanColumn` already exposes `data-column-key` on its shell so a consumer
13
+ can style/select one column via CSS (`cls_hazo_kanban_column[data-column-key='...']`,
14
+ pubsos's own board chrome). Lanes had no equivalent — the per-lane row
15
+ wrapper, heading and collapsed strip carried only the shared
16
+ `cls_hazo_kanban_lane*` hooks, with no way to target one specific lane (e.g.
17
+ a "Marked Items" lane that should read louder than an ordinary tag lane)
18
+ without matching on lane title text. `data-lane-key={lane.key}` is now on
19
+ the lane row wrapper (`cls_hazo_kanban_lane`), the heading
20
+ (`cls_hazo_kanban_lane_head`) and the collapsed strip
21
+ (`cls_hazo_kanban_lane_strip`) — same attribute name and placement pattern
22
+ as the column hook.
23
+
24
+ ## v6.11.0 (2026-08-24)
25
+
26
+ ### Added — `LocalePicker` + `createPackageT` i18n infrastructure (FR-030)
27
+
28
+ First of the i18n-prerequisite work for the still-unbuilt `hazo_i18n`
29
+ package (see `00.lib/design/PRD-hazo_i18n.md`, §2.1 / §11 / §16.3).
30
+ `hazo_i18n` is headless by design, so the locale picker component lives
31
+ here; `hazo_ui` also starts routing its own ~150 baked-in English strings
32
+ through an i18n bridge instead of hardcoding them. Everything in this
33
+ release works identically whether `hazo_i18n` is installed or not —
34
+ **an English-only consumer sees zero change** (same text, same markup, same
35
+ `en-US` number/currency formatting).
36
+
37
+ - **New `<LocalePicker>`** (`hazo_ui` and the slim `hazo_ui/locale-picker`
38
+ entry) — a standard language-switcher dropdown, purely prop-driven, no
39
+ `hazo_i18n` dependency:
40
+ ```tsx
41
+ <LocalePicker
42
+ variant="dropdown" // "dropdown" | "inline" | "compact"
43
+ show="auto" // "auto" (renders null below 2 locales) | "always" | "never"
44
+ display="native" // "native" | "native+code" | "code"
45
+ flags={false}
46
+ alternates={{ en: "https://x.com/", de: "https://x.com/de" }}
47
+ />
48
+ ```
49
+ Built on the same Radix `DropdownMenu`/`DropdownMenuRadioGroup` primitives
50
+ as `src/components/ui/dropdown-menu.tsx`. Every option is a real `<a href>`
51
+ (via Radix `asChild`), not a JS-only click handler, so it stays crawlable
52
+ and middle-click-openable — options can only ever point at URLs present in
53
+ the `alternates` map (the same map a consumer already builds for `<head>`
54
+ hreflang). Native language names come from `Intl.DisplayNames`,
55
+ instantiated per-language in that language's own locale, so German shows
56
+ "Deutsch" and Swedish shows "svenska" — never an English name. `flags:
57
+ true` renders inline SVG (a small embedded set for common locales, a
58
+ generic globe icon otherwise) — **never emoji**, since Segoe UI Emoji ships
59
+ no regional-indicator glyphs and flag emoji render as broken letter-boxes
60
+ on Windows Chrome/Edge. `lang={code}` is set on both the trigger and each
61
+ option.
62
+
63
+ - **New `createPackageT(namespace, fallbackCatalog)`**
64
+ (`src/lib/create_package_t.ts`) — the i18n bridge every `hazo_*` package
65
+ will eventually adopt for its own strings. Probes for `hazo_i18n` once via
66
+ `optional_import("hazo_i18n")` from `hazo_core/client` (the same
67
+ optional-peer pattern used elsewhere in this package); the returned `t()`
68
+ delegates to `hazo_i18n`'s translator when present and resolvable, and
69
+ otherwise looks up the key in the bundled fallback catalog with simple
70
+ `{{var}}` interpolation. Pure function, no React dependency. Also exports
71
+ `resolvePackageLocale(defaultLocale)`, used by the table's number
72
+ formatting (see below) — it only ever overrides the default when
73
+ `hazo_i18n` exposes a synchronous, SSR/CSR-identical locale getter, and
74
+ falls back to the static default otherwise, so it can never introduce a
75
+ hydration mismatch.
76
+
77
+ - **`src/locales/en.json`** — the bundled English catalog, ~69 keys covering
78
+ the four files below.
79
+
80
+ - **Wired `t()` into the four files that hold 55%+ of this package's
81
+ translatable strings** (per the FR's own survey):
82
+ `hazo_ui_table/index.tsx` (pagination: "Showing X–Y of Z", Prev/Next +
83
+ their aria-labels, search placeholder, sort tooltip, empty/no-results/error
84
+ states), `hazo_ui_rte/toolbar/toolbar.tsx` (all 28 `tooltip=` values —
85
+ Bold, Italic, Undo, Align left, etc. — popover body copy is deferred, not
86
+ in scope), `hazo_ui_rte/types.ts` (new `get_translated_block_types()`
87
+ helper for the block-type dropdown's 9 labels; `BLOCK_TYPES` itself is
88
+ unchanged for any direct consumer), and
89
+ `hazo_ui_multi_filter_dialog/index.tsx` (dialog title/description,
90
+ field-input placeholders, On/Off, Active filters tooltip, footer buttons,
91
+ ~21 keys total). Every existing `labels`/default-prop override
92
+ (`HazoUiTableProps.empty`/`.noResults`, `booleanLabels`, the dialog's
93
+ `title`/`description`, etc.) still takes precedence over `t()` — verified
94
+ by regression tests. The remaining ~40 strings across the rest of the
95
+ package are explicitly deferred to `hazo_i18n`'s own creation.
96
+
97
+ - **Number-locale threading** — `hazo_ui_table`'s `DEFAULT_NUMBER_LOCALE`
98
+ pin now resolves through `resolvePackageLocale("en-US")` instead of a bare
99
+ constant. **Behavior for everyone without `hazo_i18n` installed is
100
+ unchanged** (still pinned to `"en-US"`, still hydration-safe by
101
+ construction); this only starts mattering once `hazo_i18n` exists and
102
+ exposes a synchronous locale getter. Date formatting (`date-fns`'s
103
+ `"MMM d, yyyy"` pattern in the table and the filter dialog) is unchanged —
104
+ deferred, since locale-aware date formatting needs a `date-fns/locale`
105
+ import per BCP-47 code, out of scope for this pass.
106
+
107
+ - **`hazo_i18n` added as an optional peer** (`peerDependencies` +
108
+ `peerDependenciesMeta.hazo_i18n.optional: true`) — it doesn't exist on npm
109
+ yet, so this is forward-declaration; `optional_import` means a missing or
110
+ unpublished `hazo_i18n` never breaks `npm install` or the build.
111
+
112
+ - **New test infrastructure** — this package had no unit test runner before
113
+ this release. Added `jest.config.cjs` (ESM + ts-jest + jsdom, mirrors
114
+ `hazo_theme`'s config) and `npm run test`; 32 tests across 4 files cover
115
+ `createPackageT`'s fallback/interpolation/delegation behavior,
116
+ `LocalePicker`'s auto-hide/real-`<a>`/flag/no-emoji/precedence rules, the
117
+ table's translated-catalog wiring and empty/noResults override precedence,
118
+ and an SSR-vs-CSR hydration-safety check for formatted currency.
119
+
120
+ ## v6.10.0 (2026-08-15)
121
+
122
+ ### Added — `HazoUiKanban.renderColumnHeaderExtra`
123
+
124
+ `renderColumnFooter` already gave consumers a slot under a column's body;
125
+ there was no equivalent for the header. `renderColumnHeaderExtra` fills that
126
+ gap — a render-prop that fires once per rendered copy of a column and returns
127
+ a node placed inline in the header, next to the title and count badge:
128
+
129
+ ```tsx
130
+ <HazoUiKanban
131
+ columns={columns}
132
+ items={items}
133
+ renderCard={(item) => <Card item={item} />}
134
+ renderColumnHeaderExtra={({ columnKey, laneKey }) =>
135
+ columnKey !== "todo" ? null : (
136
+ <SegmentedToggle value={view} onChange={setView} options={["All", "Recent"]} />
137
+ )
138
+ }
139
+ />
140
+ ```
141
+
142
+ Same lane-scoping caveat as `renderColumnFooter`: on a laned board it fires
143
+ once per (lane, column) pair, and the unmatched-lane sentinel row never gets
144
+ one. The internal `render_column` parameter that gated this (`with_footer`)
145
+ is renamed `with_consumer_callbacks` since it now gates both render-props for
146
+ the same reason — leaking `UNMATCHED_LANE_KEY` to a consumer callback isn't
147
+ specific to the footer.
148
+
149
+ The returned node is wrapped so its pointer/mouse/key events stop
150
+ propagation before reaching dnd-kit, the same guard already used by the
151
+ card's pencil-edit affordance — this matters on a sectioned column, where the
152
+ header is itself a droppable, and is a harmless no-op elsewhere.
153
+
8
154
  ## v6.9.0 (2026-08-06)
9
155
 
10
156
  ### Added — `Badge`, `StatusBadge`, `makeStatusBadge`