Библиотека утилит для Angular | ng-hub-ui-utils

Библиотека утилит для Angular с pipe, управлением фокусом, оверлеями, переходами и хелперами перевода, общими для переиспользуемых UI-компонентов.

Последнее обновление 20 сент. 2026 г.

Обзор

Почему команды ищут эту библиотеку

Используйте эту библиотеку утилит для Angular, чтобы не переписывать низкоуровневые части, стоящие за оверлеями, ловушками фокуса, хелперами перевода и распространёнными утилитами шаблонов.

Установка

npm install ng-hub-ui-utils

Перейти к

Идеально для

  • общая UI-инфраструктура
  • системы оверлеев
  • управление фокусом
  • хелперы шаблонов

О библиотеке utils

ng-hub-ui-utils — это общая основа за многими переиспользуемыми UI-паттернами. Она полезна, когда команды Angular хотят централизовать низкоуровневые части, которые поддерживают согласованность систем оверлеев, управления фокусом, pipe и хелперов перевода между проектами.

Руководства по возможностям

Управление фокусом

Захват и управление фокусом клавиатуры внутри элементов

Примеры:
Захват фокуса

Удерживайте фокус внутри модального окна или диалога с помощью hubFocusTrap().

Focus Trap Demo

Click "Enable Focus Trap" and try to Tab outside the blue box. Focus will stay trapped inside.

Focus Trap Area

Status: 🔓 Free

Focusable Elements

The selector FOCUSABLE_ELEMENTS_SELECTOR finds all focusable elements.

getFocusableBoundaryElements(container)→ [first, last] elements
Фокусируемые элементы

Получайте граничные фокусируемые элементы с помощью getFocusableBoundaryElements().

Focus Trap Demo

Click "Enable Focus Trap" and try to Tab outside the blue box. Focus will stay trapped inside.

Focus Trap Area

Status: 🔓 Free

Focusable Elements

The selector FOCUSABLE_ELEMENTS_SELECTOR finds all focusable elements.

getFocusableBoundaryElements(container)→ [first, last] elements

Интернационализация (i18n)

Сервисы и pipes перевода для поддержки нескольких языков

Примеры:
Сервис перевода

HubTranslationService для управления переводами

TranslatePipe - Basic usage

'welcome' | translate→ Welcome to ng-hub-ui

TranslatePipe - With parameters

'greeting' | translate:{ name: 'Carlos' }→ Hello, Carlos!

TranslatePipe - Nested keys

'buttons.save' | translate→ Save
'buttons.cancel' | translate→ Cancel

Programmatic access

translationService.getTranslation('buttons.delete')→ Delete
Pipe перевода

TranslatePipe для переводов в шаблонах

TranslatePipe - Basic usage

'welcome' | translate→ Welcome to ng-hub-ui

TranslatePipe - With parameters

'greeting' | translate:{ name: 'Carlos' }→ Hello, Carlos!

TranslatePipe - Nested keys

'buttons.save' | translate→ Save
'buttons.cancel' | translate→ Cancel

Programmatic access

translationService.getTranslation('buttons.delete')→ Delete
Провайдер перевода

provideHubTranslations() для настройки приложения

TranslatePipe - Basic usage

'welcome' | translate→ Welcome to ng-hub-ui

TranslatePipe - With parameters

'greeting' | translate:{ name: 'Carlos' }→ Hello, Carlos!

TranslatePipe - Nested keys

'buttons.save' | translate→ Save
'buttons.cancel' | translate→ Cancel

Programmatic access

translationService.getTranslation('buttons.delete')→ Delete
Внешний адаптер i18n

Настройте один реактивный мост переводов в app.config.ts для всех библиотек Hub UI.

Настройте один реактивный мост переводов в app.config.ts для всех библиотек Hub UI.

Код
Import:
Template:
Component:

Система оверлеев

Создание позиционированных overlays и плавающих элементов

Примеры:
Сервис overlay

HubOverlayService для программного создания overlay

A connected overlay positioned below the trigger, with a backdrop that closes it on outside click.

Ссылка на overlay

