@filip.mazev/blocks 1.4.9 → 1.4.11

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.
Files changed (37) hide show
  1. package/README.md +15 -3
  2. package/core/styles/_index.scss +8 -0
  3. package/core/styles/_overlay.scss +4 -0
  4. package/core/styles/_palettes.scss +56 -0
  5. package/core/styles/_theme-engine.scss +180 -149
  6. package/core/styles/themes/_azure-coral-theme.scss +4 -0
  7. package/core/styles/themes/_blue-theme.scss +5 -0
  8. package/core/styles/themes/_cyberpunk-theme.scss +3 -2
  9. package/core/styles/themes/_graphite-sky-blue-theme.scss +19 -0
  10. package/core/styles/themes/_green-theme.scss +4 -3
  11. package/core/styles/themes/_high-contrast-theme.scss +25 -6
  12. package/core/styles/themes/_lime-violet-theme.scss +4 -0
  13. package/core/styles/themes/_orange-theme.scss +4 -3
  14. package/core/styles/themes/_purple-theme.scss +4 -3
  15. package/core/styles/themes/_red-theme.scss +4 -3
  16. package/core/styles/themes/_scarlet-cyan-theme.scss +4 -0
  17. package/core/styles/themes/_tangerine-indigo-theme.scss +4 -0
  18. package/core/styles/themes/_violet-teal-theme.scss +4 -0
  19. package/core/styles/themes/_white-theme.scss +4 -27
  20. package/fesm2022/filip.mazev-blocks-core.mjs +142 -32
  21. package/fesm2022/filip.mazev-blocks-core.mjs.map +1 -1
  22. package/fesm2022/filip.mazev-blocks-effects-runtime.mjs +1 -1
  23. package/fesm2022/filip.mazev-blocks-effects-runtime.mjs.map +1 -1
  24. package/fesm2022/filip.mazev-blocks-forms.mjs +25 -39
  25. package/fesm2022/filip.mazev-blocks-forms.mjs.map +1 -1
  26. package/fesm2022/filip.mazev-blocks-primitives.mjs +1246 -650
  27. package/fesm2022/filip.mazev-blocks-primitives.mjs.map +1 -1
  28. package/fesm2022/filip.mazev-blocks.mjs +1 -1
  29. package/fesm2022/filip.mazev-blocks.mjs.map +1 -1
  30. package/package.json +1 -1
  31. package/schematics/ng-add/index.d.ts +10 -1
  32. package/schematics/ng-add/index.js +15 -4
  33. package/schematics/ng-add/schema.json +26 -15
  34. package/types/filip.mazev-blocks-core.d.ts +20 -7
  35. package/types/filip.mazev-blocks-forms.d.ts +2 -3
  36. package/types/filip.mazev-blocks-primitives.d.ts +142 -9
  37. package/types/filip.mazev-blocks.d.ts +1 -1
@@ -1,12 +1,13 @@
1
1
  import { BxSurfaceDirective } from "@filip.mazev/blocks/effects";
2
2
  import * as i0 from "@angular/core";
3
- import { ChangeDetectionStrategy, Component, DestroyRef, Directive, ElementRef, HostListener, InjectionToken, NgZone, PLATFORM_ID, ViewContainerRef, booleanAttribute, computed, effect, forwardRef, inject, input, output, signal } from "@angular/core";
4
- import { BX_I18N, BxA11yService, BxOverlayPositionService, BxShortcutService, inheritOverlayTheme, isTextWithKey, uuidv4 } from "@filip.mazev/blocks/core";
3
+ import { ChangeDetectionStrategy, Component, DestroyRef, Directive, ElementRef, HostListener, InjectionToken, NgZone, PLATFORM_ID, TemplateRef, ViewContainerRef, afterNextRender, afterRenderEffect, booleanAttribute, computed, contentChild, contentChildren, effect, forwardRef, inject, input, model, output, signal, viewChild, viewChildren } from "@angular/core";
4
+ import { BX_I18N, BxA11yService, BxOverlayPositionService, BxShortcutService, inheritOverlayTheme, isTextWithKey, resolveTokenToCssVar, trackOverlayOrigin, uuidv4 } from "@filip.mazev/blocks/core";
5
5
  import { Icon } from "@filip.mazev/blocks/icons";
6
+ import { NgTemplateOutlet, isPlatformBrowser } from "@angular/common";
7
+ import { AriaDescriber, FocusMonitor } from "@angular/cdk/a11y";
6
8
  import { Overlay } from "@angular/cdk/overlay";
7
9
  import { ComponentPortal, TemplatePortal } from "@angular/cdk/portal";
8
10
  import { createGlassTransition, inheritGlassLighting } from "@filip.mazev/blocks/effects/runtime";
9
- import { isPlatformBrowser } from "@angular/common";
10
11
  import { Subscription } from "rxjs";
11
12
  var Tooltip = class Tooltip {
12
13
  i18n = inject(BX_I18N, { optional: true });
@@ -43,7 +44,7 @@ var Tooltip = class Tooltip {
43
44
  } },
44
45
  ngImport: i0,
45
46
  template: "<div class=\"bx-tooltip-surface\" bxSurface [class.bx-tooltip-animated]=\"isAnimated()\">\n @if (resolvedText()) {\n {{ resolvedText() }}\n }\n</div>",
46
- styles: [":host{display:block;pointer-events:none;z-index:600}.bx-tooltip-surface[data-bx-surface]{position:relative;padding:.25rem .5rem;border:1px solid var(--bx-glass-edge);border-radius:var(--bx-glass-radius, var(--bx-rounded-sm, .25rem));font-size:.75rem;font-family:inherit;max-width:min(22rem,100vw - 1.5rem);white-space:normal;overflow-wrap:anywhere;--bx-glass-unified-lighting: 1;--bx-glass-surface: var(--bx-bg-neutral);--bx-glass-edge: var(--bx-border-default);--bx-glass-text: var(--bx-text-primary);--bx-glass-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);--bx-glass-min-opacity: 1;--bx-glass-decoration-opacity: 0;--bx-glass-rim-visibility: .35;-webkit-backdrop-filter:none;backdrop-filter:none}.bx-tooltip-surface[data-bx-surface]:after{content:\"\";position:absolute;z-index:2;width:.5rem;height:.5rem;box-sizing:border-box;border-radius:0;background:var(--bx-glass-surface);border:0 solid var(--bx-tooltip-arrow-edge, var(--bx-glass-edge));transform:translate(-50%,-50%) rotate(45deg);pointer-events:none}@media(prefers-contrast:more){.bx-tooltip-surface[data-bx-surface]{--bx-glass-surface: var(--bx-bg-surface);--bx-glass-edge: currentcolor}.bx-tooltip-surface[data-bx-surface]:after{border-color:currentcolor}}:host-context(.bx-overlay-pos-bottom) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-top, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-bottom) .bx-tooltip-surface:after{top:-1px;left:clamp(.75rem,var(--bx-tooltip-arrow-x, 50%),100% - .75rem);border-top-width:1px;border-left-width:1px}:host-context(.bx-overlay-pos-top) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-bottom, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-top) .bx-tooltip-surface:after{top:calc(100% + 1px);left:clamp(.75rem,var(--bx-tooltip-arrow-x, 50%),100% - .75rem);border-right-width:1px;border-bottom-width:1px}:host-context(.bx-overlay-pos-left) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-right, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-left) .bx-tooltip-surface:after{left:calc(100% + 1px);top:clamp(.5rem,var(--bx-tooltip-arrow-y, 50%),100% - .5rem);border-top-width:1px;border-right-width:1px}:host-context(.bx-overlay-pos-right) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-left, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-right) .bx-tooltip-surface:after{left:-1px;top:clamp(.5rem,var(--bx-tooltip-arrow-y, 50%),100% - .5rem);border-bottom-width:1px;border-left-width:1px}@media(forced-colors:active){.bx-tooltip-surface[data-bx-surface]:after{background:Canvas;border-color:CanvasText}}\n"],
47
+ styles: [":host{display:block;pointer-events:none;z-index:600}.bx-tooltip-surface[data-bx-surface]{--bx-glass-radius: var(--bx-rounded-sm, .25rem);position:relative;padding:.25rem .5rem;border:1px solid var(--bx-glass-edge);border-radius:var(--bx-glass-radius, var(--bx-rounded-sm, .25rem));font-size:.75rem;font-family:inherit;max-width:min(22rem,100vw - 1.5rem);white-space:normal;overflow-wrap:anywhere;--bx-glass-unified-lighting: 1;--bx-glass-surface: var(--bx-bg-neutral);--bx-glass-edge: var(--bx-border-default);--bx-glass-text: var(--bx-text-primary);--bx-glass-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);--bx-glass-min-opacity: 1;--bx-glass-decoration-opacity: 0;--bx-glass-rim-visibility: .35;-webkit-backdrop-filter:none;backdrop-filter:none}.bx-tooltip-surface[data-bx-surface]:after{content:\"\";position:absolute;z-index:2;width:.5rem;height:.5rem;box-sizing:border-box;border-radius:0;background:var(--bx-glass-surface);border:0 solid var(--bx-tooltip-arrow-edge, var(--bx-glass-edge));transform:translate(-50%,-50%) rotate(45deg);pointer-events:none}@media(prefers-contrast:more){.bx-tooltip-surface[data-bx-surface]{--bx-glass-surface: var(--bx-bg-surface);--bx-glass-edge: currentcolor}.bx-tooltip-surface[data-bx-surface]:after{border-color:currentcolor}}:host-context(.bx-overlay-pos-bottom) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-top, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-bottom) .bx-tooltip-surface:after{top:-1px;left:clamp(.75rem,var(--bx-tooltip-arrow-x, 50%),100% - .75rem);border-top-width:1px;border-left-width:1px}:host-context(.bx-overlay-pos-top) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-bottom, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-top) .bx-tooltip-surface:after{top:calc(100% + 1px);left:clamp(.75rem,var(--bx-tooltip-arrow-x, 50%),100% - .75rem);border-right-width:1px;border-bottom-width:1px}:host-context(.bx-overlay-pos-left) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-right, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-left) .bx-tooltip-surface:after{left:calc(100% + 1px);top:clamp(.5rem,var(--bx-tooltip-arrow-y, 50%),100% - .5rem);border-top-width:1px;border-right-width:1px}:host-context(.bx-overlay-pos-right) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-left, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-right) .bx-tooltip-surface:after{left:-1px;top:clamp(.5rem,var(--bx-tooltip-arrow-y, 50%),100% - .5rem);border-bottom-width:1px;border-left-width:1px}@media(forced-colors:active){.bx-tooltip-surface[data-bx-surface]:after{background:Canvas;border-color:CanvasText}}\n"],
47
48
  dependencies: [{
48
49
  kind: "directive",
49
50
  type: BxSurfaceDirective,
@@ -72,7 +73,7 @@ i0.ɵɵngDeclareClassMetadata({
72
73
  imports: [BxSurfaceDirective],
73
74
  changeDetection: ChangeDetectionStrategy.OnPush,
74
75
  template: "<div class=\"bx-tooltip-surface\" bxSurface [class.bx-tooltip-animated]=\"isAnimated()\">\n @if (resolvedText()) {\n {{ resolvedText() }}\n }\n</div>",
75
- styles: [":host{display:block;pointer-events:none;z-index:600}.bx-tooltip-surface[data-bx-surface]{position:relative;padding:.25rem .5rem;border:1px solid var(--bx-glass-edge);border-radius:var(--bx-glass-radius, var(--bx-rounded-sm, .25rem));font-size:.75rem;font-family:inherit;max-width:min(22rem,100vw - 1.5rem);white-space:normal;overflow-wrap:anywhere;--bx-glass-unified-lighting: 1;--bx-glass-surface: var(--bx-bg-neutral);--bx-glass-edge: var(--bx-border-default);--bx-glass-text: var(--bx-text-primary);--bx-glass-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);--bx-glass-min-opacity: 1;--bx-glass-decoration-opacity: 0;--bx-glass-rim-visibility: .35;-webkit-backdrop-filter:none;backdrop-filter:none}.bx-tooltip-surface[data-bx-surface]:after{content:\"\";position:absolute;z-index:2;width:.5rem;height:.5rem;box-sizing:border-box;border-radius:0;background:var(--bx-glass-surface);border:0 solid var(--bx-tooltip-arrow-edge, var(--bx-glass-edge));transform:translate(-50%,-50%) rotate(45deg);pointer-events:none}@media(prefers-contrast:more){.bx-tooltip-surface[data-bx-surface]{--bx-glass-surface: var(--bx-bg-surface);--bx-glass-edge: currentcolor}.bx-tooltip-surface[data-bx-surface]:after{border-color:currentcolor}}:host-context(.bx-overlay-pos-bottom) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-top, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-bottom) .bx-tooltip-surface:after{top:-1px;left:clamp(.75rem,var(--bx-tooltip-arrow-x, 50%),100% - .75rem);border-top-width:1px;border-left-width:1px}:host-context(.bx-overlay-pos-top) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-bottom, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-top) .bx-tooltip-surface:after{top:calc(100% + 1px);left:clamp(.75rem,var(--bx-tooltip-arrow-x, 50%),100% - .75rem);border-right-width:1px;border-bottom-width:1px}:host-context(.bx-overlay-pos-left) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-right, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-left) .bx-tooltip-surface:after{left:calc(100% + 1px);top:clamp(.5rem,var(--bx-tooltip-arrow-y, 50%),100% - .5rem);border-top-width:1px;border-right-width:1px}:host-context(.bx-overlay-pos-right) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-left, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-right) .bx-tooltip-surface:after{left:-1px;top:clamp(.5rem,var(--bx-tooltip-arrow-y, 50%),100% - .5rem);border-bottom-width:1px;border-left-width:1px}@media(forced-colors:active){.bx-tooltip-surface[data-bx-surface]:after{background:Canvas;border-color:CanvasText}}\n"]
76
+ styles: [":host{display:block;pointer-events:none;z-index:600}.bx-tooltip-surface[data-bx-surface]{--bx-glass-radius: var(--bx-rounded-sm, .25rem);position:relative;padding:.25rem .5rem;border:1px solid var(--bx-glass-edge);border-radius:var(--bx-glass-radius, var(--bx-rounded-sm, .25rem));font-size:.75rem;font-family:inherit;max-width:min(22rem,100vw - 1.5rem);white-space:normal;overflow-wrap:anywhere;--bx-glass-unified-lighting: 1;--bx-glass-surface: var(--bx-bg-neutral);--bx-glass-edge: var(--bx-border-default);--bx-glass-text: var(--bx-text-primary);--bx-glass-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);--bx-glass-min-opacity: 1;--bx-glass-decoration-opacity: 0;--bx-glass-rim-visibility: .35;-webkit-backdrop-filter:none;backdrop-filter:none}.bx-tooltip-surface[data-bx-surface]:after{content:\"\";position:absolute;z-index:2;width:.5rem;height:.5rem;box-sizing:border-box;border-radius:0;background:var(--bx-glass-surface);border:0 solid var(--bx-tooltip-arrow-edge, var(--bx-glass-edge));transform:translate(-50%,-50%) rotate(45deg);pointer-events:none}@media(prefers-contrast:more){.bx-tooltip-surface[data-bx-surface]{--bx-glass-surface: var(--bx-bg-surface);--bx-glass-edge: currentcolor}.bx-tooltip-surface[data-bx-surface]:after{border-color:currentcolor}}:host-context(.bx-overlay-pos-bottom) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-top, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-bottom) .bx-tooltip-surface:after{top:-1px;left:clamp(.75rem,var(--bx-tooltip-arrow-x, 50%),100% - .75rem);border-top-width:1px;border-left-width:1px}:host-context(.bx-overlay-pos-top) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-bottom, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-top) .bx-tooltip-surface:after{top:calc(100% + 1px);left:clamp(.75rem,var(--bx-tooltip-arrow-x, 50%),100% - .75rem);border-right-width:1px;border-bottom-width:1px}:host-context(.bx-overlay-pos-left) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-right, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-left) .bx-tooltip-surface:after{left:calc(100% + 1px);top:clamp(.5rem,var(--bx-tooltip-arrow-y, 50%),100% - .5rem);border-top-width:1px;border-right-width:1px}:host-context(.bx-overlay-pos-right) .bx-tooltip-surface{--bx-tooltip-arrow-edge: var(--bx-glass-lit-edge-left, var(--bx-glass-edge))}:host-context(.bx-overlay-pos-right) .bx-tooltip-surface:after{left:-1px;top:clamp(.5rem,var(--bx-tooltip-arrow-y, 50%),100% - .5rem);border-bottom-width:1px;border-left-width:1px}@media(forced-colors:active){.bx-tooltip-surface[data-bx-surface]:after{background:Canvas;border-color:CanvasText}}\n"]
76
77
  }]
77
78
  }],
