ng-hub-ui-utils 22.12.0 → 22.13.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/README.md CHANGED
@@ -116,8 +116,8 @@ export class ExampleComponent {
116
116
  overlayY: 'top'
117
117
  }]);
118
118
 
119
- // Attach component to overlay
120
- const componentRef = overlayRef.attach(MyComponent);
119
+ // Render a component (or a TemplateRef) into the overlay; you get the host element back
120
+ const overlayElement = overlayRef.attach(MyComponent);
121
121
  }
122
122
  }
123
123
  ```
@@ -128,9 +128,11 @@ export class ExampleComponent {
128
128
  this.overlayService.create({ zIndex: 1100 }); // OverlayConfig.zIndex: number | string — takes precedence over the token
129
129
  ```
130
130
 
131
- ### 🎯 Popup Service (Base Class)
131
+ ### 🎯 Popup Service
132
132
 
133
- Base service for creating custom popup implementations.
133
+ `PopupService<T>` hosts a dynamically created popup component and runs its show/hide
134
+ transition. It is a concrete class — subclass it when the popup needs its own API, as
135
+ below, or provide it through a factory.
134
136
 
135
137
  ```typescript
136
138
  import { PopupService } from 'ng-hub-ui-utils';
@@ -356,16 +358,17 @@ import { GetPipe, UcfirstPipe } from 'ng-hub-ui-utils';
356
358
 
357
359
  ### 🏷️ Tooltip Directive
358
360
 
359
- Add a lightweight, themeable tooltip to any element with the `[tooltip]` directive.
360
- The tooltip is appended to `<body>` (never clipped) and shows on hover/focus.
361
+ Add a lightweight, themeable tooltip to any element with the `HubTooltipDirective`
362
+ (`[hubTooltip]`). The tooltip is appended to `<body>` (never clipped) and shows on
363
+ hover/focus.
361
364
 
362
365
  ```typescript
363
- import { TooltipDirective } from 'ng-hub-ui-utils';
366
+ import { HubTooltipDirective } from 'ng-hub-ui-utils';
364
367
 
365
368
  @Component({
366
369
  standalone: true,
367
- imports: [TooltipDirective],
368
- template: `<button tooltip="Save changes" placement="top">Save</button>`
370
+ imports: [HubTooltipDirective],
371
+ template: `<button hubTooltip="Save changes" hubTooltipPlacement="top">Save</button>`
369
372
  })
370
373
  export class ExampleComponent {}
371
374
  ```
@@ -378,8 +381,40 @@ export class ExampleComponent {}
378
381
  > @use 'ng-hub-ui-utils/styles/tooltip';
379
382
  > ```
380
383
 
381
- Inputs: `tooltip` (text), `placement` (`top` | `bottom` | `left` | `right`, default `top`),
382
- `delay` (fade ms, default `150`), `offset` (px, default `8`).
384
+ Inputs: `hubTooltip` (text), `hubTooltipPlacement` (`top` | `bottom` | `left` | `right`,
385
+ default `top`), `hubTooltipDelay` (fade ms, default `150`), `hubTooltipOffset` (px, default `8`).
386
+
387
+ > **`TooltipDirective` (`[tooltip]`) is deprecated since 22.9.0.** It still works,
388
+ > unchanged — both directives are thin shells over the same `HubTooltipController` — but its
389
+ > bare input names (`tooltip`, `placement`, `delay`, `offset`) belong to every directive on
390
+ > the element that declares them, which is how it collided with `[hubDropdown]`'s own
391
+ > `placement` and with the `tooltip` input of `<hub-badge>`. Migration is attribute for
392
+ > attribute: `tooltip` → `hubTooltip`, `placement` → `hubTooltipPlacement`,
393
+ > `delay` → `hubTooltipDelay`, `offset` → `hubTooltipOffset`.
394
+
395
+ Show the label **only while the host is truncated** with `HubOverflowTooltipDirective`
396
+ (`[hubOverflowTooltip]`), which tracks truncation live with a `ResizeObserver` and a
397
+ `MutationObserver` and resolves its tooltip through `HUB_TOOLTIP_ADAPTER`:
398
+
399
+ ```html
400
+ <span class="label" [hubOverflowTooltip]="item.label">{{ item.label }}</span>
401
+ ```
402
+
403
+ The element that is **hovered** and the element that is **measured** need not be the same.
404
+ By default they are, but a control whose text is clipped by a box inside it wants them apart:
405
+ the hover area is the whole control, while the only box that can report truncation is the
406
+ inner one — a child that clips its own text never lets the overflow reach its parent, so
407
+ measuring the parent reports none and the tooltip goes quiet. Point
408
+ `hubOverflowTooltipMeasure` at the inner box with a CSS selector, resolved inside the host:
409
+
410
+ ```html
411
+ <div class="chip" [hubOverflowTooltip]="item.label" hubOverflowTooltipMeasure=".chip__title">
412
+ <span class="chip__icon"></span>
413
+ <span class="chip__title">{{ item.label }}</span>
414
+ </div>
415
+ ```
416
+
417
+ Unset — or pointing at nothing — the host measures itself, exactly as before.
383
418
 