OverlayRef для управления жизненным циклом overlay

A connected overlay positioned below the trigger, with a backdrop that closes it on outside click.

Позиционирование

ConnectionPositionPair для гибкого позиционирования

A connected overlay positioned below the trigger, with a backdrop that closes it on outside click.

Сервис popup

Упрощённое управление popup

Примеры:
Создание popup

HubPopupService для быстрого создания popup

Nothing open.

Pipes

Вспомогательные pipes для шаблонов

Примеры:
Pipes для проверки типов

IsStringPipe, IsObjectPipe, IsObservablePipe

GetPipe - Access nested properties

user | get:'profile.name'→ John Doe
user | get:'profile.address.city'→ New York

IsStringPipe - Type checking

'Hello' | isString→ true
123 | isString→ false

IsObjectPipe - Object detection

{ a: 1 } | isObject→ true
'string' | isObject→ false

UcfirstPipe - Capitalize first letter

'hello world' | ucfirst→ Hello world

UnwrapAsyncPipe - Observable or plain value, same template

status$ | unwrapAsync→ nothing emitted yet
'not an observable' | unwrapAsync→ not an observable
GetPipe

Доступ к вложенным свойствам объекта через точечную нотацию

GetPipe - Access nested properties

user | get:'profile.name'→ John Doe
user | get:'profile.address.city'→ New York

IsStringPipe - Type checking

'Hello' | isString→ true
123 | isString→ false

IsObjectPipe - Object detection

{ a: 1 } | isObject→ true
'string' | isObject→ false

UcfirstPipe - Capitalize first letter

'hello world' | ucfirst→ Hello world

UnwrapAsyncPipe - Observable or plain value, same template

status$ | unwrapAsync→ nothing emitted yet
'not an observable' | unwrapAsync→ not an observable
UcfirstPipe

Перевод первой буквы строки в верхний регистр

GetPipe - Access nested properties

user | get:'profile.name'→ John Doe
user | get:'profile.address.city'→ New York

IsStringPipe - Type checking

'Hello' | isString→ true
123 | isString→ false

IsObjectPipe - Object detection

{ a: 1 } | isObject→ true
'string' | isObject→ false

UcfirstPipe - Capitalize first letter

'hello world' | ucfirst→ Hello world

UnwrapAsyncPipe - Observable or plain value, same template

status$ | unwrapAsync→ nothing emitted yet
'not an observable' | unwrapAsync→ not an observable
UnwrapAsyncPipe

Разворачивание observables и promises в шаблонах

GetPipe - Access nested properties

user | get:'profile.name'→ John Doe
user | get:'profile.address.city'→ New York

IsStringPipe - Type checking

'Hello' | isString→ true
123 | isString→ false

IsObjectPipe - Object detection

{ a: 1 } | isObject→ true
'string' | isObject→ false

UcfirstPipe - Capitalize first letter

'hello world' | ucfirst→ Hello world

UnwrapAsyncPipe - Observable or plain value, same template

status$ | unwrapAsync→ nothing emitted yet
'not an observable' | unwrapAsync→ not an observable

Вспомогательные функции

Вспомогательные функции общего назначения

Примеры:
Type guards

isString(), isNumber(), isDefined(), isPromise()

Type Guards

isString('hello')→ true
isNumber(42)→ true
isDefined(null)→ false
isDefined('value')→ true

Deep Equality

equals({a:1}, {a:1})→ true
equals([1,2], [1,2])→ true

Object Access

getValue(user, 'profile.name')→ John Doe

String Interpolation

interpolateString('Hello {{name}}', {name: 'World'})→ Hello World

String Utilities

removeAccents('Ñoño café')→ Nono cafe
padNumber(5)→ 05
Утилиты для строк

removeAccents(), interpolateString(), regExpEscape()

Type Guards

isString('hello')→ true
isNumber(42)→ true
isDefined(null)→ false
isDefined('value')→ true

Deep Equality

equals({a:1}, {a:1})→ true
equals([1,2], [1,2])→ true

Object Access

getValue(user, 'profile.name')→ John Doe

String Interpolation