78
79
  propDecorators: { text: [{
@@ -104,21 +105,8 @@ var Menu = class Menu {
104
105
  isStandalone: true,
105
106
  selector: "bx-menu",
106
107
  ngImport: i0,
107
- template: "<div bxSurface role=\"menu\" class=\"bx-menu-wrapper\" [class.bx-menu-animated]=\"isAnimated()\">\n <ng-content></ng-content>\n</div>\n",
108
- styles: [":host{display:block}.bx-menu-wrapper{--bx-glass-surface: var(--bx-bg-surface);--bx-glass-text: var(--bx-text-primary);--bx-glass-edge: var(--bx-border-default);--bx-glass-min-opacity: 0;--bx-glass-decoration-opacity: 1;--bx-glass-rim-visibility: 1;--bx-glass-unified-lighting: 0;--bx-glass-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);background-color:var(--bx-glass-surface);border:1px solid var(--bx-glass-edge);border-radius:var(--bx-glass-radius, var(--bx-rounded-lg, .75rem));box-shadow:0 2px 4px #0000001a,0 1px 2px -1px #0000001a;padding:.25rem;display:flex;flex-direction:column;min-width:12rem}.bx-menu-wrapper.bx-menu-animated{transition:transform .2s cubic-bezier(.25,.8,.25,1)}.bx-menu-wrapper{transform-origin:top}.bx-menu-wrapper.is-hidden{opacity:0;transform:scaleY(.95);pointer-events:none}:host([data-bx-surface]){border-radius:var(--bx-glass-radius, var(--bx-rounded-lg, .75rem));--bx-glass-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);color:var(--bx-glass-text, var(--bx-text-primary))}:host([data-bx-surface]) .bx-menu-wrapper{background:transparent;border-color:transparent;border-radius:inherit;box-shadow:none}\n"],
109
- dependencies: [{
110
- kind: "directive",
111
- type: BxSurfaceDirective,
112
- selector: "[bxSurface]",
113
- inputs: [
114
- "bxSurfaceInteractive",
115
- "bxSurfaceGlassiness",
116
- "bxSurfaceEmission",
117
- "bxSurfaceFocusEmission",
118
- "bxReducedTransparency"
119
- ],
120
- exportAs: ["bxSurface"]
121
- }],
108
+ template: "<div data-bx-surface=\"solid\" role=\"menu\" class=\"bx-menu-wrapper\" [class.bx-menu-animated]=\"isAnimated()\">\n <ng-content></ng-content>\n</div>\n",
109
+ styles: [":host{display:block}.bx-menu-wrapper{--bx-glass-surface: var(--bx-bg-surface);--bx-glass-text: var(--bx-text-primary);--bx-glass-edge: var(--bx-border-default);--bx-glass-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);--bx-glass-radius: var(--bx-rounded-lg, .75rem);background-color:var(--bx-glass-surface);border:1px solid var(--bx-glass-edge);border-radius:var(--bx-glass-radius, var(--bx-rounded-lg, .75rem));box-shadow:0 2px 4px #0000001a,0 1px 2px -1px #0000001a;padding:.25rem;display:flex;flex-direction:column;min-width:12rem}.bx-menu-wrapper[data-bx-surface=solid]{background:var(--bx-glass-surface)!important;-webkit-backdrop-filter:none;backdrop-filter:none}.bx-menu-wrapper.bx-menu-animated{transition:transform .2s cubic-bezier(.25,.8,.25,1)}.bx-menu-wrapper{transform-origin:top}.bx-menu-wrapper.is-hidden{opacity:0;transform:scaleY(.95);pointer-events:none}:host([data-bx-surface]){border-radius:var(--bx-glass-radius, var(--bx-rounded-lg, .75rem));--bx-glass-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);color:var(--bx-glass-text, var(--bx-text-primary))}:host([data-bx-surface]) .bx-menu-wrapper{background:transparent;border-color:transparent;border-radius:inherit;box-shadow:none}\n"],
122
110
  changeDetection: i0.ChangeDetectionStrategy.OnPush
123
111
  });
124
112
  };
@@ -131,10 +119,9 @@ i0.ɵɵngDeclareClassMetadata({
131
119
  type: Component,
132
120
  args: [{
133
121
  selector: "bx-menu",
134
- imports: [BxSurfaceDirective],
135
122
  changeDetection: ChangeDetectionStrategy.OnPush,
136
- template: "<div bxSurface role=\"menu\" class=\"bx-menu-wrapper\" [class.bx-menu-animated]=\"isAnimated()\">\n <ng-content></ng-content>\n</div>\n",
137
- styles: [":host{display:block}.bx-menu-wrapper{--bx-glass-surface: var(--bx-bg-surface);--bx-glass-text: var(--bx-text-primary);--bx-glass-edge: var(--bx-border-default);--bx-glass-min-opacity: 0;--bx-glass-decoration-opacity: 1;--bx-glass-rim-visibility: 1;--bx-glass-unified-lighting: 0;--bx-glass-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);background-color:var(--bx-glass-surface);border:1px solid var(--bx-glass-edge);border-radius:var(--bx-glass-radius, var(--bx-rounded-lg, .75rem));box-shadow:0 2px 4px #0000001a,0 1px 2px -1px #0000001a;padding:.25rem;display:flex;flex-direction:column;min-width:12rem}.bx-menu-wrapper.bx-menu-animated{transition:transform .2s cubic-bezier(.25,.8,.25,1)}.bx-menu-wrapper{transform-origin:top}.bx-menu-wrapper.is-hidden{opacity:0;transform:scaleY(.95);pointer-events:none}:host([data-bx-surface]){border-radius:var(--bx-glass-radius, var(--bx-rounded-lg, .75rem));--bx-glass-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);color:var(--bx-glass-text, var(--bx-text-primary))}:host([data-bx-surface]) .bx-menu-wrapper{background:transparent;border-color:transparent;border-radius:inherit;box-shadow:none}\n"]
123
+ template: "<div data-bx-surface=\"solid\" role=\"menu\" class=\"bx-menu-wrapper\" [class.bx-menu-animated]=\"isAnimated()\">\n <ng-content></ng-content>\n</div>\n",
124
+ styles: [":host{display:block}.bx-menu-wrapper{--bx-glass-surface: var(--bx-bg-surface);--bx-glass-text: var(--bx-text-primary);--bx-glass-edge: var(--bx-border-default);--bx-glass-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);--bx-glass-radius: var(--bx-rounded-lg, .75rem);background-color:var(--bx-glass-surface);border:1px solid var(--bx-glass-edge);border-radius:var(--bx-glass-radius, var(--bx-rounded-lg, .75rem));box-shadow:0 2px 4px #0000001a,0 1px 2px -1px #0000001a;padding:.25rem;display:flex;flex-direction:column;min-width:12rem}.bx-menu-wrapper[data-bx-surface=solid]{background:var(--bx-glass-surface)!important;-webkit-backdrop-filter:none;backdrop-filter:none}.bx-menu-wrapper.bx-menu-animated{transition:transform .2s cubic-bezier(.25,.8,.25,1)}.bx-menu-wrapper{transform-origin:top}.bx-menu-wrapper.is-hidden{opacity:0;transform:scaleY(.95);pointer-events:none}:host([data-bx-surface]){border-radius:var(--bx-glass-radius, var(--bx-rounded-lg, .75rem));--bx-glass-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);color:var(--bx-glass-text, var(--bx-text-primary))}:host([data-bx-surface]) .bx-menu-wrapper{background:transparent;border-color:transparent;border-radius:inherit;box-shadow:none}\n"]
138
125
  }]
139
126
  }]
140
127
  });
@@ -345,432 +332,1108 @@ i0.ɵɵngDeclareClassMetadata({
345
332
  }]
346
333
  }
347
334
  });