384
419
  Theme it from any scope with `--hub-tooltip-*` variables:
385
420
 
@@ -394,8 +429,14 @@ Theme it from any scope with `--hub-tooltip-*` variables:
394
429
 
395
430
  Available tokens: `--hub-tooltip-bg`, `--hub-tooltip-color`, `--hub-tooltip-opacity`,
396
431
  `--hub-tooltip-padding-x`, `--hub-tooltip-padding-y`, `--hub-tooltip-border-radius`,
397
- `--hub-tooltip-font-size`, `--hub-tooltip-max-width`, `--hub-tooltip-zindex`,
398
- `--hub-tooltip-transition-duration`, `--hub-tooltip-shadow`, `--hub-tooltip-font-family`.
432
+ `--hub-tooltip-font-size`, `--hub-tooltip-font-weight`, `--hub-tooltip-line-height`,
433
+ `--hub-tooltip-max-width`, `--hub-tooltip-zindex`, `--hub-tooltip-transition-duration`,
434
+ `--hub-tooltip-shadow`, `--hub-tooltip-font-family`, `--hub-tooltip-white-space`,
435
+ `--hub-tooltip-text-align`.
436
+
437
+ The last two arrived in 22.10.0, for the tooltip that carries a sentence rather than a
438
+ name: they are set on the **host**, which is the only element you can reach, because the
439
+ bubble itself lives on `<body>`, outside every component's styles.
399
440
 
400
441
  #### Tooltip adapter for other libraries (`hubTooltipAdapter`)
401
442
 
@@ -414,10 +455,26 @@ providers: [
414
455
  ];