interpolateString('Hello {{name}}', {name: 'World'})→ Hello World

String Utilities

removeAccents('Ñoño café')→ Nono cafe
padNumber(5)→ 05
Утилиты для объектов

equals() для глубокого сравнения, getValue() для точечной нотации

Type Guards

isString('hello')→ true
isNumber(42)→ true
isDefined(null)→ false
isDefined('value')→ true

Deep Equality

equals({a:1}, {a:1})→ true
equals([1,2], [1,2])→ true

Object Access

getValue(user, 'profile.name')→ John Doe

String Interpolation

interpolateString('Hello {{name}}', {name: 'World'})→ Hello World

String Utilities

removeAccents('Ñoño café')→ Nono cafe
padNumber(5)→ 05
Утилиты для DOM

closest(), reflow(), getActiveElement()

closest()

Click anywhere inside; the helper walks up to the nearest panel.

outer
inner

closest(target, '[data-panel]') → —

reflow()

Replaying an animation means removing the class and adding it back. Without a forced reflow the browser coalesces both into no change at all.

replay me
getActiveElement()

Focus either control. The second one lives inside a shadow root.

document.activeElement → —

getActiveElement() → —

runInZone()

Both streams are created with runOutsideAngular; only one is piped through the operator.

plain subscriberisInAngularZone() → —
piped through runInZone(zone)isInAngularZone() → —
ticks0
Утилиты RxJS

Оператор runInZone() для интеграции с NgZone

closest()

Click anywhere inside; the helper walks up to the nearest panel.

outer
inner

closest(target, '[data-panel]') → —

reflow()

Replaying an animation means removing the class and adding it back. Without a forced reflow the browser coalesces both into no change at all.

replay me
getActiveElement()

Focus either control. The second one lives inside a shadow root.

document.activeElement → —

getActiveElement() → —

runInZone()

Both streams are created with runOutsideAngular; only one is piped through the operator.

plain subscriberisInAngularZone() → —
piped through runInZone(zone)isInAngularZone() → —
ticks0

Утилиты для scrollbar

Измерение и компенсация ширины scrollbar

Примеры:
Ширина scrollbar

Функции scrollbarWidth() и scrollbarPadding()

ScrollBar Service

The ScrollBar service helps manage scrollbar visibility and compensate for layout shifts when hiding scrollbars (e.g., when opening modals).

Use Case: Modal Body Lock

When opening a modal, you typically hide the body scrollbar. The ScrollBar.hide() method handles this automatically and returns a reverter function.

Status: 🔓 Normal scrolling

Code Example

import { inject } from '@angular/core';
import { ScrollBar } from 'ng-hub-ui-utils';

export class ModalService {
  private scrollBar = inject(ScrollBar);
  private revertScrollbar: (() => void) | null = null;

  openModal() {
    // Hide scrollbar and get reverter function
    this.revertScrollbar = this.scrollBar.hide();
  }

  closeModal() {
    // Restore scrollbar
    if (this.revertScrollbar) {
      this.revertScrollbar();
      this.revertScrollbar = null;
    }
  }
}

Переходы

Хелперы для переходов CSS

Примеры:
Запуск перехода

hubRunTransition() для программных переходов CSS

transition: height 600ms · measured at 0 ms
  1. Run a transition to see when the observable completes.

Подсказка

Лёгкие настраиваемые подсказки для любого элемента через директиву [hubTooltip].

Примеры:
Директива подсказки

Примените [hubTooltip] вместе с hubTooltipPlacement, чтобы показывать позиционированную подпись при наведении или фокусе; оформляется переменными --hub-tooltip-*. Прежняя [tooltip] по-прежнему работает, но устарела: её входы без префикса конфликтуют с другими директивами на том же элементе.

Placements (hover the buttons)
Themed with --hub-tooltip-* variables

Цвет

Разбор, измерение и преобразование CSS-цветов без DOM

Примеры:
Разбор цвета

parseColor(), toHex() и isValidColor() распознают hex, rgb(), hsl(), oklch(), oklab() и 148 именованных цветов, в том числе при серверном рендеринге

Try one:

