@zambon-dev/ui 0.0.0-stage → 0.0.1-bootstrap.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/CHANGELOG.md +2325 -0
- package/README.md +81 -3
- package/fesm2022/zambon-dev-ui-framework.mjs +3997 -0
- package/fesm2022/zambon-dev-ui-framework.mjs.map +1 -0
- package/fesm2022/zambon-dev-ui-library.mjs +4280 -0
- package/fesm2022/zambon-dev-ui-library.mjs.map +1 -0
- package/fesm2022/zambon-dev-ui-shared.mjs +1802 -0
- package/fesm2022/zambon-dev-ui-shared.mjs.map +1 -0
- package/fesm2022/zambon-dev-ui-testing.mjs +80 -0
- package/fesm2022/zambon-dev-ui-testing.mjs.map +1 -0
- package/fesm2022/zambon-dev-ui.mjs +8 -0
- package/fesm2022/zambon-dev-ui.mjs.map +1 -0
- package/i18n/framework/en.json +63 -0
- package/i18n/framework/pt.json +63 -0
- package/i18n/shared/external-content/en.json +12 -0
- package/i18n/shared/external-content/pt.json +12 -0
- package/i18n/shared/language-selector/en.json +5 -0
- package/i18n/shared/language-selector/pt.json +5 -0
- package/i18n/shared/login/en.json +14 -0
- package/i18n/shared/login/pt.json +14 -0
- package/i18n/shared/operations-history/en.json +5 -0
- package/i18n/shared/operations-history/pt.json +5 -0
- package/i18n/shared/services-history/en.json +14 -0
- package/i18n/shared/services-history/pt.json +14 -0
- package/i18n/shared/top-bar/en.json +7 -0
- package/i18n/shared/top-bar/pt.json +7 -0
- package/package.json +106 -4
- package/styles/common.scss +10 -0
- package/styles/components/button.scss +101 -0
- package/styles/components/card.scss +13 -0
- package/styles/components/dropdown-static.scss +35 -0
- package/styles/components/input-group.scss +44 -0
- package/styles/components/input.scss +99 -0
- package/styles/components/label.scss +3 -0
- package/styles/components/row.scss +48 -0
- package/styles/mixins/background.scss +31 -0
- package/styles/theme.css +75 -0
- package/styles/variables.scss +60 -0
- package/types/zambon-dev-ui-framework.d.ts +1066 -0
- package/types/zambon-dev-ui-library.d.ts +1233 -0
- package/types/zambon-dev-ui-shared.d.ts +655 -0
- package/types/zambon-dev-ui-testing.d.ts +56 -0
- package/types/zambon-dev-ui.d.ts +3 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,2325 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@zambon-dev/ui` are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
> **Upgrading?** Each release lists what changed and, under
|
|
9
|
+
> **⚠ Breaking Changes / Migration**, the concrete steps required to move to that version.
|
|
10
|
+
> When no migration is needed, that subsection is omitted.
|
|
11
|
+
|
|
12
|
+
## [Unreleased]
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
### Deprecated
|
|
19
|
+
|
|
20
|
+
### Removed
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
### ⚠ Breaking Changes / Migration
|
|
25
|
+
|
|
26
|
+
- **The layers in this release are `library` 4.0.5, `framework` 5.0.5 and `shared` 6.0.4.**
|
|
27
|
+
Moving here from an earlier version of any of the three means taking that layer's remaining
|
|
28
|
+
releases too; they are under **Package history** below. The one worth naming is
|
|
29
|
+
`ModalComponent.isShown`, which became `isOpen` and is a `Signal<boolean>`: reading the
|
|
30
|
+
signal object itself is valid TypeScript and always truthy, so the build will not catch a miss.
|
|
31
|
+
Grep for `isShown` and `isModalShown`.
|
|
32
|
+
|
|
33
|
+
- **This package replaces `@zambon-dev/library`, `@zambon-dev/framework` and
|
|
34
|
+
`@zambon-dev/shared`.** Install `@zambon-dev/ui` in place of all three and rewrite the
|
|
35
|
+
imports:
|
|
36
|
+
|
|
37
|
+
| Was | Now |
|
|
38
|
+
| ----------------------------- | -------------------------- |
|
|
39
|
+
| `@zambon-dev/library` | `@zambon-dev/ui/library` |
|
|
40
|
+
| `@zambon-dev/framework` | `@zambon-dev/ui/framework` |
|
|
41
|
+
| `@zambon-dev/shared` | `@zambon-dev/ui/shared` |
|
|
42
|
+
| `@zambon-dev/library/testing` | `@zambon-dev/ui/testing` |
|
|
43
|
+
|
|
44
|
+
`@zambon-dev/ui` also re-exports all three layers, so a single import works where the layer
|
|
45
|
+
does not matter. See [the migration guide](https://github.com/RicardoZambon/ZLibraries/blob/main/docs/guides/migrating-to-ui.md).
|
|
46
|
+
|
|
47
|
+
- **Stylesheet subpaths name their file.** `@zambon-dev/library/styles` and
|
|
48
|
+
`@zambon-dev/shared/styles` both existed and resolved to different stylesheets, so the bare
|
|
49
|
+
subpath is gone: use `@zambon-dev/ui/styles/variables.scss`,
|
|
50
|
+
`@zambon-dev/ui/styles/common.scss` or `@zambon-dev/ui/styles/theme.css`.
|
|
51
|
+
|
|
52
|
+
- **Translation assets moved under a per-layer folder**, to `@zambon-dev/ui/i18n/framework` and
|
|
53
|
+
`@zambon-dev/ui/i18n/shared`. Applications that spread `ZAMBON_FRAMEWORK_I18N_ASSET` or
|
|
54
|
+
`ZAMBON_SHARED_I18N_ASSET` into their build assets need no change — the constants carry the
|
|
55
|
+
new path. A hand-written asset entry does.
|
|
56
|
+
|
|
57
|
+
- **The peer set is the union of the three packages'**, so `@angular/cdk`,
|
|
58
|
+
`@angular/platform-browser`, `@auth0/angular-jwt`, `@microsoft/signalr`,
|
|
59
|
+
`ngx-resize-observer` and `ngx-translate-multi-http-loader` are all required. Every
|
|
60
|
+
application consuming these packages already installs them.
|
|
61
|
+
|
|
62
|
+
- **`AngularAssetPattern` is exported once**, from the library layer, rather than declared
|
|
63
|
+
separately by each i18n helper. Import it from `@zambon-dev/ui` or
|
|
64
|
+
`@zambon-dev/ui/library`; the framework and shared layers no longer export it.
|
|
65
|
+
|
|
66
|
+
## Package history
|
|
67
|
+
|
|
68
|
+
The three packages this one replaces. Their releases stay installable from npm; nothing further
|
|
69
|
+
will be published on them.
|
|
70
|
+
|
|
71
|
+
### Library
|
|
72
|
+
|
|
73
|
+
UI components, directives, pipes and the data-grid services. Published as `@zambon-dev/library` through 4.0.5.
|
|
74
|
+
|
|
75
|
+
#### [4.0.5](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v4.0.5) - 2026-10-24
|
|
76
|
+
|
|
77
|
+
##### Changed
|
|
78
|
+
|
|
79
|
+
- **A grid row's checkbox follows the grid's selection by being told, not by forcing a pass.**
|
|
80
|
+
`DataGridRowComponent` held its selected state in a plain field and called
|
|
81
|
+
`ChangeDetectorRef.detectChanges()` on itself whenever the grid announced a change. That
|
|
82
|
+
rendered -- there is no behaviour change here -- but it rendered because the component forced
|
|
83
|
+
a pass rather than because anything was notified, which is the one thing this package's
|
|
84
|
+
components are not supposed to need. The state is a signal now and the `ChangeDetectorRef` is
|
|
85
|
+
gone; it was the last one in the package.
|
|
86
|
+
|
|
87
|
+
#### [4.0.4](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v4.0.4) - 2026-10-24
|
|
88
|
+
|
|
89
|
+
##### Changed
|
|
90
|
+
|
|
91
|
+
- **`DataProviderService` announces the entity it holds.** The identifier behind `entityID`
|
|
92
|
+
and `hasEntityID` is a signal now. It is read from templates -- `@zambon-dev/framework`'s New,
|
|
93
|
+
Edit and Views buttons all appear only once a record exists -- and the route resolving or a
|
|
94
|
+
save assigning an id told Angular nothing. Both accessors keep their shape, so no call site
|
|
95
|
+
changes.
|
|
96
|
+
|
|
97
|
+
#### [4.0.3](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v4.0.3) - 2026-10-24
|
|
98
|
+
|
|
99
|
+
##### Changed
|
|
100
|
+
|
|
101
|
+
- **Every component in this package now renders under any change-detection strategy the host
|
|
102
|
+
application uses, including zoneless.** `CatalogSelectComponent` was the last one. Nothing here
|
|
103
|
+
depends on being walked on every event, and
|
|
104
|
+
`@angular-eslint/prefer-on-push-component-change-detection` is an error in this package rather
|
|
105
|
+
than a warning, so it stays that way.
|
|
106
|
+
|
|
107
|
+
##### ⚠ Breaking Changes / Migration
|
|
108
|
+
|
|
109
|
+
- **`CatalogSelectComponent` exposes its inputs as signals.** Binding to it from a template is
|
|
110
|
+
unchanged -- `[label]="..."`, `[searchEndpoint]="..."`, `[filters]="..."` and the rest all
|
|
111
|
+
still work. Only code reading one off a component instance needs `()`. `controlName` and
|
|
112
|
+
`displayControlName` are required now, which they always were in practice.
|
|
113
|
+
|
|
114
|
+
`entriesList`, `filters` and `searchEndpoint` stay plain inputs. They are imperative
|
|
115
|
+
configuration that re-initialises the search, no template reads them, and making them signals
|
|
116
|
+
would have moved that side effect into an `effect()` for no gain.
|
|
117
|
+
|
|
118
|
+
#### [4.0.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v4.0.2) - 2026-10-24
|
|
119
|
+
|
|
120
|
+
- Maintenance release (no consumer-facing changes were documented).
|
|
121
|
+
|
|
122
|
+
#### [4.0.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v4.0.1) - 2026-10-24
|
|
123
|
+
|
|
124
|
+
##### Changed
|
|
125
|
+
|
|
126
|
+
- **`FormService` and the datasets announce their own changes.** A component reading
|
|
127
|
+
`formService.loading`, `formService.model`, `formService.isEditMode`, `formService.isValid`,
|
|
128
|
+
`dataGridDataset.selectedRowKeys`, `resultDataset.displayedRows` or a grid's loading state from
|
|
129
|
+
its template now re-renders when that state changes, whatever strategy the host application
|
|
130
|
+
uses. `columns` is deliberately left alone: a concrete dataset declares it once and never
|
|
131
|
+
changes it, so making it reactive would only have broken every subclass that declares it.
|
|
132
|
+
**Nothing to do on your side** -- these were already accessors, or became accessors of the same
|
|
133
|
+
shape, so every read and write keeps working. `isValid` also
|
|
134
|
+
mirrors the `FormGroup`'s status, which is not a signal and never notified one, so a control
|
|
135
|
+
bound to it no longer keeps its first answer.
|
|
136
|
+
|
|
137
|
+
- **`RibbonButtonComponent`, `FormInputComponent` and `DataGridComponent` render under any
|
|
138
|
+
change-detection strategy.** Their inputs are signals, and none of them depends on being walked
|
|
139
|
+
on every event any more. The grid also stops calling `ChangeDetectorRef.detectChanges()` and
|
|
140
|
+
stops re-entering the Angular zone to publish a measurement -- a signal write reaches the
|
|
141
|
+
screen on its own.
|
|
142
|
+
|
|
143
|
+
- **Seven more components render under any change-detection strategy**, continuing the work the last
|
|
144
|
+
major started: `ModalComponent`, `GroupAccordionComponent`, `GroupScrollSpyComponent`,
|
|
145
|
+
`MultiSelectComponent`, `MultiEditorComponent`, `FormInputGroupComponent` and
|
|
146
|
+
`MultiSelectResultGridComponent`. Their state is signals now, and none of them declares a
|
|
147
|
+
strategy. Four library components are still to go; they are named in
|
|
148
|
+
`change-detection-contract.spec.ts`.
|
|
149
|
+
|
|
150
|
+
##### ⚠ Breaking Changes / Migration
|
|
151
|
+
|
|
152
|
+
- **`ModalComponent.isShown` is now `isOpen`, and it is a `Signal<boolean>`.** The rename is
|
|
153
|
+
deliberate: a signal keeping the old name would leave `if (modal.isShown)` compiling and
|
|
154
|
+
permanently true. Replace `modal.isShown` with `modal.isOpen()`.
|
|
155
|
+
|
|
156
|
+
- **`IModal` changed with it.** It declares `readonly isOpen: Signal<boolean>` instead of
|
|
157
|
+
`get isShown(): boolean`, and no longer declares `size` or `title` -- those are presentational
|
|
158
|
+
inputs each component declares for itself, and nothing drove a modal through them. If you
|
|
159
|
+
implement `IModal` by wrapping our modal, delegate with a signal view query:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
private readonly modal = viewChild(ModalComponent);
|
|
163
|
+
public readonly isOpen: Signal<boolean> = computed(() => this.modal()?.isOpen() ?? false);
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
A `computed` reading a plain `@ViewChild` never re-runs when the query resolves, so the wrapper
|
|
167
|
+
would answer against an undefined child for ever.
|
|
168
|
+
|
|
169
|
+
- **`ModalComponent`'s inputs are signals.** Reading one in your own template needs `()`:
|
|
170
|
+
`title()`, `size()`, `position()`, `dialog()`, `closeButtonText()`, `modalProcessing()`. Binding
|
|
171
|
+
to them from a template is unchanged. `closeButtonText` and `modalProcessing` are `model()`, so
|
|
172
|
+
code that sets them keeps working through `.set(...)`; the other four are `input()` and can only
|
|
173
|
+
be bound.
|
|
174
|
+
|
|
175
|
+
- **`GroupAccordionComponent.label` is a required signal input**, and `maxHeight` is a signal.
|
|
176
|
+
`GroupScrollSpyComponent` exposes `titles`, `activeSection`, `sections` and `scrollSpy` as
|
|
177
|
+
signals. `MultiSelectComponent` and `MultiEditorComponent` expose their inputs as signals --
|
|
178
|
+
`itemTemplate()`, `formGroup()`, `addButtonLabel()`, `removeButtonLabel()`, `showAddButton()`
|
|
179
|
+
and `showDeleteButton()`.
|
|
180
|
+
|
|
181
|
+
- **`RibbonButtonComponent`, `FormInputComponent` and `DataGridComponent` expose their inputs as
|
|
182
|
+
signals.** Binding to them from a template is unchanged. Only code reading one off an instance needs `()` --
|
|
183
|
+
`button.loading()` rather than `button.loading`.
|
|
184
|
+
|
|
185
|
+
Two are `model()` rather than `input()` because the components write them as well:
|
|
186
|
+
`RibbonButtonComponent.loading`, which `BaseButton` drives through `startLoading` and
|
|
187
|
+
`finishLoading`, and `FormInputComponent.fixedValue`, which is two-way bound through `ngModel`.
|
|
188
|
+
`FormInputComponent.format` is also a `model()`, since it defaults itself for date types when
|
|
189
|
+
the consumer supplies none.
|
|
190
|
+
|
|
191
|
+
`RibbonButtonComponent.action` is an `output()`, which has no no-argument overload, so emitting
|
|
192
|
+
"no option chosen" is now an explicit `emit(undefined)`.
|
|
193
|
+
|
|
194
|
+
- **`FormInputGroupComponent` and `MultiSelectResultGridComponent` expose their inputs as signals
|
|
195
|
+
too.** Binding to them from a template is unchanged -- `[label]="..."`, `[type]="..."`,
|
|
196
|
+
`[validations]="..."` and the rest all still work. Only code reading one off a component
|
|
197
|
+
instance needs `()`. `controlName` is required now, which it always was in practice.
|
|
198
|
+
|
|
199
|
+
#### [4.0.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v4.0.0) - 2026-10-24
|
|
200
|
+
|
|
201
|
+
##### Added
|
|
202
|
+
|
|
203
|
+
- **A test harness that catches a component which silently stops rendering**, published as
|
|
204
|
+
`@zambon-dev/library/testing`. `renderUnderOnPush(MyComponent)` renders a component beneath a
|
|
205
|
+
clean OnPush ancestor -- which is what your application is -- and `render.tick()` runs a pass
|
|
206
|
+
that such an ancestor can still short-circuit. A component that updates state without telling
|
|
207
|
+
Angular cannot update its DOM under it, which is the failure a plain `TestBed` fixture cannot
|
|
208
|
+
express. Use it on your own components as you remove `ChangeDetectionStrategy.Eager` from them.
|
|
209
|
+
It works under both a zone and a zoneless test environment, so you can adopt it without
|
|
210
|
+
changing your test setup first.
|
|
211
|
+
|
|
212
|
+
##### Fixed
|
|
213
|
+
|
|
214
|
+
- **The sidebar fills in on its own again.** In an application whose components use Angular 22's
|
|
215
|
+
default strategy -- which is now OnPush -- the sidebar rendered its loading spinner and only
|
|
216
|
+
filled in when the user next clicked something. Its menus arrived from HTTP, were assigned to a
|
|
217
|
+
field, and nothing told Angular to render. `SidebarService` and `SidebarComponent` now hold that
|
|
218
|
+
state in signals, so it reaches the screen whatever strategy your application uses.
|
|
219
|
+
- **A sidebar item's selected state reaches its host element.** `[class.selected]` is a host
|
|
220
|
+
binding, which Angular evaluates during the _parent's_ pass, so the component's own
|
|
221
|
+
`detectChanges()` could never refresh it and writing it afterwards raised NG0100.
|
|
222
|
+
- **A ribbon group hides and shows itself when its buttons' visibility resolves.** The group reads
|
|
223
|
+
its children's visibility from a host binding; nothing notified it when an asynchronous access
|
|
224
|
+
check flipped one.
|
|
225
|
+
|
|
226
|
+
##### ⚠ Breaking Changes / Migration
|
|
227
|
+
|
|
228
|
+
- **`RibbonGroupChild.visible` is now `isVisible`, and it is a `Signal<boolean>`.** If you
|
|
229
|
+
implement `RibbonGroupChild` yourself -- a custom ribbon button -- replace the field:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
// before
|
|
233
|
+
public visible = true;
|
|
234
|
+
// after
|
|
235
|
+
public readonly isVisible: Signal<boolean> = computed(() => ...); // or signal(true)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
A plain field cannot work here: the group reads it from a host binding, and nothing can notify
|
|
239
|
+
the group when a field changes.
|
|
240
|
+
|
|
241
|
+
- **`RibbonGroupComponent.children` is a signal query**, `Signal<readonly RibbonGroupChild[]>`
|
|
242
|
+
instead of `QueryList`. Replace `children.length` with `children().length` and
|
|
243
|
+
`children.get(0)` with `children()[0]`.
|
|
244
|
+
|
|
245
|
+
- **`SidebarService.isActive`, `isCollapsed` and `shouldActivate` are accessors over signals.**
|
|
246
|
+
Reading and writing them is unchanged. But if your own `SidebarService` subclass **redeclares**
|
|
247
|
+
any of them as a field -- `public override isActive = false;` -- delete that line. A field
|
|
248
|
+
shadows the accessor, and the sidebar stops reacting to it.
|
|
249
|
+
|
|
250
|
+
#### [3.2.5](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.2.5) - 2026-10-22
|
|
251
|
+
|
|
252
|
+
##### Fixed
|
|
253
|
+
|
|
254
|
+
- **A list reopened from the menu no longer comes back empty.** Opening a record from a list that
|
|
255
|
+
had been scrolled, then reopening that list from the menu, showed the column headers over an empty
|
|
256
|
+
body. The tab reuses the list's cached view, as it should, but while that view sat detached the
|
|
257
|
+
grid kept measuring its CDK viewport: the body leaving the document is a resize, and a load that
|
|
258
|
+
finishes after the user has left is followed by a re-measure too. Measured detached, the viewport
|
|
259
|
+
worked out its range from a scroll offset of 0 and left its own change-detection flag stuck, so
|
|
260
|
+
once reattached it computed the right range but could not write the content offset out — the rows
|
|
261
|
+
were rendered, translated to the top, while the viewport showed where the user had scrolled to.
|
|
262
|
+
Whether something later rescued it was a matter of timing, which is why it came and went. A
|
|
263
|
+
detached grid now measures nothing; coming back is a resize as well, so it measures on the way in.
|
|
264
|
+
|
|
265
|
+
#### [3.2.4](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.2.4) - 2026-10-22
|
|
266
|
+
|
|
267
|
+
##### Fixed
|
|
268
|
+
|
|
269
|
+
- **A submit button inside a modal no longer reloads the whole application.** Confirming a filter
|
|
270
|
+
applied it and then, about a second later, reloaded the page. The modal's content sits in a native
|
|
271
|
+
`<form>`, and a `type="submit"` button — `framework-button-filters`' Confirm, or any `<button>`
|
|
272
|
+
without a type — made the browser submit it: a GET to the current URL, leaving a bare `?` behind.
|
|
273
|
+
Nothing in the modal ever cancelled that; the consumer's `FormGroupDirective` did, by accident,
|
|
274
|
+
for as long as the submit bubbled through it. Since 3.2.1 moved the modal's host to `<body>` it no
|
|
275
|
+
longer does, so the form now prevents its own native submission. The event still bubbles.
|
|
276
|
+
|
|
277
|
+
#### [3.2.3](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.2.3) - 2026-09-26
|
|
278
|
+
|
|
279
|
+
##### Fixed
|
|
280
|
+
|
|
281
|
+
- **A ribbon button's options are no longer cut off by whatever the button sits in.** The list was
|
|
282
|
+
absolutely positioned inside the button, which works only while nothing between it and the page
|
|
283
|
+
clips. `lib-data-grid` does: it is `overflow: hidden`, its buttons bar is its first child, and a
|
|
284
|
+
child list's grid is only a few rows tall -- so the export button's options opened downwards into
|
|
285
|
+
the grid and lost their lower part, measured at 129px of list against 65px of room. The list now
|
|
286
|
+
renders in a CDK overlay attached to the document, as `lib-catalog-select` already did, and the
|
|
287
|
+
overlay also flips it above the button when there is no room below. The ribbon no longer needs
|
|
288
|
+
its own rule to pin a right-hand button’s list to the right, since the overlay works that out.
|
|
289
|
+
|
|
290
|
+
#### [3.2.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.2.2) - 2026-09-26
|
|
291
|
+
|
|
292
|
+
##### Fixed
|
|
293
|
+
|
|
294
|
+
- **Component styles are compiled again.** The Tailwind 4 migration left every component's own
|
|
295
|
+
stylesheet unprocessed in the published bundle, so its `@apply` rules reached the browser as an
|
|
296
|
+
at-rule the browser does not know and it dropped every rule that used one. In practice the
|
|
297
|
+
components rendered unstyled in any application installing 3.2.1. ng-packagr resolves its
|
|
298
|
+
PostCSS configuration from the library directory rather than from the workspace root, and the
|
|
299
|
+
migration added one only at the root. Nothing failed along the way: Storybook compiles from
|
|
300
|
+
source and never sees the packaged artifact, which is why `tools/check-packaged-styles.mjs` now
|
|
301
|
+
runs against that artifact in CI and again before every publish.
|
|
302
|
+
|
|
303
|
+
#### [3.2.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.2.1) - 2026-09-26
|
|
304
|
+
|
|
305
|
+
##### Fixed
|
|
306
|
+
|
|
307
|
+
- **Dialogs no longer open underneath the navigation.** `lib-modal` is `fixed` at `z-index: 50`,
|
|
308
|
+
which should put it over everything, but z-index only ranks siblings inside one stacking context
|
|
309
|
+
and the tab panel these views render in is its own. Inside it the 50 counted for nothing against
|
|
310
|
+
the sidebar outside it, and the tab strip stayed visible and clickable behind the dialog.
|
|
311
|
+
|
|
312
|
+
The modal now moves its host to `<body>` on init, which is the one place with nothing above it.
|
|
313
|
+
This covers every dialog built on it: multi-editor, multi-select, the filter panels, the confirm
|
|
314
|
+
and error modals, and the operations-history detail.
|
|
315
|
+
|
|
316
|
+
#### [3.2.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.2.0) - 2026-09-25
|
|
317
|
+
|
|
318
|
+
##### Changed
|
|
319
|
+
|
|
320
|
+
- **Tailwind CSS 4.** The component styles in this package are compiled against Tailwind 4 now.
|
|
321
|
+
They are expanded at build time, so nothing in the published bundle changed shape -- but the
|
|
322
|
+
application consuming it has to be on Tailwind 4 as well.
|
|
323
|
+
|
|
324
|
+
#### [3.1.7](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.1.7) - 2026-09-25
|
|
325
|
+
|
|
326
|
+
##### Fixed
|
|
327
|
+
|
|
328
|
+
- **A menu with children no longer runs its label under the expand chevron.** The chevron and the
|
|
329
|
+
external-link glyph sit in an absolute slot at the row's right edge, outside the anchor's flow,
|
|
330
|
+
so nothing reserved room for them: "Fluxo de delegação" overlapped the chevron by 11px. The row
|
|
331
|
+
reserves the slot now and the anchor fills what is left, so a long label ellipsises before it.
|
|
332
|
+
|
|
333
|
+
Rows with one of those glyphs give up the fixed anchor width to do this, so their label reflows
|
|
334
|
+
while the rail collapses instead of sliding out of view. Only those rows, only during the
|
|
335
|
+
animation.
|
|
336
|
+
|
|
337
|
+
- **`lib-data-grid` and `lib-group-container` follow their container's corner.** Both were a flat
|
|
338
|
+
`rounded-lg`, which inside the tab panel put an 8px corner 9px in from a 20px one — close enough
|
|
339
|
+
to look like a mistake rather than a choice. They read `var(--surface-radius, 0.5rem)` now: the
|
|
340
|
+
panel publishes the radius that keeps the two parallel, and everywhere else the 0.5rem default
|
|
341
|
+
is exactly what they had.
|
|
342
|
+
|
|
343
|
+
#### [3.1.6](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.1.6) - 2026-09-25
|
|
344
|
+
|
|
345
|
+
##### Fixed
|
|
346
|
+
|
|
347
|
+
- **Icons drawn in CSS survive a FontAwesome major.** Four pseudo-elements set
|
|
348
|
+
`font-family: 'Font Awesome 6 Free'` literally: the sidebar item's expand/collapse chevron and
|
|
349
|
+
its external-link marker, and the checked-checkbox tick in `form-input` and in the published
|
|
350
|
+
`styles/components/input.scss`. FontAwesome 7 renames the family to `Font Awesome 7 Free` and,
|
|
351
|
+
unlike the FontAwesome 5 names, does not keep a 6 alias — so on an application that had moved to
|
|
352
|
+
v7 the family stopped resolving and every one of those glyphs rendered as a fallback box.
|
|
353
|
+
|
|
354
|
+
They now read `var(--fa-family-classic, 'Font Awesome 6 Free')`. FontAwesome 7 defines that
|
|
355
|
+
variable; 6 does not, so it takes the fallback. The same stylesheet is correct on both, and on
|
|
356
|
+
8 as well as long as FontAwesome keeps the variable.
|
|
357
|
+
|
|
358
|
+
#### [3.1.5](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.1.5) - 2026-09-24
|
|
359
|
+
|
|
360
|
+
##### Fixed
|
|
361
|
+
|
|
362
|
+
- **Pressing Enter in the multi-select search no longer saves the changes.** The search box sits
|
|
363
|
+
inside the modal's form, so Enter submitted it and the confirmation ran alongside the search --
|
|
364
|
+
committing a selection the user was still building. Enter now searches and nothing else.
|
|
365
|
+
|
|
366
|
+
- **The remove button in the multi-select panel works for an entry the search never showed.** It
|
|
367
|
+
removed by deselecting the row in the grid on the left, which knows nothing about a row it has
|
|
368
|
+
not loaded, so clicking the cross on an entry further down the list did nothing. The panel now
|
|
369
|
+
tells the dataset directly, and still deselects the grid row when there is one.
|
|
370
|
+
|
|
371
|
+
- **Grid checkboxes are no longer drawn as disabled.** The disabled attribute was bound to the
|
|
372
|
+
boolean itself, and an attribute binding writes `disabled="false"` for false -- which disables a
|
|
373
|
+
control just as firmly as any other value. Every checkbox rendered faded and unusable, leaving
|
|
374
|
+
the row label as the only way to toggle a selection.
|
|
375
|
+
|
|
376
|
+
- **A multi-select or multi-editor no longer adds a gap below the page.** Both render nothing but
|
|
377
|
+
a fixed-position modal, yet their host still counted as a child in the parent's layout, so a
|
|
378
|
+
flex column with a gap reserved a slot for it under the last visible element.
|
|
379
|
+
|
|
380
|
+
**Requires Tailwind CSS 4**, and `@zambon-dev/shared` 5.x, which ships the theme. The migration
|
|
381
|
+
steps are in the `@zambon-dev/shared` changelog for this release; there is nothing specific to
|
|
382
|
+
this package to do beyond following them.
|
|
383
|
+
|
|
384
|
+
#### [3.1.4](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.1.4) - 2026-09-23
|
|
385
|
+
|
|
386
|
+
##### Fixed
|
|
387
|
+
|
|
388
|
+
- **A `lib-catalog-select` with `searchable` off no longer looks disabled.** It was drawn on the
|
|
389
|
+
same grey as a disabled field, so nothing distinguished a picker you are meant to click from a
|
|
390
|
+
control that is not yours to touch.
|
|
391
|
+
|
|
392
|
+
Turning searching off makes the inner input read-only to the keyboard, and `lib-form-input`
|
|
393
|
+
paints a read-only input as disabled by default. That default is right for `readOnly`, which
|
|
394
|
+
means the value cannot be changed at all; it is wrong here, where only typing is withheld. The
|
|
395
|
+
disabled appearance is now tied to `readOnly` alone.
|
|
396
|
+
|
|
397
|
+
#### [3.1.3](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.1.3) - 2026-09-23
|
|
398
|
+
|
|
399
|
+
##### Added
|
|
400
|
+
|
|
401
|
+
- **`lib-catalog-select` takes a `searchable` input.** A catalog of one or two entries is a picker,
|
|
402
|
+
not a search: the text box invites typing that only gets in the way, and the minimum-length rule
|
|
403
|
+
hides the very entries the user opened the field to choose from. Setting `[searchable]="false"`
|
|
404
|
+
leaves the input read-only to the keyboard while the dropdown still opens on click and the clear
|
|
405
|
+
button still works.
|
|
406
|
+
|
|
407
|
+
It differs from `readOnly`, which means the value cannot be changed at all and takes the clear
|
|
408
|
+
button away. It does not suit an endpoint that answers `shouldUseCriteria`: that endpoint
|
|
409
|
+
returns nothing until it is given criteria, and there would be no way left to type any.
|
|
410
|
+
|
|
411
|
+
- **`lib-catalog-select` exposes `refresh()`.** The entries are fetched once, when the field
|
|
412
|
+
initialises, so a screen that changes the data behind the catalog -- granting a role, moving an
|
|
413
|
+
employee -- kept offering the list it read before the change. Calling `refresh()` re-reads the
|
|
414
|
+
catalog, including whether its endpoint now requires search criteria.
|
|
415
|
+
|
|
416
|
+
##### Fixed
|
|
417
|
+
|
|
418
|
+
- **`lib-catalog-select` no longer reports "no results" when its endpoint arrives after the field
|
|
419
|
+
does.** A screen that reads the endpoint off a model it loads over HTTP binds an empty string
|
|
420
|
+
first. The field initialised against that, showed _no results found_ on the first open, and only
|
|
421
|
+
filled in once the user typed something and cleared it again.
|
|
422
|
+
|
|
423
|
+
With no endpoint the component takes its static-list path, finds an empty list and latches the
|
|
424
|
+
message. `searchEndpoint` was a plain input, so nothing re-ran the search when the real value
|
|
425
|
+
landed -- unlike `entriesList` and `filters`, which have always refreshed on change. It is now a
|
|
426
|
+
setter and re-reads the catalog, treating a new endpoint as a new catalog rather than carrying
|
|
427
|
+
over what the previous one reported about needing search criteria.
|
|
428
|
+
|
|
429
|
+
#### [3.1.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.1.2) - 2026-09-22
|
|
430
|
+
|
|
431
|
+
##### Fixed
|
|
432
|
+
|
|
433
|
+
- **Group headings in a narrow navigation panel no longer spill their label past the edge.** On a
|
|
434
|
+
window under 768px the panel opens at its collapsed width, but each group still printed its full
|
|
435
|
+
label -- clipped mid-word against a bar with no room for it -- where it should show the short
|
|
436
|
+
divider the collapsed panel uses.
|
|
437
|
+
|
|
438
|
+
The width that widens the panel is declared inside the `md` query, so below that breakpoint the
|
|
439
|
+
panel stays narrow whatever its expanded class says. The heading, though, was keyed off that
|
|
440
|
+
class rather than the width it actually got. It now follows the width, with the overlay still
|
|
441
|
+
excluded -- that one does widen the panel at any size.
|
|
442
|
+
|
|
443
|
+
#### [3.1.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.1.1) - 2026-09-22
|
|
444
|
+
|
|
445
|
+
##### Fixed
|
|
446
|
+
|
|
447
|
+
- **`lib-catalog-select` now offers its clear button for a value it did not see arrive.** A field
|
|
448
|
+
restored from a saved filter showed the value and its label but no way to empty it, so the only
|
|
449
|
+
escape was to clear the whole filter and start again.
|
|
450
|
+
|
|
451
|
+
The button asked an internal field that is written when the component itself takes the value --
|
|
452
|
+
a pick in the dropdown, or the initial read in `ngOnInit`. A form patched after the component
|
|
453
|
+
has initialised, and patched without an event, reaches the form control and the display and
|
|
454
|
+
nothing else. The button now asks the form control, which is the value that actually counts.
|
|
455
|
+
|
|
456
|
+
#### [3.1.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.1.0) - 2026-09-21
|
|
457
|
+
|
|
458
|
+
##### Added
|
|
459
|
+
|
|
460
|
+
- Added the --tabs-* design tokens to the published stylesheet. They style @zambon-dev/framework's
|
|
461
|
+
tab strip; they live here because this package owns the shared token layer, alongside --sidebar-*.
|
|
462
|
+
|
|
463
|
+
#### [3.0.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.0.1) - 2026-09-21
|
|
464
|
+
|
|
465
|
+
- Maintenance release (no consumer-facing changes were documented).
|
|
466
|
+
|
|
467
|
+
#### [3.0.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v3.0.0) - 2026-09-21
|
|
468
|
+
|
|
469
|
+
- Maintenance release (no consumer-facing changes were documented).
|
|
470
|
+
|
|
471
|
+
#### [2.0.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v2.0.1) - 2026-09-21
|
|
472
|
+
|
|
473
|
+
##### Changed
|
|
474
|
+
|
|
475
|
+
- `RibbonComponent`, `FormGroupComponent` and `GroupContainerComponent` now use
|
|
476
|
+
`ChangeDetectionStrategy.OnPush`. They render from their inputs only, so behaviour is
|
|
477
|
+
unchanged, but consuming applications no longer check them on every change detection pass.
|
|
478
|
+
|
|
479
|
+
#### [2.0.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v2.0.0) - 2026-09-21
|
|
480
|
+
|
|
481
|
+
##### Changed
|
|
482
|
+
|
|
483
|
+
- **Accessibility.** Every interactive element that previously responded only to a mouse is now
|
|
484
|
+
reachable and operable by keyboard. `RibbonButtonComponent` (button, dropdown toggle and each
|
|
485
|
+
option), `GroupAccordionComponent`, `GroupScrollSpyComponent`, `SidebarItemComponent` and the
|
|
486
|
+
`MultiSelectComponent` result grid gained focus, `Enter`/`Space` activation and the matching ARIA
|
|
487
|
+
roles and state. `CatalogSelectComponent` status rows are now `role="presentation"`; its retry
|
|
488
|
+
action is a real button.
|
|
489
|
+
- `MultiEditorComponent` and the multi-select result grid render their actions as `<button>`
|
|
490
|
+
rather than `<a>`. They are styled identically but now honour `disabled` natively.
|
|
491
|
+
|
|
492
|
+
> **The 1.6.x line is retired.** It carried the library for applications still on Angular 19 while
|
|
493
|
+
> `main` moved to Angular 22, publishing under its own `1.6.x` dist-tag. Its branch is gone; the
|
|
494
|
+
> final state is kept in the `angular-19/library-1.6.x` tag. Every fix recorded below also shipped
|
|
495
|
+
> in a 3.x release, which requires Angular 22.
|
|
496
|
+
|
|
497
|
+
#### [1.6.9](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.6.9) - 2026-09-24
|
|
498
|
+
|
|
499
|
+
##### Fixed
|
|
500
|
+
|
|
501
|
+
- **Pressing Enter in the multi-select search no longer saves the changes.** The search box sits
|
|
502
|
+
inside the modal's form, so Enter submitted it and the confirmation ran alongside the search --
|
|
503
|
+
committing a selection the user was still building. Enter now searches and nothing else.
|
|
504
|
+
|
|
505
|
+
- **The remove button in the multi-select panel works for an entry the search never showed.** It
|
|
506
|
+
removed by deselecting the row in the grid on the left, which knows nothing about a row it has
|
|
507
|
+
not loaded, so clicking the cross on an entry further down the list did nothing. The panel now
|
|
508
|
+
tells the dataset directly, and still deselects the grid row when there is one.
|
|
509
|
+
|
|
510
|
+
- **Grid checkboxes are no longer drawn as disabled.** The disabled attribute was bound to the
|
|
511
|
+
boolean itself, and an attribute binding writes `disabled="false"` for false -- which disables a
|
|
512
|
+
control just as firmly as any other value. Every checkbox rendered faded and unusable, leaving
|
|
513
|
+
the row label as the only way to toggle a selection.
|
|
514
|
+
|
|
515
|
+
- **A multi-select or multi-editor no longer adds a gap below the page.** Both render nothing but
|
|
516
|
+
a fixed-position modal, yet their host still counted as a child in the parent's layout, so a
|
|
517
|
+
flex column with a gap reserved a slot for it under the last visible element.
|
|
518
|
+
|
|
519
|
+
#### [1.6.8](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.6.8) - 2026-09-23
|
|
520
|
+
|
|
521
|
+
##### Fixed
|
|
522
|
+
|
|
523
|
+
- **A `lib-catalog-select` with `searchable` off no longer looks disabled.** It was drawn on the
|
|
524
|
+
same grey as a disabled field, so nothing distinguished a picker you are meant to click from a
|
|
525
|
+
control that is not yours to touch.
|
|
526
|
+
|
|
527
|
+
Turning searching off makes the inner input read-only to the keyboard, and `lib-form-input`
|
|
528
|
+
paints a read-only input as disabled by default. That default is right for `readOnly`, which
|
|
529
|
+
means the value cannot be changed at all; it is wrong here, where only typing is withheld. The
|
|
530
|
+
disabled appearance is now tied to `readOnly` alone.
|
|
531
|
+
|
|
532
|
+
#### [1.6.7](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.6.7) - 2026-09-23
|
|
533
|
+
|
|
534
|
+
##### Added
|
|
535
|
+
|
|
536
|
+
- **`lib-catalog-select` takes a `searchable` input.** A catalog of one or two entries is a picker,
|
|
537
|
+
not a search: the text box invites typing that only gets in the way, and the minimum-length rule
|
|
538
|
+
hides the very entries the user opened the field to choose from. Setting `[searchable]="false"`
|
|
539
|
+
leaves the input read-only to the keyboard while the dropdown still opens on click and the clear
|
|
540
|
+
button still works.
|
|
541
|
+
|
|
542
|
+
It differs from `readOnly`, which means the value cannot be changed at all and takes the clear
|
|
543
|
+
button away. It does not suit an endpoint that answers `shouldUseCriteria`: that endpoint
|
|
544
|
+
returns nothing until it is given criteria, and there would be no way left to type any.
|
|
545
|
+
|
|
546
|
+
- **`lib-catalog-select` exposes `refresh()`.** The entries are fetched once, when the field
|
|
547
|
+
initialises, so a screen that changes the data behind the catalog -- granting a role, moving an
|
|
548
|
+
employee -- kept offering the list it read before the change. Calling `refresh()` re-reads the
|
|
549
|
+
catalog, including whether its endpoint now requires search criteria.
|
|
550
|
+
|
|
551
|
+
##### Fixed
|
|
552
|
+
|
|
553
|
+
- **`lib-catalog-select` no longer reports "no results" when its endpoint arrives after the field
|
|
554
|
+
does.** A screen that reads the endpoint off a model it loads over HTTP binds an empty string
|
|
555
|
+
first. The field initialised against that, showed _no results found_ on the first open, and only
|
|
556
|
+
filled in once the user typed something and cleared it again.
|
|
557
|
+
|
|
558
|
+
With no endpoint the component takes its static-list path, finds an empty list and latches the
|
|
559
|
+
message. `searchEndpoint` was a plain input, so nothing re-ran the search when the real value
|
|
560
|
+
landed -- unlike `entriesList` and `filters`, which have always refreshed on change. It is now a
|
|
561
|
+
setter and re-reads the catalog, treating a new endpoint as a new catalog rather than carrying
|
|
562
|
+
over what the previous one reported about needing search criteria.
|
|
563
|
+
|
|
564
|
+
#### [1.6.6](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.6.6) - 2026-09-22
|
|
565
|
+
|
|
566
|
+
##### Fixed
|
|
567
|
+
|
|
568
|
+
- **Group headings in a narrow navigation panel no longer spill their label past the edge.** On a
|
|
569
|
+
window under 768px the panel opens at its collapsed width, but each group still printed its full
|
|
570
|
+
label -- clipped mid-word against a bar with no room for it -- where it should show the short
|
|
571
|
+
divider the collapsed panel uses.
|
|
572
|
+
|
|
573
|
+
The width that widens the panel is declared inside the `md` query, so below that breakpoint the
|
|
574
|
+
panel stays narrow whatever its expanded class says. The heading, though, was keyed off that
|
|
575
|
+
class rather than the width it actually got. It now follows the width, with the overlay still
|
|
576
|
+
excluded -- that one does widen the panel at any size.
|
|
577
|
+
|
|
578
|
+
#### [1.6.5](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.6.5) - 2026-09-22
|
|
579
|
+
|
|
580
|
+
##### Fixed
|
|
581
|
+
|
|
582
|
+
- **`lib-catalog-select` now offers its clear button for a value it did not see arrive.** A field
|
|
583
|
+
restored from a saved filter showed the value and its label but no way to empty it, so the only
|
|
584
|
+
escape was to clear the whole filter and start again.
|
|
585
|
+
|
|
586
|
+
The button asked an internal field that is written when the component itself takes the value --
|
|
587
|
+
a pick in the dropdown, or the initial read in `ngOnInit`. A form patched after the component
|
|
588
|
+
has initialised, and patched without an event, reaches the form control and the display and
|
|
589
|
+
nothing else. The button now asks the form control, which is the value that actually counts.
|
|
590
|
+
|
|
591
|
+
#### [1.6.4](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.6.4) - 2026-09-21
|
|
592
|
+
|
|
593
|
+
##### Fixed
|
|
594
|
+
|
|
595
|
+
- **Host styles now apply.** Ten components declared their host block as `:host-context {` with no
|
|
596
|
+
argument. That is not a valid host selector, so every rule inside was silently dropped -- the
|
|
597
|
+
ribbon never became a flex row (its buttons stacked vertically), `lib-group-container` never got
|
|
598
|
+
its card background or border, and `lib-data-grid`, `lib-form-group`, `lib-group-accordion`,
|
|
599
|
+
`lib-group-scroll-spy`, `lib-catalog-select`, `lib-ribbon-group`, `lib-ribbon-button` and the
|
|
600
|
+
multi-select result grid all rendered unstyled at the host level. They are `:host {` now.
|
|
601
|
+
|
|
602
|
+
- `CatalogSelectComponent` subscribed to its `forkJoin` with an empty handler; it now subscribes
|
|
603
|
+
without one. No behavioural change.
|
|
604
|
+
|
|
605
|
+
#### [1.6.3](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.6.3) - 2026-09-20
|
|
606
|
+
|
|
607
|
+
##### Fixed
|
|
608
|
+
|
|
609
|
+
- Declared `ngx-resize-observer` and `rxjs` in `peerDependencies`. `DataGridRowComponent` imports
|
|
610
|
+
`ngx-resize-observer` at runtime, but the package was never declared, so installing
|
|
611
|
+
`@zambon-dev/library` on its own left the dependency unresolved.
|
|
612
|
+
- Added a README to the published package.
|
|
613
|
+
|
|
614
|
+
##### ⚠ Breaking Changes / Migration
|
|
615
|
+
|
|
616
|
+
**Requires Angular 22.** Every `@angular/*` peer range moved from `^19.1.0` to `^22.0.0`, and
|
|
617
|
+
the package is built and tested against Angular 22.1 with TypeScript 6.0.
|
|
618
|
+
|
|
619
|
+
Upgrade your application to Angular 22 first — Angular does not support skipping majors, so go
|
|
620
|
+
19 → 20 → 21 → 22 with `ng update`. Angular 22 also requires Node.js 22.22.3 or newer.
|
|
621
|
+
|
|
622
|
+
- Requires `ngx-resize-observer` 4.x (the Angular 22 line); the peer range moved from `^3.0.0`.
|
|
623
|
+
|
|
624
|
+
**`FormInputComponent` outputs renamed.** Four outputs were named after native DOM events. Because
|
|
625
|
+
`<lib-form-input>` is a real element, `(change)` and `(input)` fired a consumer's handler twice --
|
|
626
|
+
once from the native event bubbling out of the inner control, once from the component -- and
|
|
627
|
+
`(blur)`/`(focus)` shadowed the native events. All four are typed `any`, so TypeScript could not
|
|
628
|
+
catch the mismatch.
|
|
629
|
+
|
|
630
|
+
Rename the bindings in your templates:
|
|
631
|
+
|
|
632
|
+
| Before | After |
|
|
633
|
+
| ---------- | ---------------- |
|
|
634
|
+
| `(blur)` | `(blurred)` |
|
|
635
|
+
| `(change)` | `(changed)` |
|
|
636
|
+
| `(focus)` | `(focused)` |
|
|
637
|
+
| `(input)` | `(inputChanged)` |
|
|
638
|
+
|
|
639
|
+
`(fixedValueChanged)` is unchanged, but its payload type is now `string` instead of `any` --
|
|
640
|
+
matching what it always emitted.
|
|
641
|
+
|
|
642
|
+
If you were relying on the double-fire, expect one call per event now instead of two.
|
|
643
|
+
|
|
644
|
+
#### [1.6.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.6.2) - 2026-09-20
|
|
645
|
+
|
|
646
|
+
##### Fixed
|
|
647
|
+
|
|
648
|
+
- **A `lib-data-grid` column with no room from the start no longer drags its heading out of line.**
|
|
649
|
+
1.6.1 removes the heading of a collapsed column, but only once that column had measured a real
|
|
650
|
+
width at some point. A column that never has any room does not, so it kept a heading over nothing
|
|
651
|
+
and pushed every column to its right along -- up to 263px on a fourteen-column screen, which is
|
|
652
|
+
the same drift 1.6.1 set out to remove.
|
|
653
|
+
|
|
654
|
+
Whether a zero is believable is now settled by the row rather than by what the column measured
|
|
655
|
+
before: if the row has a width the layout is live, so a column reporting zero really has
|
|
656
|
+
collapsed.
|
|
657
|
+
|
|
658
|
+
- **`lib-data-grid` headings no longer stay behind after you revisit a tab.** Re-activating a
|
|
659
|
+
detached tab puts the grid back in the page with its horizontal scroll at the start again, and no
|
|
660
|
+
scroll event follows to say so, so the headings kept the offset they had when you left: on a grid
|
|
661
|
+
scrolled 300px every heading sat 300px away from its column. The header now reconciles with the
|
|
662
|
+
body whenever the grid is re-measured, which is what re-inserting it triggers.
|
|
663
|
+
|
|
664
|
+
#### [1.6.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.6.1) - 2026-09-19
|
|
665
|
+
|
|
666
|
+
##### Fixed
|
|
667
|
+
|
|
668
|
+
- **`lib-data-grid` headings now sit over their own column.** On a grid with many columns each
|
|
669
|
+
heading drifted further from its cells than the last -- up to 263px on a fourteen-column screen --
|
|
670
|
+
and scrolling sideways separated them completely.
|
|
671
|
+
|
|
672
|
+
The body lays the columns out as a CSS grid inside the CDK viewport; the heading row is a flex
|
|
673
|
+
row outside it that copies each column's measured width. Four things pulled the two apart:
|
|
674
|
+
|
|
675
|
+
- The heading cells could **shrink**, so once the columns together were wider than the view every
|
|
676
|
+
heading was compressed a little and the error accumulated across the row.
|
|
677
|
+
- The heading row **did not follow the body sideways**. The rows scroll inside the viewport and
|
|
678
|
+
the headings simply stayed where they were.
|
|
679
|
+
- A column was **measured once and never again**, while the body keeps resizing its
|
|
680
|
+
`minmax(x, min-content)` columns as virtual scrolling brings different content into view.
|
|
681
|
+
- A column measured at **zero** -- a `1fr` column collapsed for want of room -- left its heading
|
|
682
|
+
with no width at all, so it kept its text width and shoved everything to its right along.
|
|
683
|
+
|
|
684
|
+
Headings are now fixed to the measured width, translate with the body's horizontal scroll, track
|
|
685
|
+
every resize, and a heading whose column has collapsed is removed rather than left taking a gap
|
|
686
|
+
the body does not have. Measured in a running application the drift is zero across every column,
|
|
687
|
+
at rest and scrolled.
|
|
688
|
+
|
|
689
|
+
A column that declares no `size` is laid out as `minmax(0, 1fr)`, which is free to collapse. If
|
|
690
|
+
one of yours does, its heading now goes with it rather than staying behind as a label over
|
|
691
|
+
nothing. Give such a column a real minimum -- `size: 'minmax(8rem, 1fr)'` -- to keep both on
|
|
692
|
+
screen.
|
|
693
|
+
|
|
694
|
+
#### [1.6.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.6.0) - 2026-09-16
|
|
695
|
+
|
|
696
|
+
##### Added
|
|
697
|
+
|
|
698
|
+
- **`DisplayControls`**, which records the form controls that exist only to show a catalog
|
|
699
|
+
selection's label. `lib-catalog-select` works in pairs — `controlName` holds the identifier,
|
|
700
|
+
`displayControlName` holds the text — and it creates the second itself when a screen does not
|
|
701
|
+
declare one, so a form carries controls nobody wrote down. It now marks whichever control it
|
|
702
|
+
drives as its display, and `framework-button-filters` reads that mark to keep those out of what
|
|
703
|
+
it submits.
|
|
704
|
+
|
|
705
|
+
The mark is held in a `WeakSet` keyed by the control instance, not by its name: two forms may
|
|
706
|
+
each have an `employeeName`, and only the one a catalog select drives is a display control.
|
|
707
|
+
Nothing about an application changes — declaring the display control yourself still works, and
|
|
708
|
+
it is marked just the same.
|
|
709
|
+
|
|
710
|
+
#### [1.5.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.5.0) - 2026-09-09
|
|
711
|
+
|
|
712
|
+
##### Added
|
|
713
|
+
|
|
714
|
+
- **`SidebarConfigs.shouldDeriveAreasFromRootMenus`** — derive region headers from the menu tree
|
|
715
|
+
instead of from `SidebarMenu.region`. Off by default, so nothing changes until you opt in.
|
|
716
|
+
|
|
717
|
+
With it on, a top-level menu that has children **and no URL** stops being a collapsible node and
|
|
718
|
+
becomes an area header, with its children rendered flat beneath it as top-level items — icons
|
|
719
|
+
included. A top-level menu that has its own URL stays an item, and a parent that carries a URL
|
|
720
|
+
stays collapsible, so only the menus that were already acting purely as groups change shape.
|
|
721
|
+
|
|
722
|
+
The point is where the grouping lives. `region` is a label repeated on every item that belongs to
|
|
723
|
+
a group, matched by exact string equality: a typo silently splits one area into two, and the
|
|
724
|
+
area has no row of its own to carry an order or a translation. Derived from the tree, the area
|
|
725
|
+
_is_ a menu row — it already has a translated label and an order — and nothing has to be
|
|
726
|
+
duplicated across its items.
|
|
727
|
+
|
|
728
|
+
The children of an area are fetched **eagerly**, at load, because they are rendered without a
|
|
729
|
+
click and the lazy load a collapsible parent relies on would never fire. That is one extra
|
|
730
|
+
request per area. `region` keeps working exactly as before for anyone who prefers it; the two
|
|
731
|
+
mechanisms are independent and the flag chooses between them.
|
|
732
|
+
|
|
733
|
+
- **`SidebarService.loadChildrenFor(parentMenu)`** — loads a parent’s children and returns them as
|
|
734
|
+
an observable, for callers that need them before the user clicks. `loadChildren` is unchanged: it
|
|
735
|
+
is still the fire-and-forget variant that raises `childrenLoading` and `childrenFailed`, and it
|
|
736
|
+
now delegates to this one.
|
|
737
|
+
|
|
738
|
+
##### ⚠ Breaking Changes / Migration
|
|
739
|
+
|
|
740
|
+
None. `shouldDeriveAreasFromRootMenus` defaults to `false`, so a sidebar keeps grouping by
|
|
741
|
+
`region` and keeps rendering top-level parents as collapsible nodes until you set it.
|
|
742
|
+
|
|
743
|
+
#### [1.4.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.4.1) - 2026-09-08
|
|
744
|
+
|
|
745
|
+
- Maintenance release (no consumer-facing changes were documented).
|
|
746
|
+
|
|
747
|
+
#### [1.4.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.4.0) - 2026-09-08
|
|
748
|
+
|
|
749
|
+
##### Added
|
|
750
|
+
|
|
751
|
+
- **Sidebar menu items can now open an external destination instead of an internal route.**
|
|
752
|
+
`SidebarMenu` gained an optional `openMode` (`SidebarMenuOpenMode`): `Internal` (the default),
|
|
753
|
+
`ExternalNewTab`, or `ExternalEmbedded`. Read it through the exported
|
|
754
|
+
`toSidebarMenuOpenMode(menu.openMode)` helper rather than comparing the field directly — the
|
|
755
|
+
value is deserialized straight from your menu endpoint, so the helper accepts the mode as a
|
|
756
|
+
camelCase string, as a case-insensitive variant of it, or as the enum ordinal an ASP.NET Core
|
|
757
|
+
enum property serializes to. Anything unrecognized — an omitted field from a backend that
|
|
758
|
+
predates this feature, `null`, or a mode a newer version adds — degrades to `Internal`, so a
|
|
759
|
+
menu you have not touched behaves exactly as it does today.
|
|
760
|
+
|
|
761
|
+
- **`SidebarService.menuExternalUrlSelected`** — a separate output carrying the items whose mode
|
|
762
|
+
is not `Internal`. They are deliberately **not** announced on `menuUrlSelected`, which keeps its
|
|
763
|
+
existing contract: the URL it carries is always an Angular route. Acting on external items needs
|
|
764
|
+
`@zambon-dev/shared` (which opens the browser tab or the embedded view), or your own subscription
|
|
765
|
+
to the new output. Until then an external item highlights and does nothing, rather than sending
|
|
766
|
+
`https://…` to the router.
|
|
767
|
+
|
|
768
|
+
- **`SidebarConfigs.externalLinkText`** — the tooltip on items that open in a new browser tab.
|
|
769
|
+
Defaults to `Opens in a new browser tab` and, like `errorText` and `loadingText`, is rendered
|
|
770
|
+
as-is rather than through the translate pipe, so pass an already-localized string.
|
|
771
|
+
|
|
772
|
+
##### Changed
|
|
773
|
+
|
|
774
|
+
- **Selection now follows the open mode.** An item that opens in a new browser tab is treated as an
|
|
775
|
+
action rather than a destination: it takes no selection pill and, importantly, does not clear the
|
|
776
|
+
selection of the view the user is still looking at. Items that open embedded become real
|
|
777
|
+
application tabs and keep today’s selection behaviour exactly. A parent that also carries a URL
|
|
778
|
+
still expands, so it cannot become impossible to open.
|
|
779
|
+
|
|
780
|
+
- **Items that open in a new browser tab show a trailing outbound glyph.** It sits in the same
|
|
781
|
+
absolutely-positioned slot as the parent chevron, so a long label still ellipsises inside the
|
|
782
|
+
anchor and the collapsed rail is unchanged, and it fades in with the sidebar like the other
|
|
783
|
+
affordances. Embedded items get no glyph — they stay inside the application. Items with no
|
|
784
|
+
`openMode`, or with `openMode: Internal`, render exactly as before.
|
|
785
|
+
|
|
786
|
+
##### ⚠ Breaking Changes / Migration
|
|
787
|
+
|
|
788
|
+
None. `openMode` is optional, so a backend that does not send it produces today’s behaviour, and
|
|
789
|
+
`menuUrlSelected` is unchanged for internal items. If you subscribe to `menuUrlSelected` yourself
|
|
790
|
+
and want external items too, add a subscription to `menuExternalUrlSelected`.
|
|
791
|
+
|
|
792
|
+
#### [1.3.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.3.2) - 2026-07-30
|
|
793
|
+
|
|
794
|
+
##### Added
|
|
795
|
+
|
|
796
|
+
- **Storybook: `Data Grid/Data Grid ▸ Multi Select Tall Rows` story** — multi-selection over rows
|
|
797
|
+
taller than the default `rowHeight` (41.6px), the height a row needs once it shows a thumbnail.
|
|
798
|
+
Misalignment in the selection column is obvious at that height, so this is the story to check the
|
|
799
|
+
selection checkbox against. Development-only: stories are excluded from the package build.
|
|
800
|
+
|
|
801
|
+
##### Changed
|
|
802
|
+
|
|
803
|
+
- **`lib-ribbon` now hides itself when it contains no visible buttons.** A screen with no ribbon
|
|
804
|
+
actions — a dashboard, or any view that projects an empty `#ribbon` template — previously still
|
|
805
|
+
rendered the bar's border, background and padding around nothing, wasting vertical space at the top
|
|
806
|
+
of the page. This mirrors `lib-ribbon-group`, which already hid itself when none of its children
|
|
807
|
+
were visible. Ribbons with at least one visible group are unaffected. See
|
|
808
|
+
**⚠ Breaking Changes / Migration** below.
|
|
809
|
+
|
|
810
|
+
##### Fixed
|
|
811
|
+
|
|
812
|
+
- **`lib-data-grid`'s multi-selection checkbox is now vertically centred in the row.** The checkbox is
|
|
813
|
+
styled with `vertical-align: super`, which lifts it off the middle of its line box — roughly 6px at
|
|
814
|
+
the default font size. At the default `configs.rowHeight` (41.6px) the drift is easy to miss, but on
|
|
815
|
+
taller rows (say `rowHeight: 56` for a row showing a thumbnail) the checkbox visibly hugged the top
|
|
816
|
+
of the row while the text columns stayed centred. The selection cell now lays its checkbox out as a
|
|
817
|
+
flex item, so it is centred by the box model and `vertical-align` no longer applies to it. The cell
|
|
818
|
+
is also tagged with a `selection` class, mirroring the header's `header-cell selection`. Horizontal
|
|
819
|
+
placement is unchanged, and no consumer changes are required.
|
|
820
|
+
- **Form inputs no longer render a stray bordered box when `type` is set as a static attribute.**
|
|
821
|
+
Angular keeps static attributes in the DOM even when a directive consumes them as an `@Input()`, so
|
|
822
|
+
`<lib-form-input-group type="checkbox">` left a `type="checkbox"` attribute on the host element —
|
|
823
|
+
where `@tailwindcss/forms`' tag-agnostic `[type="checkbox"]` base rules styled the host itself as a
|
|
824
|
+
checkbox (1px border, white fill, `height: 1rem`), drawing an empty-input box around the field row
|
|
825
|
+
and tinting its icon. `FormInputGroupComponent` and `FormInputComponent` now clear the attribute via
|
|
826
|
+
a host binding. This affects every value of `type` (`checkbox`, `date`, `number`, `password`, …);
|
|
827
|
+
both `type="checkbox"` and `[type]="'checkbox'"` now render identically, so existing call sites are
|
|
828
|
+
fixed with no changes required.
|
|
829
|
+
- **`lib-multi-editor` no longer discards rows the user did not type into.** `MultiEditorDataset.newData()`
|
|
830
|
+
exists so implementations can pre-fill a new row, but `onNewClick` reached the form through
|
|
831
|
+
`reset()` + `patchValue()`, which leaves it pristine — and the change tracker only records a row
|
|
832
|
+
while `formGroup.dirty`. Pre-filled values were therefore shown in the grid and then dropped from
|
|
833
|
+
the save batch. The visible symptom was worse than lost defaults: adding several rows and editing
|
|
834
|
+
only the last one saved just that row, because `onSaveClick` validates the current selection only,
|
|
835
|
+
so the earlier rows silently vanished on refresh. `newData()`'s values are now registered when the
|
|
836
|
+
row is added.
|
|
837
|
+
- **`lib-catalog-select` no longer renders an empty caption after a form reset.** `FormService.resetForm()`
|
|
838
|
+
clears every control and then patches the model back, so the component receives the value it already
|
|
839
|
+
held. It skipped refreshing the caption in that case, on the assumption that an unchanged value means
|
|
840
|
+
the caption is still on screen — but the reset had emptied the display control a moment earlier. The
|
|
841
|
+
field therefore rendered blank whenever the stored value equalled the control's initial value, which
|
|
842
|
+
is why it only ever showed on the entry the form defaults to: a `nonNullable` control declared as
|
|
843
|
+
`new FormControl(0)` bound to an enum whose first member is `0`, for instance. Values differing from
|
|
844
|
+
the initial one refreshed normally and always looked right, which made the fault read like a
|
|
845
|
+
translation or data problem rather than a reset one. The caption is now rewritten whenever it has
|
|
846
|
+
drifted from the selected entry, so the value alone no longer decides. Selects backed by a
|
|
847
|
+
`searchEndpoint` are unaffected, since their caption comes from the server-side selection rather than
|
|
848
|
+
`entriesList`. No consumer changes are required.
|
|
849
|
+
|
|
850
|
+
##### ⚠ Breaking Changes / Migration
|
|
851
|
+
|
|
852
|
+
`lib-multi-editor` now sends rows that `newData()` pre-filled but the user never edited. Nothing to do
|
|
853
|
+
if your `newData()` returns an empty object (`{}`) — the common "blank row" convention — since a
|
|
854
|
+
value-less row is still skipped and the posted batch is byte-for-byte what it was before. If your
|
|
855
|
+
`newData()` _does_ return field values, those rows now reach `saveData()` where they were previously
|
|
856
|
+
dropped, so a batch may contain entries it did not before; verify your backend rejects or defaults
|
|
857
|
+
them as you expect.
|
|
858
|
+
|
|
859
|
+
`lib-ribbon` now hides itself when it has neither a visible `lib-ribbon-group` nor any other
|
|
860
|
+
projected content. No action needed if your ribbons use groups (the normal case). The rule is a
|
|
861
|
+
global selector at specificity (0,1,2), so overriding it requires `!important`. Because the bar can
|
|
862
|
+
now appear and disappear rather than merely resize, anything positioned relative to the ribbon may
|
|
863
|
+
shift when a button's visibility resolves (e.g. as permissions arrive).
|
|
864
|
+
|
|
865
|
+
#### [1.3.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.3.1) - 2026-07-28
|
|
866
|
+
|
|
867
|
+
> There is no `1.3.0` on npm: the version number was taken by an early publish that was later
|
|
868
|
+
> unpublished, and npm never allows a retired version number to be reused. The sidebar rework below
|
|
869
|
+
> therefore shipped as `1.3.1`. Upgrade straight from `1.2.0` to `1.3.1`.
|
|
870
|
+
|
|
871
|
+
##### Added
|
|
872
|
+
|
|
873
|
+
- Semantic `--sidebar-*` design tokens covering the sidebar's surfaces, text, selection, glass, and
|
|
874
|
+
radii (`--sidebar-bg`, `--sidebar-nav-bg`, `--sidebar-text`, `--sidebar-text-muted`,
|
|
875
|
+
`--sidebar-item-hover-bg`, `--sidebar-item-selected-bg`, `--sidebar-blur`, `--sidebar-shadow`,
|
|
876
|
+
`--sidebar-tree-line`, `--sidebar-radius`, `--sidebar-item-radius`, …) so consumers can re-theme
|
|
877
|
+
the sidebar entirely via CSS custom properties.
|
|
878
|
+
- **Menu regions**: an optional `SidebarMenu.region` label groups top-level items under an uppercase
|
|
879
|
+
group header (e.g. "MAIN"). Items without a `region` render ungrouped, so it stays
|
|
880
|
+
backward-compatible. When the rail is collapsed, each header cross-fades to a short separator line
|
|
881
|
+
so the groups remain visually distinct. The grouping type is exported as `SidebarRegion`.
|
|
882
|
+
|
|
883
|
+
##### Changed
|
|
884
|
+
|
|
885
|
+
- Selected menu items now render as a rounded **pill** highlight (inset when expanded) instead of the
|
|
886
|
+
previous 4px left accent bar.
|
|
887
|
+
- The sidebar is now a **translucent, blurred "glass" panel** — rounded corners, a soft drop shadow,
|
|
888
|
+
and a semi-transparent brand surface — rather than a solid, square, edge-to-edge rail (all
|
|
889
|
+
token-driven via `--sidebar-bg`, `--sidebar-blur`, `--sidebar-shadow`, `--sidebar-radius`).
|
|
890
|
+
- **Child (sub-menu) items** now render a rounded **tree-connector** line instead of a menu icon;
|
|
891
|
+
top-level items keep their icons.
|
|
892
|
+
- Collapsing/expanding the rail is now **animated end to end**: the toggle chevron and the
|
|
893
|
+
expandable-parent caret rotate in the same direction, the selection pill inset and item icons ease
|
|
894
|
+
into place, and region headers cross-fade to their separators — all in sync with the rail width.
|
|
895
|
+
|
|
896
|
+
##### ⚠ Breaking Changes / Migration
|
|
897
|
+
|
|
898
|
+
The sidebar surface is now **translucent** ("glass"). If your app does not place a colored backdrop
|
|
899
|
+
behind the sidebar, the rail will look faint over plain/neutral content — either add a
|
|
900
|
+
colored/gradient backdrop behind it (see `@shared`'s `--app-backdrop` in `MainLayoutComponent`) or
|
|
901
|
+
override `--sidebar-bg` (and the other `--sidebar-*` surface tokens) with opaque values to keep a
|
|
902
|
+
solid rail.
|
|
903
|
+
|
|
904
|
+
#### [1.2.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.2.0) - 2026-07-24
|
|
905
|
+
|
|
906
|
+
##### Added
|
|
907
|
+
|
|
908
|
+
- `SidebarComponent` now renders its own collapse/expand toggle: a light circular chevron "handle"
|
|
909
|
+
that straddles the sidebar's right edge (`angle-left` to collapse when expanded, `angle-right` to
|
|
910
|
+
expand when collapsed), matching the conventional rail-collapse affordance.
|
|
911
|
+
- `SidebarComponent` now has a content-projection slot (`<ng-content>`) rendered as a footer at the
|
|
912
|
+
bottom of the sidebar — for extra elements after the menu (e.g. an app version). It is hidden when
|
|
913
|
+
the rail is collapsed and collapses entirely when nothing is projected.
|
|
914
|
+
|
|
915
|
+
##### Changed
|
|
916
|
+
|
|
917
|
+
- `SidebarComponent` no longer renders the logo or the user-profile header — it now shows only the
|
|
918
|
+
navigation toggle and menu. Application branding and user identity are expected to live in the top
|
|
919
|
+
bar (`shared-top-bar` in `@shared`).
|
|
920
|
+
- Sidebar menu items now use an on-palette hover surface (a subtle light overlay) instead of the
|
|
921
|
+
previous off-palette `slate-700`.
|
|
922
|
+
|
|
923
|
+
##### ⚠ Breaking Changes / Migration
|
|
924
|
+
|
|
925
|
+
If you relied on the sidebar's logo (`SidebarConfigs.logoCollapsedPath` / `logoExpandedPath`) or its
|
|
926
|
+
user profile (`SidebarService.getUserProfile()`), move that presentation to the top bar. Those
|
|
927
|
+
config options and the `getUserProfile()` method still exist but are no longer consumed by
|
|
928
|
+
`SidebarComponent`.
|
|
929
|
+
|
|
930
|
+
#### [1.1.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.1.1) - 2026-06-17
|
|
931
|
+
|
|
932
|
+
- Baseline release: the changelog starts being tracked from this version. Earlier history is
|
|
933
|
+
available via [GitHub Releases](https://github.com/RicardoZambon/ZLibraries/releases) and the
|
|
934
|
+
`library-v*` tags.
|
|
935
|
+
|
|
936
|
+
### Framework
|
|
937
|
+
|
|
938
|
+
Tabbed navigation, view scaffolding, ribbon buttons and modals. Published as `@zambon-dev/framework` through 5.0.5.
|
|
939
|
+
|
|
940
|
+
#### [5.0.5](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v5.0.5) - 2026-10-24
|
|
941
|
+
|
|
942
|
+
- Maintenance release (no consumer-facing changes were documented).
|
|
943
|
+
|
|
944
|
+
#### [5.0.4](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v5.0.4) - 2026-10-24
|
|
945
|
+
|
|
946
|
+
##### Changed
|
|
947
|
+
|
|
948
|
+
- **Every component in this package now renders under any change-detection strategy the host
|
|
949
|
+
application chooses, zoneless included.** The ribbon buttons were the last group still
|
|
950
|
+
declaring `Eager`, and the one that depended on it most: a button answers to things that
|
|
951
|
+
arrive after the first paint -- an access check resolving, a grid announcing a selection, a
|
|
952
|
+
load finishing -- and none of that reached the screen beneath a clean `OnPush` ancestor.
|
|
953
|
+
Their state is signals now, and `src/lib/change-detection-contract.spec.ts` asserts over the
|
|
954
|
+
public API that nothing in this package opts out again.
|
|
955
|
+
|
|
956
|
+
- **A view's `loading` flag notifies.** `ViewBase` backs it with a signal behind the same
|
|
957
|
+
accessors, so a subclass reading or writing `this.loading` needs no change and its own
|
|
958
|
+
template finally reflects it.
|
|
959
|
+
|
|
960
|
+
- **The legacy form's `viewMode` notifies.** Same treatment, same accessors. It is what the
|
|
961
|
+
legacy Edit and Save buttons render from.
|
|
962
|
+
|
|
963
|
+
##### Fixed
|
|
964
|
+
|
|
965
|
+
- **A button no longer asks twice whether its actions are allowed.** `allowedActions` fired the
|
|
966
|
+
check from its setter _and_ the constructor fired it again before any input had arrived, so
|
|
967
|
+
every button with an access list made two requests. It is one stream over the input now.
|
|
968
|
+
|
|
969
|
+
- **A button with an access list is disabled until the answer arrives**, rather than clickable
|
|
970
|
+
and then hidden. This is what `RibbonButtonComponent` already does with a dropdown option
|
|
971
|
+
whose access has not come back.
|
|
972
|
+
|
|
973
|
+
- **The confirm and delete buttons show their modal's spinner again.** Their footer button read
|
|
974
|
+
`modal.isLoading` through a template reference, which became a signal object in 5.0.3 -- and
|
|
975
|
+
an object is always truthy, so the spinner was permanently on.
|
|
976
|
+
|
|
977
|
+
##### ⚠ Breaking Changes / Migration
|
|
978
|
+
|
|
979
|
+
- **`BaseButton`'s state is signals.** Binding to any of them from a template is unchanged.
|
|
980
|
+
Code that touches them is not:
|
|
981
|
+
|
|
982
|
+
| Before | After |
|
|
983
|
+
| ---------------------------- | -------------------------- |
|
|
984
|
+
| `this.disabled = true` | `this.disabled.set(true)` |
|
|
985
|
+
| `this.loading = true` | `this.loading.set(true)` |
|
|
986
|
+
| `this.options = [...]` | `this.options.set([...])` |
|
|
987
|
+
| `this.options.find(...)` | `this.options().find(...)` |
|
|
988
|
+
| `this.allowedActions` | `this.allowedActions()` |
|
|
989
|
+
| `this.iconSize` | `this.iconSize()` |
|
|
990
|
+
| `this.button.startLoading()` | `this.startLoading()` |
|
|
991
|
+
|
|
992
|
+
`startLoading()` and `finishLoading(status)` keep their signatures. `disabled`, `loading` and
|
|
993
|
+
`options` are `model()`, so a subclass can still drive them; the rest are `input()`.
|
|
994
|
+
|
|
995
|
+
- **Every button component's own inputs are signals too**, and so are the view queries they
|
|
996
|
+
expose. The ones a host application is most likely to read directly:
|
|
997
|
+
`ButtonFiltersComponent.modal` is now a `Signal<ModalComponent | undefined>`, and
|
|
998
|
+
`ButtonViewsLegacyComponent.views` is an `input()`. A template reading one through a
|
|
999
|
+
reference -- `#btn` then `btn.disabled` -- needs `btn.disabled()`; without the call it reads
|
|
1000
|
+
the signal object, which is always truthy.
|
|
1001
|
+
|
|
1002
|
+
- **`ViewBase` has a new protected member, `loadingState`**, the signal behind `loading`. A
|
|
1003
|
+
subclass that already declares something by that name has to rename it.
|
|
1004
|
+
|
|
1005
|
+
- **`DefaultDetailsTabViewComponent`'s protected state is signals**: `containerIcon`,
|
|
1006
|
+
`containerTitle` and `detailsViewRoute`. A subclass that reads one needs `()`; one that
|
|
1007
|
+
assigns one needs `.set(...)`.
|
|
1008
|
+
|
|
1009
|
+
- The New, Edit and Views buttons only appear once a record exists, which they read from
|
|
1010
|
+
`DataProviderService`. That now announces its entity id, so install the `@zambon-dev/library`
|
|
1011
|
+
released alongside this version rather than an older 4.x.
|
|
1012
|
+
|
|
1013
|
+
#### [5.0.3](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v5.0.3) - 2026-10-24
|
|
1014
|
+
|
|
1015
|
+
##### Changed
|
|
1016
|
+
|
|
1017
|
+
- **The breadcrumbs, the confirm modal and the error modal render under any change-detection
|
|
1018
|
+
strategy.** The breadcrumbs were the clearest case of a getter over state a service mutates
|
|
1019
|
+
elsewhere, and both modals are driven imperatively by a parent calling `showModal` from a
|
|
1020
|
+
subscription -- a message or a title landed with nothing marking the view dirty.
|
|
1021
|
+
|
|
1022
|
+
- **The ribbon strip renders without the view forcing a change-detection pass.**
|
|
1023
|
+
`DefaultTabViewComponent` holds the published template in a signal, and `TabViewService` holds
|
|
1024
|
+
its active view in one. The `detectChanges()`/`markForCheck()` pair they used to need is gone,
|
|
1025
|
+
and with it the rule that the ribbon template could never be assigned after the first pass --
|
|
1026
|
+
a signal write there schedules another pass rather than throwing NG0100.
|
|
1027
|
+
|
|
1028
|
+
##### ⚠ Breaking Changes / Migration
|
|
1029
|
+
|
|
1030
|
+
- **`ConfirmModalComponent` and `ErrorModalComponent` expose their inputs as signals.** Binding to
|
|
1031
|
+
them from a template is unchanged. Code that sets one before showing the modal keeps working
|
|
1032
|
+
through `.set(...)`, because the ones a parent drives -- `errorMessage`, `isLoading`, and the
|
|
1033
|
+
error modal's `title` and `subtitle` -- are `model()`. Code that _reads_ one needs `()`:
|
|
1034
|
+
`confirmModal.isLoading()` rather than `confirmModal.isLoading`.
|
|
1035
|
+
|
|
1036
|
+
- **`DefaultTabViewComponent` no longer takes constructor arguments.** It injects `TabViewService`
|
|
1037
|
+
itself and no longer injects `ChangeDetectorRef` at all. A subclass calling
|
|
1038
|
+
`super(changeDetectorRef, tabViewService)` should call `super()`, or drop its constructor.
|
|
1039
|
+
|
|
1040
|
+
- **`DefaultTabViewComponent.ribbonTemplate` is a `Signal<TemplateRef | undefined>`.** Read it as
|
|
1041
|
+
`ribbonTemplate()`. `TabViewService.onUpdateRibbonTemplate` is unchanged, so anything
|
|
1042
|
+
subscribing to it keeps working.
|
|
1043
|
+
|
|
1044
|
+
#### [5.0.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v5.0.2) - 2026-10-24
|
|
1045
|
+
|
|
1046
|
+
##### Changed
|
|
1047
|
+
|
|
1048
|
+
- **Internal only: `ButtonFiltersComponent` reads `RibbonButtonComponent.loading` as a signal.**
|
|
1049
|
+
The compiler caught the old form: in a condition, the signal object itself is always true.
|
|
1050
|
+
Nothing in this package's own API changed.
|
|
1051
|
+
|
|
1052
|
+
#### [5.0.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v5.0.1) - 2026-10-24
|
|
1053
|
+
|
|
1054
|
+
##### Changed
|
|
1055
|
+
|
|
1056
|
+
- **Internal only: call sites moved with `@zambon-dev/library`'s modal API.** `ModalComponent.isShown`
|
|
1057
|
+
became `isOpen()`, and the confirm and error modals delegate to it through a signal view query.
|
|
1058
|
+
Nothing in this package's own API changed.
|
|
1059
|
+
|
|
1060
|
+
##### Fixed
|
|
1061
|
+
|
|
1062
|
+
- **The peer range on its sibling packages now admits the majors they were released at.** The last
|
|
1063
|
+
release published `@zambon-dev/library` 4.0.0, `@zambon-dev/framework` 5.0.0 and
|
|
1064
|
+
`@zambon-dev/shared` 6.0.0, but the ranges here still asked for the previous majors, so an
|
|
1065
|
+
application installing two of them together got `ERESOLVE`. Nothing else changed.
|
|
1066
|
+
|
|
1067
|
+
#### [5.0.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v5.0.0) - 2026-10-24
|
|
1068
|
+
|
|
1069
|
+
##### Changed
|
|
1070
|
+
|
|
1071
|
+
- **The view and button base classes no longer declare a change-detection strategy.** This is not
|
|
1072
|
+
a behaviour change: Angular has never inherited `changeDetection`, so the `Eager` these carried
|
|
1073
|
+
applied to the base class itself -- which is never instantiated -- and never to your subclasses.
|
|
1074
|
+
Your components have compiled to OnPush since you upgraded to Angular 22, whatever the base said.
|
|
1075
|
+
The declaration is gone because it described nothing.
|
|
1076
|
+
|
|
1077
|
+
##### Fixed
|
|
1078
|
+
|
|
1079
|
+
- **Ribbon buttons appear once their access check resolves.** `BaseButton` resolved visibility
|
|
1080
|
+
from an asynchronous `checkActionsAreAllowed` call and assigned a plain field, which told Angular
|
|
1081
|
+
nothing. Under Angular 22's default strategy the button stayed hidden until something else forced
|
|
1082
|
+
a render.
|
|
1083
|
+
- **The tab strip renders the tabs you open.** In an application whose root component uses
|
|
1084
|
+
Angular 22's default strategy, opening anything from the menu changed the URL and left the tab
|
|
1085
|
+
bar, the view, the ribbon and the breadcrumbs blank: `TabsComponent` reads the open tabs
|
|
1086
|
+
through getters on `TabService`, which mutates its arrays in place, and nothing told the
|
|
1087
|
+
component to render. `TabService` now announces its own changes, so the tabs, the titles that
|
|
1088
|
+
resolve later, and the breadcrumbs all reach the screen. Found by running SecurityHub against
|
|
1089
|
+
this package rather than by a test -- which is why one now guards it.
|
|
1090
|
+
|
|
1091
|
+
- **The tab strip re-measures when it narrows.** Bringing the selected tab back into view hung off
|
|
1092
|
+
`ngAfterViewChecked`, which only ever ran because zone.js produced a pass on every event. The
|
|
1093
|
+
`ResizeObserver` -- which is how a browser actually reports a box changing -- now does the full
|
|
1094
|
+
re-measure, and the post-render work uses `afterEveryRender`.
|
|
1095
|
+
|
|
1096
|
+
##### ⚠ Breaking Changes / Migration
|
|
1097
|
+
|
|
1098
|
+
- **`BaseButton.visible` is now `isVisible`, and it is a signal.** Every component that extends
|
|
1099
|
+
`BaseButton` is affected, in its template and its code:
|
|
1100
|
+
|
|
1101
|
+
| Replace | With |
|
|
1102
|
+
| --------------------- | ----------------------- |
|
|
1103
|
+
| `@if (visible)` | `@if (isVisible())` |
|
|
1104
|
+
| `this.visible` | `this.isVisible()` |
|
|
1105
|
+
| `!isAccessLoaded` | `!isAccessLoaded()` |
|
|
1106
|
+
| `this.isAccessLoaded` | `this.isAccessLoaded()` |
|
|
1107
|
+
|
|
1108
|
+
Both were renamed or re-typed deliberately, so a call site you miss is a compile error rather
|
|
1109
|
+
than a button that quietly never appears. Before you start, turn on `strictTemplates` and
|
|
1110
|
+
`"interpolatedSignalNotInvoked": "error"` under `angularCompilerOptions.extendedDiagnostics` in
|
|
1111
|
+
your `tsconfig.json`, which makes `{{ someSignal }}` a build failure too.
|
|
1112
|
+
|
|
1113
|
+
- **A component that delegates its visibility to an inner button needs a signal view query.** If
|
|
1114
|
+
you implement `RibbonGroupChild` by wrapping a `framework-button` and forwarding its state,
|
|
1115
|
+
a rename alone is not enough -- a `computed` reading a plain `@ViewChild` never re-runs when
|
|
1116
|
+
the query resolves:
|
|
1117
|
+
|
|
1118
|
+
```ts
|
|
1119
|
+
// before
|
|
1120
|
+
@ViewChild(ButtonComponent) private buttonComponent!: ButtonComponent;
|
|
1121
|
+
public get visible(): boolean {
|
|
1122
|
+
return this.buttonComponent?.visible ?? false;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// after
|
|
1126
|
+
private readonly buttonComponent = viewChild(ButtonComponent);
|
|
1127
|
+
public readonly isVisible: Signal<boolean> = computed(() => this.buttonComponent()?.isVisible() ?? false);
|
|
1128
|
+
```
|
|
1129
|
+
|
|
1130
|
+
Every other read of that query moves to `this.buttonComponent()?.…` as well.
|
|
1131
|
+
|
|
1132
|
+
- **`TabsComponent` no longer injects `ChangeDetectorRef`.** Only relevant if you construct it
|
|
1133
|
+
yourself; used through its selector, nothing changes.
|
|
1134
|
+
|
|
1135
|
+
#### [4.0.5](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v4.0.5) - 2026-10-22
|
|
1136
|
+
|
|
1137
|
+
##### Fixed
|
|
1138
|
+
|
|
1139
|
+
- **Going back in one tab no longer drops a view another tab still has open.** Every open tab keeps
|
|
1140
|
+
its views in the reuse cache — the one it shows or one in its history — and a view should leave
|
|
1141
|
+
that cache only once no tab has its URL open. `navigateCurrentTabBack` cleared the handle of every
|
|
1142
|
+
entry it removed regardless, so a record open in two tabs lost its cached view in the other one
|
|
1143
|
+
too. It now checks first, as `closeTab` and `replaceCurrentTabSubView` already did.
|
|
1144
|
+
|
|
1145
|
+
#### [4.0.4](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v4.0.4) - 2026-10-10
|
|
1146
|
+
|
|
1147
|
+
##### Fixed
|
|
1148
|
+
|
|
1149
|
+
- **A record’s non-form views can render their edit button again.** `framework-button-edit` asks
|
|
1150
|
+
for `FormService` and `DataProviderService` together, and the details shell provided only the
|
|
1151
|
+
second — so a child list routed as its own view (Aplicações → Visões → Business Places, Centros
|
|
1152
|
+
de Custo) threw `NG0201: No provider found for FormService` before it rendered at all. The shell
|
|
1153
|
+
provides one now; a form view’s own provider still shadows it, so forms are unchanged.
|
|
1154
|
+
|
|
1155
|
+
- **The breadcrumbs no longer sit further from the ribbon than the ribbon does from its content.**
|
|
1156
|
+
They carried `padding-bottom` _and_ `margin-bottom` around their rule, 8px + rule + 8px, against
|
|
1157
|
+
the 8px below the ribbon. The tab panel spaces its children with a gap now, as the views do, and
|
|
1158
|
+
the breadcrumbs carry neither — 9px, the gap plus the rule itself.
|
|
1159
|
+
|
|
1160
|
+
#### [4.0.3](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v4.0.3) - 2026-10-02
|
|
1161
|
+
|
|
1162
|
+
##### Fixed
|
|
1163
|
+
|
|
1164
|
+
- **Closing a tab no longer sends the tab beside it back to its root.** The focused tab was
|
|
1165
|
+
navigated to `activeTabs[i]`, which is each tab’s _root_ — the entry the strip takes its label
|
|
1166
|
+
from — rather than the top of its history. A tab opened as a list and then drilled into a record
|
|
1167
|
+
went back to the list, under the record’s own tab, until the tab was clicked again. Every other
|
|
1168
|
+
navigation in the service already took the top of the stack; this one is now shared with
|
|
1169
|
+
`activateTab` so the two cannot drift apart.
|
|
1170
|
+
|
|
1171
|
+
- **The gap between the ribbon and a list is the one the view asks for.** `router-outlet` renders
|
|
1172
|
+
nothing but is still a flex item, and a flex item of zero height takes a gap on each side, so a
|
|
1173
|
+
list view showed 1rem where its host asked for 0.5rem — while a detail view, whose outlet is
|
|
1174
|
+
nested inside `lib-group-container`, showed 0.5rem. The outlet is out of the flow now, which
|
|
1175
|
+
leaves the spacing to the host rather than to what each view happens to contain.
|
|
1176
|
+
|
|
1177
|
+
#### [4.0.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v4.0.2) - 2026-10-01
|
|
1178
|
+
|
|
1179
|
+
##### Fixed
|
|
1180
|
+
|
|
1181
|
+
- **Loading the package under Jest no longer throws
|
|
1182
|
+
`Cannot access 'ButtonNewLegacyComponent' before initialization`.** `LegacyTabViewDetails`
|
|
1183
|
+
queries its New button with `@ViewChild`, and the legacy buttons import the views barrel back,
|
|
1184
|
+
so the class is defined before the button: in the published bundle the button sits further down
|
|
1185
|
+
the file. An application build never noticed, because its linker moves the reference into a
|
|
1186
|
+
function, but a JIT runtime reads it the moment the package is imported, so every spec that
|
|
1187
|
+
imported anything from `@zambon-dev/framework` failed before running a test. The query is a
|
|
1188
|
+
forward reference now. Nothing to change in applications, and specs excluded for this reason can
|
|
1189
|
+
be put back.
|
|
1190
|
+
|
|
1191
|
+
#### [4.0.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v4.0.1) - 2026-09-26
|
|
1192
|
+
|
|
1193
|
+
##### Fixed
|
|
1194
|
+
|
|
1195
|
+
- **The selected tab and the panel no longer have a seam between them.** The panel drew its rim
|
|
1196
|
+
on all four sides, and along the top that rim met the selected tab’s fill rather than the
|
|
1197
|
+
backdrop it was meant for: `--tabs-border` is 60% white against the tab’s 88%, so a _darker_
|
|
1198
|
+
hairline ran exactly along the join and made the tab read as a separate, slightly lighter shape
|
|
1199
|
+
sitting on the panel. The top edge belongs to the tabs, which carry no border, so the panel no
|
|
1200
|
+
longer draws one there.
|
|
1201
|
+
|
|
1202
|
+
#### [4.0.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v4.0.0) - 2026-09-26
|
|
1203
|
+
|
|
1204
|
+
##### Fixed
|
|
1205
|
+
|
|
1206
|
+
- **The filters button no longer waits for an event that has already happened.** It starts
|
|
1207
|
+
disabled and learns otherwise from the grid's `loadFinished`, which is right only for a
|
|
1208
|
+
button that exists before its grid first loads. A ribbon template is re-created whenever a
|
|
1209
|
+
record's views are switched, so a button built then reached a grid that had finished loading
|
|
1210
|
+
long ago and stayed unclickable until something made the grid load again. It now reads the
|
|
1211
|
+
state from the dataset as well.
|
|
1212
|
+
|
|
1213
|
+
#### [3.2.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v3.2.2) - 2026-09-26
|
|
1214
|
+
|
|
1215
|
+
##### Fixed
|
|
1216
|
+
|
|
1217
|
+
- **Component styles are compiled again.** The Tailwind 4 migration left every component's own
|
|
1218
|
+
stylesheet unprocessed in the published bundle, so its `@apply` rules reached the browser as an
|
|
1219
|
+
at-rule the browser does not know and it dropped every rule that used one. In practice the
|
|
1220
|
+
components rendered unstyled in any application installing 3.2.1. ng-packagr resolves its
|
|
1221
|
+
PostCSS configuration from the library directory rather than from the workspace root, and the
|
|
1222
|
+
migration added one only at the root. Nothing failed along the way: Storybook compiles from
|
|
1223
|
+
source and never sees the packaged artifact, which is why `tools/check-packaged-styles.mjs` now
|
|
1224
|
+
runs against that artifact in CI and again before every publish.
|
|
1225
|
+
|
|
1226
|
+
#### [3.2.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v3.2.1) - 2026-09-26
|
|
1227
|
+
|
|
1228
|
+
##### Fixed
|
|
1229
|
+
|
|
1230
|
+
- **Both tab scroll controls stay put while the strip overflows**, disabled at the end they point
|
|
1231
|
+
at rather than removed. Rendering them per-direction meant the one being reached for disappeared
|
|
1232
|
+
on arrival: the strip changed width under the pointer and the click landed on nothing, which is
|
|
1233
|
+
what made the arrows look like they sometimes did nothing.
|
|
1234
|
+
|
|
1235
|
+
- **The last tab no longer floats past the panel's rounded corner.** While a tab or a scroll
|
|
1236
|
+
control holds the strip's right edge, the panel's top-right corner is square, because the card's
|
|
1237
|
+
corner is up on the strip; rounding both left whatever sat at the edge standing over the sweep
|
|
1238
|
+
where the panel had curved away. With the tabs short of the edge the corner is the panel's again
|
|
1239
|
+
and stays round.
|
|
1240
|
+
|
|
1241
|
+
#### [3.2.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v3.2.0) - 2026-09-25
|
|
1242
|
+
|
|
1243
|
+
##### Changed
|
|
1244
|
+
|
|
1245
|
+
- **Tailwind CSS 4.** The component styles in this package are compiled against Tailwind 4 now.
|
|
1246
|
+
They are expanded at build time, so nothing in the published bundle changed shape -- but the
|
|
1247
|
+
application consuming it has to be on Tailwind 4 as well.
|
|
1248
|
+
|
|
1249
|
+
#### [3.1.5](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v3.1.5) - 2026-09-25
|
|
1250
|
+
|
|
1251
|
+
##### Fixed
|
|
1252
|
+
|
|
1253
|
+
- **The selected tab and the panel are the same colour again.** Both already named an 88% white,
|
|
1254
|
+
but the panel lays its fill over a backdrop it has blurred and the tab laid the identical fill
|
|
1255
|
+
over an unblurred one — two results from one token, and a visible seam between them. The tab
|
|
1256
|
+
carries the same blur now.
|
|
1257
|
+
|
|
1258
|
+
- **A tab reaching the right edge no longer sticks out past the panel's rounded corner.** The
|
|
1259
|
+
strip spans the panel's full width, so once the tabs filled it the last one's square corner was
|
|
1260
|
+
left standing over the point where the panel had already curved away. The strip carries the
|
|
1261
|
+
panel's corner now, which clips whatever reaches it; the scroll chevron does the same when it
|
|
1262
|
+
is the element at the edge.
|
|
1263
|
+
|
|
1264
|
+
- **The tab panel publishes `--surface-radius`** for whatever sits directly inside it — the
|
|
1265
|
+
container's radius minus its own padding and border, which is the value that keeps an inner box
|
|
1266
|
+
looking parallel to it. `@zambon-dev/library`'s grid and group container read it.
|
|
1267
|
+
|
|
1268
|
+
##### ⚠ Breaking Changes / Migration
|
|
1269
|
+
|
|
1270
|
+
**Requires Tailwind CSS 4**, and `@zambon-dev/shared` 5.x, which ships the theme. The migration
|
|
1271
|
+
steps are in the `@zambon-dev/shared` changelog for this release; there is nothing specific to
|
|
1272
|
+
this package to do beyond following them.
|
|
1273
|
+
|
|
1274
|
+
#### [3.1.4](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v3.1.4) - 2026-09-25
|
|
1275
|
+
|
|
1276
|
+
##### Fixed
|
|
1277
|
+
|
|
1278
|
+
- **The peer range on `@zambon-dev/library` now matches the version this package is built against.**
|
|
1279
|
+
It still asked for `^2.0.0` after the library moved to 3.x, so installing `framework` and
|
|
1280
|
+
`library` together at their current releases failed `npm install` with ERESOLVE. An application
|
|
1281
|
+
could only get past it with `--legacy-peer-deps` or an override, both of which switch off the
|
|
1282
|
+
peer checking that would have caught a genuine mismatch.
|
|
1283
|
+
|
|
1284
|
+
#### [3.1.3](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v3.1.3) - 2026-09-24
|
|
1285
|
+
|
|
1286
|
+
##### Fixed
|
|
1287
|
+
|
|
1288
|
+
- **A confirm or error modal no longer adds a gap below the page.** Both render nothing but a
|
|
1289
|
+
fixed-position modal, yet their host still counted as a child in the parent's layout, so a flex
|
|
1290
|
+
column with a gap reserved a slot for it under the last visible element -- which is why a detail
|
|
1291
|
+
view and some list views sat further from the bottom of the window than the rest.
|
|
1292
|
+
|
|
1293
|
+
#### [3.1.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v3.1.2) - 2026-09-22
|
|
1294
|
+
|
|
1295
|
+
##### Fixed
|
|
1296
|
+
|
|
1297
|
+
- **A filtered view no longer loses its Filter and Clear filters state when another details tab is
|
|
1298
|
+
opened.** The grid stayed filtered while the ribbon came back as though nothing were filtered, so
|
|
1299
|
+
the only way out was to filter again. Opening a list view never did it -- which is what made the
|
|
1300
|
+
behaviour look arbitrary.
|
|
1301
|
+
|
|
1302
|
+
Every details tab keeps its router subscription alive, including the ones the reuse strategy has
|
|
1303
|
+
detached, and the lookup that finds the active details route reads the router, which names
|
|
1304
|
+
whichever tab the navigation went to. A detached tab therefore adopted the incoming tab's base
|
|
1305
|
+
path and switched itself to that tab's view, tearing its own ribbon down and building a fresh
|
|
1306
|
+
one -- and the new buttons knew nothing of the filter still held by the grid. A list view is not
|
|
1307
|
+
a details route, so the lookup found nothing and the tab was left alone.
|
|
1308
|
+
|
|
1309
|
+
A tab now answers only to navigations matching its own route definition, which still covers the
|
|
1310
|
+
one navigation that legitimately changes its path: the redirect from `/new` to `/:id` after a
|
|
1311
|
+
save.
|
|
1312
|
+
|
|
1313
|
+
#### [3.1.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v3.1.1) - 2026-09-22
|
|
1314
|
+
|
|
1315
|
+
##### Fixed
|
|
1316
|
+
|
|
1317
|
+
- **Switching views in a details tab no longer moves the buttons to the wrong view.** Opening a
|
|
1318
|
+
second view showed the first view's buttons instead of its own, and going back to the first view
|
|
1319
|
+
left it with no buttons at all -- on the employee screen, Histórico showed the Detalhes ribbon and
|
|
1320
|
+
Detalhes then showed an empty one.
|
|
1321
|
+
|
|
1322
|
+
1.4.1 taught a tab to claim a ribbon published before its view had been named, which is what a tab
|
|
1323
|
+
opened straight at a non-default view needs. It claimed on every naming, though, and the empty id
|
|
1324
|
+
is not a spare bucket: it is the default view's own id, since that view is the one whose URL
|
|
1325
|
+
carries no sub-path. From the second switch onwards the template being claimed belonged to the
|
|
1326
|
+
view being left. Only the first naming may claim it now.
|
|
1327
|
+
|
|
1328
|
+
Also released as 1.4.2 on the 1.4.x line, for applications still on Angular 19.
|
|
1329
|
+
|
|
1330
|
+
#### [3.1.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v3.1.0) - 2026-09-21
|
|
1331
|
+
|
|
1332
|
+
##### Added
|
|
1333
|
+
|
|
1334
|
+
- **Tabs close on middle click**, and with `Delete` while a tab has focus.
|
|
1335
|
+
- **The tab strip scrolls.** It was `overflow: hidden`, so once the tabs were wider than the bar the
|
|
1336
|
+
ones past the right edge could not be reached at all. Selecting a tab also scrolls it into view.
|
|
1337
|
+
- **Scroll controls at each end of the strip**, shown only while there is something to scroll to.
|
|
1338
|
+
- **A vertical mouse wheel over the strip scrolls it horizontally**, so a plain wheel mouse can
|
|
1339
|
+
reach an off-screen tab without going to the controls. Trackpad and `Shift`+wheel gestures, which
|
|
1340
|
+
already produce horizontal deltas, are left to the browser.
|
|
1341
|
+
|
|
1342
|
+
##### Changed
|
|
1343
|
+
|
|
1344
|
+
- **Tabs restyled to match the sidebar's glass**: a translucent, blurred, rounded panel, with the
|
|
1345
|
+
active tab and the content sharing one fill so they read as a single card. Every colour, radius
|
|
1346
|
+
and dimension comes from a `--tabs-*` custom property now, so the look can be retuned without
|
|
1347
|
+
touching the component. Breadcrumbs follow the same tokens.
|
|
1348
|
+
|
|
1349
|
+
The glass is painted on a `::before` layer rather than on the panel itself. `backdrop-filter`
|
|
1350
|
+
makes an element the containing block for its `position: fixed` descendants, and views render
|
|
1351
|
+
hidden, viewport-width modal overlays inside the panel — filtering it directly pulled those into
|
|
1352
|
+
its scroll width and produced a phantom horizontal scrollbar.
|
|
1353
|
+
|
|
1354
|
+
- **The tablist is a single tab stop.** `Tab` reaches the selected tab and the arrow keys move
|
|
1355
|
+
between tabs from there, with `Home`/`End` for the ends. Previously every tab was its own tab
|
|
1356
|
+
stop and there was no arrow-key support.
|
|
1357
|
+
- **The panel is a real `tabpanel`**, with `aria-controls` on each tab and `aria-labelledby` on the
|
|
1358
|
+
panel, so assistive technology can tell which panel a tab owns.
|
|
1359
|
+
- **Close buttons are named after their tab** — "Close Acme Industries" rather than six identical
|
|
1360
|
+
"Close tab". `Tabs-CloseTab` takes a `title` parameter now; if you have overridden that key in
|
|
1361
|
+
your own translations, add `{{title}}` to it.
|
|
1362
|
+
- **A tab keeps its width while its title loads.** The spinner sits beside the label instead of
|
|
1363
|
+
replacing it, so the strip no longer reflows as each title resolves.
|
|
1364
|
+
- **Tabs compress before the strip scrolls**, down to `--tabs-item-min-width`, so a few more fit
|
|
1365
|
+
before any of them goes off screen. Overflowing tabs fade out at the edges instead of being cut
|
|
1366
|
+
off mid-tab.
|
|
1367
|
+
- **The selected tab is pulled back into view when the strip narrows**, not only when the selection
|
|
1368
|
+
changes. Resizing the window or collapsing the sidebar used to be able to leave you looking at a
|
|
1369
|
+
panel whose tab was nowhere on screen.
|
|
1370
|
+
- Breadcrumb separators are drawn in CSS rather than as a FontAwesome glyph codepoint, which was
|
|
1371
|
+
tied to a specific FontAwesome major.
|
|
1372
|
+
|
|
1373
|
+
#### [3.0.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v3.0.1) - 2026-09-21
|
|
1374
|
+
|
|
1375
|
+
- Maintenance release (no consumer-facing changes were documented).
|
|
1376
|
+
|
|
1377
|
+
#### [3.0.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v3.0.0) - 2026-09-21
|
|
1378
|
+
|
|
1379
|
+
- Maintenance release (no consumer-facing changes were documented).
|
|
1380
|
+
|
|
1381
|
+
#### [2.0.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v2.0.0) - 2026-09-21
|
|
1382
|
+
|
|
1383
|
+
##### Changed
|
|
1384
|
+
|
|
1385
|
+
- **Accessibility.** `TabsComponent` renders proper `tablist`/`tab` semantics with `aria-selected`,
|
|
1386
|
+
its tabs are keyboard-activatable and the close control is a labelled `<button>`.
|
|
1387
|
+
`TabBreadcrumbsComponent` entries are focusable and activate with `Enter`/`Space`.
|
|
1388
|
+
- Added the `Tabs-CloseTab` translation key (en, pt) for the tab close button's accessible name.
|
|
1389
|
+
|
|
1390
|
+
> **The 1.4.x line is retired.** It carried the framework for applications still on Angular 19
|
|
1391
|
+
> while `main` moved to Angular 22, publishing under its own `1.4.x` dist-tag. Its branch is gone;
|
|
1392
|
+
> the final state is kept in the `angular-19/framework-1.4.x` tag. Every fix recorded below also
|
|
1393
|
+
> shipped in a 3.x or 4.x release, which requires Angular 22.
|
|
1394
|
+
|
|
1395
|
+
#### [1.4.7](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.4.7) - 2026-10-01
|
|
1396
|
+
|
|
1397
|
+
##### Fixed
|
|
1398
|
+
|
|
1399
|
+
- **Loading the package under Jest no longer throws `Cannot access 'ButtonNewLegacyComponent'
|
|
1400
|
+
before initialization`.** `LegacyTabViewDetails` queries its New button with `@ViewChild`, and
|
|
1401
|
+
the legacy buttons import the views barrel back, so the class is defined before the button: in
|
|
1402
|
+
the published bundle the button sits further down the file. An application build never noticed,
|
|
1403
|
+
because its linker moves the reference into a function, but a JIT runtime reads it the moment
|
|
1404
|
+
the package is imported, so every spec that imported anything from `@zambon-dev/framework`
|
|
1405
|
+
failed before running a test. The query is a forward reference now. Nothing to change in
|
|
1406
|
+
applications, and specs excluded for this reason can be put back.
|
|
1407
|
+
|
|
1408
|
+
#### [1.4.6](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.4.6) - 2026-09-24
|
|
1409
|
+
|
|
1410
|
+
##### Fixed
|
|
1411
|
+
|
|
1412
|
+
- **A confirm or error modal no longer adds a gap below the page.** Both render nothing but a
|
|
1413
|
+
fixed-position modal, yet their host still counted as a child in the parent's layout, so a flex
|
|
1414
|
+
column with a gap reserved a slot for it under the last visible element -- which is why a detail
|
|
1415
|
+
view and some list views sat further from the bottom of the window than the rest.
|
|
1416
|
+
|
|
1417
|
+
#### [1.4.5](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.4.5) - 2026-09-22
|
|
1418
|
+
|
|
1419
|
+
##### Fixed
|
|
1420
|
+
|
|
1421
|
+
- **A filtered view no longer loses its Filter and Clear filters state when another details tab is
|
|
1422
|
+
opened.** The grid stayed filtered while the ribbon came back as though nothing were filtered, so
|
|
1423
|
+
the only way out was to filter again. Opening a list view never did it -- which is what made the
|
|
1424
|
+
behaviour look arbitrary.
|
|
1425
|
+
|
|
1426
|
+
Every details tab keeps its router subscription alive, including the ones the reuse strategy has
|
|
1427
|
+
detached, and the lookup that finds the active details route reads the router, which names
|
|
1428
|
+
whichever tab the navigation went to. A detached tab therefore adopted the incoming tab's base
|
|
1429
|
+
path and switched itself to that tab's view, tearing its own ribbon down and building a fresh
|
|
1430
|
+
one -- and the new buttons knew nothing of the filter still held by the grid. A list view is not
|
|
1431
|
+
a details route, so the lookup found nothing and the tab was left alone.
|
|
1432
|
+
|
|
1433
|
+
A tab now answers only to navigations matching its own route definition, which still covers the
|
|
1434
|
+
one navigation that legitimately changes its path: the redirect from `/new` to `/:id` after a
|
|
1435
|
+
save.
|
|
1436
|
+
|
|
1437
|
+
#### [1.4.4](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.4.4) - 2026-09-22
|
|
1438
|
+
|
|
1439
|
+
##### Fixed
|
|
1440
|
+
|
|
1441
|
+
- **Switching views in a details tab no longer moves the buttons to the wrong view.** Opening a
|
|
1442
|
+
second view showed the first view's buttons instead of its own, and going back to the first view
|
|
1443
|
+
left it with no buttons at all -- on the employee screen, Histórico showed the Detalhes ribbon and
|
|
1444
|
+
Detalhes then showed an empty one.
|
|
1445
|
+
|
|
1446
|
+
1.4.1 taught a tab to claim a ribbon published before its view had been named, which is what a tab
|
|
1447
|
+
opened straight at a non-default view needs. It claimed on every naming, though, and the empty id
|
|
1448
|
+
is not a spare bucket: it is the default view's own id, since that view is the one whose URL
|
|
1449
|
+
carries no sub-path. From the second switch onwards the template being claimed belonged to the
|
|
1450
|
+
view being left. Only the first naming may claim it now.
|
|
1451
|
+
|
|
1452
|
+
#### [1.4.3](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.4.3) - 2026-09-21
|
|
1453
|
+
|
|
1454
|
+
##### Fixed
|
|
1455
|
+
|
|
1456
|
+
- **Host styles now apply.** `DefaultTabViewComponent`, `DefaultDetailsTabViewComponent`,
|
|
1457
|
+
`TabBreadcrumbsComponent` and `ButtonFiltersComponent` declared their host block as
|
|
1458
|
+
`:host-context {` with no argument, so every rule inside was silently dropped. Visible effects:
|
|
1459
|
+
no gap between the ribbon and the view below it, and breadcrumbs with neither their bottom rule
|
|
1460
|
+
nor their spacing. They are `:host {` now.
|
|
1461
|
+
|
|
1462
|
+
- `ButtonFiltersComponent.validateFormFunction` was typed `Function`, which accepted any
|
|
1463
|
+
function-like value. It is now `() => void`, matching how it is actually invoked. Consumers
|
|
1464
|
+
passing a function that takes arguments or returns a value will now see a type error.
|
|
1465
|
+
- Removed empty constructors from `ConfirmModalComponent` and `TabViewService`.
|
|
1466
|
+
|
|
1467
|
+
- `DefaultDetailsTabViewComponent` imported `ButtonViewsComponent` through the `buttons` barrel,
|
|
1468
|
+
which re-exports `./legacy`, whose buttons import the `views` barrel, which re-exports this
|
|
1469
|
+
component. The cycle left one of the component's own imports undefined at evaluation time and
|
|
1470
|
+
broke any attempt to instantiate it outside a fully booted application. It now imports the
|
|
1471
|
+
button by its concrete path.
|
|
1472
|
+
|
|
1473
|
+
#### [1.4.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.4.2) - 2026-09-20
|
|
1474
|
+
|
|
1475
|
+
##### Fixed
|
|
1476
|
+
|
|
1477
|
+
- Declared `rxjs` in `peerDependencies`; it is used throughout the package but was never declared.
|
|
1478
|
+
- Added a README to the published package.
|
|
1479
|
+
|
|
1480
|
+
##### ⚠ Breaking Changes / Migration
|
|
1481
|
+
|
|
1482
|
+
**Requires Angular 22.** Every `@angular/*` peer range moved from `^19.1.0` to `^22.0.0`, and
|
|
1483
|
+
the package is built and tested against Angular 22.1 with TypeScript 6.0.
|
|
1484
|
+
|
|
1485
|
+
Upgrade your application to Angular 22 first — Angular does not support skipping majors, so go
|
|
1486
|
+
19 → 20 → 21 → 22 with `ng update`. Angular 22 also requires Node.js 22.22.3 or newer.
|
|
1487
|
+
|
|
1488
|
+
**Requires `@zambon-dev/library` 2.x.** The peer range moved from `^1.6.0` to `^2.0.0`. See the
|
|
1489
|
+
library changelog for the `FormInputComponent` output renames; if your application binds those
|
|
1490
|
+
outputs directly, apply that migration too.
|
|
1491
|
+
|
|
1492
|
+
#### [1.4.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.4.1) - 2026-09-19
|
|
1493
|
+
|
|
1494
|
+
##### Fixed
|
|
1495
|
+
|
|
1496
|
+
- **A details tab with child views no longer loses its ribbon after you visit another tab and come
|
|
1497
|
+
back.** Every button of the child view disappeared -- filter, clear filters, refresh, export --
|
|
1498
|
+
while the grid below carried on showing its filtered rows, and nothing brought them back short of
|
|
1499
|
+
switching views.
|
|
1500
|
+
|
|
1501
|
+
A child view publishes its ribbon from `ngAfterViewInit`, and the router only names the active
|
|
1502
|
+
view afterwards, so the first template of a tab was cached under an empty id. Looking it up later
|
|
1503
|
+
under the real name missed and emptied the ribbon. It never recovered because a tab being
|
|
1504
|
+
re-activated has its child **re-attached** rather than re-created, so `ngAfterViewInit` does not
|
|
1505
|
+
run again and nothing publishes a second time.
|
|
1506
|
+
|
|
1507
|
+
A list screen was unaffected: it has no view switching, so the lookup never happened.
|
|
1508
|
+
|
|
1509
|
+
#### [1.4.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.4.0) - 2026-09-16
|
|
1510
|
+
|
|
1511
|
+
##### Changed
|
|
1512
|
+
|
|
1513
|
+
- **`framework-button-filters` no longer submits the labels of catalog selections.** A filters
|
|
1514
|
+
form reads its values straight off the form, so the display control `lib-catalog-select` adds
|
|
1515
|
+
for its own use went to the backend beside the real filters — a request filtering by employee
|
|
1516
|
+
carried `employeeName: "753 - ADEMILSON LOPES MAGALHAES"` next to `employeeID: 125`.
|
|
1517
|
+
|
|
1518
|
+
They were ignored, being filters no service declares, but the day one does filter by a name it
|
|
1519
|
+
would receive the formatted label rather than the stored value and quietly match nothing.
|
|
1520
|
+
|
|
1521
|
+
The labels are still **kept** for the modal: reopening it patches them back into the form, and a
|
|
1522
|
+
catalog select backed by a `searchEndpoint` cannot recover its text from the identifier alone —
|
|
1523
|
+
it only resolves a display out of a local entries list. Dropping them from what is stored would
|
|
1524
|
+
have left the field showing a selection with nothing written in it.
|
|
1525
|
+
|
|
1526
|
+
##### ⚠ Breaking Changes / Migration
|
|
1527
|
+
|
|
1528
|
+
- **Upgrade `@zambon-dev/library` together with this release.** The peer range moved from
|
|
1529
|
+
`^1.0.0` to `^1.6.0`, because `framework-button-filters` now imports `DisplayControls` from it.
|
|
1530
|
+
On an older library that export does not exist, and the filters button fails — at build time if
|
|
1531
|
+
the bundler checks exports, otherwise the first time a filter is submitted. The old range would
|
|
1532
|
+
have let npm resolve that combination without a word of warning.
|
|
1533
|
+
|
|
1534
|
+
- **A backend no longer receives the label of a catalog selection.** If one of your services
|
|
1535
|
+
filters by a key that a `lib-catalog-select` uses as its `displayControlName` — the `xxxName`
|
|
1536
|
+
that comes paired with an `xxxID` — that filter now arrives empty and the query stops narrowing,
|
|
1537
|
+
silently returning more rows than before rather than failing.
|
|
1538
|
+
|
|
1539
|
+
To check, take each filters form and, for every `lib-catalog-select` in it, note the
|
|
1540
|
+
`displayControlName`. Then look for a `TryFilter` on that name in the service behind the list.
|
|
1541
|
+
Any hit has to move to the identifier instead: the label was never the stored value — it is what
|
|
1542
|
+
the catalog chose to display, so filtering by it was already matching on formatting.
|
|
1543
|
+
|
|
1544
|
+
#### [1.3.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.3.1) - 2026-09-10
|
|
1545
|
+
|
|
1546
|
+
##### Fixed
|
|
1547
|
+
|
|
1548
|
+
- **Returning to an already-open tab no longer crashes with `Maximum call stack size exceeded`.**
|
|
1549
|
+
It affected any screen whose route nests two empty-path levels — the shape every list screen uses:
|
|
1550
|
+
`path: ''` with `DefaultTabViewComponent`, and a child `path: ''` with the list component.
|
|
1551
|
+
|
|
1552
|
+
`CustomReuseStrategy` keyed its detached-view cache on `route.component.name`, and a production
|
|
1553
|
+
build renames every class: esbuild wraps each component as `X = (() => { class i { } return i; })()`,
|
|
1554
|
+
so the name is one mangled letter that the whole chunk shares. An empty path contributes no URL
|
|
1555
|
+
segment, so both levels also resolved to the same URL — which left the two cache keys identical.
|
|
1556
|
+
Storing one handle overwrote the other, both route levels were then handed the same detached view,
|
|
1557
|
+
and Angular blew the stack building a router state whose node was its own descendant.
|
|
1558
|
+
|
|
1559
|
+
The key is now built from the component's **identity** and the route's depth rather than its name.
|
|
1560
|
+
Applications need to change nothing. Worth knowing when reading old reports of this crash: it only
|
|
1561
|
+
ever reproduced in a minified build — `ng serve` keeps real class names — and only on the screens
|
|
1562
|
+
whose chunk happened to mangle to the same letter as the framework's, so a rebuild could move the
|
|
1563
|
+
symptom from one screen to another.
|
|
1564
|
+
|
|
1565
|
+
#### [1.3.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.3.0) - 2026-07-30
|
|
1566
|
+
|
|
1567
|
+
##### Added
|
|
1568
|
+
|
|
1569
|
+
- `framework-button-export` now accepts a **`defaultOption` `@Input()`** — the index into `options`
|
|
1570
|
+
exported when the main button itself is clicked, defaulting to `0` (Excel). This mirrors the input
|
|
1571
|
+
`framework-button-save` already had; `-1` means "no default" and makes the button only toggle its
|
|
1572
|
+
dropdown. See **Changed** below for the behaviour this changes by default.
|
|
1573
|
+
|
|
1574
|
+
##### Changed
|
|
1575
|
+
|
|
1576
|
+
- **`framework-button-export` now exports on a single click**, to Excel by default, instead of only
|
|
1577
|
+
opening its format menu. `RibbonButtonComponent` treats a `defaultOption` of -1 as "no default" and
|
|
1578
|
+
merely toggles the dropdown; Export never set one, so the common case took two clicks while
|
|
1579
|
+
`framework-button-save` (which sets 0) took one. Export now sets 0 as well, through the new
|
|
1580
|
+
`defaultOption` `@Input()`. The format menu is unchanged and still reachable from the button's
|
|
1581
|
+
dropdown caret, so every format remains available.
|
|
1582
|
+
|
|
1583
|
+
##### Fixed
|
|
1584
|
+
|
|
1585
|
+
- **Routed list views now fill the available height automatically.** `DefaultTabViewComponent` gave its
|
|
1586
|
+
routed child no layout and `lib-data-grid` has no `flex-grow` of its own, so a grid collapsed to its
|
|
1587
|
+
`rowsToDisplay × rowHeight` minimum with empty space beneath it, and every consuming list component
|
|
1588
|
+
had to repeat the same `:host { flex; flex-grow; overflow: hidden }` + `lib-data-grid { flex-grow }`
|
|
1589
|
+
stylesheet to compensate. `TabViewList` now carries a `framework-view-list` host class — inherited by
|
|
1590
|
+
every subclass — which `DefaultTabViewComponent` styles, so **consuming apps can delete those
|
|
1591
|
+
per-component stylesheets**. Routed screens that do not extend `TabViewList` (forms, dashboards) are
|
|
1592
|
+
unaffected.
|
|
1593
|
+
|
|
1594
|
+
- `framework-default-tab-view` and `framework-default-details-tab-view` no longer log
|
|
1595
|
+
`NG0100: ExpressionChangedAfterItHasBeenCheckedError` on every render in development mode.
|
|
1596
|
+
Both views assigned a placeholder ribbon template from `ngAfterViewInit` — after Angular had
|
|
1597
|
+
already checked the ribbon's `*ngTemplateOutlet` binding — which tripped the dev-mode change
|
|
1598
|
+
detection check one to three times per load. The placeholder rendered no content, so it has
|
|
1599
|
+
been removed and the outlet is now left empty until a view publishes its own `#ribbon`
|
|
1600
|
+
template. The rendered output is unchanged, and ribbon buttons still appear immediately for
|
|
1601
|
+
list and detail screens.
|
|
1602
|
+
|
|
1603
|
+
##### ⚠ Breaking Changes / Migration
|
|
1604
|
+
|
|
1605
|
+
None — nothing has to change for your app to compile against this release. Two behaviour changes to
|
|
1606
|
+
check, and one optional cleanup:
|
|
1607
|
+
|
|
1608
|
+
- **Absolutely-positioned content inside a routed list view is now clipped.** The full-height layout
|
|
1609
|
+
applied to `framework-view-list` includes `overflow: hidden`, so a popover, dropdown or tooltip that
|
|
1610
|
+
a list screen renders with `position: absolute` inside its own host is now cut off at the host's
|
|
1611
|
+
edges. Apps that had already written the equivalent `:host { overflow: hidden }` stylesheet by hand
|
|
1612
|
+
are unaffected — the clipping was already in place there. Only list screens that had no such
|
|
1613
|
+
stylesheet change. Fix by portaling the content out of the host (CDK Overlay, as `lib-catalog-select`
|
|
1614
|
+
does) rather than relying on `position: absolute`.
|
|
1615
|
+
- If you relied on `framework-button-export`'s first click opening the format menu rather than
|
|
1616
|
+
exporting, set `[defaultOption]="-1"` to restore that behaviour.
|
|
1617
|
+
- _Optional cleanup:_ components extending `TabViewList` can now delete their
|
|
1618
|
+
`:host { display: flex; flex-grow: 1; overflow: hidden }` / `lib-data-grid { flex-grow: 1 }`
|
|
1619
|
+
stylesheets — `DefaultTabViewComponent` styles the inherited `framework-view-list` host class.
|
|
1620
|
+
|
|
1621
|
+
#### [1.2.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.2.1) - 2026-07-29
|
|
1622
|
+
|
|
1623
|
+
##### Fixed
|
|
1624
|
+
|
|
1625
|
+
- Lint configuration only: the library's ESLint selector rules now accept the `framework-` prefix
|
|
1626
|
+
that every `@framework` component already uses. They previously still expected the Angular
|
|
1627
|
+
scaffold's `lib` prefix, so the entire component surface reported
|
|
1628
|
+
`@angular-eslint/component-selector` errors. No selector was renamed, and no published API or
|
|
1629
|
+
runtime behavior changed — nothing to do on upgrade.
|
|
1630
|
+
|
|
1631
|
+
##### ⚠ Breaking Changes / Migration
|
|
1632
|
+
|
|
1633
|
+
None.
|
|
1634
|
+
|
|
1635
|
+
#### [1.2.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.2.0) - 2026-07-24
|
|
1636
|
+
|
|
1637
|
+
##### Added
|
|
1638
|
+
|
|
1639
|
+
- `AppConfig` now accepts optional application metadata through a second `options` argument:
|
|
1640
|
+
`appName`, `companyName`, `environment`, `logoUrl`, `version`, `notificationsEnabled`, and
|
|
1641
|
+
`notificationsUrl`. These are consumed by the shared application shell (top bar + navigation).
|
|
1642
|
+
Existing `new AppConfig(baseUrl)` calls continue to work unchanged (`notificationsEnabled`
|
|
1643
|
+
defaults to `false`, other new fields to empty strings).
|
|
1644
|
+
|
|
1645
|
+
##### ⚠ Breaking Changes / Migration
|
|
1646
|
+
|
|
1647
|
+
None.
|
|
1648
|
+
|
|
1649
|
+
#### [1.1.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.1.1) - 2026-06-20
|
|
1650
|
+
|
|
1651
|
+
##### Fixed
|
|
1652
|
+
|
|
1653
|
+
- `framework-button-export` now forwards its `iconSize` input to the underlying ribbon button,
|
|
1654
|
+
so setting `iconSize` correctly resizes the export button's icon. Previously the input was
|
|
1655
|
+
ignored and the icon always rendered at the default size.
|
|
1656
|
+
|
|
1657
|
+
#### [1.1.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.1.0) - 2026-06-02
|
|
1658
|
+
|
|
1659
|
+
- Baseline release: the changelog starts being tracked from this version. Earlier history is
|
|
1660
|
+
available via [GitHub Releases](https://github.com/RicardoZambon/ZLibraries/releases) and the
|
|
1661
|
+
`framework-v*` tags.
|
|
1662
|
+
|
|
1663
|
+
### Shared
|
|
1664
|
+
|
|
1665
|
+
Authentication, layouts, audit-history features and cross-cutting services. Published as `@zambon-dev/shared` through 6.0.4.
|
|
1666
|
+
|
|
1667
|
+
#### [6.0.4](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v6.0.4) - 2026-10-24
|
|
1668
|
+
|
|
1669
|
+
- Maintenance release (no consumer-facing changes were documented).
|
|
1670
|
+
|
|
1671
|
+
#### [6.0.3](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v6.0.3) - 2026-10-24
|
|
1672
|
+
|
|
1673
|
+
##### Changed
|
|
1674
|
+
|
|
1675
|
+
- **Every component in this package now renders under any change-detection strategy the host
|
|
1676
|
+
application chooses, zoneless included.** This was the last package. The state each one shows
|
|
1677
|
+
is signals: the embedded-content screen, which decides everything after its menu lookup
|
|
1678
|
+
answers; the notifications panel and the language list, both closed from a window click that
|
|
1679
|
+
is no binding of theirs; the user profile, filled on init; the login form's error, spinner and
|
|
1680
|
+
success state; and the audit screens' selected service and operation.
|
|
1681
|
+
|
|
1682
|
+
##### ⚠ Breaking Changes / Migration
|
|
1683
|
+
|
|
1684
|
+
- **The state these components expose is signals.** Binding to an input from a template is
|
|
1685
|
+
unchanged. Code that reads one needs `()`:
|
|
1686
|
+
|
|
1687
|
+
| Component | Members |
|
|
1688
|
+
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
1689
|
+
| `ServicesHistoryViewComponent` | `controllerName`, `entityID`, `usersCatalogEndpoint` -- `model()`, so setting one still works through `.set(...)` |
|
|
1690
|
+
| `ServicesHistoryChildListComponent` | `selectedServiceID` |
|
|
1691
|
+
| `OperationsHistoryChildListComponent` | `selectedNewValues`, `selectedOldValues` |
|
|
1692
|
+
| `OperationsHistoryModalComponent` | `newValues`, `oldValues` |
|
|
1693
|
+
| `NotificationsComponent` | `dropdown` |
|
|
1694
|
+
| `LanguageSelectorComponent` | `dropdown`, `showFlag`, `title` |
|
|
1695
|
+
| `LoginComponent` | `username` |
|
|
1696
|
+
|
|
1697
|
+
A template reading one through a reference -- `#list` then `list.selectedServiceID` -- needs
|
|
1698
|
+
the call; without it the signal object itself is read, and an object is always truthy.
|
|
1699
|
+
|
|
1700
|
+
- **`ServicesHistoryViewComponent.controllerName` is no longer a required input.** It is a
|
|
1701
|
+
`model()` defaulting to `''`, because the component also fills it from the route's
|
|
1702
|
+
`controllerName` data -- where nothing binds it and `required` never applied. Routed usage is
|
|
1703
|
+
unchanged; a template that binds it is unchanged; a template that forgot to is no longer a
|
|
1704
|
+
compile error, so check yours.
|
|
1705
|
+
|
|
1706
|
+
- **`TopBarComponent.logout` and `ServicesHistoryChildListComponent.filtersChanged` are
|
|
1707
|
+
`output()` rather than `EventEmitter`.** Binding to them is unchanged; calling `.emit()` on
|
|
1708
|
+
one from outside the component no longer compiles, which was never supported anyway.
|
|
1709
|
+
|
|
1710
|
+
#### [6.0.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v6.0.2) - 2026-10-24
|
|
1711
|
+
|
|
1712
|
+
##### Changed
|
|
1713
|
+
|
|
1714
|
+
- **Internal only: one spec follows `ViewBase`'s `loading` onto a signal.** `@zambon-dev/framework`
|
|
1715
|
+
backs that flag with a signal now, and a spec here stubs the component with `Object.create`,
|
|
1716
|
+
which runs no field initialiser. Nothing in this package's own API changed.
|
|
1717
|
+
|
|
1718
|
+
#### [6.0.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v6.0.1) - 2026-10-24
|
|
1719
|
+
|
|
1720
|
+
##### Fixed
|
|
1721
|
+
|
|
1722
|
+
- **The peer range on its sibling packages now admits the majors they were released at.** The last
|
|
1723
|
+
release published `@zambon-dev/library` 4.0.0, `@zambon-dev/framework` 5.0.0 and
|
|
1724
|
+
`@zambon-dev/shared` 6.0.0, but the ranges here still asked for the previous majors, so an
|
|
1725
|
+
application installing two of them together got `ERESOLVE`. Nothing else changed.
|
|
1726
|
+
|
|
1727
|
+
##### ⚠ Breaking Changes / Migration
|
|
1728
|
+
|
|
1729
|
+
- **`OperationsHistoryModalComponent.isModalShown` is a `Signal<boolean>`.** It followed
|
|
1730
|
+
`@zambon-dev/library`'s `ModalComponent.isShown` becoming `isOpen`, and delegates through a
|
|
1731
|
+
signal view query so it answers correctly once that child resolves. Read it as
|
|
1732
|
+
`isModalShown()`. The compiler will not catch a miss here -- reading the signal object itself is
|
|
1733
|
+
valid TypeScript and always truthy -- so grep for `isModalShown` rather than relying on the
|
|
1734
|
+
build.
|
|
1735
|
+
|
|
1736
|
+
#### [6.0.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v6.0.0) - 2026-10-24
|
|
1737
|
+
|
|
1738
|
+
##### Changed
|
|
1739
|
+
|
|
1740
|
+
- **`FiltersBase` no longer declares a change-detection strategy**, for the same reason as the
|
|
1741
|
+
framework's view bases: it was never inherited. Your filter components are unaffected.
|
|
1742
|
+
|
|
1743
|
+
##### Fixed
|
|
1744
|
+
|
|
1745
|
+
- **The services-history author filter appears when its catalog is supplied.** The template read
|
|
1746
|
+
the endpoint through a getter over a plain `@Input()`, which under Angular 22's default strategy
|
|
1747
|
+
raised NG0100 rather than rendering.
|
|
1748
|
+
|
|
1749
|
+
##### ⚠ Breaking Changes / Migration
|
|
1750
|
+
|
|
1751
|
+
- **`ServicesHistoryFilterComponent.usersCatalogEndpoint` is a signal input.** Binding it from a
|
|
1752
|
+
template -- `[usersCatalogEndpoint]="endpoint"` -- is unchanged. Assigning it directly on a
|
|
1753
|
+
component instance no longer works; use the binding, or `componentRef.setInput` in a test.
|
|
1754
|
+
|
|
1755
|
+
#### [5.0.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v5.0.2) - 2026-09-30
|
|
1756
|
+
|
|
1757
|
+
##### Changed
|
|
1758
|
+
|
|
1759
|
+
- `UtcDatePipe.transform()` accepts `Date | string`. An API response holds the timestamp as a
|
|
1760
|
+
string even where the model types it as `Date`; existing calls compile unchanged.
|
|
1761
|
+
|
|
1762
|
+
##### Fixed
|
|
1763
|
+
|
|
1764
|
+
- **`utcDate` no longer moves a timestamp to the previous day east of Greenwich.** It re-read the
|
|
1765
|
+
UTC wall-clock time with hour arithmetic that kept the time of day but, at any positive UTC
|
|
1766
|
+
offset, landed on the day before: `2026-06-15T14:30` showed as June 14 in Madrid (UTC+2). It
|
|
1767
|
+
never showed on CI, which runs in UTC, nor west of Greenwich, where the arithmetic happens to
|
|
1768
|
+
come out right. The pipe now shifts by the zone's offset.
|
|
1769
|
+
|
|
1770
|
+
- **A timestamp that already declares its zone is no longer shifted a second time.** A string
|
|
1771
|
+
ending in `Z` or `±hh:mm` is parsed as the right instant by `Date` itself, so the pipe now
|
|
1772
|
+
returns it as is, and keeps working if an API starts sending the marker.
|
|
1773
|
+
|
|
1774
|
+
- **A value that is not a date returns `null`**, the same as an empty one, instead of an invalid
|
|
1775
|
+
`Date` that makes Angular's `date` pipe throw.
|
|
1776
|
+
|
|
1777
|
+
#### [5.0.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v5.0.1) - 2026-09-26
|
|
1778
|
+
|
|
1779
|
+
##### Fixed
|
|
1780
|
+
|
|
1781
|
+
- **The operations list no longer keeps a service the services list has dropped.** Reloading the
|
|
1782
|
+
services grid clears its selection but left the last chosen service behind, so the panel beside
|
|
1783
|
+
it went on showing that service’s operations -- including after a filter that no longer lists
|
|
1784
|
+
it. The selection is dropped with the reload now.
|
|
1785
|
+
|
|
1786
|
+
- **The audit date range no longer depends on the server’s time zone.** The two bounds were sent
|
|
1787
|
+
as instants with a trailing `Z`, and `DateTime.TryParse` reads that as an instant and answers
|
|
1788
|
+
with the _machine’s_ local time for it -- so the same request meant different things on a server
|
|
1789
|
+
in UTC and one in UTC-3, while the stored column is plain UTC either way. They are sent as the
|
|
1790
|
+
UTC wall clock now, with no designator.
|
|
1791
|
+
|
|
1792
|
+
- **The peer range on `@zambon-dev/framework` admits 4.x.** 5.0.0 shipped peering `^3.0.0`, hours
|
|
1793
|
+
after framework reached 4.0.0, so installing the two together failed with ERESOLVE. Nothing in
|
|
1794
|
+
this repository could see it -- the workspace resolves siblings from `dist`, so every build,
|
|
1795
|
+
test and Storybook passes with a range no consumer can satisfy. `tools/check-peer-ranges.mjs`
|
|
1796
|
+
now compares each range against the release tags, in PR validation.
|
|
1797
|
+
|
|
1798
|
+
#### [5.0.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v5.0.0) - 2026-09-26
|
|
1799
|
+
|
|
1800
|
+
##### Added
|
|
1801
|
+
|
|
1802
|
+
- **Refresh and filters on the audit view.** `ServicesHistoryViewComponent` now contributes a
|
|
1803
|
+
ribbon, with a refresh button and `ServicesHistoryFilterComponent`: the action as text
|
|
1804
|
+
(matched with LIKE), the author, a date range, and whether to list only the operations on the
|
|
1805
|
+
record being audited. The backend side is `AuditFilters` in ZWebAPI; the author filter needs a
|
|
1806
|
+
catalog, which belongs to the application, so it appears only when the view's
|
|
1807
|
+
`usersCatalogEndpoint` is supplied — through the route's data or bound directly.
|
|
1808
|
+
|
|
1809
|
+
The two picked dates are widened to whole local days and sent as UTC instants, because the
|
|
1810
|
+
history stores UTC and the backend compares what it is given: sent as picked, "from the 5th to
|
|
1811
|
+
the 5th" is one instant at midnight and matches nothing.
|
|
1812
|
+
|
|
1813
|
+
##### ⚠ Breaking Changes / Migration
|
|
1814
|
+
|
|
1815
|
+
- **`ServicesHistoryChildListComponent` no longer provides its own `DataGridDataset`.**
|
|
1816
|
+
`ServicesHistoryViewComponent` provides it instead, so that the ribbon's refresh and filter
|
|
1817
|
+
buttons — which resolve the dataset from their own injector — reach the same instance the grid
|
|
1818
|
+
reads. Nothing changes for the view, which is how this component is meant to be used. Rendering
|
|
1819
|
+
the child list anywhere else now needs the dataset provided above it:
|
|
1820
|
+
|
|
1821
|
+
```ts
|
|
1822
|
+
providers: [{ provide: DataGridDataset, useClass: ServicesHistoryDataset }],
|
|
1823
|
+
```
|
|
1824
|
+
|
|
1825
|
+
#### [4.2.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v4.2.2) - 2026-09-26
|
|
1826
|
+
|
|
1827
|
+
##### Fixed
|
|
1828
|
+
|
|
1829
|
+
- **Component styles are compiled again.** The Tailwind 4 migration left every component's own
|
|
1830
|
+
stylesheet unprocessed in the published bundle, so its `@apply` rules reached the browser as an
|
|
1831
|
+
at-rule the browser does not know and it dropped every rule that used one. In practice the
|
|
1832
|
+
components rendered unstyled in any application installing 4.2.0 or 4.2.1. ng-packagr resolves its
|
|
1833
|
+
PostCSS configuration from the library directory rather than from the workspace root, and the
|
|
1834
|
+
migration added one only at the root. Nothing failed along the way: Storybook compiles from source
|
|
1835
|
+
and never sees the packaged artifact, which is why `tools/check-packaged-styles.mjs` now runs
|
|
1836
|
+
against that artifact in CI and again before every publish.
|
|
1837
|
+
|
|
1838
|
+
#### [4.2.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v4.2.1) - 2026-09-26
|
|
1839
|
+
|
|
1840
|
+
##### Fixed
|
|
1841
|
+
|
|
1842
|
+
- **`styles/theme.css` is reachable from an application.** It shipped with the Tailwind 4 release
|
|
1843
|
+
but was missing from the `exports` map, so the one supported way to use it —
|
|
1844
|
+
`@reference '@zambon-dev/shared/styles/theme.css'` in a consumer stylesheet — failed the export
|
|
1845
|
+
check and the build stopped. Found on the first application to migrate.
|
|
1846
|
+
|
|
1847
|
+
#### [4.2.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v4.2.0) - 2026-09-25
|
|
1848
|
+
|
|
1849
|
+
##### Changed
|
|
1850
|
+
|
|
1851
|
+
- **Tailwind CSS 4.** The stylesheets these packages ship are built with Tailwind 4 now. Nothing
|
|
1852
|
+
about the components changed; the framework underneath them did.
|
|
1853
|
+
|
|
1854
|
+
#### [4.1.3](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v4.1.3) - 2026-09-25
|
|
1855
|
+
|
|
1856
|
+
##### Fixed
|
|
1857
|
+
|
|
1858
|
+
- **The tab panel's drop shadow is no longer sliced off.** The layout's `.content` was
|
|
1859
|
+
`overflow-y-auto`, which made it a clipping box on both axes, and what it clipped was the
|
|
1860
|
+
panel's shadow — 18px of shadow against the 8px it left below. It never needed to scroll:
|
|
1861
|
+
`framework-tabs` is full height and the panel scrolls inside it, measured as
|
|
1862
|
+
`scrollHeight === clientHeight` with the panel in place. Two nested scroll containers where one
|
|
1863
|
+
will do.
|
|
1864
|
+
|
|
1865
|
+
#### [4.1.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v4.1.2) - 2026-09-25
|
|
1866
|
+
|
|
1867
|
+
##### Fixed
|
|
1868
|
+
|
|
1869
|
+
- **Icons drawn in CSS survive a FontAwesome major.** Four pseudo-elements set
|
|
1870
|
+
`font-family: 'Font Awesome 6 Free'` literally: the sidebar item's expand/collapse chevron and
|
|
1871
|
+
its external-link marker, and the checked-checkbox tick in `form-input` and in the published
|
|
1872
|
+
`styles/components/input.scss`. FontAwesome 7 renames the family to `Font Awesome 7 Free` and,
|
|
1873
|
+
unlike the FontAwesome 5 names, does not keep a 6 alias — so on an application that had moved to
|
|
1874
|
+
v7 the family stopped resolving and every one of those glyphs rendered as a fallback box.
|
|
1875
|
+
|
|
1876
|
+
They now read `var(--fa-family-classic, 'Font Awesome 6 Free')`. FontAwesome 7 defines that
|
|
1877
|
+
variable; 6 does not, so it takes the fallback. The same stylesheet is correct on both, and on
|
|
1878
|
+
8 as well as long as FontAwesome keeps the variable.
|
|
1879
|
+
|
|
1880
|
+
#### [4.1.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v4.1.1) - 2026-09-25
|
|
1881
|
+
|
|
1882
|
+
##### Fixed
|
|
1883
|
+
|
|
1884
|
+
- **The peer ranges on `@zambon-dev/library` and `@zambon-dev/framework` now match the versions
|
|
1885
|
+
this package is built against.** Both still asked for `^2.0.0` after those packages moved to 3.x,
|
|
1886
|
+
so installing the three together at their current releases failed `npm install` with ERESOLVE. An
|
|
1887
|
+
application could only get past it with `--legacy-peer-deps` or an override, both of which switch
|
|
1888
|
+
off the peer checking that would have caught a genuine mismatch.
|
|
1889
|
+
|
|
1890
|
+
**Requires Tailwind CSS 4.** An application on 3.x cannot consume this release: `common.scss`
|
|
1891
|
+
references a theme in v4 syntax, and the two versions do not understand each other's directives.
|
|
1892
|
+
|
|
1893
|
+
Tailwind 4 has no JavaScript config — the theme is CSS custom properties — so the `primary` palette
|
|
1894
|
+
that consumers used to declare in their own `tailwind.config.js` now ships here, in
|
|
1895
|
+
`@zambon-dev/shared/styles/theme.css`. That file is the Tailwind entry point and the theme in one.
|
|
1896
|
+
|
|
1897
|
+
In the application:
|
|
1898
|
+
|
|
1899
|
+
1. Install `tailwindcss@^4` and `@tailwindcss/postcss@^4`, and remove `tailwind.config.js`. A
|
|
1900
|
+
`.postcssrc.json` with `{"plugins": {"@tailwindcss/postcss": {}}}` replaces it.
|
|
1901
|
+
|
|
1902
|
+
2. Load the theme **before** the shared stylesheet, and load it as CSS rather than through Sass.
|
|
1903
|
+
Sass's `@use` of a plain CSS file drops the `@layer` blocks the theme relies on, silently:
|
|
1904
|
+
|
|
1905
|
+
```jsonc
|
|
1906
|
+
// angular.json / project.json, styles:
|
|
1907
|
+
"node_modules/@zambon-dev/shared/styles/theme.css",
|
|
1908
|
+
"node_modules/@zambon-dev/shared/styles/common.scss",
|
|
1909
|
+
```
|
|
1910
|
+
|
|
1911
|
+
3. Any stylesheet of your own that uses `@apply` needs to say where its theme comes from:
|
|
1912
|
+
`@reference '@zambon-dev/shared/styles/theme.css';`. In 3.x the PostCSS plugin carried the
|
|
1913
|
+
config to every file; in 4 a file is on its own without this.
|
|
1914
|
+
|
|
1915
|
+
4. If you kept a custom palette in `tailwind.config.js`, move it into a `@theme` block. Colours
|
|
1916
|
+
become `--color-<name>-<shade>`.
|
|
1917
|
+
|
|
1918
|
+
`theme.css` also restores two v3 defaults that Tailwind 4 dropped, because the components were
|
|
1919
|
+
built against them: the default border colour (`gray-200`, not `currentColor`) and the pointer
|
|
1920
|
+
cursor on buttons. Both are marked in the file. Taking either away is a visual decision, not a
|
|
1921
|
+
cleanup.
|
|
1922
|
+
|
|
1923
|
+
**What was verified, and what was not.** `tools/style-snapshot` captures the computed styles of
|
|
1924
|
+
every element in all 43 stories and diffs two builds; the migration was driven by that diff rather
|
|
1925
|
+
than by reading release notes, and it caught three regressions nothing else reported — 615 elements
|
|
1926
|
+
whose borders turned black, 41 buttons that lost their pointer cursor, and a sidebar that rendered
|
|
1927
|
+
collapsed because `@screen md` is a v3 directive that 4 leaves as an unknown at-rule for the browser
|
|
1928
|
+
to drop.
|
|
1929
|
+
|
|
1930
|
+
What remains is 138 differing values in 20 distinct changes, all triaged and none of them obviously
|
|
1931
|
+
wrong: `divide-*` moved its border from the top of one element to the bottom of the previous one,
|
|
1932
|
+
which is the documented v4 selector change; a handful of rings and shadows compose their transparent
|
|
1933
|
+
layers differently; and three elements are 8px shorter. **These want an eye on them before release.**
|
|
1934
|
+
Colour values across the board are the same colours in OKLCH, which is what v4's palette is.
|
|
1935
|
+
|
|
1936
|
+
#### [4.1.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v4.1.0) - 2026-09-21
|
|
1937
|
+
|
|
1938
|
+
##### Added
|
|
1939
|
+
|
|
1940
|
+
- Added the `--tabs-*` design tokens to the published stylesheet, mirroring the set
|
|
1941
|
+
`@zambon-dev/library` gained in 3.1.0. They style `@zambon-dev/framework`'s tab strip —
|
|
1942
|
+
colours, radii, blur, the shadow, the minimum and maximum tab width, and the width of the
|
|
1943
|
+
scroll controls and edge fades — and they sit alongside `--sidebar-*` in the same layer, so
|
|
1944
|
+
the strip can be retuned without touching the component.
|
|
1945
|
+
|
|
1946
|
+
This release shipped them but originally recorded no entry; the note was added afterwards.
|
|
1947
|
+
|
|
1948
|
+
#### [4.0.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v4.0.1) - 2026-09-21
|
|
1949
|
+
|
|
1950
|
+
##### Fixed
|
|
1951
|
+
|
|
1952
|
+
- **Host styles now apply.** The services-history view and both history child lists declared their
|
|
1953
|
+
host block as `:host-context {` with no argument, so every rule inside was silently dropped.
|
|
1954
|
+
They are `:host {` now.
|
|
1955
|
+
|
|
1956
|
+
#### [4.0.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v4.0.0) - 2026-09-21
|
|
1957
|
+
|
|
1958
|
+
- Maintenance release (no consumer-facing changes were documented).
|
|
1959
|
+
|
|
1960
|
+
#### [3.0.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v3.0.1) - 2026-09-21
|
|
1961
|
+
|
|
1962
|
+
##### Changed
|
|
1963
|
+
|
|
1964
|
+
- `LoginLayoutComponent`, `HomeComponent`, `BrandComponent` and `EnvironmentBadgeComponent` now
|
|
1965
|
+
use `ChangeDetectionStrategy.OnPush`. Behaviour is unchanged; they render from inputs or from
|
|
1966
|
+
immutable application config.
|
|
1967
|
+
|
|
1968
|
+
- **Accessibility.** The operations-history row opens its detail modal from the keyboard as well as
|
|
1969
|
+
the mouse.
|
|
1970
|
+
|
|
1971
|
+
#### [3.0.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v3.0.0) - 2026-09-21
|
|
1972
|
+
|
|
1973
|
+
##### Changed
|
|
1974
|
+
|
|
1975
|
+
- **Accessibility.** The operations-history row opens its detail modal from the keyboard as well as
|
|
1976
|
+
the mouse.
|
|
1977
|
+
|
|
1978
|
+
> **The 2.1.x line is retired.** It carried shared for applications still on Angular 19 while
|
|
1979
|
+
> `main` moved to Angular 22, publishing under its own `2.1.x` dist-tag. Its branch is gone; the
|
|
1980
|
+
> final state is kept in the `angular-19/shared-2.1.x` tag. Every fix recorded below also shipped
|
|
1981
|
+
> in a 5.x release, which requires Angular 22.
|
|
1982
|
+
|
|
1983
|
+
#### [2.1.4](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v2.1.4) - 2026-09-30
|
|
1984
|
+
|
|
1985
|
+
##### Changed
|
|
1986
|
+
|
|
1987
|
+
- `UtcDatePipe.transform()` accepts `Date | string`. An API response holds the timestamp as a
|
|
1988
|
+
string even where the model types it as `Date`; existing calls compile unchanged.
|
|
1989
|
+
|
|
1990
|
+
##### Fixed
|
|
1991
|
+
|
|
1992
|
+
- **`utcDate` no longer moves a timestamp to the previous day east of Greenwich.** It re-read the
|
|
1993
|
+
UTC wall-clock time with hour arithmetic that kept the time of day but, at any positive UTC
|
|
1994
|
+
offset, landed on the day before: `2026-06-15T14:30` showed as June 14 in Madrid (UTC+2). It
|
|
1995
|
+
never showed on CI, which runs in UTC, nor west of Greenwich, where the arithmetic happens to
|
|
1996
|
+
come out right. The pipe now shifts by the zone's offset.
|
|
1997
|
+
|
|
1998
|
+
- **A timestamp that already declares its zone is no longer shifted a second time.** A string
|
|
1999
|
+
ending in `Z` or `±hh:mm` is parsed as the right instant by `Date` itself, so the pipe now
|
|
2000
|
+
returns it as is, and keeps working if an API starts sending the marker.
|
|
2001
|
+
|
|
2002
|
+
- **A value that is not a date returns `null`**, the same as an empty one, instead of an invalid
|
|
2003
|
+
`Date` that makes Angular's `date` pipe throw.
|
|
2004
|
+
|
|
2005
|
+
#### [2.1.3](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v2.1.3) - 2026-09-21
|
|
2006
|
+
|
|
2007
|
+
- Maintenance release (no consumer-facing changes were documented).
|
|
2008
|
+
|
|
2009
|
+
#### [2.1.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v2.1.2) - 2026-09-20
|
|
2010
|
+
|
|
2011
|
+
##### Fixed
|
|
2012
|
+
|
|
2013
|
+
- Declared `rxjs` and `@angular/platform-browser` in `peerDependencies`. `DomSanitizer` is used by
|
|
2014
|
+
`ExternalContentComponent` and `BypassHtmlSanitizerPipe`, but neither package was declared.
|
|
2015
|
+
- Added a README to the published package.
|
|
2016
|
+
|
|
2017
|
+
##### ⚠ Breaking Changes / Migration
|
|
2018
|
+
|
|
2019
|
+
**Requires Angular 22.** Every `@angular/*` peer range moved from `^19.1.0` to `^22.0.0`, and
|
|
2020
|
+
the package is built and tested against Angular 22.1 with TypeScript 6.0.
|
|
2021
|
+
|
|
2022
|
+
Upgrade your application to Angular 22 first — Angular does not support skipping majors, so go
|
|
2023
|
+
19 → 20 → 21 → 22 with `ng update`. Angular 22 also requires Node.js 22.22.3 or newer.
|
|
2024
|
+
|
|
2025
|
+
**Requires `@zambon-dev/library` 2.x and `@zambon-dev/framework` 2.x.** Both peer ranges moved to
|
|
2026
|
+
`^2.0.0`. See the library changelog for the `FormInputComponent` output renames.
|
|
2027
|
+
|
|
2028
|
+
#### [2.1.1](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v2.1.1) - 2026-09-15
|
|
2029
|
+
|
|
2030
|
+
##### Fixed
|
|
2031
|
+
|
|
2032
|
+
- **An embedded destination served over `http` now says why it cannot be shown, instead of
|
|
2033
|
+
rendering an empty frame.** A browser refuses to embed an `http://` frame inside an `https://`
|
|
2034
|
+
page — an iframe is active mixed content, and the block is unconditional. Nothing on the page
|
|
2035
|
+
can permit it: no attribute, no header, and not CSP, which only ever restricts further. The
|
|
2036
|
+
frame was mounted anyway, the browser dropped it silently, and after the timeout the screen
|
|
2037
|
+
offered the “still loading” hint, which pointed at the wrong cause.
|
|
2038
|
+
|
|
2039
|
+
Unlike a site refusing to be framed — which is genuinely unknowable from JavaScript — this one
|
|
2040
|
+
is decidable up front, so `ExternalContentComponent` now compares the page’s protocol with the
|
|
2041
|
+
destination’s and shows a message naming the real reason. **Open in a new browser tab keeps
|
|
2042
|
+
working and is the way out:** a top-level navigation to `http://` is not mixed content. For a
|
|
2043
|
+
destination that only speaks `http`, configure the menu item as _external, new tab_ rather than
|
|
2044
|
+
_external, embedded_; to embed it, put it behind an `https` reverse proxy.
|
|
2045
|
+
|
|
2046
|
+
Two new keys, `ExternalContent-Insecure-Title` and `ExternalContent-Insecure-Message`, ship in
|
|
2047
|
+
`en` and `pt`. An application that overrides this feature’s translations needs to add them.
|
|
2048
|
+
|
|
2049
|
+
#### [2.1.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v2.1.0) - 2026-09-08
|
|
2050
|
+
|
|
2051
|
+
##### Added
|
|
2052
|
+
|
|
2053
|
+
- **External sidebar destinations are now opened, in either of two modes.** `MainLayoutComponent`
|
|
2054
|
+
subscribes to `@zambon-dev/library`’s new `SidebarService.menuExternalUrlSelected`: an item whose
|
|
2055
|
+
`openMode` is `ExternalNewTab` opens in a new browser tab (`noopener,noreferrer`), and one marked
|
|
2056
|
+
`ExternalEmbedded` opens an application tab that displays the destination in a sandboxed iframe,
|
|
2057
|
+
keeping the user inside the application. Internal routes are untouched.
|
|
2058
|
+
|
|
2059
|
+
- **`ExternalUrlResolverService`** — substitutes the runtime placeholders in an external menu URL
|
|
2060
|
+
and vets the result. The placeholder set is closed and case-sensitive: `{email}`, `{language}`,
|
|
2061
|
+
`{userId}`, `{userName}`. Every substituted value is `encodeURIComponent`-ed, so a name containing
|
|
2062
|
+
`&` cannot inject a query parameter — which also means one placeholder must occupy one whole path
|
|
2063
|
+
segment or query-parameter value. A supported placeholder with no value becomes an empty string
|
|
2064
|
+
(with a console warning); an unrecognized `{…}` is left exactly as configured, so a report URL
|
|
2065
|
+
that legitimately contains braces, such as `?filter={"a":1}`, is not corrupted.
|
|
2066
|
+
|
|
2067
|
+
No authentication token is ever substituted, and the set is closed by construction rather than
|
|
2068
|
+
reflected off the stored user info — `AuthenticationService` persists the whole sign-in response
|
|
2069
|
+
under `userInfo`, tokens included, so a reflective implementation would let a URL configured as
|
|
2070
|
+
`?t={token}` hand the JWT to a third party.
|
|
2071
|
+
|
|
2072
|
+
`{language}` and `{userName}` work today. `{userId}` and `{email}` resolve to an empty string
|
|
2073
|
+
until your `Authentication/SignIn` and `Authentication/RefreshToken` responses include `userID`
|
|
2074
|
+
and `email` (see `ICurrentUserInfo` below).
|
|
2075
|
+
|
|
2076
|
+
- **`ExternalContentComponent` + `externalContentRoutes`** — the embedded view. Spread the routes
|
|
2077
|
+
into `MainLayoutComponent`’s children:
|
|
2078
|
+
|
|
2079
|
+
```ts
|
|
2080
|
+
{ path: '', component: MainLayoutComponent, canActivate: [AuthGuard], children: [
|
|
2081
|
+
...externalContentRoutes,
|
|
2082
|
+
// your own features
|
|
2083
|
+
] }
|
|
2084
|
+
```
|
|
2085
|
+
|
|
2086
|
+
Register them even if you only plan to use `ExternalNewTab`: an embedded item configured without
|
|
2087
|
+
them opens a tab that immediately bounces to the home route.
|
|
2088
|
+
|
|
2089
|
+
The view is an ordinary `TabViewBase` hosted by `DefaultTabViewComponent`, so its actions appear
|
|
2090
|
+
in the application ribbon (a **Page** group with **Refresh** and **Open in a new browser tab**)
|
|
2091
|
+
and look like every other screen’s — the shipped `externalContentRoutes` already wires that host
|
|
2092
|
+
up, which is the other reason not to hand-write the routes. **Refresh** carries the same icon and
|
|
2093
|
+
label as `framework-button-refresh`, because reloading a report is the same action as refreshing
|
|
2094
|
+
a grid.
|
|
2095
|
+
|
|
2096
|
+
Refreshing genuinely tears the frame down and builds a new one — a cross-origin frame cannot be
|
|
2097
|
+
navigated any other way — so the old render visibly goes away instead of sitting there while you
|
|
2098
|
+
wonder whether anything happened. The button spins and the panel shows a loading overlay until
|
|
2099
|
+
the destination reports `load`, or until the slow-frame delay elapses, so a destination that
|
|
2100
|
+
never reports one cannot leave the controls stuck.
|
|
2101
|
+
|
|
2102
|
+
**Open in a new browser tab** is always available, because many sites refuse to be embedded
|
|
2103
|
+
(`X-Frame-Options`, CSP `frame-ancestors`) and a browser gives JavaScript no reliable way to
|
|
2104
|
+
detect that — if nothing has loaded after a few seconds the view also shows a hint saying so. An
|
|
2105
|
+
`https` application cannot embed an `http` destination at all; the same button is the way out.
|
|
2106
|
+
|
|
2107
|
+
The tab URL is `/external-content/<menu id>` and never carries the destination, so no one can
|
|
2108
|
+
hand-craft a link that makes your application frame an arbitrary site. Pressing F5 on an embedded
|
|
2109
|
+
tab restores both the frame and the tab title from `sessionStorage`. Opening that URL in a _fresh_
|
|
2110
|
+
browser tab can only work if your `SidebarService.getMenuFromUrl()` resolves
|
|
2111
|
+
`/external-content/<id>`; otherwise the view says the content is unavailable and asks the user to
|
|
2112
|
+
reopen it from the menu.
|
|
2113
|
+
|
|
2114
|
+
- **`EXTERNAL_CONTENT_CONFIGS`** — `allowedOrigins` (default `[]`, meaning any `http`/`https`
|
|
2115
|
+
origin) and `slowFrameHintDelay` (default 5000 ms). **Populate `allowedOrigins` in production.**
|
|
2116
|
+
Embedded and new-tab destinations are already rejected unless they are absolute `http`/`https`
|
|
2117
|
+
URLs, which stops a `javascript:` or `data:` URL in your menu table from executing in your users’
|
|
2118
|
+
session; an origin allowlist narrows what is left from “any site on the internet” to your known
|
|
2119
|
+
report hosts. The iframe is sandboxed with a fixed, non-configurable token list that withholds
|
|
2120
|
+
`allow-top-navigation`, so a framed site cannot navigate your application away. Do not point an
|
|
2121
|
+
embedded item at your own application’s origin — use an internal route for that.
|
|
2122
|
+
|
|
2123
|
+
- **`ICurrentUserInfo.email`, `.userID`, `.username`** (all optional) — the values behind
|
|
2124
|
+
`{email}`, `{userId}` and `{userName}`. `username` was already being persisted in the base64
|
|
2125
|
+
`userInfo` entry and is now simply typed, so `{userName}` needs no backend change; `email` and
|
|
2126
|
+
`userID` must be added to your sign-in and refresh responses.
|
|
2127
|
+
|
|
2128
|
+
- **Translations** for the embedded view under `i18n/external-content/`, already included in
|
|
2129
|
+
`ZAMBON_SHARED_I18N_RESOURCES`.
|
|
2130
|
+
|
|
2131
|
+
##### Fixed
|
|
2132
|
+
|
|
2133
|
+
- **A deep link whose URL the menu API cannot resolve no longer surfaces an unhandled error.**
|
|
2134
|
+
`MainLayoutComponent` resolves a deep-linked tab’s title through `SidebarService.getMenuFromUrl()`
|
|
2135
|
+
and had no error handler, so a 404 became an unhandled rejection in the console. The title is
|
|
2136
|
+
best-effort and the failure is now swallowed — which matters more now that the embedded-content
|
|
2137
|
+
route is a URL shape most menu endpoints do not know.
|
|
2138
|
+
|
|
2139
|
+
##### ⚠ Breaking Changes / Migration
|
|
2140
|
+
|
|
2141
|
+
- **Requires `@zambon-dev/library` 1.4.0 or later.** `SidebarMenuOpenMode`,
|
|
2142
|
+
`toSidebarMenuOpenMode` and `SidebarService.menuExternalUrlSelected` ship in that release. The
|
|
2143
|
+
declared peer range still allows older versions, so upgrade both packages together.
|
|
2144
|
+
- **To use external menu items, register `externalContentRoutes`** as children of
|
|
2145
|
+
`MainLayoutComponent` (snippet above), and make your menu endpoint return `openMode`. The
|
|
2146
|
+
resolver accepts it as the camelCase string (`"internal"`, `"externalNewTab"`,
|
|
2147
|
+
`"externalEmbedded"`, matched case-insensitively) or as the enum ordinal (`0`, `1`, `2`), so a
|
|
2148
|
+
plain ASP.NET Core enum property works with no converter.
|
|
2149
|
+
- **The embedded view needs both i18n bundles registered.** Its own strings ship in this package
|
|
2150
|
+
(`ZAMBON_SHARED_I18N_RESOURCES`, under `assets/i18n/zambon-dev/shared/external-content/`), but the
|
|
2151
|
+
ribbon reuses three keys owned by `@zambon-dev/framework` — `RibbonGroup-Page`, `Button-Refresh`
|
|
2152
|
+
and `Loading` — so `ZAMBON_FRAMEWORK_I18N_RESOURCES` has to be registered too, or those render as
|
|
2153
|
+
raw keys. Any application that already uses framework buttons registers it; if your
|
|
2154
|
+
`TranslateLoader` hand-lists prefixes instead of spreading the two constants, add both.
|
|
2155
|
+
- Nothing else changes: applications with no `openMode` on any menu item behave exactly as before
|
|
2156
|
+
and need no action.
|
|
2157
|
+
|
|
2158
|
+
#### [2.0.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v2.0.0) - 2026-07-30
|
|
2159
|
+
|
|
2160
|
+
##### Added
|
|
2161
|
+
|
|
2162
|
+
- **Storybook: `Shared/App Showcase` story** — a full-height, navigable demo of the complete
|
|
2163
|
+
application shell, backed by in-memory mock data. Clicking the sidebar entries (Dashboard,
|
|
2164
|
+
General ▸ Customers/Units, Security ▸ Users) opens tabs that render working list-views and
|
|
2165
|
+
detail-views through the real hosts (`DefaultTabViewComponent` /
|
|
2166
|
+
`DefaultDetailsTabViewComponent`, `TabViewList` / `FormView`, and the `framework-button-*`
|
|
2167
|
+
ribbon buttons). It covers:
|
|
2168
|
+
|
|
2169
|
+
- a branded top bar (logo, app name, subtitle, environment badge and a working notifications
|
|
2170
|
+
bell) and a versioned sidebar footer;
|
|
2171
|
+
- a mocked audit/history view, reachable from the detail views' Views button;
|
|
2172
|
+
- `framework-button-filters` (via a `FiltersBase` component per entity) and
|
|
2173
|
+
`framework-button-export` on every list: the mock dataset honours `IListParameters.filters`, so
|
|
2174
|
+
filtering visibly narrows the grid, and exporting downloads a real CSV of the filtered rows;
|
|
2175
|
+
- a **child list of addresses** on the Customers detail view, edited through `lib-multi-editor` —
|
|
2176
|
+
`ChildList` and `MultiEditorModal` together, with the accordion gated on `hasEntityID` (a child
|
|
2177
|
+
collection needs a persisted parent), the Edit button opening the multi-editor rather than
|
|
2178
|
+
entering form edit mode, and add/remove/edit applied as one batch. The Customers grid's City
|
|
2179
|
+
column is **derived** from the customer's first address, so editing an address is reflected in
|
|
2180
|
+
the parent list and its filter with nothing to keep in sync;
|
|
2181
|
+
- mocked request latency, so loading states are observable;
|
|
2182
|
+
- full `en`/`pt` translation, so the language selector switches the entire showcase.
|
|
2183
|
+
|
|
2184
|
+
Development-only: it lives entirely in `app-showcase.stories.ts`, which is excluded from the
|
|
2185
|
+
package build.
|
|
2186
|
+
|
|
2187
|
+
##### Fixed
|
|
2188
|
+
|
|
2189
|
+
- **Audit history models now match the JSON the audit endpoints actually return.**
|
|
2190
|
+
`IServicesHistoryList` and `IOperationsHistoryList` declared an `ID` property, and
|
|
2191
|
+
`IOperationsHistoryList` declared `entityId`. Neither key is ever sent. The audit endpoints
|
|
2192
|
+
(`POST /{controller}/{entityID}/Audit` and `.../Audit/{serviceHistoryID}`) serialize under
|
|
2193
|
+
ASP.NET Core's default camelCase policy, which lowercases only a _leading_ run of capitals — so the
|
|
2194
|
+
backend's `ID` goes out as `id` and its `EntityID` goes out as `entityID`. Typing a row against
|
|
2195
|
+
`.ID` or `.entityId` therefore compiled fine and read `undefined` at run time. See
|
|
2196
|
+
**⚠ Breaking Changes / Migration** below.
|
|
2197
|
+
|
|
2198
|
+
This was also a latent trap for anything supplying its own audit rows: the grid resolves row
|
|
2199
|
+
identity through `compareProperty`, which is `'id'`, so a row carrying only `ID` could never be
|
|
2200
|
+
selected — clicking a service entry left the operations grid empty. Rows shaped like the real
|
|
2201
|
+
payload work unchanged; `@shared`'s own runtime behaviour is not affected by this release.
|
|
2202
|
+
|
|
2203
|
+
- **The top bar's notifications bell is no longer pushed off-centre by its unread badge.** The badge
|
|
2204
|
+
is an absolutely-positioned overlay, so it adds no width — but it is still a DOM sibling after the
|
|
2205
|
+
bell icon, which defeated the `.btn i:not(:last-child)` margin guard in the global button styles.
|
|
2206
|
+
That left 8px of dead space to the icon's right and widened the button from 38px to 46px, but only
|
|
2207
|
+
while an unread count was showing, so it came and went with the count. The margin is now cleared
|
|
2208
|
+
for that button specifically; the shared guard, which is correct for icon-plus-label buttons, is
|
|
2209
|
+
unchanged.
|
|
2210
|
+
|
|
2211
|
+
- **Lint: `shared-` component/directive selector prefix is now accepted.**
|
|
2212
|
+
`libs/shared/eslint.config.mjs` still carried the scaffolded `prefix: 'lib'`, so the library's own
|
|
2213
|
+
`shared-`-prefixed components (`shared-main-layout`, `shared-login-layout`) failed
|
|
2214
|
+
`@angular-eslint/component-selector`. Both selector rules now accept `['lib', 'shared']`.
|
|
2215
|
+
|
|
2216
|
+
##### ⚠ Breaking Changes / Migration
|
|
2217
|
+
|
|
2218
|
+
Two properties on the audit history models were renamed to the keys the backend actually sends. Both
|
|
2219
|
+
models are exported from the package root, so anything typed against the old names will now fail to
|
|
2220
|
+
compile. Nothing in `@shared` changes behaviour at run time — the fix is to the declared types.
|
|
2221
|
+
|
|
2222
|
+
| Model | Before | After |
|
|
2223
|
+
| ------------------------ | ------------------- | ------------------- |
|
|
2224
|
+
| `IServicesHistoryList` | `ID: number` | `id: number` |
|
|
2225
|
+
| `IOperationsHistoryList` | `ID: number` | `id: number` |
|
|
2226
|
+
| `IOperationsHistoryList` | `entityId?: number` | `entityID?: number` |
|
|
2227
|
+
|
|
2228
|
+
To upgrade:
|
|
2229
|
+
|
|
2230
|
+
1. Rename `.ID` to `.id` wherever you read or construct an `IServicesHistoryList` or
|
|
2231
|
+
`IOperationsHistoryList`. If the compiler now reports the property as missing, that code was
|
|
2232
|
+
reading `undefined` before — it never matched the payload.
|
|
2233
|
+
2. Rename `.entityId` to `.entityID` on `IOperationsHistoryList`. The capitals are deliberate and
|
|
2234
|
+
match the backend's `EntityID`; do not "correct" them back.
|
|
2235
|
+
3. If you supply audit rows yourself — a test double, a Storybook mock, a hand-rolled
|
|
2236
|
+
`ServicesHistoryService` / `OperationsHistoryService` — emit `id`, not `ID`. Rows keyed only on
|
|
2237
|
+
`ID` were never selectable, so a service row's selection could not reach the operations grid.
|
|
2238
|
+
4. If you worked around that by overriding `compareProperty` to `'ID'` in a `ServicesHistoryDataset`
|
|
2239
|
+
or `OperationsHistoryDataset` subclass, remove the override. The inherited `'id'` is now correct.
|
|
2240
|
+
|
|
2241
|
+
#### [1.2.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v1.2.0) - 2026-07-28
|
|
2242
|
+
|
|
2243
|
+
##### Added
|
|
2244
|
+
|
|
2245
|
+
- A tokenized app backdrop — `--app-backdrop`, a subtle light-gray gradient — rendered behind the
|
|
2246
|
+
whole layout shell so the sidebar's new translucent "glass" surface (from `@library`) reads against
|
|
2247
|
+
a colored background.
|
|
2248
|
+
|
|
2249
|
+
##### Changed
|
|
2250
|
+
|
|
2251
|
+
- `MainLayoutComponent` now floats the sidebar in a padded region over the shared `--app-backdrop`
|
|
2252
|
+
gradient (the shell background was a flat gray). This gives the `@library` glass sidebar a colored
|
|
2253
|
+
surface to read against; the content area shares the same subtle backdrop.
|
|
2254
|
+
|
|
2255
|
+
##### ⚠ Breaking Changes / Migration
|
|
2256
|
+
|
|
2257
|
+
None. The `--app-backdrop` and floating sidebar region are applied automatically by
|
|
2258
|
+
`MainLayoutComponent`; no consumer action is required.
|
|
2259
|
+
|
|
2260
|
+
#### [1.1.0](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v1.1.0) - 2026-07-24
|
|
2261
|
+
|
|
2262
|
+
##### Added
|
|
2263
|
+
|
|
2264
|
+
- New application top bar (`shared-top-bar`), rendered by `MainLayoutComponent`, composing the
|
|
2265
|
+
brand (app name, company name, optional logo), an environment badge, notifications, the language
|
|
2266
|
+
selector, and the user profile — alongside the existing sidebar-collapse and logout controls.
|
|
2267
|
+
- `NotificationsService` (root-provided) that streams notifications from a **SignalR hub**. It
|
|
2268
|
+
exposes `getNotifications()`, `getUnreadCount()`, `markAsRead()`, `markAllAsRead()`, `start()`,
|
|
2269
|
+
`stop()`, and an `isEnabled` flag. When enabled it connects (authenticated with the current JWT)
|
|
2270
|
+
to `AppConfig.notificationsUrl` and listens for the server's `ReceiveNotifications` push. The top
|
|
2271
|
+
bar drives it — apps do not call it directly.
|
|
2272
|
+
- The notifications feature is toggleable and configurable via `AppConfig.notificationsEnabled` and
|
|
2273
|
+
`AppConfig.notificationsUrl`; when disabled (or no URL is set) the bell is not rendered.
|
|
2274
|
+
- Notifications show an icon, title, and description, and — when a `callToActionUrl` is present —
|
|
2275
|
+
clicking navigates to it (internal routes via the Angular router, external URLs in a new tab).
|
|
2276
|
+
- Individually reusable top-bar components: `BrandComponent`, `EnvironmentBadgeComponent`,
|
|
2277
|
+
`NotificationsComponent`, `UserProfileComponent`, and the composing `TopBarComponent`.
|
|
2278
|
+
- `top-bar` i18n bundle (`en`/`pt`) registered in `ZAMBON_SHARED_I18N_RESOURCES`.
|
|
2279
|
+
- New peer dependency `@microsoft/signalr` (^10.0.0), used by `NotificationsService`.
|
|
2280
|
+
- `LanguageSelectorComponent` gained a `showFlag` input; when `true` the toggle shows the current
|
|
2281
|
+
language's flag on a light-gray button instead of the text label (used by the top bar; the login
|
|
2282
|
+
page keeps the text label).
|
|
2283
|
+
- `MainLayoutComponent` projects the application version (`AppConfig.version`) into the sidebar's
|
|
2284
|
+
footer slot, shown at the bottom of the navigation when set.
|
|
2285
|
+
|
|
2286
|
+
##### Changed
|
|
2287
|
+
|
|
2288
|
+
- `ICurrentUserInfo` gained optional `pictureUrl` and `position` fields, displayed in the top-bar
|
|
2289
|
+
user profile. The avatar falls back to the user's initials when `pictureUrl` is absent, and the
|
|
2290
|
+
position line is hidden when `position` is empty.
|
|
2291
|
+
- **`INotification` redefined** to `{ title, description, icon, callToActionUrl?, isRead }` (was
|
|
2292
|
+
`{ id, title, message, read, createdAt }`) to match the SignalR hub contract.
|
|
2293
|
+
- The top bar no longer renders a sidebar-collapse button — the sidebar owns its own collapse/expand
|
|
2294
|
+
toggle. The user profile now uses a light, on-palette surface (neutral chip + brand-colored avatar)
|
|
2295
|
+
instead of the previous dark chip.
|
|
2296
|
+
- On small screens (< 768px) the top bar hides the brand text (app name + company; the logo remains)
|
|
2297
|
+
and the entire user profile, for a compact layout.
|
|
2298
|
+
- `LanguageSelectorComponent` now renders language flags as self-contained inline SVGs (US for `en`,
|
|
2299
|
+
Brazil for `pt`) instead of `/flags/*.png` background images, in both the flag toggle and the
|
|
2300
|
+
dropdown. The selector no longer depends on app-provided flag assets; apps that supplied custom
|
|
2301
|
+
`/flags/*.png` images will no longer see them in the language selector.
|
|
2302
|
+
|
|
2303
|
+
##### ⚠ Breaking Changes / Migration
|
|
2304
|
+
|
|
2305
|
+
- **`INotification` shape changed** (see above). If you referenced the old fields (`id`, `message`,
|
|
2306
|
+
`read`, `createdAt`), update to `title`, `description`, `icon`, `callToActionUrl`, `isRead`.
|
|
2307
|
+
- **Notifications now require `@microsoft/signalr`.** Install it in the consuming app
|
|
2308
|
+
(`npm i @microsoft/signalr`) — it is a peer dependency.
|
|
2309
|
+
|
|
2310
|
+
To populate the top bar, provide `appName`, `companyName`, `environment`, and optionally `logoUrl`
|
|
2311
|
+
via `AppConfig` options (requires `@zambon-dev/framework` with the new `AppConfig` options). The
|
|
2312
|
+
environment badge maps `DEV`/`QA`/`STG` to colors, renders any other non-empty value with a neutral
|
|
2313
|
+
style, and is hidden for `PROD` or when no environment is set.
|
|
2314
|
+
|
|
2315
|
+
To enable notifications, set `notificationsEnabled: true` and `notificationsUrl` (the SignalR hub
|
|
2316
|
+
URL) in `AppConfig`, and implement a hub that pushes the notification list to clients via a
|
|
2317
|
+
`ReceiveNotifications` invocation (and optionally handles a `MarkAllAsRead` invoke). See
|
|
2318
|
+
`docs/guides/top-bar-configuration.md` for the full consuming-app guide. The user's
|
|
2319
|
+
`pictureUrl`/`position` are supplied by the auth response when available.
|
|
2320
|
+
|
|
2321
|
+
#### [1.0.2](https://github.com/RicardoZambon/ZLibraries/releases/tag/shared-v1.0.2) - 2026-05-02
|
|
2322
|
+
|
|
2323
|
+
- Baseline release: the changelog starts being tracked from this version. Earlier history is
|
|
2324
|
+
available via [GitHub Releases](https://github.com/RicardoZambon/ZLibraries/releases) and the
|
|
2325
|
+
`shared-v*` tags.
|