415
456
  ```
416
457
 
458
+ Inside this package the same token works the other way round: `[hubOverflowTooltip]`
459
+ resolves its tooltip through `HUB_TOOLTIP_ADAPTER`, which defaults to `hubTooltipAdapter`.
460
+ Swap it app-wide, or for one subtree, with `provideHubTooltip()`:
461
+
462
+ ```ts
463
+ import { provideHubTooltip, HubTooltipAdapter } from 'ng-hub-ui-utils';
464
+
465
+ const myTooltip: HubTooltipAdapter = {
466
+ attach(host, text, options) {
467
+ /* … returns a HubTooltipHandle with update(text) and destroy() */
468
+ }
469
+ };
470
+
471
+ providers: [provideHubTooltip(myTooltip)];
472
+ ```
473
+
417
474
  Also available: the imperative `HubTooltipController` (engine) and the
418
- `HubTooltipAdapter` / `HubTooltipHandle` / `HubTooltipOptions` types. See the
419
- ecosystem-wide [Synergies & agnosticism](../../README.md#synergies--agnosticism)
420
- section.
475
+ `HubTooltipAdapter` / `HubTooltipHandle` / `HubTooltipOptions` / `HubTooltipPlacement`
476
+ types. See the ecosystem-wide
477
+ [Synergies & agnosticism](../../README.md#synergies--agnosticism) section.
421
478
 
422
479
  ## 🚀 Installation
423
480
 
@@ -505,6 +562,20 @@ interface HubTranslationConfig {
505
562
 
506
563
  The configuration is also exposed through the `HUB_TRANSLATION_CONFIG` injection token for advanced scenarios.
507
564
 
565
+ ### `HUB_TRANSLATION_PREFIX`
566
+
567
+ Injection token that scopes a library's lookups to a collision-safe `HUBUI.<LIBRARY>.*`
568
+ namespace. `TranslatePipe` resolves the prefixed key first and falls back to the bare key,
569
+ so a flat dictionary that predates the token keeps working untouched.
570
+
571
+ ```typescript
572
+ providers: [{ provide: HUB_TRANSLATION_PREFIX, useValue: 'HUBUI.TABLE' }];
573
+ ```
574
+
575
+ The adapter types are exported alongside it: `HubTranslationSource`,
576
+ `HubTranslationOverrides`, `HubTranslationAdapterConfig`, `HubTranslationAdapterFactory`
577
+ and the `HUB_TRANSLATION_SOURCE` token `provideHubTranslationAdapter()` registers.
578
+
508
579
  ### `HubTranslationService`
509
580
 
510
581
  Injectable service that holds the active translations and notifies subscribers when they change.
@@ -584,6 +655,19 @@ These functions back the i18n system and are exported for direct use:
584
655
 
585
656
  - `regExpEscape(text: string): string` - Escapes special characters for RegExp
586
657
  - `removeAccents(str: string): string` - Removes accents from text
658
+ - `interpolateString(expr?: string, params?: any, templateMatcher?: RegExp): string` - Replaces `{{ token }}` placeholders
659
+ - `generateUniqueId(length: number): string` - Random alphanumeric id, for a DOM node that needs one
660
+
661
+ ### Object Functions
662
+
663
+ - `equals(o1: any, o2: any): boolean` - Deep equality
664
+ - `getValue(target: any, key: string): any` - Reads a nested value by dot-notation key
665
+ - `isObject(item: any): boolean` - Whether the value is a non-array object
666
+ - `mergeDeep(target: any, source: any): any` - Recursive merge; the only deep object helper in the package
667
+
668
+ ### Signal Utilities
669
+
670
+ - `debouncedSignal<T>(source: Signal<T>, delay?: number | Signal<number>): Signal<T>` - Mirrors a signal, delaying each change; the delay can itself be a signal
587
671
 
588
672
  ### DOM Functions
589
673
 
@@ -591,6 +675,10 @@ These functions back the i18n system and are exported for direct use:
591
675
  - `reflow(element: HTMLElement): DOMRect` - Forces browser reflow
592
676
  - `getActiveElement(root?: Document | ShadowRoot): Element | null` - Gets active element including Shadow DOM
593
677
 
678
+ ### Accent Resolution
679
+
680
+ - `resolveHubAccent(value: string | null | undefined): string | null` - The "any colour" accent resolver shared across the family: a bareword becomes `var(--hub-sys-color-<name>, <name>)`, a literal `#hex` / `rgb()` / `oklch()` / `var()` passes through unchanged, and an empty value yields `null`
681
+
594
682
  ### Colour Functions
595
683
 
596
684
  - `parseColor(value): HubRgb | null` - Parses hex (3/4/6/8), `rgb()`, `hsl()`, `oklch()`, `oklab()`, the 148 CSS named colours and `transparent`, in modern and legacy syntax. No DOM, so it runs under SSR. Returns `null` — never throws — for anything it cannot resolve, `var()` and `currentColor` included