#0b6eff
parsed
parseColor(){ r: 11, g: 110, b: 255, a: 1 }
toHex()#0b6eff
rgbToOklch(){ l: 0.58, c: 0.23, h: 260 }
isValidColor()true
toRgb()already parsed — same object back
HUB_NAMED_COLORSnot a bareword — 148 in the table
Контраст и читаемость

contrastRatio() (WCAG 2), contrastAPCA() и readableOn(), выбирающая цвет текста в соответствии с токеном --hub-sys-color-*-on

readableOn(accent, metric)
Agrees with the design-system token on all nine
primary
#0d6efd

ink#ffffff
WCAG4.5:1
APCA-75.8
secondary
#6c757d

ink#ffffff
WCAG4.69:1
APCA-78
success
#198754

ink#ffffff
WCAG4.53:1
APCA-76.5
danger
#dc3545

ink#ffffff
WCAG4.53:1
APCA-75.4
warning
#ffc107

ink#000000
WCAG12.88:1
APCA76
info
#0dcaf0

ink#000000
WCAG10.72:1
APCA66.9
neutral
#6c757d

ink#ffffff
WCAG4.69:1
APCA-78
light
#f8f9fa

ink#000000
WCAG19.92:1
APCA102.4
dark
#212529

ink#ffffff
WCAG15.43:1
APCA-105

A highlighted border marks a chip whose ink differs from what --hub-sys-color-*-on paints in CSS.


Translucent ink has to be composited before it is measured

relativeLuminance() ignores alpha, because a translucent colour has no luminance of its own until something is behind it. Measure the ink as written and you are scoring solid black; compositeOver() blends it onto the surface first, which is what the eye is reading.

Sample text
relativeLuminance(surface)0.1833
contrastRatio(ink, surface)4.67:1
…with compositeOver(ink, surface)2.75:1
Преобразования OKLCh

rgbToOklch(), oklchToRgb(), maxSrgbChroma() и clampToSrgbGamut() для работы с палитрой в том пространстве, где смешивает дизайн-система

red 21°
0.232
amber 85°
0.118
green 157°
0.138
cyan 218°
0.104
blue 260°
0.232
purple 320°
0.277

clampToSrgbGamut()

Asking for chroma 0.35 on the amber hue at the current lightness.

clipped per channel#d24900
chroma reduced#9a7300
in gamut as asked: false
chroma kept: 0.118
Вывод палитры

harmoniseSemantics() поворачивает success, warning, danger и info к оттенку бренда не более чем на 15°, а tintNeutrals() так же наклоняет серую шкалу с ограничением цветности в 0,015. Светлота не меняется, поэтому контраст, ради которого выбиралась каждая роль, сохраняется.

Primary
oklch(0.55 0.17 260)
success #00866bwas #198754
warning #e7cd16was #ffc107
danger #d8336bwas #dc3545
info #44c4ffwas #0dcaf0
100
200
300
400
500
600
700
800
900

Hue rotation capped at 15°; neutral chroma capped at 0.015. Success and danger stay 173° apart.

Ключевые возможности

Последние изменения

Version 22.15.2 - 9/20/26, 12:00 AM

changed: The package manifest carries a description and a keyword list. It was the only one of the 26 with neither, while seventeen libraries depend on it, and both fields are what npm ranks a search on. Metadata only: no code, types or styles change.

Version 22.15.1 - 9/16/26, 12:00 AM

changed: Repository, issue and README links follow the move to the hub-env organization. Issues for every Hub UI package are now gathered in hub-env/hub-ui, and the repository and bugs fields of the manifest point at the new addresses. No code, types or styles change.

Version 22.15.0 - 9/8/26, 12:00 AM

added: harmoniseSemantics() and tintNeutrals(), so a brand colour derives the whole palette in one place instead of once per product. The first rotates success, warning, danger and info towards the brand's hue; the second leans the grey ramp the same way. Both leave lightness exactly where the anchor had it, because lightness is what carries the contrast each role was chosen for. Two exported numbers decide the result: HUB_MAX_HUE_SHIFT (15°), which is what keeps success and danger more than 90° apart for a reader with deuteranopia — they start 135.8° apart and a brand hue between them closes the gap by up to twice the cap — and HUB_MAX_NEUTRAL_CHROMA (0.015), which sits between the design system's own gray-500 (0.0145) and gray-600 (0.0165), so a tinted ramp is never more colourful than the grey people already accept as grey.