348
- var TooltipDirective = class TooltipDirective {
349
- elementRef = inject(ElementRef);
350
- overlay = inject(Overlay);
351
- positionBuilder = inject(BxOverlayPositionService);
352
- a11y = inject(BxA11yService);
353
- zone = inject(NgZone);
354
- id = `bx-tooltip-${uuidv4()}`;
355
- bxTooltip = input.required(...ngDevMode ? [{ debugName: "bxTooltip" }] : /* istanbul ignore next */ []);
356
- placement = input("bottom", ...ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []);
357
- overlayRef = null;
358
- transition;
359
- disconnectTheme;
360
- disconnectLighting;
361
- positionSubscription;
362
- keyboardSubscription;
363
- revision = 0;
364
- hovered = false;
365
- focused = false;
366
- constructor() {
367
- inject(DestroyRef).onDestroy(() => this.dispose());
368
- }
369
- show() {
370
- if (!this.bxTooltip()) return;
371
- const revision = ++this.revision;
372
- if (!this.overlayRef) {
373
- const strategy = this.positionBuilder.getElementStrategy(this.elementRef.nativeElement, this.placement()).withFlexibleDimensions(false);
374
- this.overlayRef = this.overlay.create({
375
- positionStrategy: strategy,
376
- scrollStrategy: this.overlay.scrollStrategies.reposition(),
377
- hasBackdrop: false,
378
- panelClass: "bx-tooltip-pane"
379
- });
380
- const pane = this.overlayRef.overlayElement;
381
- pane.id = this.id;
382
- pane.setAttribute("role", "tooltip");
383
- this.disconnectTheme = this.zone.runOutsideAngular(() => inheritOverlayTheme(this.elementRef.nativeElement, pane));
384
- this.disconnectLighting = inheritGlassLighting(pane, this.elementRef.nativeElement);
385
- const component = this.overlayRef.attach(new ComponentPortal(Tooltip));
386
- pane.style.pointerEvents = "none";
387
- this.keyboardSubscription = this.overlayRef.keydownEvents().subscribe((event) => this.onEscape(event));
388
- component.setInput("text", this.bxTooltip());
389
- component.changeDetectorRef.detectChanges();
390
- const positionArrow = () => {
391
- const trigger = this.elementRef.nativeElement.getBoundingClientRect();
392
- const bounds = pane.getBoundingClientRect();
393
- pane.style.setProperty("--bx-tooltip-arrow-x", `${trigger.left + trigger.width / 2 - bounds.left}px`);
394
- pane.style.setProperty("--bx-tooltip-arrow-y", `${trigger.top + trigger.height / 2 - bounds.top}px`);
395
- this.transition?.refresh();
396
- };
397
- this.positionSubscription = strategy.positionChanges.subscribe(positionArrow);
398
- this.overlayRef.updatePosition();
399
- positionArrow();
400
- if (!this.a11y.isReducedMotion()) this.transition = this.zone.runOutsideAngular(() => createGlassTransition(this.elementRef.nativeElement, pane));
401
- }
402
- const source = this.elementRef.nativeElement;
403
- const ids = new Set((source.getAttribute("aria-describedby") ?? "").split(/\s+/).filter(Boolean));
404
- ids.add(this.id);
405
- source.setAttribute("aria-describedby", [...ids].join(" "));
406
- Promise.resolve(this.zone.runOutsideAngular(() => this.transition?.enter())).catch(() => {
407
- if (revision === this.revision) {
408
- this.transition?.destroy();
409
- this.transition = void 0;
410
- }
411
- });
412
- }
413
- hide() {
414
- const revision = ++this.revision;
415
- this.removeDescription();
416
- if (!this.overlayRef) return;
417
- const transition = this.transition;
418
- if (transition && !this.a11y.isReducedMotion()) this.zone.runOutsideAngular(() => transition.exit()).then(() => {
419
- if (revision === this.revision) this.dispose();
420
- }).catch(() => {
421
- if (revision === this.revision) this.dispose();
422
- });
423
- else this.dispose();
424
- }
425
- onEnter() {
426
- this.hovered = true;
427
- this.show();
428
- }
429
- onLeave() {
430
- this.hovered = false;
431
- if (!this.focused) this.hide();
432
- }
433
- onFocus() {
434
- this.focused = true;
435
- this.show();
436
- }
437
- onBlur(event) {
438
- if (event.relatedTarget instanceof Node && this.elementRef.nativeElement.contains(event.relatedTarget)) return;
439
- this.focused = false;
440
- if (!this.hovered) this.hide();
441
- }
442
- onEscape(event) {
443
- if (event.key === "Escape" && this.overlayRef) {
444
- event.stopPropagation();
445
- this.hovered = false;
446
- this.focused = false;
447
- this.hide();
448
- }
449
- }
450
- removeDescription() {
451
- const source = this.elementRef.nativeElement;
452
- const ids = (source.getAttribute("aria-describedby") ?? "").split(/\s+/).filter((id) => id && id !== this.id);
453
- if (ids.length) source.setAttribute("aria-describedby", ids.join(" "));
454
- else source.removeAttribute("aria-describedby");
455
- }
456
- dispose() {
457
- this.removeDescription();
458
- this.keyboardSubscription?.unsubscribe();
459
- this.keyboardSubscription = void 0;
460
- this.positionSubscription?.unsubscribe();
461
- this.positionSubscription = void 0;
462
- this.disconnectTheme?.();
463
- this.disconnectTheme = void 0;
464
- this.transition?.destroy();
465
- this.transition = void 0;
466
- this.overlayRef?.dispose();
467
- this.overlayRef = null;
468
- this.disconnectLighting?.();
469
- this.disconnectLighting = void 0;
470
- }
335
+ const DEFAULT_TABS_VARIANT = "segmented";
336
+ const DEFAULT_TABS_LABEL = "Tabs";
337
+ const TAB_NAVIGATION_KEYS = /* @__PURE__ */ new Set([
338
+ "ArrowLeft",
339
+ "ArrowRight",
340
+ "Home",
341
+ "End"
342
+ ]);
343
+ const DEFAULT_TAB_COLOR = "primary";
344
+ const TAB_EMISSION_OPACITY = .15;
345
+ const TAB_COLOR_TOKENS = {
346
+ primary: "primary",
347
+ accent: "accent",
348
+ success: "text-success",
349
+ danger: "text-danger",
350
+ info: "text-info",
351
+ warn: "text-warn",
352
+ grayscale: "text-primary"
353
+ };
354
+ function resolveTabText(text, adapter) {
355
+ adapter?.version?.();
356
+ return isTextWithKey(text) ? adapter?.translate(text.key) ?? text.key : text;
357
+ }
358
+ function resolveTabColor(color) {
359
+ return color ? resolveTokenToCssVar(color) : null;
360
+ }
361
+ var TabContentDirective = class TabContentDirective {
362
+ template = inject(TemplateRef);
471
363
  static ɵfac = i0.ɵɵngDeclareFactory({
472
364
  minVersion: "12.0.0",
473
365
  version: "22.2.3",
474
366
  ngImport: i0,
475
- type: TooltipDirective,
367
+ type: TabContentDirective,
476
368
  deps: [],
477
369
  target: i0.ɵɵFactoryTarget.Directive
478
370
  });
479
371
  static ɵdir = i0.ɵɵngDeclareDirective({
480
- minVersion: "17.1.0",
372
+ minVersion: "14.0.0",
481
373
  version: "22.2.3",
482
- type: TooltipDirective,
374
+ type: TabContentDirective,
483
375
  isStandalone: true,
484
- selector: "[bxTooltip]",
376
+ selector: "ng-template[bxTabContent]",
377
+ ngImport: i0
378
+ });
379
+ };
380
+ i0.ɵɵngDeclareClassMetadata({
381
+ minVersion: "12.0.0",
382
+ version: "22.2.3",
383
+ ngImport: i0,
384
+ type: TabContentDirective,
385
+ decorators: [{
386
+ type: Directive,
387
+ args: [{
388
+ selector: "ng-template[bxTabContent]",
389
+ standalone: true
390
+ }]
391
+ }]
392
+ });
393
+ var Tab = class Tab {
394
+ id = uuidv4();
395
+ i18n = inject(BX_I18N, { optional: true });
396
+ projectedContent = viewChild("content", ...ngDevMode ? [{ debugName: "projectedContent" }] : /* istanbul ignore next */ []);
397
+ lazyContent = contentChild(TabContentDirective, ...ngDevMode ? [{ debugName: "lazyContent" }] : /* istanbul ignore next */ []);
398
+ label = input.required(...ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []);
399
+ value = input(this.id, ...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
400
+ disabled = input(false, {
401
+ ...ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {},
402
+ transform: booleanAttribute
403
+ });
404
+ color = input(DEFAULT_TAB_COLOR, ...ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []);
405
+ textColor = input(...ngDevMode ? [void 0, { debugName: "textColor" }] : /* istanbul ignore next */ []);
406
+ highlightColor = input(...ngDevMode ? [void 0, { debugName: "highlightColor" }] : /* istanbul ignore next */ []);
407
+ contentColor = input(...ngDevMode ? [void 0, { debugName: "contentColor" }] : /* istanbul ignore next */ []);
408
+ icon = input(...ngDevMode ? [void 0, { debugName: "icon" }] : /* istanbul ignore next */ []);
409
+ endIcon = input(...ngDevMode ? [void 0, { debugName: "endIcon" }] : /* istanbul ignore next */ []);
410
+ triggerId = `bx-tab-${this.id}`;
411
+ panelId = `bx-tab-panel-${this.id}`;
412
+ resolvedLabel = computed(() => resolveTabText(this.label(), this.i18n), ...ngDevMode ? [{ debugName: "resolvedLabel" }] : /* istanbul ignore next */ []);
413
+ resolvedHighlight = computed(() => resolveTabColor(this.highlightColor() ?? TAB_COLOR_TOKENS[this.color()]), ...ngDevMode ? [{ debugName: "resolvedHighlight" }] : /* istanbul ignore next */ []);
414
+ resolvedEmission = computed(() => `color-mix(in srgb, ${this.resolvedHighlight()} ${TAB_EMISSION_OPACITY * 100}%, transparent)`, ...ngDevMode ? [{ debugName: "resolvedEmission" }] : /* istanbul ignore next */ []);
415
+ resolvedTextColor = computed(() => resolveTabColor(this.textColor()), ...ngDevMode ? [{ debugName: "resolvedTextColor" }] : /* istanbul ignore next */ []);
416
+ resolvedSelectedText = computed(() => this.resolvedTextColor() ?? resolveTabColor(this.color() === "primary" ? "text-brand" : this.color() === "accent" ? "text-accent" : TAB_COLOR_TOKENS[this.color()]), ...ngDevMode ? [{ debugName: "resolvedSelectedText" }] : /* istanbul ignore next */ []);
417
+ resolvedContentColor = computed(() => resolveTabColor(this.contentColor()), ...ngDevMode ? [{ debugName: "resolvedContentColor" }] : /* istanbul ignore next */ []);
418
+ template = computed(() => this.lazyContent()?.template ?? this.projectedContent(), ...ngDevMode ? [{ debugName: "template" }] : /* istanbul ignore next */ []);
419
+ static ɵfac = i0.ɵɵngDeclareFactory({
420
+ minVersion: "12.0.0",
421
+ version: "22.2.3",
422
+ ngImport: i0,
423
+ type: Tab,
424
+ deps: [],
425
+ target: i0.ɵɵFactoryTarget.Component
426
+ });
427
+ static ɵcmp = i0.ɵɵngDeclareComponent({
428
+ minVersion: "17.2.0",
429
+ version: "22.2.3",
430
+ type: Tab,
431
+ isStandalone: true,
432
+ selector: "bx-tab",
485
433
  inputs: {
486
- bxTooltip: {
487
- classPropertyName: "bxTooltip",
488
- publicName: "bxTooltip",
434
+ label: {
435
+ classPropertyName: "label",
436
+ publicName: "label",
489
437
  isSignal: true,
490
438
  isRequired: true,
491
439
  transformFunction: null
492
440
  },
493
- placement: {
494
- classPropertyName: "placement",
495
- publicName: "placement",
441
+ value: {
442
+ classPropertyName: "value",
443
+ publicName: "value",
444
+ isSignal: true,
445
+ isRequired: false,
446
+ transformFunction: null
447
+ },
448
+ disabled: {
449
+ classPropertyName: "disabled",
450
+ publicName: "disabled",
451
+ isSignal: true,
452
+ isRequired: false,
453
+ transformFunction: null
454
+ },
455
+ color: {
456
+ classPropertyName: "color",
457
+ publicName: "color",
458
+ isSignal: true,
459
+ isRequired: false,
460
+ transformFunction: null
461
+ },
462
+ textColor: {
463
+ classPropertyName: "textColor",
464
+ publicName: "textColor",
465
+ isSignal: true,
466
+ isRequired: false,
467
+ transformFunction: null
468
+ },
469
+ highlightColor: {
470
+ classPropertyName: "highlightColor",
471
+ publicName: "highlightColor",
472
+ isSignal: true,
473
+ isRequired: false,
474
+ transformFunction: null
475
+ },
476
+ contentColor: {
477
+ classPropertyName: "contentColor",
478
+ publicName: "contentColor",
479
+ isSignal: true,
480
+ isRequired: false,
481
+ transformFunction: null
482
+ },
483
+ icon: {
484
+ classPropertyName: "icon",
485
+ publicName: "icon",
486
+ isSignal: true,
487
+ isRequired: false,
488
+ transformFunction: null
489
+ },
490
+ endIcon: {
491
+ classPropertyName: "endIcon",
492
+ publicName: "endIcon",
496
493
  isSignal: true,
497
494
  isRequired: false,
498
495
  transformFunction: null
499
496
  }
500
497
  },
501
- host: { listeners: {
502
- "mouseenter": "onEnter()",
503
- "mouseleave": "onLeave()",
504
- "focusin": "onFocus()",
505
- "focusout": "onBlur($event)"
506
- } },
507
- ngImport: i0
498
+ queries: [{
499
+ propertyName: "lazyContent",
500
+ first: true,
501
+ predicate: TabContentDirective,
502
+ descendants: true,
503
+ isSignal: true
504
+ }],
505
+ viewQueries: [{
506
+ propertyName: "projectedContent",
507
+ first: true,
508
+ predicate: ["content"],
509
+ descendants: true,
510
+ isSignal: true
511
+ }],
512
+ ngImport: i0,
513
+ template: "<ng-template #content><ng-content></ng-content></ng-template>\n",
514
+ styles: [":host{display:none}\n"],
515
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
508
516
  });
509
517
  };
510
518
  i0.ɵɵngDeclareClassMetadata({
511
519
  minVersion: "12.0.0",
512
520
  version: "22.2.3",
513
521
  ngImport: i0,
514
- type: TooltipDirective,
522
+ type: Tab,
515
523
  decorators: [{
516
- type: Directive,
517
- args: [{ selector: "[bxTooltip]" }]
524
+ type: Component,
525
+ args: [{
526
+ selector: "bx-tab",
527
+ standalone: true,
528
+ changeDetection: ChangeDetectionStrategy.OnPush,
529
+ template: "<ng-template #content><ng-content></ng-content></ng-template>\n",
530
+ styles: [":host{display:none}\n"]
531
+ }]
518
532
  }],
519
- ctorParameters: () => [],
520
533
  propDecorators: {
521
- bxTooltip: [{
534
+ projectedContent: [{
535
+ type: i0.ViewChild,
536
+ args: ["content", { isSignal: true }]
537
+ }],
538
+ lazyContent: [{
539
+ type: i0.ContentChild,
540
+ args: [i0.forwardRef(() => TabContentDirective), { isSignal: true }]
541
+ }],
542
+ label: [{
522
543
  type: i0.Input,
523
544
  args: [{
524
545
  isSignal: true,
525
- alias: "bxTooltip",
546
+ alias: "label",
526
547
  required: true
527
548
  }]
528
549
  }],
529
- placement: [{
550
+ value: [{
530
551
  type: i0.Input,
531
552
  args: [{
532
553
  isSignal: true,
533
- alias: "placement",
554
+ alias: "value",
534
555
  required: false
535
556
  }]
536
557
  }],
537
- onEnter: [{
538
- type: HostListener,
539
- args: ["mouseenter"]
558
+ disabled: [{
559
+ type: i0.Input,
560
+ args: [{
561
+ isSignal: true,
562
+ alias: "disabled",
563
+ required: false
564
+ }]
540
565
  }],
541
- onLeave: [{
542
- type: HostListener,
543
- args: ["mouseleave"]
566
+ color: [{
567
+ type: i0.Input,
568
+ args: [{
569
+ isSignal: true,
570
+ alias: "color",
571
+ required: false
572
+ }]
544
573
  }],
545
- onFocus: [{
546
- type: HostListener,
547
- args: ["focusin"]
574
+ textColor: [{
575
+ type: i0.Input,
576
+ args: [{
577
+ isSignal: true,
578
+ alias: "textColor",
579
+ required: false
580
+ }]
548
581
  }],
549
- onBlur: [{
550
- type: HostListener,
551
- args: ["focusout", ["$event"]]
552
- }]
553
- }
554
- });
555
- const BX_OVERLAY_CONTEXT = new InjectionToken("BX_OVERLAY_CONTEXT");
556
- var DropdownDirective = class DropdownDirective {
557
- elementRef = inject(ElementRef);
558
- overlay = inject(Overlay);
559
- positionBuilder = inject(BxOverlayPositionService);
560
- overlayContext = inject(BX_OVERLAY_CONTEXT, { optional: true });
561
- a11y = inject(BxA11yService);
562
- zone = inject(NgZone);
582
+ highlightColor: [{
583
+ type: i0.Input,
584
+ args: [{
585
+ isSignal: true,
586
+ alias: "highlightColor",
587
+ required: false
588
+ }]
589
+ }],
590
+ contentColor: [{
591
+ type: i0.Input,
592
+ args: [{
593
+ isSignal: true,
594
+ alias: "contentColor",
595
+ required: false
596
+ }]
597
+ }],
598
+ icon: [{
599
+ type: i0.Input,
600
+ args: [{
601
+ isSignal: true,
602
+ alias: "icon",
603
+ required: false
604
+ }]
605
+ }],
606
+ endIcon: [{
607
+ type: i0.Input,
608
+ args: [{
609
+ isSignal: true,
610
+ alias: "endIcon",
611
+ required: false
612
+ }]
613
+ }]
614
+ }
615
+ });
616
+ var Tabs = class Tabs {
617
+ i18n = inject(BX_I18N, { optional: true });
618
+ triggers = viewChildren("trigger", ...ngDevMode ? [{ debugName: "triggers" }] : /* istanbul ignore next */ []);
619
+ a11y = inject(BxA11yService);
620
+ hasRendered = signal(false, ...ngDevMode ? [{ debugName: "hasRendered" }] : /* istanbul ignore next */ []);
621
+ tabs = contentChildren(Tab, ...ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []);
622
+ activeTab = computed(() => {
623
+ const enabled = this.tabs().filter((tab) => !tab.disabled());
624
+ return enabled.find((tab) => tab.value() === this.selectedValue()) ?? enabled[0];
625
+ }, ...ngDevMode ? [{ debugName: "activeTab" }] : /* istanbul ignore next */ []);
626
+ resolvedLabel = computed(() => resolveTabText(this.ariaLabel(), this.i18n), ...ngDevMode ? [{ debugName: "resolvedLabel" }] : /* istanbul ignore next */ []);
627
+ variant = input(DEFAULT_TABS_VARIANT, ...ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []);
628
+ ariaLabel = input(DEFAULT_TABS_LABEL, ...ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []);
629
+ selectedValue = model(null, ...ngDevMode ? [{ debugName: "selectedValue" }] : /* istanbul ignore next */ []);
630
+ constructor() {
631
+ afterNextRender(() => this.hasRendered.set(true));
632
+ afterRenderEffect(() => {
633
+ const value = this.activeTab()?.value() ?? null;
634
+ if (this.selectedValue() !== value) this.selectedValue.set(value);
635
+ });
636
+ }
637
+ select(value) {
638
+ if (this.tabs().find((item) => item.value() === value && !item.disabled())) this.selectedValue.set(value);
639
+ }
640
+ onTabKey(event, tab) {
641
+ if (event.altKey || event.ctrlKey || event.metaKey || !TAB_NAVIGATION_KEYS.has(event.key)) return;
642
+ const enabled = this.tabs().filter((item) => !item.disabled());
643
+ const current = enabled.indexOf(tab);
644
+ if (current < 0) return;
645
+ event.preventDefault();
646
+ event.stopPropagation();
647
+ const button = event.currentTarget;
648
+ const rtl = button.ownerDocument.defaultView?.getComputedStyle(button).direction === "rtl";
649
+ const direction = (event.key === "ArrowRight" ? 1 : -1) * (rtl ? -1 : 1);
650
+ const next = enabled[event.key === "Home" ? 0 : event.key === "End" ? enabled.length - 1 : (current + direction + enabled.length) % enabled.length];
651
+ this.select(next.value());
652
+ const trigger = this.triggers().find((item) => item.nativeElement.id === next.triggerId)?.nativeElement;
653
+ trigger?.focus({ preventScroll: true });
654
+ trigger?.scrollIntoView({
655
+ block: "nearest",
656
+ inline: "nearest"
657
+ });
658
+ }
659
+ static ɵfac = i0.ɵɵngDeclareFactory({
660
+ minVersion: "12.0.0",
661
+ version: "22.2.3",
662
+ ngImport: i0,
663
+ type: Tabs,
664
+ deps: [],
665
+ target: i0.ɵɵFactoryTarget.Component
666
+ });
667
+ static ɵcmp = i0.ɵɵngDeclareComponent({
668
+ minVersion: "17.0.0",
669
+ version: "22.2.3",
670
+ type: Tabs,
671
+ isStandalone: true,
672
+ selector: "bx-tabs",
673
+ inputs: {
674
+ variant: {
675
+ classPropertyName: "variant",
676
+ publicName: "variant",
677
+ isSignal: true,
678
+ isRequired: false,
679
+ transformFunction: null
680
+ },
681
+ ariaLabel: {
682
+ classPropertyName: "ariaLabel",
683
+ publicName: "ariaLabel",
684
+ isSignal: true,
685
+ isRequired: false,
686
+ transformFunction: null
687
+ },
688
+ selectedValue: {
689
+ classPropertyName: "selectedValue",
690
+ publicName: "selectedValue",
691
+ isSignal: true,
692
+ isRequired: false,
693
+ transformFunction: null
694
+ }
695
+ },
696
+ outputs: { selectedValue: "selectedValueChange" },
697
+ host: { properties: {
698
+ "class.bx-tabs-underline": "variant() === \"underline\"",
699
+ "class.bx-tabs-animated": "hasRendered() && !a11y.isReducedMotion()"
700
+ } },
701
+ queries: [{
702
+ propertyName: "tabs",
703
+ predicate: Tab,
704
+ isSignal: true
705
+ }],
706
+ viewQueries: [{
707
+ propertyName: "triggers",
708
+ predicate: ["trigger"],
709
+ descendants: true,
710
+ isSignal: true
711
+ }],
712
+ ngImport: i0,
713
+ template: "<ng-content select=\"bx-tab\"></ng-content>\n\n<div bxSurface data-bx-surface=\"glass\" class=\"bx-tab-header\" role=\"tablist\" [attr.aria-label]=\"resolvedLabel()\" aria-orientation=\"horizontal\">\n<div class=\"bx-tab-list\">\n @for (tab of tabs(); track tab) {\n <button #trigger bxSurface data-bx-surface=\"glass\" class=\"bx-tab-trigger\" type=\"button\" role=\"tab\"\n [bxSurfaceEmission]=\"tab.resolvedEmission()\"\n [style.--bx-tab-highlight]=\"tab.resolvedHighlight()\" [style.--bx-tab-text]=\"tab.resolvedTextColor()\"\n [style.--bx-tab-selected-text]=\"tab.resolvedSelectedText()\"\n [id]=\"tab.triggerId\" [attr.aria-controls]=\"tab.panelId\" [attr.aria-selected]=\"tab === activeTab()\"\n [attr.tabindex]=\"tab === activeTab() ? 0 : -1\" [disabled]=\"tab.disabled()\"\n [class.is-selected]=\"tab === activeTab()\" (click)=\"select(tab.value())\" (keydown)=\"onTabKey($event, tab)\">\n @if (tab.icon()) { <bx-icon [name]=\"tab.icon()!\" size=\"16\" /> }\n <span>{{ tab.resolvedLabel() }}</span>\n @if (tab.endIcon()) { <bx-icon [name]=\"tab.endIcon()!\" size=\"16\" /> }\n </button>\n }\n</div>\n</div>\n\n<div class=\"bx-tab-panels\">\n @for (tab of tabs(); track tab) {\n <div class=\"bx-tab-panel\" role=\"tabpanel\" tabindex=\"0\" [id]=\"tab.panelId\"\n [style.color]=\"tab.resolvedContentColor()\"\n [attr.aria-labelledby]=\"tab.triggerId\" [hidden]=\"tab !== activeTab()\">\n @if (tab === activeTab()) {\n <ng-container [ngTemplateOutlet]=\"tab.template()\"></ng-container>\n }\n </div>\n }\n</div>\n",
714
+ styles: [":host{display:flex;flex-direction:column;min-width:0;min-height:0;box-sizing:border-box;width:100%;max-width:100%;gap:var(--bx-tabs-panel-gap, 1.5rem)}.bx-tab-header{--bx-glass-surface: var(--bx-bg-surface-alt);--bx-glass-edge: var(--bx-border-default);--bx-glass-shadow: none;--bx-glass-opacity: calc(.1 + .25 * var(--bx-glassiness, .5));--bx-glass-radius: .5rem;display:flex;align-self:flex-start;flex:none;box-sizing:border-box;width:fit-content;max-width:100%;padding:.25rem;border:1px solid var(--bx-border-default);border-radius:.5rem}.bx-tab-list{display:flex;flex:1 1 auto;gap:.25rem;min-width:0;padding:1px;overflow:auto hidden;scrollbar-width:thin}.bx-tab-trigger{--bx-glass-surface: var(--bx-bg-surface);--bx-glass-text: var(--bx-tab-text, var(--bx-text-secondary));--bx-glass-edge: transparent;--bx-glass-radius: .25rem;--bx-glass-opacity: 0;--bx-glass-decoration-opacity: 0;--bx-glass-rim-visibility: 0;--bx-glass-shadow: none;display:inline-flex;align-items:center;gap:.5rem;flex:none;padding:.5rem .75rem;border:1px solid transparent;border-radius:.25rem;font:inherit;font-size:.875rem;white-space:nowrap;cursor:pointer}.bx-tab-trigger:hover:not(:disabled,.is-selected){--bx-glass-text: var(--bx-tab-text, var(--bx-text-primary));--bx-glass-opacity: .15}.bx-tab-trigger.is-selected{--bx-glass-surface: color-mix( in srgb, var(--bx-tab-highlight, var(--bx-primary)) 35%, var(--bx-bg-surface) );--bx-glass-text: var(--bx-tab-selected-text, var(--bx-text-brand));--bx-glass-edge: color-mix( in srgb, var(--bx-tab-highlight, var(--bx-primary)) 35%, transparent );--bx-glass-opacity: .45;--bx-glass-decoration-opacity: .5;--bx-glass-rim-visibility: .65;--bx-glass-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1)}.bx-tab-trigger:disabled{opacity:.4;cursor:not-allowed}.bx-tab-trigger:focus-visible{outline:2px solid var(--bx-tab-highlight, var(--bx-border-brand));outline-offset:-2px}.bx-tab-panels{flex:1 1 auto;min-width:0;min-height:0;max-height:var(--bx-tabs-max-panel-height, none);overflow:auto;overscroll-behavior:contain}.bx-tab-panel{min-width:0}.bx-tab-panel:focus-visible{outline:2px solid var(--bx-border-brand);outline-offset:-2px}:host(.bx-tabs-animated) .bx-tab-trigger{transition:background-color .1s ease-in-out,color .1s ease-in-out,box-shadow .1s ease-in-out}:host(.bx-tabs-underline){gap:var(--bx-tabs-panel-gap, 0)}:host(.bx-tabs-underline) .bx-tab-header{align-self:stretch;width:auto;padding:0;border:0;border-bottom:1px solid var(--bx-border-default);border-radius:0}:host(.bx-tabs-underline) .bx-tab-list{gap:0}:host(.bx-tabs-underline) .bx-tab-trigger{padding:.75rem 1rem;border-inline-end:1px solid var(--bx-border-default);border-radius:0;font-size:.8rem}:host(.bx-tabs-underline) .bx-tab-trigger.is-selected{--bx-glass-shadow: inset 0 -2px 0 var(--bx-tab-highlight, var(--bx-border-brand))}@media(forced-colors:active){.bx-tab-trigger.is-selected{outline:2px solid Highlight;outline-offset:-2px}}\n"],
715
+ dependencies: [
716
+ {
717
+ kind: "directive",
718
+ type: NgTemplateOutlet,
719
+ selector: "[ngTemplateOutlet]",
720
+ inputs: [
721
+ "ngTemplateOutletContext",
722
+ "ngTemplateOutlet",
723
+ "ngTemplateOutletInjector"
724
+ ]
725
+ },
726
+ {
727
+ kind: "directive",
728
+ type: BxSurfaceDirective,
729
+ selector: "[bxSurface]",
730
+ inputs: [
731
+ "bxSurfaceInteractive",
732
+ "bxSurfaceGlassiness",
733
+ "bxSurfaceEmission",
734
+ "bxSurfaceFocusEmission",
735
+ "bxReducedTransparency"
736
+ ],
737
+ exportAs: ["bxSurface"]
738
+ },
739
+ {
740
+ kind: "component",
741
+ type: Icon,
742
+ selector: "bx-icon",
743
+ inputs: [
744
+ "name",
745
+ "size",
746
+ "color",
747
+ "bgColor",
748
+ "strokeWidth",
749
+ "decorative",
750
+ "ariaLabel"
751
+ ]
752
+ }
753
+ ],
754
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
755
+ });
756
+ };
757
+ i0.ɵɵngDeclareClassMetadata({
758
+ minVersion: "12.0.0",
759
+ version: "22.2.3",
760
+ ngImport: i0,
761
+ type: Tabs,
762
+ decorators: [{
763
+ type: Component,
764
+ args: [{
765
+ selector: "bx-tabs",
766
+ standalone: true,
767
+ imports: [
768
+ NgTemplateOutlet,
769
+ BxSurfaceDirective,
770
+ Icon
771
+ ],
772
+ changeDetection: ChangeDetectionStrategy.OnPush,
773
+ host: {
774
+ "[class.bx-tabs-underline]": "variant() === \"underline\"",
775
+ "[class.bx-tabs-animated]": "hasRendered() && !a11y.isReducedMotion()"
776
+ },
777
+ template: "<ng-content select=\"bx-tab\"></ng-content>\n\n<div bxSurface data-bx-surface=\"glass\" class=\"bx-tab-header\" role=\"tablist\" [attr.aria-label]=\"resolvedLabel()\" aria-orientation=\"horizontal\">\n<div class=\"bx-tab-list\">\n @for (tab of tabs(); track tab) {\n <button #trigger bxSurface data-bx-surface=\"glass\" class=\"bx-tab-trigger\" type=\"button\" role=\"tab\"\n [bxSurfaceEmission]=\"tab.resolvedEmission()\"\n [style.--bx-tab-highlight]=\"tab.resolvedHighlight()\" [style.--bx-tab-text]=\"tab.resolvedTextColor()\"\n [style.--bx-tab-selected-text]=\"tab.resolvedSelectedText()\"\n [id]=\"tab.triggerId\" [attr.aria-controls]=\"tab.panelId\" [attr.aria-selected]=\"tab === activeTab()\"\n [attr.tabindex]=\"tab === activeTab() ? 0 : -1\" [disabled]=\"tab.disabled()\"\n [class.is-selected]=\"tab === activeTab()\" (click)=\"select(tab.value())\" (keydown)=\"onTabKey($event, tab)\">\n @if (tab.icon()) { <bx-icon [name]=\"tab.icon()!\" size=\"16\" /> }\n <span>{{ tab.resolvedLabel() }}</span>\n @if (tab.endIcon()) { <bx-icon [name]=\"tab.endIcon()!\" size=\"16\" /> }\n </button>\n }\n</div>\n</div>\n\n<div class=\"bx-tab-panels\">\n @for (tab of tabs(); track tab) {\n <div class=\"bx-tab-panel\" role=\"tabpanel\" tabindex=\"0\" [id]=\"tab.panelId\"\n [style.color]=\"tab.resolvedContentColor()\"\n [attr.aria-labelledby]=\"tab.triggerId\" [hidden]=\"tab !== activeTab()\">\n @if (tab === activeTab()) {\n <ng-container [ngTemplateOutlet]=\"tab.template()\"></ng-container>\n }\n </div>\n }\n</div>\n",
778
+ styles: [":host{display:flex;flex-direction:column;min-width:0;min-height:0;box-sizing:border-box;width:100%;max-width:100%;gap:var(--bx-tabs-panel-gap, 1.5rem)}.bx-tab-header{--bx-glass-surface: var(--bx-bg-surface-alt);--bx-glass-edge: var(--bx-border-default);--bx-glass-shadow: none;--bx-glass-opacity: calc(.1 + .25 * var(--bx-glassiness, .5));--bx-glass-radius: .5rem;display:flex;align-self:flex-start;flex:none;box-sizing:border-box;width:fit-content;max-width:100%;padding:.25rem;border:1px solid var(--bx-border-default);border-radius:.5rem}.bx-tab-list{display:flex;flex:1 1 auto;gap:.25rem;min-width:0;padding:1px;overflow:auto hidden;scrollbar-width:thin}.bx-tab-trigger{--bx-glass-surface: var(--bx-bg-surface);--bx-glass-text: var(--bx-tab-text, var(--bx-text-secondary));--bx-glass-edge: transparent;--bx-glass-radius: .25rem;--bx-glass-opacity: 0;--bx-glass-decoration-opacity: 0;--bx-glass-rim-visibility: 0;--bx-glass-shadow: none;display:inline-flex;align-items:center;gap:.5rem;flex:none;padding:.5rem .75rem;border:1px solid transparent;border-radius:.25rem;font:inherit;font-size:.875rem;white-space:nowrap;cursor:pointer}.bx-tab-trigger:hover:not(:disabled,.is-selected){--bx-glass-text: var(--bx-tab-text, var(--bx-text-primary));--bx-glass-opacity: .15}.bx-tab-trigger.is-selected{--bx-glass-surface: color-mix( in srgb, var(--bx-tab-highlight, var(--bx-primary)) 35%, var(--bx-bg-surface) );--bx-glass-text: var(--bx-tab-selected-text, var(--bx-text-brand));--bx-glass-edge: color-mix( in srgb, var(--bx-tab-highlight, var(--bx-primary)) 35%, transparent );--bx-glass-opacity: .45;--bx-glass-decoration-opacity: .5;--bx-glass-rim-visibility: .65;--bx-glass-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1)}.bx-tab-trigger:disabled{opacity:.4;cursor:not-allowed}.bx-tab-trigger:focus-visible{outline:2px solid var(--bx-tab-highlight, var(--bx-border-brand));outline-offset:-2px}.bx-tab-panels{flex:1 1 auto;min-width:0;min-height:0;max-height:var(--bx-tabs-max-panel-height, none);overflow:auto;overscroll-behavior:contain}.bx-tab-panel{min-width:0}.bx-tab-panel:focus-visible{outline:2px solid var(--bx-border-brand);outline-offset:-2px}:host(.bx-tabs-animated) .bx-tab-trigger{transition:background-color .1s ease-in-out,color .1s ease-in-out,box-shadow .1s ease-in-out}:host(.bx-tabs-underline){gap:var(--bx-tabs-panel-gap, 0)}:host(.bx-tabs-underline) .bx-tab-header{align-self:stretch;width:auto;padding:0;border:0;border-bottom:1px solid var(--bx-border-default);border-radius:0}:host(.bx-tabs-underline) .bx-tab-list{gap:0}:host(.bx-tabs-underline) .bx-tab-trigger{padding:.75rem 1rem;border-inline-end:1px solid var(--bx-border-default);border-radius:0;font-size:.8rem}:host(.bx-tabs-underline) .bx-tab-trigger.is-selected{--bx-glass-shadow: inset 0 -2px 0 var(--bx-tab-highlight, var(--bx-border-brand))}@media(forced-colors:active){.bx-tab-trigger.is-selected{outline:2px solid Highlight;outline-offset:-2px}}\n"]
779
+ }]
780
+ }],
781
+ ctorParameters: () => [],
782
+ propDecorators: {
783
+ triggers: [{
784
+ type: i0.ViewChildren,
785
+ args: ["trigger", { isSignal: true }]
786
+ }],
787
+ tabs: [{
788
+ type: i0.ContentChildren,
789
+ args: [i0.forwardRef(() => Tab), { isSignal: true }]
790
+ }],
791
+ variant: [{
792
+ type: i0.Input,
793
+ args: [{
794
+ isSignal: true,
795
+ alias: "variant",
796
+ required: false
797
+ }]
798
+ }],
799
+ ariaLabel: [{
800
+ type: i0.Input,
801
+ args: [{
802
+ isSignal: true,
803
+ alias: "ariaLabel",
804
+ required: false
805
+ }]
806
+ }],
807
+ selectedValue: [{
808
+ type: i0.Input,
809
+ args: [{
810
+ isSignal: true,
811
+ alias: "selectedValue",
812
+ required: false
813
+ }]
814
+ }, {
815
+ type: i0.Output,
816
+ args: ["selectedValueChange"]
817
+ }]
818
+ }
819
+ });
820
+ const BX_OVERLAY_CONTEXT = new InjectionToken("BX_OVERLAY_CONTEXT");
821
+ var ContextMenuDirective = class ContextMenuDirective {
822
+ overlay = inject(Overlay);
823
+ positionBuilder = inject(BxOverlayPositionService);
824
+ overlayContext = inject(BX_OVERLAY_CONTEXT, { optional: true });
825
+ elementRef = inject(ElementRef);
826
+ a11y = inject(BxA11yService);
827
+ zone = inject(NgZone);
563
828
  vcr = inject(ViewContainerRef);
564
- isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
565
- _isOpen = signal(false, ...ngDevMode ? [{ debugName: "_isOpen" }] : /* istanbul ignore next */ []);
566
- overlayId = signal(null, ...ngDevMode ? [{ debugName: "overlayId" }] : /* istanbul ignore next */ []);
567
- isOpen = this._isOpen.asReadonly();
568
- _state = signal("closed", ...ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []);
569
- state = this._state.asReadonly();
570
- bxDropdown = input.required(...ngDevMode ? [{ debugName: "bxDropdown" }] : /* istanbul ignore next */ []);
829
+ bxContextMenu = input.required(...ngDevMode ? [{ debugName: "bxContextMenu" }] : /* istanbul ignore next */ []);
830
+ bxContextMenuLongPress = input(true, {
831
+ ...ngDevMode ? { debugName: "bxContextMenuLongPress" } : /* istanbul ignore next */ {},
832
+ transform: booleanAttribute
833
+ });
834
+ onPointerDown = (event) => {
835
+ this.lastPointerType = event.pointerType;
836
+ this.cancelTouchPress();
837
+ if (event.isPrimary) this.clearTouchGuard();
838
+ if (event.pointerType !== "touch" || !event.isPrimary || event.button !== 0 || !this.bxContextMenuLongPress() || this.hasNativeTouchAction(event)) return;
839
+ const doc = this.elementRef.nativeElement.ownerDocument;
840
+ const win = doc.defaultView;
841
+ if (!win || this.hasTextSelection()) return;
842
+ const press = {
843
+ pointerId: event.pointerId,
844
+ x: event.clientX,
845
+ y: event.clientY,
846
+ timer: 0,
847
+ opened: false,
848
+ events: new AbortController()
849
+ };
850
+ this.touchPress = press;
851
+ const options = {
852
+ capture: true,
853
+ passive: true,
854
+ signal: press.events.signal
855
+ };
856
+ const finish = () => this.cancelTouchPress();
857
+ doc.addEventListener("pointermove", (move) => {
858
+ if (move.pointerId === press.pointerId && !press.opened && Math.hypot(move.clientX - press.x, move.clientY - press.y) > 10) finish();
859
+ }, options);
860
+ for (const type of ["pointerup", "pointercancel"]) doc.addEventListener(type, (end) => {
861
+ if (end.pointerId === press.pointerId) finish();
862
+ }, options);
863
+ doc.addEventListener("pointerdown", (next) => {
864
+ if (next.pointerId !== press.pointerId) finish();
865
+ }, options);
866
+ doc.addEventListener("scroll", finish, options);
867
+ doc.addEventListener("visibilitychange", () => {
868
+ if (doc.hidden) finish();
869
+ }, options);
870
+ win.addEventListener("blur", finish, { signal: press.events.signal });
871
+ press.timer = win.setTimeout(() => this.zone.run(() => this.openTouchMenu(press)), 500);
872
+ };
873
+ overlayRef = null;
874
+ transition;
875
+ point;
876
+ disconnectTheme;
877
+ disconnectLighting;
878
+ subscriptions = new Subscription();
879
+ revision = 0;
880
+ touchPress;
881
+ touchGuard;
882
+ touchGuardTimer;
883
+ lastPointerType = "";
884
+ constructor() {
885
+ const source = this.elementRef.nativeElement;
886
+ if (source.ownerDocument.defaultView) this.zone.runOutsideAngular(() => source.addEventListener("pointerdown", this.onPointerDown, { passive: true }));
887
+ inject(DestroyRef).onDestroy(() => {
888
+ ++this.revision;
889
+ source.removeEventListener("pointerdown", this.onPointerDown);
890
+ this.cancelTouchPress();
891
+ this.clearTouchGuard();
892
+ this.dispose();
893
+ });
894
+ }
895
+ open(event) {
896
+ if (event.type === "contextmenu" && this.lastPointerType === "touch") {
897
+ const press = this.touchPress;
898
+ if (!press) return;
899
+ if (!this.bxContextMenuLongPress() || this.hasNativeTouchAction(event) || this.hasTextSelection()) {
900
+ this.cancelTouchPress();
901
+ return;
902
+ }
903
+ event.preventDefault();
904
+ this.openTouchMenu(press);
905
+ return;
906
+ }
907
+ event.preventDefault();
908
+ this.cancelTouchPress();
909
+ this.clearTouchGuard();
910
+ this.openAt(event.clientX, event.clientY);
911
+ }
912
+ close(restoreFocus = false) {
913
+ if (!this.overlayRef) return;
914
+ const revision = ++this.revision;
915
+ if (restoreFocus || this.overlayRef.overlayElement.contains(this.elementRef.nativeElement.ownerDocument.activeElement)) this.elementRef.nativeElement.focus({ preventScroll: true });
916
+ this.overlayRef.overlayElement.inert = true;
917
+ const transition = this.transition;
918
+ if (transition && !this.a11y.isReducedMotion()) this.zone.runOutsideAngular(() => transition.exit()).then(() => {
919
+ if (revision === this.revision) this.dispose();
920
+ }).catch(() => {
921
+ if (revision === this.revision) this.dispose();
922
+ });
923
+ else this.dispose();
924
+ }
925
+ onKey(event) {
926
+ if (event.key !== "ContextMenu" && !(event.shiftKey && event.key === "F10")) return;
927
+ event.preventDefault();
928
+ this.lastPointerType = "";
929
+ this.cancelTouchPress();
930
+ this.clearTouchGuard();
931
+ const rect = this.elementRef.nativeElement.getBoundingClientRect();
932
+ this.openAt(rect.left + Math.min(24, rect.width / 2), rect.bottom);
933
+ this.items()[0]?.focus();
934
+ }
935
+ onDocumentClick(event) {
936
+ const target = event.target;
937
+ if (target && this.overlayRef?.overlayElement.contains(target) && target.closest("button, a, [role=\"menuitem\"]")) this.close();
938
+ }
939
+ hasNativeTouchAction(event) {
940
+ const source = this.elementRef.nativeElement;
941
+ for (const node of event.composedPath()) if (node instanceof HTMLElement) {
942
+ if (node.isContentEditable || node.matches("input, textarea, select, :disabled, [aria-disabled=\"true\"], [inert]")) return true;
943
+ if (node === source) break;
944
+ }
945
+ return !!source.closest(":disabled, [aria-disabled=\"true\"], [inert]");
946
+ }
947
+ hasTextSelection() {
948
+ const source = this.elementRef.nativeElement;
949
+ const selection = source.ownerDocument.getSelection();
950
+ return !!selection && !selection.isCollapsed && (source.contains(selection.anchorNode) || source.contains(selection.focusNode));
951
+ }
952
+ openTouchMenu(press) {
953
+ if (this.touchPress !== press || press.opened) return;
954
+ const source = this.elementRef.nativeElement;
955
+ if (!this.bxContextMenuLongPress() || !source.isConnected || source.isContentEditable || source.closest(":disabled, [aria-disabled=\"true\"], [inert]") || this.hasTextSelection()) {
956
+ this.cancelTouchPress();
957
+ return;
958
+ }
959
+ press.opened = true;
960
+ this.elementRef.nativeElement.ownerDocument.defaultView?.clearTimeout(press.timer);
961
+ this.guardTouchRelease();
962
+ this.openAt(press.x, press.y + 12);
963
+ }
964
+ cancelTouchPress() {
965
+ const press = this.touchPress;
966
+ if (!press) return;
967
+ this.touchPress = void 0;
968
+ const win = this.elementRef.nativeElement.ownerDocument.defaultView;
969
+ win?.clearTimeout(press.timer);
970
+ press.events.abort();
971
+ if (press.opened && win) this.touchGuardTimer = win.setTimeout(() => this.clearTouchGuard(), 800);
972
+ }
973
+ guardTouchRelease() {
974
+ this.clearTouchGuard();
975
+ const doc = this.elementRef.nativeElement.ownerDocument;
976
+ const guard = new AbortController();
977
+ this.touchGuard = guard;
978
+ const options = {
979
+ capture: true,
980
+ signal: guard.signal
981
+ };
982
+ doc.addEventListener("click", (event) => {
983
+ if (event.detail === 0) return;
984
+ event.preventDefault();
985
+ event.stopImmediatePropagation();
986
+ this.clearTouchGuard();
987
+ }, options);
988
+ doc.addEventListener("contextmenu", (event) => {
989
+ event.preventDefault();
990
+ event.stopImmediatePropagation();
991
+ }, options);
992
+ doc.addEventListener("pointerdown", (event) => {
993
+ if (event.isPrimary) this.clearTouchGuard();
994
+ }, {
995
+ ...options,
996
+ passive: true
997
+ });
998
+ doc.addEventListener("keydown", () => this.clearTouchGuard(), options);
999
+ }
1000
+ clearTouchGuard() {
1001
+ this.touchGuard?.abort();
1002
+ this.touchGuard = void 0;
1003
+ this.elementRef.nativeElement.ownerDocument.defaultView?.clearTimeout(this.touchGuardTimer);
1004
+ this.touchGuardTimer = void 0;
1005
+ }
1006
+ openAt(x, y) {
1007
+ this.dispose();
1008
+ ++this.revision;
1009
+ const source = this.elementRef.nativeElement;
1010
+ this.overlayRef = this.overlay.create({
1011
+ positionStrategy: this.positionBuilder.getCoordinateStrategy(x, y),
1012
+ scrollStrategy: this.overlay.scrollStrategies.noop(),
1013
+ usePopover: !!this.overlayContext?.useTopLayer || !!source.closest("dialog, [role=\"dialog\"], [role=\"alertdialog\"], bx-modal, bx-modal-core, [popover]"),
1014
+ hasBackdrop: true,
1015
+ backdropClass: "cdk-overlay-transparent-backdrop"
1016
+ });
1017
+ this.subscriptions = new Subscription();
1018
+ const win = source.ownerDocument.defaultView;
1019
+ if (win) this.zone.runOutsideAngular(() => {
1020
+ const closeOnScroll = (event) => {
1021
+ const target = event.target;
1022
+ if (this.overlayRef?.overlayElement.inert) return;
1023
+ if (target === win || target instanceof Node && target.contains(source)) this.zone.run(() => this.close());
1024
+ };
1025
+ win.addEventListener("scroll", closeOnScroll, {
1026
+ capture: true,
1027
+ passive: true
1028
+ });
1029
+ this.subscriptions.add(() => win.removeEventListener("scroll", closeOnScroll, true));
1030
+ });
1031
+ this.subscriptions.add(this.overlayRef.backdropClick().subscribe(() => this.close()));
1032
+ this.subscriptions.add(this.overlayRef.detachments().subscribe(() => this.dispose()));
1033
+ this.subscriptions.add(this.overlayRef.keydownEvents().subscribe((event) => this.onMenuKey(event)));
1034
+ const pane = this.overlayRef.overlayElement;
1035
+ this.disconnectTheme = this.zone.runOutsideAngular(() => inheritOverlayTheme(source, pane));
1036
+ this.disconnectLighting = inheritGlassLighting(pane, source);
1037
+ this.overlayRef.attach(new TemplatePortal(this.bxContextMenu(), this.vcr)).detectChanges();
1038
+ this.overlayRef.updatePosition();
1039
+ if (!this.a11y.isReducedMotion()) {
1040
+ const point = source.ownerDocument.createElement("span");
1041
+ point.setAttribute("aria-hidden", "true");
1042
+ Object.assign(point.style, {
1043
+ position: "fixed",
1044
+ left: `${x}px`,
1045
+ top: `${y}px`,
1046
+ width: "1px",
1047
+ height: "1px",
1048
+ pointerEvents: "none"
1049
+ });
1050
+ source.ownerDocument.body.append(point);
1051
+ this.point = point;
1052
+ const transition = this.zone.runOutsideAngular(() => createGlassTransition(point, pane));
1053
+ this.transition = transition;
1054
+ this.zone.runOutsideAngular(() => transition.enter()).catch(() => {
1055
+ this.transition?.destroy();
1056
+ this.transition = void 0;
1057
+ });
1058
+ }
1059
+ }
1060
+ items() {
1061
+ return Array.from(this.overlayRef?.overlayElement.querySelectorAll("button:not(:disabled), a[href], [role=\"menuitem\"]") ?? []).filter((item) => !item.matches("[disabled], [aria-disabled=\"true\"]") && !item.closest("[hidden], [inert]"));
1062
+ }
1063
+ onMenuKey(event) {
1064
+ if (event.key === "Escape") {
1065
+ event.preventDefault();
1066
+ event.stopPropagation();
1067
+ this.close(true);
1068
+ return;
1069
+ }
1070
+ if (event.target?.closest("input,textarea,[contenteditable=\"true\"]")) return;
1071
+ if (![
1072
+ "ArrowDown",
1073
+ "ArrowUp",
1074
+ "Home",
1075
+ "End"
1076
+ ].includes(event.key)) return;
1077
+ const items = this.items();
1078
+ if (!items.length) return;
1079
+ event.preventDefault();
1080
+ const current = items.indexOf(this.elementRef.nativeElement.ownerDocument.activeElement);
1081
+ items[event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : event.key === "ArrowDown" ? (current + 1) % items.length : current < 0 ? items.length - 1 : (current - 1 + items.length) % items.length].focus();
1082
+ }
1083
+ dispose() {
1084
+ this.subscriptions.unsubscribe();
1085
+ this.disconnectTheme?.();
1086
+ this.disconnectTheme = void 0;
1087
+ this.transition?.destroy();
1088
+ this.transition = void 0;
1089
+ this.point?.remove();
1090
+ this.point = void 0;
1091
+ const overlay = this.overlayRef;
1092
+ this.overlayRef = null;
1093
+ overlay?.dispose();
1094
+ this.disconnectLighting?.();
1095
+ this.disconnectLighting = void 0;
1096
+ }
1097
+ static ɵfac = i0.ɵɵngDeclareFactory({
1098
+ minVersion: "12.0.0",
1099
+ version: "22.2.3",
1100
+ ngImport: i0,
1101
+ type: ContextMenuDirective,
1102
+ deps: [],
1103
+ target: i0.ɵɵFactoryTarget.Directive
1104
+ });
1105
+ static ɵdir = i0.ɵɵngDeclareDirective({
1106
+ minVersion: "17.1.0",
1107
+ version: "22.2.3",
1108
+ type: ContextMenuDirective,
1109
+ isStandalone: true,
1110
+ selector: "[bxContextMenu]",
1111
+ inputs: {
1112
+ bxContextMenu: {
1113
+ classPropertyName: "bxContextMenu",
1114
+ publicName: "bxContextMenu",
1115
+ isSignal: true,
1116
+ isRequired: true,
1117
+ transformFunction: null
1118
+ },
1119
+ bxContextMenuLongPress: {
1120
+ classPropertyName: "bxContextMenuLongPress",
1121
+ publicName: "bxContextMenuLongPress",
1122
+ isSignal: true,
1123
+ isRequired: false,
1124
+ transformFunction: null
1125
+ }
1126
+ },
1127
+ host: { listeners: {
1128
+ "contextmenu": "open($event)",
1129
+ "keydown": "onKey($event)",
1130
+ "document:click": "onDocumentClick($event)"
1131
+ } },
1132
+ ngImport: i0
1133
+ });
1134
+ };
1135
+ i0.ɵɵngDeclareClassMetadata({
1136
+ minVersion: "12.0.0",
1137
+ version: "22.2.3",
1138
+ ngImport: i0,
1139
+ type: ContextMenuDirective,
1140
+ decorators: [{
1141
+ type: Directive,
1142
+ args: [{ selector: "[bxContextMenu]" }]
1143
+ }],
1144
+ ctorParameters: () => [],
1145
+ propDecorators: {
1146
+ bxContextMenu: [{
1147
+ type: i0.Input,
1148
+ args: [{
1149
+ isSignal: true,
1150
+ alias: "bxContextMenu",
1151
+ required: true
1152
+ }]
1153
+ }],
1154
+ bxContextMenuLongPress: [{
1155
+ type: i0.Input,
1156
+ args: [{
1157
+ isSignal: true,
1158
+ alias: "bxContextMenuLongPress",
1159
+ required: false
1160
+ }]
1161
+ }],
1162
+ open: [{
1163
+ type: HostListener,
1164
+ args: ["contextmenu", ["$event"]]
1165
+ }],
1166
+ onKey: [{
1167
+ type: HostListener,
1168
+ args: ["keydown", ["$event"]]
1169
+ }],
1170
+ onDocumentClick: [{
1171
+ type: HostListener,
1172
+ args: ["document:click", ["$event"]]
1173
+ }]
1174
+ }
1175
+ });
1176
+ var TooltipDirective = class TooltipDirective {
1177
+ elementRef = inject(ElementRef);
1178
+ overlay = inject(Overlay);
1179
+ positionBuilder = inject(BxOverlayPositionService);
1180
+ a11y = inject(BxA11yService);
1181
+ zone = inject(NgZone);
1182
+ focusMonitor = inject(FocusMonitor);
1183
+ describer = inject(AriaDescriber);
1184
+ i18n = inject(BX_I18N, { optional: true });
1185
+ contextMenu = inject(ContextMenuDirective, { optional: true });
1186
+ id = `bx-tooltip-${uuidv4()}`;
1187
+ bxTooltip = input.required(...ngDevMode ? [{ debugName: "bxTooltip" }] : /* istanbul ignore next */ []);
571
1188
  placement = input("bottom", ...ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []);
572
- bxTransition = input("fade", ...ngDevMode ? [{ debugName: "bxTransition" }] : /* istanbul ignore next */ []);
1189
+ bxTooltipTouch = input("none", ...ngDevMode ? [{ debugName: "bxTooltipTouch" }] : /* istanbul ignore next */ []);
573
1190
  overlayRef = null;
574
- transition = null;
575
- subscriptions = new Subscription();
576
- revision = 0;
1191
+ transition;
577
1192
  disconnectTheme;
578
1193
  disconnectLighting;
1194
+ positionSubscription;
1195
+ keyboardSubscription;
1196
+ revision = 0;
1197
+ hovered = false;
1198
+ focused = false;
1199
+ lastPointerType = "";
1200
+ touchEvents;
1201
+ holdTimer;
1202
+ touchHideTimer;
1203
+ touchVisible = false;
1204
+ closeTouchTap = false;
579
1205
  constructor() {
1206
+ const source = this.elementRef.nativeElement;
1207
+ const focus = source.ownerDocument.defaultView ? this.focusMonitor.monitor(source, true).subscribe((origin) => this.zone.run(() => {
1208
+ this.focused = origin === "keyboard";
1209
+ if (this.focused) {
1210
+ this.clearTouchInteraction();
1211
+ this.show();
1212
+ } else if (!this.hovered && !this.touchEvents) this.hide();
1213
+ })) : void 0;
1214
+ effect((onCleanup) => {
1215
+ this.i18n?.version?.();
1216
+ const text = this.bxTooltip();
1217
+ const description = isTextWithKey(text) ? this.i18n?.translate(text.key) ?? text.key : text;
1218
+ if (!description) return;
1219
+ this.describer.describe(source, description, "tooltip");
1220
+ onCleanup(() => this.describer.removeDescription(source, description, "tooltip"));
1221
+ });
580
1222
  inject(DestroyRef).onDestroy(() => {
581
1223
  ++this.revision;
1224
+ focus?.unsubscribe();
1225
+ if (focus) this.focusMonitor.stopMonitoring(source);
1226
+ this.clearTouchInteraction();
582
1227
  this.dispose();
583
1228
  });
584
- effect(() => {
585
- if (this.a11y.isReducedMotion() && this.transition) {
586
- ++this.revision;
587
- this.transition.destroy();
588
- this.transition = null;
589
- if (this.isOpen()) this._state.set("open");
590
- else this.dispose();
591
- }
592
- });
593
- }
594
- toggle(event) {
595
- if (this.isDisabled()) return;
596
- event?.stopPropagation();
597
- if (this.isOpen()) this.close();
598
- else this.open();
599
- }
600
- onTriggerKeydown(event) {
601
- if (this.bxTransition() === "none" || this.isDisabled() || this.isEditing(event.target)) return;
602
- if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
603
- event.preventDefault();
604
- event.stopPropagation();
605
- this.open();
606
- const items = this.menuItems();
607
- (event.key === "ArrowUp" ? items[items.length - 1] : items[0])?.focus({ preventScroll: true });
608
1229
  }
609
- open() {
610
- if (!this.isBrowser || this.isOpen() || this.isDisabled()) return;
1230
+ show() {
1231
+ if (!this.bxTooltip()) return;
611
1232
  const revision = ++this.revision;
612
- this._isOpen.set(true);
613
- this._state.set("opening");
614
- if (!this.overlayRef) this.attachOverlay();
615
- const overlay = this.overlayRef;
616
- if (!overlay) return;
617
- overlay.overlayElement.inert = false;
618
- overlay.overlayElement.style.removeProperty("pointer-events");
619
- if (this.bxTransition() !== "none" && !this.a11y.isReducedMotion()) {
620
- const transition = this.transition ??= this.zone.runOutsideAngular(() => createGlassTransition(this.elementRef.nativeElement, overlay.overlayElement));
621
- this.zone.runOutsideAngular(() => transition.enter()).then(() => {
622
- if (revision === this.revision && this.isOpen()) this.zone.run(() => this._state.set("open"));
623
- }).catch(() => {
624
- if (revision === this.revision && this.isOpen()) {
625
- this.transition?.destroy();
626
- this.transition = null;
627
- this._state.set("open");
628
- }
1233
+ if (!this.overlayRef) {
1234
+ const strategy = this.positionBuilder.getElementStrategy(this.elementRef.nativeElement, this.placement()).withFlexibleDimensions(false);
1235
+ this.overlayRef = this.overlay.create({
1236
+ positionStrategy: strategy,
1237
+ scrollStrategy: this.overlay.scrollStrategies.reposition(),
1238
+ hasBackdrop: false,
1239
+ panelClass: "bx-tooltip-pane"
629
1240
  });
630
- } else this._state.set("open");
1241
+ const pane = this.overlayRef.overlayElement;
1242
+ pane.id = this.id;
1243
+ pane.setAttribute("role", "tooltip");
1244
+ pane.setAttribute("aria-hidden", "true");
1245
+ this.disconnectTheme = this.zone.runOutsideAngular(() => inheritOverlayTheme(this.elementRef.nativeElement, pane));
1246
+ this.disconnectLighting = inheritGlassLighting(pane, this.elementRef.nativeElement);
1247
+ const component = this.overlayRef.attach(new ComponentPortal(Tooltip));
1248
+ pane.style.pointerEvents = "none";
1249
+ this.keyboardSubscription = this.overlayRef.keydownEvents().subscribe((event) => this.onEscape(event));
1250
+ component.setInput("text", this.bxTooltip());
1251
+ component.changeDetectorRef.detectChanges();
1252
+ const positionArrow = () => {
1253
+ const trigger = this.elementRef.nativeElement.getBoundingClientRect();
1254
+ const bounds = pane.getBoundingClientRect();
1255
+ pane.style.setProperty("--bx-tooltip-arrow-x", `${trigger.left + trigger.width / 2 - bounds.left}px`);
1256
+ pane.style.setProperty("--bx-tooltip-arrow-y", `${trigger.top + trigger.height / 2 - bounds.top}px`);
1257
+ this.transition?.refresh();
1258
+ };
1259
+ this.positionSubscription = strategy.positionChanges.subscribe(positionArrow);
1260
+ this.overlayRef.updatePosition();
1261
+ positionArrow();
1262
+ if (!this.a11y.isReducedMotion()) this.transition = this.zone.runOutsideAngular(() => createGlassTransition(this.elementRef.nativeElement, pane));
1263
+ }
1264
+ Promise.resolve(this.zone.runOutsideAngular(() => this.transition?.enter())).catch(() => {
1265
+ if (revision === this.revision) {
1266
+ this.transition?.destroy();
1267
+ this.transition = void 0;
1268
+ }
1269
+ });
631
1270
  }
632
- close(restoreFocus = false) {
633
- if (!this.overlayRef || !this.isOpen()) return;
1271
+ hide() {
1272
+ this.clearTouchInteraction();
634
1273
  const revision = ++this.revision;
635
- const overlay = this.overlayRef;
636
- this._isOpen.set(false);
637
- this._state.set("closing");
638
- const source = this.elementRef.nativeElement;
639
- if (restoreFocus || overlay.overlayElement.contains(source.ownerDocument.activeElement)) source.focus({ preventScroll: true });
640
- overlay.overlayElement.inert = true;
641
- overlay.overlayElement.style.pointerEvents = "none";
1274
+ if (!this.overlayRef) return;
642
1275
  const transition = this.transition;
643
1276
  if (transition && !this.a11y.isReducedMotion()) this.zone.runOutsideAngular(() => transition.exit()).then(() => {
644
- if (revision === this.revision && !this.isOpen()) this.zone.run(() => this.dispose());
1277
+ if (revision === this.revision) this.dispose();
645
1278
  }).catch(() => {
646
- if (revision === this.revision && !this.isOpen()) this.dispose();
1279
+ if (revision === this.revision) this.dispose();
647
1280
  });
648
1281
  else this.dispose();
649
1282
  }
650
- onDocumentClick(event) {
651
- if (!this.overlayRef?.hasAttached() || !this.isOpen()) return;
652
- const target = event.target;
653
- if (!target || this.elementRef.nativeElement.contains(target)) return;
654
- if (!this.overlayRef.overlayElement.contains(target) || this.bxTransition() === "none" || target instanceof Element && target.closest("button, a, [role=\"menuitem\"]")) this.close();
1283
+ onEnter(event) {
1284
+ if (event.pointerType !== "mouse") return;
1285
+ this.clearTouchInteraction();
1286
+ this.hovered = true;
1287
+ this.show();
655
1288
  }
656
- attachOverlay() {
657
- const positionStrategy = this.positionBuilder.getElementStrategy(this.elementRef.nativeElement, this.placement(), 4);
658
- const overlay = this.overlay.create({
659
- positionStrategy,
660
- scrollStrategy: this.overlay.scrollStrategies.noop(),
661
- usePopover: !!this.overlayContext?.useTopLayer || !!this.elementRef.nativeElement.closest("dialog, [role=\"dialog\"], [role=\"alertdialog\"], bx-modal, bx-modal-core, [popover]"),
662
- hasBackdrop: this.bxTransition() === "none",
663
- backdropClass: "cdk-overlay-transparent-backdrop"
664
- });
665
- this.overlayRef = overlay;
666
- this.overlayId.set(overlay.overlayElement.id || null);
667
- this.disconnectTheme = this.zone.runOutsideAngular(() => inheritOverlayTheme(this.elementRef.nativeElement, overlay.overlayElement));
668
- this.disconnectLighting = inheritGlassLighting(overlay.overlayElement, this.elementRef.nativeElement);
669
- this.subscriptions = new Subscription();
670
- this.trackScroll(overlay, positionStrategy);
671
- this.subscriptions.add(overlay.backdropClick().subscribe(() => this.close()));
672
- this.subscriptions.add(overlay.outsidePointerEvents().subscribe((event) => {
673
- if (!this.elementRef.nativeElement.contains(event.target)) this.close();
674
- }));
675
- this.subscriptions.add(overlay.keydownEvents().subscribe((event) => {
676
- if (event.key === "Escape" && this.isOpen()) {
677
- event.preventDefault();
678
- event.stopPropagation();
679
- this.close(true);
680
- } else if (!event.defaultPrevented && this.bxTransition() !== "none" && this.isOpen()) this.navigateMenu(event);
681
- }));
682
- this.subscriptions.add(positionStrategy.positionChanges.subscribe(() => {
683
- this.zone.runOutsideAngular(() => this.transition?.refresh());
684
- }));
685
- overlay.attach(new TemplatePortal(this.bxDropdown(), this.vcr)).detectChanges();
686
- overlay.updatePosition();
1289
+ onLeave(event) {
1290
+ if (event.pointerType !== "mouse") return;
1291
+ this.hovered = false;
1292
+ if (!this.focused) this.hide();
687
1293
  }
688
- trackScroll(overlay, positionStrategy) {
689
- const source = this.elementRef.nativeElement;
690
- const view = source.ownerDocument.defaultView;
691
- if (!view) return;
692
- this.zone.runOutsideAngular(() => {
693
- const reposition = (event) => {
694
- const target = event.target;
695
- if (overlay.hasAttached() && (target === view || target instanceof Node && target.contains(source))) positionStrategy.reapplyLastPosition();
696
- };
697
- view.addEventListener("scroll", reposition, {
698
- capture: true,
699
- passive: true
700
- });
701
- this.subscriptions.add(() => view.removeEventListener("scroll", reposition, true));
702
- });
1294
+ onPointerDown(event) {
1295
+ this.lastPointerType = event.pointerType;
1296
+ if (event.pointerType !== "touch") return;
1297
+ this.closeTouchTap = this.touchVisible && this.bxTooltipTouch() === "tap";
1298
+ this.hovered = false;
1299
+ this.focused = false;
1300
+ this.hide();
1301
+ if (this.bxTooltipTouch() !== "longpress" || !event.isPrimary || event.button !== 0 || !this.canUseTouch(event)) return;
1302
+ this.zone.runOutsideAngular(() => this.startTouchInteraction(event));
703
1303
  }
704
- isDisabled() {
705
- const element = this.elementRef.nativeElement;
706
- return element.hasAttribute("disabled") || element.getAttribute("aria-disabled") === "true";
1304
+ onClick(event) {
1305
+ if (event.detail === 0 || this.lastPointerType !== "touch" || this.bxTooltipTouch() !== "tap" || !this.canUseTouch(event)) return;
1306
+ if (this.closeTouchTap) {
1307
+ this.closeTouchTap = false;
1308
+ return;
1309
+ }
1310
+ this.zone.runOutsideAngular(() => this.startTouchInteraction());
707
1311
  }
708
- menuItems() {
709
- const pane = this.overlayRef?.overlayElement;
710
- if (!pane) return [];
711
- return Array.from(pane.querySelectorAll("button, a[href], [role=\"menuitem\"]")).filter((item) => {
712
- if (item.matches(":disabled, [disabled], [aria-disabled=\"true\"]") || item.closest("[aria-disabled=\"true\"]")) return false;
713
- if (item.closest("[hidden], [inert], [aria-hidden=\"true\"]")) return false;
714
- if (item.tabIndex < 0 && !item.hasAttribute("tabindex")) return false;
715
- const style = item.ownerDocument.defaultView?.getComputedStyle(item);
716
- return !!style && style.display !== "none" && style.visibility !== "hidden" && item.getClientRects().length > 0;
717
- });
1312
+ onEscape(event) {
1313
+ if (event.key === "Escape" && this.overlayRef) {
1314
+ event.stopPropagation();
1315
+ this.hovered = false;
1316
+ this.focused = false;
1317
+ this.hide();
1318
+ }
718
1319
  }
719
- navigateMenu(event) {
720
- if (this.isEditing(event.target) || !(event.target instanceof Node) || !this.overlayRef?.overlayElement.contains(event.target)) return;
721
- if (![
722
- "ArrowDown",
723
- "ArrowUp",
724
- "Home",
725
- "End"
726
- ].includes(event.key)) return;
727
- const items = this.menuItems();
728
- if (!items.length) return;
729
- const current = items.indexOf(this.elementRef.nativeElement.ownerDocument.activeElement);
730
- let index;
731
- if (event.key === "Home") index = 0;
732
- else if (event.key === "End") index = items.length - 1;
733
- else if (event.key === "ArrowDown") index = current < 0 ? 0 : (current + 1) % items.length;
734
- else index = current < 0 ? items.length - 1 : (current - 1 + items.length) % items.length;
735
- event.preventDefault();
736
- event.stopPropagation();
737
- items[index].focus({ preventScroll: true });
1320
+ canUseTouch(event) {
1321
+ if (this.contextMenu && this.bxTooltipTouch() === "longpress") return false;
1322
+ if (this.elementRef.nativeElement.closest(":disabled, [aria-disabled=\"true\"], [inert]")) return false;
1323
+ return !event.composedPath().some((node) => node instanceof HTMLElement && (node.isContentEditable || node.matches("input, textarea, select")));
738
1324
  }
739
- isEditing(target) {
740
- return target instanceof Element && !!target.closest("input, textarea, select, [contenteditable]:not([contenteditable=\"false\"]), [role=\"textbox\"]");
1325
+ hasTextSelection() {
1326
+ const source = this.elementRef.nativeElement;
1327
+ const selection = source.ownerDocument.getSelection();
1328
+ return !!selection && !selection.isCollapsed && (source.contains(selection.anchorNode) || source.contains(selection.focusNode));
1329
+ }
1330
+ startTouchInteraction(press) {
1331
+ const source = this.elementRef.nativeElement;
1332
+ const doc = source.ownerDocument;
1333
+ const win = doc.defaultView;
1334
+ if (!win || this.hasTextSelection()) return;
1335
+ this.clearTouchInteraction();
1336
+ const events = new AbortController();
1337
+ this.touchEvents = events;
1338
+ const options = {
1339
+ capture: true,
1340
+ passive: true,
1341
+ signal: events.signal
1342
+ };
1343
+ const dismiss = () => this.zone.run(() => this.hide());
1344
+ let suppressReleaseClick = false;
1345
+ doc.addEventListener("pointerdown", (event) => {
1346
+ if (!press && event.target instanceof Node && source.contains(event.target)) return;
1347
+ dismiss();
1348
+ }, options);
1349
+ doc.addEventListener("scroll", dismiss, options);
1350
+ doc.addEventListener("visibilitychange", () => {
1351
+ if (doc.hidden) dismiss();
1352
+ }, options);
1353
+ win.addEventListener("blur", dismiss, { signal: events.signal });
1354
+ if (!press) {
1355
+ this.touchVisible = true;
1356
+ this.zone.run(() => this.show());
1357
+ return;
1358
+ }
1359
+ doc.addEventListener("pointermove", (event) => {
1360
+ if (event.pointerId === press.pointerId && !this.touchVisible && Math.hypot(event.clientX - press.clientX, event.clientY - press.clientY) > 10) dismiss();
1361
+ }, options);
1362
+ doc.addEventListener("pointerup", (event) => {
1363
+ if (event.pointerId !== press.pointerId) return;
1364
+ if (!this.touchVisible) dismiss();
1365
+ else this.touchHideTimer = win.setTimeout(dismiss, 1500);
1366
+ }, options);
1367
+ doc.addEventListener("pointercancel", (event) => {
1368
+ if (event.pointerId === press.pointerId) dismiss();
1369
+ }, options);
1370
+ doc.addEventListener("click", (event) => {
1371
+ if (!suppressReleaseClick || event.detail === 0) return;
1372
+ event.preventDefault();
1373
+ event.stopImmediatePropagation();
1374
+ suppressReleaseClick = false;
1375
+ }, {
1376
+ ...options,
1377
+ passive: false
1378
+ });
1379
+ doc.addEventListener("contextmenu", (event) => {
1380
+ if (event.target instanceof Node && source.contains(event.target) && !this.hasTextSelection()) event.preventDefault();
1381
+ }, {
1382
+ ...options,
1383
+ passive: false
1384
+ });
1385
+ this.holdTimer = win.setTimeout(() => {
1386
+ if (this.bxTooltipTouch() !== "longpress" || !source.isConnected || source.closest(":disabled, [aria-disabled=\"true\"], [inert]") || this.hasTextSelection()) {
1387
+ dismiss();
1388
+ return;
1389
+ }
1390
+ this.touchVisible = true;
1391
+ suppressReleaseClick = true;
1392
+ this.zone.run(() => this.show());
1393
+ }, 500);
1394
+ }
1395
+ clearTouchInteraction() {
1396
+ this.touchEvents?.abort();
1397
+ this.touchEvents = void 0;
1398
+ const win = this.elementRef.nativeElement.ownerDocument.defaultView;
1399
+ win?.clearTimeout(this.holdTimer);
1400
+ win?.clearTimeout(this.touchHideTimer);
1401
+ this.holdTimer = void 0;
1402
+ this.touchHideTimer = void 0;
1403
+ this.touchVisible = false;
741
1404
  }
742
1405
  dispose() {
1406
+ this.keyboardSubscription?.unsubscribe();
1407
+ this.keyboardSubscription = void 0;
1408
+ this.positionSubscription?.unsubscribe();
1409
+ this.positionSubscription = void 0;
743
1410
  this.disconnectTheme?.();
744
1411
  this.disconnectTheme = void 0;
745
1412
  this.transition?.destroy();
746
- this.transition = null;
747
- this.subscriptions.unsubscribe();
1413
+ this.transition = void 0;
748
1414
  this.overlayRef?.dispose();
749
1415
  this.overlayRef = null;
750
1416
  this.disconnectLighting?.();
751
- this.disconnectLighting = void 0;
752
- this.overlayId.set(null);
753
- this._isOpen.set(false);
754
- this._state.set("closed");
1417
+ this.disconnectLighting = void 0;
755
1418
  }
756
1419
  static ɵfac = i0.ɵɵngDeclareFactory({
757
1420
  minVersion: "12.0.0",
758
1421
  version: "22.2.3",
759
1422
  ngImport: i0,
760
- type: DropdownDirective,
1423
+ type: TooltipDirective,
761
1424
  deps: [],
762
1425
  target: i0.ɵɵFactoryTarget.Directive
763
1426
  });
764
1427
  static ɵdir = i0.ɵɵngDeclareDirective({
765
1428
  minVersion: "17.1.0",
766
1429
  version: "22.2.3",
767
- type: DropdownDirective,
1430
+ type: TooltipDirective,
768
1431
  isStandalone: true,
769
- selector: "[bxDropdown]",
1432
+ selector: "[bxTooltip]",
770
1433
  inputs: {
771
- bxDropdown: {
772
- classPropertyName: "bxDropdown",
773
- publicName: "bxDropdown",
1434
+ bxTooltip: {
1435
+ classPropertyName: "bxTooltip",
1436
+ publicName: "bxTooltip",
774
1437
  isSignal: true,
775
1438
  isRequired: true,
776
1439
  transformFunction: null
@@ -782,27 +1445,21 @@ var DropdownDirective = class DropdownDirective {
782
1445
  isRequired: false,
783
1446
  transformFunction: null
784
1447
  },
785
- bxTransition: {
786
- classPropertyName: "bxTransition",
787
- publicName: "bxTransition",
1448
+ bxTooltipTouch: {
1449
+ classPropertyName: "bxTooltipTouch",
1450
+ publicName: "bxTooltipTouch",
788
1451
  isSignal: true,
789
1452
  isRequired: false,
790
1453
  transformFunction: null
791
1454
  }
792
1455
  },
793
- host: {
794
- listeners: {
795
- "click": "toggle($event)",
796
- "keydown": "onTriggerKeydown($event)",
797
- "document:click": "onDocumentClick($event)"
798
- },
799
- properties: {
800
- "attr.aria-expanded": "isOpen()",
801
- "attr.aria-haspopup": "\"menu\"",
802
- "attr.aria-controls": "overlayId()"
803
- }
804
- },
805
- exportAs: ["bxDropdown"],
1456
+ host: { listeners: {
1457
+ "pointerenter": "onEnter($event)",
1458
+ "pointerleave": "onLeave($event)",
1459
+ "pointerdown": "onPointerDown($event)",
1460
+ "click": "onClick($event)",
1461
+ "document:keydown": "onEscape($event)"
1462
+ } },
806
1463
  ngImport: i0
807
1464
  });
808
1465
  };
@@ -810,27 +1467,18 @@ i0.ɵɵngDeclareClassMetadata({
810
1467
  minVersion: "12.0.0",
811
1468
  version: "22.2.3",
812
1469
  ngImport: i0,
813
- type: DropdownDirective,
1470
+ type: TooltipDirective,
814
1471
  decorators: [{
815
1472
  type: Directive,
816
- args: [{
817
- selector: "[bxDropdown]",
818
- exportAs: "bxDropdown",
819
- standalone: true,
820
- host: {
821
- "[attr.aria-expanded]": "isOpen()",
822
- "[attr.aria-haspopup]": "\"menu\"",
823
- "[attr.aria-controls]": "overlayId()"
824
- }
825
- }]
1473
+ args: [{ selector: "[bxTooltip]" }]
826
1474
  }],
827
1475
  ctorParameters: () => [],
828
1476
  propDecorators: {
829
- bxDropdown: [{
1477
+ bxTooltip: [{
830
1478
  type: i0.Input,
831
1479
  args: [{
832
1480
  isSignal: true,
833
- alias: "bxDropdown",
1481
+ alias: "bxTooltip",
834
1482
  required: true
835
1483
  }]
836
1484
  }],
@@ -842,339 +1490,270 @@ i0.ɵɵngDeclareClassMetadata({
842
1490
  required: false
843
1491
  }]
844
1492
  }],
845
- bxTransition: [{
1493
+ bxTooltipTouch: [{
846
1494
  type: i0.Input,
847
1495
  args: [{
848
1496
  isSignal: true,
849
- alias: "bxTransition",
1497
+ alias: "bxTooltipTouch",
850
1498
  required: false
851
1499
  }]
852
1500
  }],
853
- toggle: [{
1501
+ onEnter: [{
854
1502
  type: HostListener,
855
- args: ["click", ["$event"]]
1503
+ args: ["pointerenter", ["$event"]]
856
1504
  }],
857
- onTriggerKeydown: [{
1505
+ onLeave: [{
858
1506
  type: HostListener,
859
- args: ["keydown", ["$event"]]
1507
+ args: ["pointerleave", ["$event"]]
860
1508
  }],
861
- onDocumentClick: [{
1509
+ onPointerDown: [{
862
1510
  type: HostListener,
863
- args: ["document:click", ["$event"]]
1511
+ args: ["pointerdown", ["$event"]]
1512
+ }],
1513
+ onClick: [{
1514
+ type: HostListener,
1515
+ args: ["click", ["$event"]]
1516
+ }],
1517
+ onEscape: [{
1518
+ type: HostListener,
1519
+ args: ["document:keydown", ["$event"]]
864
1520
  }]
865
1521
  }
866
1522
  });
867
- var ContextMenuDirective = class ContextMenuDirective {
1523
+ var DropdownDirective = class DropdownDirective {
1524
+ elementRef = inject(ElementRef);
868
1525
  overlay = inject(Overlay);
869
1526
  positionBuilder = inject(BxOverlayPositionService);
870
1527
  overlayContext = inject(BX_OVERLAY_CONTEXT, { optional: true });
871
- elementRef = inject(ElementRef);
872
1528
  a11y = inject(BxA11yService);
873
1529
  zone = inject(NgZone);
874
1530
  vcr = inject(ViewContainerRef);
875
- bxContextMenu = input.required(...ngDevMode ? [{ debugName: "bxContextMenu" }] : /* istanbul ignore next */ []);
876
- bxContextMenuLongPress = input(true, {
877
- ...ngDevMode ? { debugName: "bxContextMenuLongPress" } : /* istanbul ignore next */ {},
878
- transform: booleanAttribute
879
- });
880
- onPointerDown = (event) => {
881
- this.lastPointerType = event.pointerType;
882
- this.cancelTouchPress();
883
- if (event.isPrimary) this.clearTouchGuard();
884
- if (event.pointerType !== "touch" || !event.isPrimary || event.button !== 0 || !this.bxContextMenuLongPress() || this.hasNativeTouchAction(event)) return;
885
- const doc = this.elementRef.nativeElement.ownerDocument;
886
- const win = doc.defaultView;
887
- if (!win || this.hasTextSelection()) return;
888
- const press = {
889
- pointerId: event.pointerId,
890
- x: event.clientX,
891
- y: event.clientY,
892
- timer: 0,
893
- opened: false,
894
- events: new AbortController()
895
- };
896
- this.touchPress = press;
897
- const options = {
898
- capture: true,
899
- passive: true,
900
- signal: press.events.signal
901
- };
902
- const finish = () => this.cancelTouchPress();
903
- doc.addEventListener("pointermove", (move) => {
904
- if (move.pointerId === press.pointerId && !press.opened && Math.hypot(move.clientX - press.x, move.clientY - press.y) > 10) finish();
905
- }, options);
906
- for (const type of ["pointerup", "pointercancel"]) doc.addEventListener(type, (end) => {
907
- if (end.pointerId === press.pointerId) finish();
908
- }, options);
909
- doc.addEventListener("pointerdown", (next) => {
910
- if (next.pointerId !== press.pointerId) finish();
911
- }, options);
912
- doc.addEventListener("scroll", finish, options);
913
- doc.addEventListener("visibilitychange", () => {
914
- if (doc.hidden) finish();
915
- }, options);
916
- win.addEventListener("blur", finish, { signal: press.events.signal });
917
- press.timer = win.setTimeout(() => this.zone.run(() => this.openTouchMenu(press)), 500);
918
- };
1531
+ isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
1532
+ _isOpen = signal(false, ...ngDevMode ? [{ debugName: "_isOpen" }] : /* istanbul ignore next */ []);
1533
+ overlayId = signal(null, ...ngDevMode ? [{ debugName: "overlayId" }] : /* istanbul ignore next */ []);
1534
+ isOpen = this._isOpen.asReadonly();
1535
+ _state = signal("closed", ...ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []);
1536
+ state = this._state.asReadonly();
1537
+ bxDropdown = input.required(...ngDevMode ? [{ debugName: "bxDropdown" }] : /* istanbul ignore next */ []);
1538
+ placement = input("bottom", ...ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []);
1539
+ bxTransition = input("fade", ...ngDevMode ? [{ debugName: "bxTransition" }] : /* istanbul ignore next */ []);
919
1540
  overlayRef = null;
920
- transition;
921
- point;
922
- disconnectTheme;
923
- disconnectLighting;
1541
+ transition = null;
924
1542
  subscriptions = new Subscription();
925
1543
  revision = 0;
926
- touchPress;
927
- touchGuard;
928
- touchGuardTimer;
929
- lastPointerType = "";
1544
+ disconnectTheme;
1545
+ disconnectLighting;
930
1546
  constructor() {
931
- const source = this.elementRef.nativeElement;
932
- if (source.ownerDocument.defaultView) this.zone.runOutsideAngular(() => source.addEventListener("pointerdown", this.onPointerDown, { passive: true }));
933
1547
  inject(DestroyRef).onDestroy(() => {
934
1548
  ++this.revision;
935
- source.removeEventListener("pointerdown", this.onPointerDown);
936
- this.cancelTouchPress();
937
- this.clearTouchGuard();
938
1549
  this.dispose();
939
1550
  });
940
- }
941
- open(event) {
942
- if (event.type === "contextmenu" && this.lastPointerType === "touch") {
943
- const press = this.touchPress;
944
- if (!press) return;
945
- if (!this.bxContextMenuLongPress() || this.hasNativeTouchAction(event) || this.hasTextSelection()) {
946
- this.cancelTouchPress();
947
- return;
1551
+ effect(() => {
1552
+ if (this.a11y.isReducedMotion() && this.transition) {
1553
+ ++this.revision;
1554
+ this.transition.destroy();
1555
+ this.transition = null;
1556
+ if (this.isOpen()) this._state.set("open");
1557
+ else this.dispose();
948
1558
  }
949
- event.preventDefault();
950
- this.openTouchMenu(press);
951
- return;
952
- }
953
- event.preventDefault();
954
- this.cancelTouchPress();
955
- this.clearTouchGuard();
956
- this.openAt(event.clientX, event.clientY);
957
- }
958
- close(restoreFocus = false) {
959
- if (!this.overlayRef) return;
960
- const revision = ++this.revision;
961
- if (restoreFocus || this.overlayRef.overlayElement.contains(this.elementRef.nativeElement.ownerDocument.activeElement)) this.elementRef.nativeElement.focus({ preventScroll: true });
962
- this.overlayRef.overlayElement.inert = true;
963
- const transition = this.transition;
964
- if (transition && !this.a11y.isReducedMotion()) this.zone.runOutsideAngular(() => transition.exit()).then(() => {
965
- if (revision === this.revision) this.dispose();
966
- }).catch(() => {
967
- if (revision === this.revision) this.dispose();
968
1559
  });
969
- else this.dispose();
970
- }
971
- onKey(event) {
972
- if (event.key !== "ContextMenu" && !(event.shiftKey && event.key === "F10")) return;
973
- event.preventDefault();
974
- this.lastPointerType = "";
975
- this.cancelTouchPress();
976
- this.clearTouchGuard();
977
- const rect = this.elementRef.nativeElement.getBoundingClientRect();
978
- this.openAt(rect.left + Math.min(24, rect.width / 2), rect.bottom);
979
- this.items()[0]?.focus();
980
- }
981
- onDocumentClick(event) {
982
- const target = event.target;
983
- if (target && this.overlayRef?.overlayElement.contains(target) && target.closest("button, a, [role=\"menuitem\"]")) this.close();
984
- }
985
- hasNativeTouchAction(event) {
986
- const source = this.elementRef.nativeElement;
987
- for (const node of event.composedPath()) if (node instanceof HTMLElement) {
988
- if (node.isContentEditable || node.matches("input, textarea, select, :disabled, [aria-disabled=\"true\"], [inert]")) return true;
989
- if (node === source) break;
990
- }
991
- return !!source.closest(":disabled, [aria-disabled=\"true\"], [inert]");
992
- }
993
- hasTextSelection() {
994
- const source = this.elementRef.nativeElement;
995
- const selection = source.ownerDocument.getSelection();
996
- return !!selection && !selection.isCollapsed && (source.contains(selection.anchorNode) || source.contains(selection.focusNode));
997
- }
998
- openTouchMenu(press) {
999
- if (this.touchPress !== press || press.opened) return;
1000
- const source = this.elementRef.nativeElement;
1001
- if (!this.bxContextMenuLongPress() || !source.isConnected || source.isContentEditable || source.closest(":disabled, [aria-disabled=\"true\"], [inert]") || this.hasTextSelection()) {
1002
- this.cancelTouchPress();
1003
- return;
1004
- }
1005
- press.opened = true;
1006
- this.elementRef.nativeElement.ownerDocument.defaultView?.clearTimeout(press.timer);
1007
- this.guardTouchRelease();
1008
- this.openAt(press.x, press.y + 12);
1009
1560
  }
1010
- cancelTouchPress() {
1011
- const press = this.touchPress;
1012
- if (!press) return;
1013
- this.touchPress = void 0;
1014
- const win = this.elementRef.nativeElement.ownerDocument.defaultView;
1015
- win?.clearTimeout(press.timer);
1016
- press.events.abort();
1017
- if (press.opened && win) this.touchGuardTimer = win.setTimeout(() => this.clearTouchGuard(), 800);
1561
+ toggle(event) {
1562
+ if (this.isDisabled()) return;
1563
+ event?.stopPropagation();
1564
+ if (this.isOpen()) this.close();
1565
+ else this.open();
1018
1566
  }
1019
- guardTouchRelease() {
1020
- this.clearTouchGuard();
1021
- const doc = this.elementRef.nativeElement.ownerDocument;
1022
- const guard = new AbortController();
1023
- this.touchGuard = guard;
1024
- const options = {
1025
- capture: true,
1026
- signal: guard.signal
1027
- };
1028
- doc.addEventListener("click", (event) => {
1029
- if (event.detail === 0) return;
1030
- event.preventDefault();
1031
- event.stopImmediatePropagation();
1032
- this.clearTouchGuard();
1033
- }, options);
1034
- doc.addEventListener("contextmenu", (event) => {
1035
- event.preventDefault();
1036
- event.stopImmediatePropagation();
1037
- }, options);
1038
- doc.addEventListener("pointerdown", (event) => {
1039
- if (event.isPrimary) this.clearTouchGuard();
1040
- }, {
1041
- ...options,
1042
- passive: true
1043
- });
1044
- doc.addEventListener("keydown", () => this.clearTouchGuard(), options);
1567
+ onTriggerKeydown(event) {
1568
+ if (this.bxTransition() === "none" || this.isDisabled() || this.isEditing(event.target)) return;
1569
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
1570
+ event.preventDefault();
1571
+ event.stopPropagation();
1572
+ this.open();
1573
+ const items = this.menuItems();
1574
+ (event.key === "ArrowUp" ? items[items.length - 1] : items[0])?.focus({ preventScroll: true });
1045
1575
  }
1046
- clearTouchGuard() {
1047
- this.touchGuard?.abort();
1048
- this.touchGuard = void 0;
1049
- this.elementRef.nativeElement.ownerDocument.defaultView?.clearTimeout(this.touchGuardTimer);
1050
- this.touchGuardTimer = void 0;
1576
+ open() {
1577
+ if (!this.isBrowser || this.isOpen() || this.isDisabled()) return;
1578
+ const revision = ++this.revision;
1579
+ this._isOpen.set(true);
1580
+ this._state.set("opening");
1581
+ if (!this.overlayRef) this.attachOverlay();
1582
+ const overlay = this.overlayRef;
1583
+ if (!overlay) return;
1584
+ overlay.overlayElement.inert = false;
1585
+ overlay.overlayElement.style.removeProperty("pointer-events");
1586
+ if (this.bxTransition() !== "none" && !this.a11y.isReducedMotion()) {
1587
+ const transition = this.transition ??= this.zone.runOutsideAngular(() => createGlassTransition(this.elementRef.nativeElement, overlay.overlayElement));
1588
+ this.zone.runOutsideAngular(() => transition.enter()).then(() => {
1589
+ if (revision === this.revision && this.isOpen()) this.zone.run(() => this._state.set("open"));
1590
+ }).catch(() => {
1591
+ if (revision === this.revision && this.isOpen()) {
1592
+ this.transition?.destroy();
1593
+ this.transition = null;
1594
+ this._state.set("open");
1595
+ }
1596
+ });
1597
+ } else this._state.set("open");
1051
1598
  }
1052
- openAt(x, y) {
1053
- this.dispose();
1054
- ++this.revision;
1599
+ close(restoreFocus = false) {
1600
+ if (!this.overlayRef || !this.isOpen()) return;
1601
+ const revision = ++this.revision;
1602
+ const overlay = this.overlayRef;
1603
+ this._isOpen.set(false);
1604
+ this._state.set("closing");
1055
1605
  const source = this.elementRef.nativeElement;
1056
- this.overlayRef = this.overlay.create({
1057
- positionStrategy: this.positionBuilder.getCoordinateStrategy(x, y),
1606
+ if (restoreFocus || overlay.overlayElement.contains(source.ownerDocument.activeElement)) source.focus({ preventScroll: true });
1607
+ overlay.overlayElement.inert = true;
1608
+ overlay.overlayElement.style.pointerEvents = "none";
1609
+ const transition = this.transition;
1610
+ if (transition && !this.a11y.isReducedMotion()) this.zone.runOutsideAngular(() => transition.exit()).then(() => {
1611
+ if (revision === this.revision && !this.isOpen()) this.zone.run(() => this.dispose());
1612
+ }).catch(() => {
1613
+ if (revision === this.revision && !this.isOpen()) this.dispose();
1614
+ });
1615
+ else this.dispose();
1616
+ }
1617
+ onDocumentClick(event) {
1618
+ if (!this.overlayRef?.hasAttached() || !this.isOpen()) return;
1619
+ const target = event.target;
1620
+ if (!target || this.elementRef.nativeElement.contains(target)) return;
1621
+ if (!this.overlayRef.overlayElement.contains(target) || this.bxTransition() === "none" || target instanceof Element && target.closest("button, a, [role=\"menuitem\"]")) this.close();
1622
+ }
1623
+ attachOverlay() {
1624
+ const positionStrategy = this.positionBuilder.getElementStrategy(this.elementRef.nativeElement, this.placement(), 4);
1625
+ const overlay = this.overlay.create({
1626
+ positionStrategy,
1058
1627
  scrollStrategy: this.overlay.scrollStrategies.noop(),
1059
- usePopover: !!this.overlayContext?.useTopLayer || !!source.closest("dialog, [role=\"dialog\"], [role=\"alertdialog\"], bx-modal, bx-modal-core, [popover]"),
1060
- hasBackdrop: true,
1628
+ usePopover: !!this.overlayContext?.useTopLayer || !!this.elementRef.nativeElement.closest("dialog, [role=\"dialog\"], [role=\"alertdialog\"], bx-modal, bx-modal-core, [popover]"),
1629
+ hasBackdrop: this.bxTransition() === "none",
1061
1630
  backdropClass: "cdk-overlay-transparent-backdrop"
1062
1631
  });
1632
+ this.overlayRef = overlay;
1633
+ this.overlayId.set(overlay.overlayElement.id || null);
1634
+ this.disconnectTheme = this.zone.runOutsideAngular(() => inheritOverlayTheme(this.elementRef.nativeElement, overlay.overlayElement));
1635
+ this.disconnectLighting = inheritGlassLighting(overlay.overlayElement, this.elementRef.nativeElement);
1063
1636
  this.subscriptions = new Subscription();
1064
- const win = source.ownerDocument.defaultView;
1065
- if (win) this.zone.runOutsideAngular(() => {
1066
- const closeOnScroll = (event) => {
1067
- const target = event.target;
1068
- if (this.overlayRef?.overlayElement.inert) return;
1069
- if (target === win || target instanceof Node && target.contains(source)) this.zone.run(() => this.close());
1070
- };
1071
- win.addEventListener("scroll", closeOnScroll, {
1072
- capture: true,
1073
- passive: true
1074
- });
1075
- this.subscriptions.add(() => win.removeEventListener("scroll", closeOnScroll, true));
1076
- });
1077
- this.subscriptions.add(this.overlayRef.backdropClick().subscribe(() => this.close()));
1078
- this.subscriptions.add(this.overlayRef.detachments().subscribe(() => this.dispose()));
1079
- this.subscriptions.add(this.overlayRef.keydownEvents().subscribe((event) => this.onMenuKey(event)));
1080
- const pane = this.overlayRef.overlayElement;
1081
- this.disconnectTheme = this.zone.runOutsideAngular(() => inheritOverlayTheme(source, pane));
1082
- this.disconnectLighting = inheritGlassLighting(pane, source);
1083
- this.overlayRef.attach(new TemplatePortal(this.bxContextMenu(), this.vcr)).detectChanges();
1084
- this.overlayRef.updatePosition();
1085
- if (!this.a11y.isReducedMotion()) {
1086
- const point = source.ownerDocument.createElement("span");
1087
- point.setAttribute("aria-hidden", "true");
1088
- Object.assign(point.style, {
1089
- position: "fixed",
1090
- left: `${x}px`,
1091
- top: `${y}px`,
1092
- width: "1px",
1093
- height: "1px",
1094
- pointerEvents: "none"
1095
- });
1096
- source.ownerDocument.body.append(point);
1097
- this.point = point;
1098
- const transition = this.zone.runOutsideAngular(() => createGlassTransition(point, pane));
1099
- this.transition = transition;
1100
- this.zone.runOutsideAngular(() => transition.enter()).catch(() => {
1101
- this.transition?.destroy();
1102
- this.transition = void 0;
1103
- });
1104
- }
1637
+ this.subscriptions.add(this.zone.runOutsideAngular(() => trackOverlayOrigin(overlay, this.elementRef.nativeElement)));
1638
+ this.subscriptions.add(overlay.backdropClick().subscribe(() => this.close()));
1639
+ this.subscriptions.add(overlay.outsidePointerEvents().subscribe((event) => {
1640
+ if (!this.elementRef.nativeElement.contains(event.target)) this.close();
1641
+ }));
1642
+ this.subscriptions.add(overlay.keydownEvents().subscribe((event) => {
1643
+ if (event.key === "Escape" && this.isOpen()) {
1644
+ event.preventDefault();
1645
+ event.stopPropagation();
1646
+ this.close(true);
1647
+ } else if (!event.defaultPrevented && this.bxTransition() !== "none" && this.isOpen()) this.navigateMenu(event);
1648
+ }));
1649
+ this.subscriptions.add(positionStrategy.positionChanges.subscribe(() => {
1650
+ this.zone.runOutsideAngular(() => this.transition?.refresh());
1651
+ }));
1652
+ overlay.attach(new TemplatePortal(this.bxDropdown(), this.vcr)).detectChanges();
1653
+ overlay.updatePosition();
1105
1654
  }
1106
- items() {
1107
- return Array.from(this.overlayRef?.overlayElement.querySelectorAll("button:not(:disabled), a[href], [role=\"menuitem\"]") ?? []).filter((item) => !item.matches("[disabled], [aria-disabled=\"true\"]") && !item.closest("[hidden], [inert]"));
1655
+ isDisabled() {
1656
+ const element = this.elementRef.nativeElement;
1657
+ return element.hasAttribute("disabled") || element.getAttribute("aria-disabled") === "true";
1108
1658
  }
1109
- onMenuKey(event) {
1110
- if (event.key === "Escape") {
1111
- event.preventDefault();
1112
- event.stopPropagation();
1113
- this.close(true);
1114
- return;
1115
- }
1116
- if (event.target?.closest("input,textarea,[contenteditable=\"true\"]")) return;
1659
+ menuItems() {
1660
+ const pane = this.overlayRef?.overlayElement;
1661
+ if (!pane) return [];
1662
+ return Array.from(pane.querySelectorAll("button, a[href], [role=\"menuitem\"]")).filter((item) => {
1663
+ if (item.matches(":disabled, [disabled], [aria-disabled=\"true\"]") || item.closest("[aria-disabled=\"true\"]")) return false;
1664
+ if (item.closest("[hidden], [inert], [aria-hidden=\"true\"]")) return false;
1665
+ if (item.tabIndex < 0 && !item.hasAttribute("tabindex")) return false;
1666
+ const style = item.ownerDocument.defaultView?.getComputedStyle(item);
1667
+ return !!style && style.display !== "none" && style.visibility !== "hidden" && item.getClientRects().length > 0;
1668
+ });
1669
+ }
1670
+ navigateMenu(event) {
1671
+ if (this.isEditing(event.target) || !(event.target instanceof Node) || !this.overlayRef?.overlayElement.contains(event.target)) return;
1117
1672
  if (![
1118
1673
  "ArrowDown",
1119
1674
  "ArrowUp",
1120
1675
  "Home",
1121
1676
  "End"
1122
1677
  ].includes(event.key)) return;
1123
- const items = this.items();
1678
+ const items = this.menuItems();
1124
1679
  if (!items.length) return;
1125
- event.preventDefault();
1126
1680
  const current = items.indexOf(this.elementRef.nativeElement.ownerDocument.activeElement);
1127
- items[event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : event.key === "ArrowDown" ? (current + 1) % items.length : current < 0 ? items.length - 1 : (current - 1 + items.length) % items.length].focus();
1681
+ let index;
1682
+ if (event.key === "Home") index = 0;
1683
+ else if (event.key === "End") index = items.length - 1;
1684
+ else if (event.key === "ArrowDown") index = current < 0 ? 0 : (current + 1) % items.length;
1685
+ else index = current < 0 ? items.length - 1 : (current - 1 + items.length) % items.length;
1686
+ event.preventDefault();
1687
+ event.stopPropagation();
1688
+ items[index].focus({ preventScroll: true });
1689
+ }
1690
+ isEditing(target) {
1691
+ return target instanceof Element && !!target.closest("input, textarea, select, [contenteditable]:not([contenteditable=\"false\"]), [role=\"textbox\"]");
1128
1692
  }
1129
1693
  dispose() {
1130
- this.subscriptions.unsubscribe();
1131
1694
  this.disconnectTheme?.();
1132
1695
  this.disconnectTheme = void 0;
1133
1696
  this.transition?.destroy();
1134
- this.transition = void 0;
1135
- this.point?.remove();
1136
- this.point = void 0;
1137
- const overlay = this.overlayRef;
1697
+ this.transition = null;
1698
+ this.subscriptions.unsubscribe();
1699
+ this.overlayRef?.dispose();
1138
1700
  this.overlayRef = null;
1139
- overlay?.dispose();
1140
1701
  this.disconnectLighting?.();
1141
1702
  this.disconnectLighting = void 0;
1703
+ this.overlayId.set(null);
1704
+ this._isOpen.set(false);
1705
+ this._state.set("closed");
1142
1706
  }
1143
1707
  static ɵfac = i0.ɵɵngDeclareFactory({
1144
1708
  minVersion: "12.0.0",
1145
1709
  version: "22.2.3",
1146
1710
  ngImport: i0,
1147
- type: ContextMenuDirective,
1711
+ type: DropdownDirective,
1148
1712
  deps: [],
1149
1713
  target: i0.ɵɵFactoryTarget.Directive
1150
1714
  });
1151
1715
  static ɵdir = i0.ɵɵngDeclareDirective({
1152
1716
  minVersion: "17.1.0",
1153
1717
  version: "22.2.3",
1154
- type: ContextMenuDirective,
1718
+ type: DropdownDirective,
1155
1719
  isStandalone: true,
1156
- selector: "[bxContextMenu]",
1720
+ selector: "[bxDropdown]",
1157
1721
  inputs: {
1158
- bxContextMenu: {
1159
- classPropertyName: "bxContextMenu",
1160
- publicName: "bxContextMenu",
1722
+ bxDropdown: {
1723
+ classPropertyName: "bxDropdown",
1724
+ publicName: "bxDropdown",
1161
1725
  isSignal: true,
1162
1726
  isRequired: true,
1163
1727
  transformFunction: null
1164
1728
  },
1165
- bxContextMenuLongPress: {
1166
- classPropertyName: "bxContextMenuLongPress",
1167
- publicName: "bxContextMenuLongPress",
1729
+ placement: {
1730
+ classPropertyName: "placement",
1731
+ publicName: "placement",
1732
+ isSignal: true,
1733
+ isRequired: false,
1734
+ transformFunction: null
1735
+ },
1736
+ bxTransition: {
1737
+ classPropertyName: "bxTransition",
1738
+ publicName: "bxTransition",
1168
1739
  isSignal: true,
1169
1740
  isRequired: false,
1170
1741
  transformFunction: null
1171
1742
  }
1172
1743
  },
1173
- host: { listeners: {
1174
- "contextmenu": "open($event)",
1175
- "keydown": "onKey($event)",
1176
- "document:click": "onDocumentClick($event)"
1177
- } },
1744
+ host: {
1745
+ listeners: {
1746
+ "click": "toggle($event)",
1747
+ "keydown": "onTriggerKeydown($event)",
1748
+ "document:click": "onDocumentClick($event)"
1749
+ },
1750
+ properties: {
1751
+ "attr.aria-expanded": "isOpen()",
1752
+ "attr.aria-haspopup": "\"menu\"",
1753
+ "attr.aria-controls": "overlayId()"
1754
+ }
1755
+ },
1756
+ exportAs: ["bxDropdown"],
1178
1757
  ngImport: i0
1179
1758
  });
1180
1759
  };
@@ -1182,34 +1761,51 @@ i0.ɵɵngDeclareClassMetadata({
1182
1761
  minVersion: "12.0.0",
1183
1762
  version: "22.2.3",
1184
1763
  ngImport: i0,
1185
- type: ContextMenuDirective,
1764
+ type: DropdownDirective,
1186
1765
  decorators: [{
1187
1766
  type: Directive,
1188
- args: [{ selector: "[bxContextMenu]" }]
1767
+ args: [{
1768
+ selector: "[bxDropdown]",
1769
+ exportAs: "bxDropdown",
1770
+ standalone: true,
1771
+ host: {
1772
+ "[attr.aria-expanded]": "isOpen()",
1773
+ "[attr.aria-haspopup]": "\"menu\"",
1774
+ "[attr.aria-controls]": "overlayId()"
1775
+ }
1776
+ }]
1189
1777
  }],
1190
1778
  ctorParameters: () => [],
1191
1779
  propDecorators: {
1192
- bxContextMenu: [{
1780
+ bxDropdown: [{
1193
1781
  type: i0.Input,
1194
1782
  args: [{
1195
1783
  isSignal: true,
1196
- alias: "bxContextMenu",
1784
+ alias: "bxDropdown",
1197
1785
  required: true
1198
1786
  }]
1199
1787
  }],
1200
- bxContextMenuLongPress: [{
1788
+ placement: [{
1201
1789
  type: i0.Input,
1202
1790
  args: [{
1203
1791
  isSignal: true,
1204
- alias: "bxContextMenuLongPress",
1792
+ alias: "placement",
1205
1793
  required: false
1206
1794
  }]
1207
1795
  }],
1208
- open: [{
1796
+ bxTransition: [{
1797
+ type: i0.Input,
1798
+ args: [{
1799
+ isSignal: true,
1800
+ alias: "bxTransition",
1801
+ required: false
1802
+ }]
1803
+ }],
1804
+ toggle: [{
1209
1805
  type: HostListener,
1210
- args: ["contextmenu", ["$event"]]
1806
+ args: ["click", ["$event"]]
1211
1807
  }],
1212
- onKey: [{
1808
+ onTriggerKeydown: [{
1213
1809
  type: HostListener,
1214
1810
  args: ["keydown", ["$event"]]
1215
1811
  }],
@@ -1277,6 +1873,6 @@ i0.ɵɵngDeclareClassMetadata({
1277
1873
  }]
1278
1874
  }] }
1279
1875
  });
1280
- export { BX_OVERLAY_CONTEXT, BxOverlayContextDirective, ContextMenuDirective, DropdownDirective, Menu, MenuItem, Tooltip, TooltipDirective };
1876
+ export { BX_OVERLAY_CONTEXT, BxOverlayContextDirective, ContextMenuDirective, DropdownDirective, Menu, MenuItem, Tab, TabContentDirective, Tabs, Tooltip, TooltipDirective };
1281
1877
 
1282
1878
  //# sourceMappingURL=filip.mazev-blocks-primitives.mjs.map