@@ -621,6 +709,27 @@ These functions back the i18n system and are exported for direct use:
621
709
  - `hubFocusTrap(zone, element, stopFocusTrap$, refocusOnClick?)` - Creates focus trap for modals/overlays
622
710
  - `FOCUSABLE_ELEMENTS_SELECTOR: string` - CSS selector for focusable elements
623
711
 
712
+ ### Drag and Drop
713
+
714
+ The engine-agnostic half of native HTML5 drag and drop, shared by the libraries that
715
+ implement it. The UI primitives — handle, placeholder and preview directives — stay in each
716
+ library, because their selectors and data models differ.
717
+
718
+ - `HubDragDropService` - Root-provided coordinator. A drag spans two component instances and the native `dataTransfer` payload is unreadable during `dragover`, so a shared service is the only reliable channel for what is being dragged and from where. Owners `register()` / `unregister()`; `begin()`, `setTarget()` and the readonly `active` / `target` / `isDragging` signals report the drag in progress. It coordinates state only — it never mutates your collections
719
+ - `moveItemInArray<T>(array, fromIndex, toIndex): void` / `transferArrayItem<T>(source, target, fromIndex, toIndex): void` / `copyArrayItem<T>(source, target, fromIndex, toIndex): void` - In-place array moves, mirroring the `@angular/cdk` helpers of the same names
720
+ - `clamp(value, max)`, `computeTargetIndex(...)`, `toAbsoluteIndex(...)`, `containsNode(...)` - Index arithmetic for sliced and nested lists
721
+ - `resolveDropPosition(...)` with `DropRect` and `DragAxis` - Where a pointer sits relative to an item: `'before'` or `'after'`, on a vertical, horizontal or grid axis
722
+ - `createNativeDragImage(...)` returning `DragImageResult` - Renders the drag preview the browser shows
723
+ - `createPointerDragSession(config: PointerDragSessionConfig): PointerDragSession` - Pointer Events fallback for touch, where native drag events are not delivered
724
+ - Types: `DropPosition`, `DragPointerMode`, `DragContainerRef<T>`, `ActiveDrag<T>`, `DragTarget<T>`, `DragRegistration`
725
+
726
+ ### Directives
727
+
728
+ - `HubTooltipDirective` (`[hubTooltip]`) - Tooltip on hover/focus. Inputs: `hubTooltip`, `hubTooltipPlacement`, `hubTooltipDelay`, `hubTooltipOffset`
729
+ - `HubOverflowTooltipDirective` (`[hubOverflowTooltip]`) - Tooltip shown only while the label is truncated. Inputs: `hubOverflowTooltip`, `placement`, `hubOverflowTooltipMeasure` (CSS selector, resolved inside the host, naming the box whose truncation decides it; defaults to the host)
730
+ - `TooltipDirective` (`[tooltip]`) - **Deprecated since 22.9.0**, kept working. Inputs: `tooltip`, `placement`, `delay`, `offset`
731
+ - `provideHubTooltip(adapter: HubTooltipAdapter)` and `HUB_TOOLTIP_ADAPTER` - Swap the implementation behind `[hubOverflowTooltip]`, app-wide or per subtree; defaults to `hubTooltipAdapter`
732
+
624
733
  ### Pipes
625
734
 
626
735
  #### GetPipe
@@ -681,18 +790,39 @@ class OverlayService {
681
790
  }
682
791
 
683
792
  class OverlayRef {
684
- attach<T>(component: ComponentType<T>): ComponentRef<T>;
793
+ // Renders a template or a component into the overlay and returns the host element,
794
+ // not a ComponentRef: the overlay owns the view it created and tears it down itself.
795
+ attach(content: TemplateRef<unknown> | Type<unknown>, viewContainerRef?: ViewContainerRef): HTMLElement;
685
796
  detach(): void;
686
797
  dispose(): void;
798
+ hasAttached(): boolean;
687
799
  updatePosition(): void;
800
+ onBackdropClick(callback: () => void): void;
801
+ // Only the topmost open overlay is told, so a dropdown inside a dialog takes Escape
802
+ // for itself and leaves the dialog open.
803
+ onKeydown(callback: (event: KeyboardEvent) => void): void;
688
804
  }