fixed: The DOM helpers no longer reach for globals a server render does not have. ScrollBar.hide(), hubRunTransition(), getTransitionDurationMs(), reflow(), getActiveElement() and OverlayRef each read window or document straight out of scope, which is a ReferenceError on the way to a prerendered page. They now resolve the document and the view from what they already hold — the injected DOCUMENT, the application's injector, or the element's own ownerDocument.defaultView — which also fixes the same helpers inside an iframe, where the global was the wrong document all along.

changed: reflow() returns DOMRect | null and no longer falls back to document.body, and getActiveElement() accepts null as its root. Both are announced in BREAKING_CHANGES.md; no call that passes an element changes behaviour.

Version 22.14.0 - 9/7/26, 12:00 AM

removed: TooltipDirective and its bare [tooltip] attribute, deprecated since 22.9.0. An unprefixed selector is a name in the application's namespace rather than the library's: Angular hands one attribute to every directive on the element that declares an input of that name, so nobody else could own a tooltip — not a consumer writing their own, not <hub-badge>, which declares a tooltip input and ended up drawing two, and not [hubDropdown], whose own placement is typed over eight values where a tooltip understands four. [hubTooltip] is the replacement, attribute for attribute: tooltip → hubTooltip, placement → hubTooltipPlacement, delay → hubTooltipDelay, offset → hubTooltipOffset. Breaking, and note that a template still writing tooltip="…" keeps compiling and silently shows nothing — see BREAKING_CHANGES.md.

added: A guard over the whole entry point: no directive of this package may claim a bare attribute. It reads Angular's own compiled definitions rather than a hand-kept list, so the rule covers the directive somebody adds next and not only the one just removed.

Version 22.13.0 - 9/7/26, 12:00 AM

added: OverlayPosition.origin, the element the strategy is connected to. It was known to the strategy and to nobody else, and an overlay that has to keep up with its anchor needs to be able to ask which element that is.

fixed: A floating panel stayed behind the moment its trigger moved. The overlay recomputed its position on scroll and on resize, and both of those describe the page moving under an origin that stays put. Nothing covered the opposite, the origin moving inside a page nobody scrolled and no window resized: a section collapsing above it, an image landing late, a sidebar accordion animating shut. The panel then hung at the height the trigger used to have, cut loose from the thing it belongs to, and every connected overlay in the family opens through this service, so all of them had it. The origin is now watched for as long as the panel is open and the panel is re-placed whenever its box actually changes, every frame of an animated collapse rather than once at the start of one. The box is read per frame and nothing is written unless it moved, so an overlay whose trigger sits still never touches the DOM, and the loop runs outside Angular zone so a zone-based application does not run change detection while a menu is open.

Version 22.12.1 - 9/6/26, 12:00 AM

changed: Every row of FUNCTIONALITIES.md now points at a runnable demo. Fifteen features (the popup service, the transition helpers, UnwrapAsyncPipe, the DOM and RxJS helpers and five of the colour functions) were listed as having no example, which left the unit tests as the only executable use of them. Each of those now has a demo on the documentation site, so the table reports coverage instead of a wishlist. No API changed.

fixed: The tooltip and overlay stylesheets resolve at the path the documentation gives. The manifest declared no exports map, so ng-packagr synthesised one for the published package, and a synthesised map lists only "." and "./package.json". A package that declares exports closes every subpath outside that map, so @use "ng-hub-ui-utils/styles/tooltip"; resolved to nothing even though the sheet shipped in styles/. Both sheets are now named in the manifest, extensionless and with the .scss suffix, the way every sibling package in the family already does, and ng-packagr merges those entries into the map it generates instead of replacing them.

fixed: The tooltip is announced to assistive technology and can be dismissed without a mouse. The bubble was a bare span with no id and no role, and the host was never pointed at it, so on an icon-only button, the case the tooltip is written for, a screen reader had nothing to read and the only workaround was an aria-label repeating the same text. The bubble is now role="tooltip" with an id the host is aria-describedby while it is on screen, and the attribute is put back exactly as it was found, so a description a consumer wrote is neither replaced nor left behind. The same change closes WCAG 1.4.13 for it: Escape dismisses the label without moving the pointer or the focus, and the label waits out a short grace period and stays put once the pointer lands on it, which is the only way to read one longer than its box. styles/tooltip.scss therefore ships pointer-events: auto instead of none, and the controller disables them again the moment it starts fading, so an invisible bubble never catches a click meant for what is under it. It is reached through HubTooltipController, so it arrives at all four entry points at once: [hubTooltip], the deprecated [tooltip], [hubOverflowTooltip] and hubTooltipAdapter.

Version 22.12.0 - 9/3/26, 12:00 AM

added: Colour utilities — parseColor(), toHex(), isValidColor(), relativeLuminance(), contrastRatio(), contrastAPCA(), readableOn(), compositeOver() and the OKLCh helpers rgbToOklch(), oklchToRgb(), maxSrgbChroma(), isInSrgbGamut() and clampToSrgbGamut(). The parser resolves hex (3/4/6/8 digits), rgb(), hsl(), oklch(), oklab() and the 148 CSS named colours in both modern and legacy syntax, with no DOM involved, so it runs under server-side rendering. It returns null rather than throwing on anything it cannot resolve.

added: readableOn() picks black or white by OKLCh perceptual lightness, the same decision the --hub-sys-color-*-on token computes in CSS, so a component that resolves its ink in TypeScript cannot disagree with the stylesheet. The alternative metrics are available: maximising the WCAG 2 ratio puts black text on the design system's own blue, green and red accents, which is why it is not the default.

Version 22.11.1 - 9/1/26, 12:00 AM

changed: The homepage in the manifest points at this library's own documentation page rather than at the site root, so the link a registry shows beside the package lands on the reference for the package the reader was already looking at. Metadata only.

Version 22.11.0 - 8/26/26, 12:00 AM

added: The overlay follows its origin. While attached it listens for scroll and resize in the capture phase and recomputes its position, coalesced into an animation frame. Before this it computed coordinates once and never again: a panel opened and then scrolled sat 122px away from the field it belonged to.

added: start and end are logical. They resolved to left and right whatever the direction, so an overlay opened from a field inside an RTL container hung off the wrong edge. The direction is read from the origin element; OverlayPosition.withDirection() overrides it.

added: OverlayRef.onKeydown(), and with it a document-level dispatcher that tells only the topmost open overlay. An overlay rarely holds focus, so a component listening on its own host never heard Escape and the panel that took over the screen could not be dismissed with the key everyone reaches for.

added: HUB_DROPDOWN_POSITIONS — the four-position fallback chain a dropdown wants, below the origin and flipping above when there is no room, expressed logically so one list serves both text directions.

fixed: Tearing an overlay down twice no longer throws. dispose() and detach() called removeChild on nodes something else may already have removed — a test teardown, a router navigation — and the DOMException took the whole destroy path with it.

Version 22.10.0 - 8/22/26, 12:00 AM

added: --hub-tooltip-white-space and --hub-tooltip-text-align, so how a label breaks and sits can be asked for per tooltip instead of through a global rule that changes every tooltip in the product. Both default to what was hard-coded, so nothing moves for anyone who says nothing.

Version 22.9.3 - 8/19/26, 12:00 AM

fixed: A tooltip whose stylesheet was never imported no longer moves the page. The element now takes position: absolute inline at creation — the same value the sheet ships — so it stops landing in normal flow at the end of the document and growing the page a scrollbar that appeared and vanished as the pointer crossed a label.

Version 22.9.2 - 8/17/26, 12:00 AM

fixed: The stylesheets are published under styles/, so @use 'ng-hub-ui-utils/styles/tooltip' names a real path instead of reaching through the package's internal folder layout.