689
805
 
690
806
  class OverlayPosition {
691
- flexibleConnectedTo(element: ElementRef | HTMLElement): this;
807
+ // The element the panel is anchored to. The overlay watches it and follows it when it moves.
808
+ readonly origin: HTMLElement | null;
809
+ flexibleConnectedTo(origin: ElementRef | HTMLElement): this;
692
810
  withPositions(positions: ConnectionPosition[]): this;
811
+ // `start` / `end` are logical and read from the origin element; this overrides that.
812
+ withDirection(direction: 'ltr' | 'rtl' | null): this;
693
813
  }
694
814
  ```
695
815
 
816
+ `HUB_DROPDOWN_POSITIONS` is the ready-made fallback chain for a dropdown — below the
817
+ origin, flipping above when there is no room — expressed logically so one list serves
818
+ both text directions:
819
+
820
+ ```typescript
821
+ import { HUB_DROPDOWN_POSITIONS } from 'ng-hub-ui-utils';
822
+
823
+ overlayService.position().flexibleConnectedTo(origin).withPositions([...HUB_DROPDOWN_POSITIONS]);
824
+ ```
825
+
696
826
  #### ScrollBar Service
697
827
 
698
828
  ```typescript
@@ -702,14 +832,26 @@ class ScrollBar {
702
832
  }
703
833
  ```
704
834
 
705
- #### PopupService<T> (Base Class)
835
+ #### PopupService&lt;T&gt;
836
+
837
+ A concrete generic class, not an abstract one: it takes the popup component type in its
838
+ constructor, and it reads its collaborators with `inject()`, so it has to be created inside
839
+ an injection context — as an `@Injectable()` subclass, or from a factory provider.
706
840
 
707
841
  ```typescript
708
- abstract class PopupService<T> {
709
- // Base system for creating dynamic popups
710
- // Extend this class to create specific popup services
711
- open(content?, templateContext?, animation?): { windowRef: ComponentRef<T>; transition$: Observable<void> };
712
- close(animation?): Observable<void>;
842
+ class PopupService<T> {
843
+ constructor(componentType: Type<T>);
844
+ open(
845
+ content?: string | TemplateRef<any>,
846
+ templateContext?: any,
847
+ animation?: boolean
848
+ ): { windowRef: ComponentRef<T>; transition$: Observable<void> };
849
+ close(animation?: boolean): Observable<void>;
850
+ }
851
+
852
+ // The nodes and view a popup projects, returned internally by the content resolver.
853
+ class ContentRef {
854
+ constructor(nodes: Node[][], viewRef?: ViewRef, componentRef?: ComponentRef<any>);
713
855
  }
714
856
  ```
715
857
 
@@ -729,7 +871,7 @@ This library doesn't include visual components, but support utilities used by ot
729
871
  | Overlay Service | Flexible overlay positioning system | ng-hub-ui-modal, ng-hub-ui-portal |
730
872
  | Focus Trap | Focus management in modals/overlays | ng-hub-ui-modal, ng-hub-ui-portal |
731
873
  | Scrollbar | Scrollbar compensation | ng-hub-ui-modal, ng-hub-ui-portal |
732
- | Popup Service | Base class for popup components | ng-hub-ui-modal, ng-hub-ui-portal |
874
+ | Popup Service | Host for dynamically created popups | ng-hub-ui-modal, ng-hub-ui-portal |
733
875
  | Transitions | Smooth animations | ng-hub-ui-accordion, ng-hub-ui-modal |
734
876
  | Type Guards | Type validation functions | ng-hub-ui-stepper |
735
877
  | Pipes | Template utilities | All Hub UI components |