Version 22.9.1 - 8/17/26, 12:00 AM

fixed: The published package declares its licence. An absent license field is not neutral — a registry reports it as unlicensed, which legally reads as all rights reserved. The intent was always MIT.

Version 22.9.0 - 8/17/26, 12:00 AM

added: [hubTooltip], a tooltip directive that can share an element. Its inputs are hubTooltip, hubTooltipPlacement, hubTooltipDelay and hubTooltipOffset: an attribute named for its owner cannot be claimed by anyone else, which is what the bare names could not promise next to [hubDropdown] (its own placement is typed over eight values) or <hub-badge> (which declares a tooltip input and drew two).

deprecated: TooltipDirective / [tooltip]. Kept working, unchanged — both directives are thin shells over the same HubTooltipController. Migration is attribute for attribute: tooltip → hubTooltip, placement → hubTooltipPlacement, delay → hubTooltipDelay, offset → hubTooltipOffset.

Version 22.8.1 - 8/15/26, 12:00 AM

fixed: A content-sized overlay no longer clips its own content into invisibility. Created with no intrinsic size, it computed to a 0×0 box whenever its content was absolutely positioned — which is exactly what a connected-position dropdown is — and the stylesheet's overflow: auto then hid what the overlay existed to display. An overlay created without an explicit width or height now opts out of clipping.

Version 22.8.0 - 8/14/26, 12:00 AM

added: provideHubTranslationAdapter() — the application-wide reactive bridge from an external translation service (transloco, ngx-translate, i18next…) into HubTranslationService. Register it once at bootstrap and every ng-hub-ui library picks up the host dictionary, re-emitting on every language change. Supports optional namespacing and deliberate per-label reactive overrides.

added: HUB_TRANSLATION_PREFIX — injection token that scopes the lookups of a library to a collision-safe HUBUI.<LIBRARY>.* namespace. TranslatePipe resolves the prefixed key first and falls back to the bare key, so existing flat dictionaries keep working untouched.

Version 22.7.2 - 8/8/26, 12:00 AM

fixed: Documentation links now point at the canonical localized URLs. The README linked to https://hubui.dev/<path> with no locale prefix and no trailing slash, and both forms are 301-redirected, so every reader arriving from npm or GitHub landed on a redirect instead of the canonical page.

Version 22.7.1 - 7/27/26, 12:00 AM

fixed: --hub-overlay-zindex / --hub-overlay-backdrop-zindex actually work now: OverlayRef resolves its inline z-index through var(--hub-overlay-zindex, 1000) / var(--hub-overlay-backdrop-zindex, 999) instead of literal values, so re-stacking an overlay no longer requires !important. Defaults are unchanged.

added: OverlayConfig.zIndex — optional explicit layer for a single overlay instance; when set it takes precedence over the token.

Version 22.7.0 - 7/7/26, 12:00 AM

added: resolveHubAccent(value) — the canonical "any colour" accent resolver shared across the ng-hub-ui family: barewords map to var(--hub-sys-color-<name>, <name>), literal #hex / rgb() / oklch() / var() values pass through unchanged, empty values yield null.

Version 22.6.1 - 7/2/26, 12:00 AM

fixed: CSS variable fallbacks realigned to the ds light defaults (e.g. --hub-ref-font-family-base falls back to the system-ui stack instead of inherit); fallbacks only apply when ng-hub-ui-ds is not loaded.

Version 22.6.0 - 6/30/26, 12:00 AM

added: HubOverflowTooltipDirective ([hubOverflowTooltip]) — shows a tooltip only while the label is actually truncated, with live tracking via ResizeObserver + MutationObserver. hubOverflowTooltipMeasure splits the two questions a chip asks separately: the tooltip covers the whole control, while a CSS selector resolved inside it names the inner box whose truncation decides whether it speaks.

added: Agnostic tooltip token — HUB_TOOLTIP_ADAPTER plus provideHubTooltip(adapter) let any tooltip implementation back [hubOverflowTooltip], app-wide or per subtree; defaults to the built-in hubTooltipAdapter.

Связанные библиотеки