@crowdfarming/oliva-ds 1.97.1-rc.1 → 1.98.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8174,57 +8174,133 @@ class TooltipDirective {
8174
8174
  tooltipPosition = TooltipPosition.TOP;
8175
8175
  tooltipForceOpen = false; // Development flag to force tooltip open. To delete when we have the final version of the tooltip.
8176
8176
  overlayRef = null;
8177
+ // Touch devices ('hover: none') emit emulated mouse events that flicker the overlay,
8178
+ // so split the interaction: hover on desktop, tap-to-toggle on touch.
8179
+ supportsHover = typeof window !== 'undefined' &&
8180
+ typeof window.matchMedia === 'function' &&
8181
+ window.matchMedia('(hover: hover)').matches;
8177
8182
  constructor(elementRef, overlay, positionBuilder) {
8178
8183
  this.elementRef = elementRef;
8179
8184
  this.overlay = overlay;
8180
8185
  this.positionBuilder = positionBuilder;
8181
8186
  }
8187
+ onMouseEnter() {
8188
+ // Ignore emulated mouseenter on touch devices — handled via tap.
8189
+ if (!this.supportsHover) {
8190
+ return;
8191
+ }
8192
+ this.show();
8193
+ }
8194
+ onMouseLeave() {
8195
+ if (!this.supportsHover) {
8196
+ return;
8197
+ }
8198
+ this.hide();
8199
+ }
8200
+ // Touch: tap toggles the tooltip; tapping outside closes it via the overlay backdrop.
8201
+ onClick() {
8202
+ if (this.supportsHover) {
8203
+ return;
8204
+ }
8205
+ if (this.overlayRef) {
8206
+ this.hide();
8207
+ }
8208
+ else {
8209
+ this.show();
8210
+ }
8211
+ }
8182
8212
  show() {
8183
8213
  if (this.overlayRef) {
8184
8214
  return;
8185
8215
  }
8186
- // 1. Create the position strategy based on the Input 'tooltipPosition'
8187
8216
  const positionStrategy = this.positionBuilder
8188
8217
  .flexibleConnectedTo(this.elementRef)
8189
- .withPositions([this.getPosition()]);
8190
- // 2. Create the overlay
8191
- this.overlayRef = this.overlay.create({ positionStrategy });
8192
- // 3. Create and attach the component portal
8218
+ .withPositions(this.getPositions());
8219
+ // On touch: a transparent backdrop closes on outside tap; close on scroll.
8220
+ this.overlayRef = this.overlay.create({
8221
+ positionStrategy,
8222
+ scrollStrategy: this.overlay.scrollStrategies.close(),
8223
+ hasBackdrop: !this.supportsHover,
8224
+ backdropClass: 'cdk-overlay-transparent-backdrop',
8225
+ });
8226
+ // Scroll-to-close detaches (not disposes); reset our ref so the next tap re-opens.
8227
+ this.overlayRef.detachments().subscribe(() => this.disposeOverlay());
8228
+ if (!this.supportsHover) {
8229
+ this.overlayRef.backdropClick().subscribe(() => this.hide());
8230
+ }
8193
8231
  const tooltipPortal = new ComponentPortal(TooltipComponent);
8194
8232
  const componentRef = this.overlayRef.attach(tooltipPortal);
8195
- // 4. Pass the text to the component
8196
8233
  componentRef.instance.text = this.libTooltip;
8197
8234
  componentRef.instance.position = this.tooltipPosition;
8235
+ // Keep the arrow on the side CDK actually applied (getPositions may flip it).
8236
+ positionStrategy.positionChanges.subscribe((change) => {
8237
+ componentRef.instance.position = this.toTooltipPosition(change.connectionPair);
8238
+ componentRef.changeDetectorRef.detectChanges();
8239
+ });
8198
8240
  }
8199
8241
  hide() {
8200
8242
  // Don't hide if forceOpen is true. Useful to test the tooltip.
8201
8243
  if (this.tooltipForceOpen) {
8202
8244
  return;
8203
8245
  }
8204
- if (this.overlayRef) {
8205
- this.overlayRef.dispose();
8206
- this.overlayRef = null;
8207
- }
8246
+ this.disposeOverlay();
8208
8247
  }
8209
8248
  // Clean up when the component that is launching the tooltip is destroyed
8210
8249
  ngOnDestroy() {
8211
- this.hide();
8250
+ this.disposeOverlay();
8212
8251
  }
8213
8252
  /**
8214
8253
  * Manually hide the tooltip. Useful to test the tooltip.
8215
8254
  */
8216
8255
  forceHide() {
8217
- if (this.overlayRef) {
8218
- this.overlayRef.dispose();
8219
- this.overlayRef = null;
8256
+ this.disposeOverlay();
8257
+ }
8258
+ disposeOverlay() {
8259
+ // Null before disposing: dispose() emits detachments(), which re-enters this
8260
+ // method — clearing first prevents a double dispose.
8261
+ const ref = this.overlayRef;
8262
+ this.overlayRef = null;
8263
+ ref?.dispose();
8264
+ }
8265
+ /**
8266
+ * Maps the position CDK actually applied back to a TooltipPosition, so the arrow
8267
+ * points to the right side after a flip.
8268
+ */
8269
+ toTooltipPosition(pair) {
8270
+ if (pair.overlayY === 'bottom') {
8271
+ return TooltipPosition.TOP;
8272
+ }
8273
+ if (pair.overlayY === 'top') {
8274
+ return TooltipPosition.BOTTOM;
8275
+ }
8276
+ if (pair.overlayX === 'end') {
8277
+ return TooltipPosition.LEFT;
8220
8278
  }
8279
+ return TooltipPosition.RIGHT;
8221
8280
  }
8222
8281
  /**
8223
- * Returns the CDK position configuration based on the 'tooltipPosition' value.
8282
+ * Ordered positions handed to CDK when the tooltip opens: the preferred one first,
8283
+ * its opposite as a fallback so CDK flips to the other side when the preferred one
8284
+ * doesn't fit.
8224
8285
  */
8225
- getPosition() {
8286
+ getPositions() {
8287
+ const opposite = {
8288
+ [TooltipPosition.TOP]: TooltipPosition.BOTTOM,
8289
+ [TooltipPosition.BOTTOM]: TooltipPosition.TOP,
8290
+ [TooltipPosition.LEFT]: TooltipPosition.RIGHT,
8291
+ [TooltipPosition.RIGHT]: TooltipPosition.LEFT,
8292
+ };
8293
+ return [
8294
+ this.getPosition(this.tooltipPosition),
8295
+ this.getPosition(opposite[this.tooltipPosition]),
8296
+ ];
8297
+ }
8298
+ /**
8299
+ * Returns the CDK position configuration for a given tooltip position.
8300
+ */
8301
+ getPosition(position) {
8226
8302
  const offset = 2; // Space between the element and the tooltip
8227
- switch (this.tooltipPosition) {
8303
+ switch (position) {
8228
8304
  case 'bottom':
8229
8305
  return {
8230
8306
  originX: 'center',
@@ -8249,18 +8325,26 @@ class TooltipDirective {
8249
8325
  overlayY: 'center',
8250
8326
  offsetX: offset,
8251
8327
  };
8252
- default: // 'top'
8328
+ default: {
8329
+ // 'top' — bottom-anchored. On iOS with viewport-fit=cover the fixed overlay
8330
+ // container is `innerHeight` tall while CDK anchors `bottom` using
8331
+ // `documentElement.clientHeight`; the gap pushes the tooltip down over the
8332
+ // origin (only affects `top`, only on touch). Compensate it in the offset.
8333
+ const viewportGap = this.supportsHover
8334
+ ? 0
8335
+ : Math.max(0, window.innerHeight - document.documentElement.clientHeight);
8253
8336
  return {
8254
8337
  originX: 'center',
8255
8338
  originY: 'top',
8256
8339
  overlayX: 'center',
8257
8340
  overlayY: 'bottom',
8258
- offsetY: -offset,
8341
+ offsetY: -offset - viewportGap,
8259
8342
  };
8343
+ }
8260
8344
  }
8261
8345
  }
8262
8346
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.7", ngImport: i0, type: TooltipDirective, deps: [{ token: i0.ElementRef }, { token: i1$4.Overlay }, { token: i1$4.OverlayPositionBuilder }], target: i0.ɵɵFactoryTarget.Directive });
8263
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.7", type: TooltipDirective, isStandalone: true, selector: "[libTooltip]", inputs: { libTooltip: "libTooltip", tooltipPosition: "tooltipPosition", tooltipForceOpen: "tooltipForceOpen" }, host: { listeners: { "mouseenter": "show()", "mouseleave": "hide()" } }, ngImport: i0 });
8347
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.7", type: TooltipDirective, isStandalone: true, selector: "[libTooltip]", inputs: { libTooltip: "libTooltip", tooltipPosition: "tooltipPosition", tooltipForceOpen: "tooltipForceOpen" }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "click": "onClick()" } }, ngImport: i0 });
8264
8348
  }
8265
8349
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.7", ngImport: i0, type: TooltipDirective, decorators: [{
8266
8350
  type: Directive,
@@ -8273,12 +8357,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.7", ngImpor
8273
8357
  type: Input
8274
8358
  }], tooltipForceOpen: [{
8275
8359
  type: Input
8276
- }], show: [{
8360
+ }], onMouseEnter: [{
8277
8361
  type: HostListener,
8278
8362
  args: ['mouseenter']
8279
- }], hide: [{
8363
+ }], onMouseLeave: [{
8280
8364
  type: HostListener,
8281
8365
  args: ['mouseleave']
8366
+ }], onClick: [{
8367
+ type: HostListener,
8368
+ args: ['click']
8282
8369
  }] } });
8283
8370
 
8284
8371
  class TooltipComponent {
@@ -8288,11 +8375,11 @@ class TooltipComponent {
8288
8375
  return `position-${this.position}`;
8289
8376
  }
8290
8377
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.7", ngImport: i0, type: TooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8291
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.7", type: TooltipComponent, isStandalone: true, selector: "lib-tooltip", inputs: { text: "text", position: "position" }, host: { properties: { "class": "this.positionClass" } }, ngImport: i0, template: "<div class=\"c-tooltip\">\n <span class=\"c-tooltip__text\">{{ text }}</span>\n</div>\n", styles: [":host{display:block;filter:drop-shadow(0 4px 8px rgba(0,0,0,.2))}.c-tooltip{position:relative;border-radius:var(--size-border-radius-sm);background:var(--color-core-background-surface-floating);display:flex;max-width:9rem;padding:var(--space-component-padding-md);flex-direction:column;justify-content:center;align-items:flex-start;color:var(--color-core-content-soft);font-family:var(--typography-body-xs-family);font-size:var(--typography-body-xs-size);font-style:normal;font-weight:var(--typography-body-xs-weight);line-height:var(--typography-body-xs-line-height);letter-spacing:var(--typography-body-xs-letter-spacing);animation:fadeIn .2s ease-in-out}.c-tooltip__text{max-width:280px}:host.position-top .c-tooltip{left:50%;transform:translate(-50%) translateY(-.5rem)}:host.position-top .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-left:16px solid transparent;border-right:16px solid transparent;border-top:16px solid var(--color-core-background-surface-floating);bottom:-.5rem;left:50%;transform:translate(-50%)}:host.position-bottom .c-tooltip{left:50%;transform:translate(-50%) translateY(.5rem)}:host.position-bottom .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-left:16px solid transparent;border-right:16px solid transparent;border-bottom:16px solid var(--color-core-background-surface-floating);top:-.5rem;left:50%;transform:translate(-50%)}:host.position-left .c-tooltip{top:50%;transform:translate(-.5rem) translateY(-50%)}:host.position-left .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-top:16px solid transparent;border-bottom:16px solid transparent;border-left:16px solid var(--color-core-background-surface-floating);right:-.5rem;top:50%;transform:translateY(-50%)}:host.position-right .c-tooltip{top:50%;transform:translate(.5rem) translateY(-50%)}:host.position-right .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-top:16px solid transparent;border-bottom:16px solid transparent;border-right:16px solid var(--color-core-background-surface-floating);left:-.5rem;top:50%;transform:translateY(-50%)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"] });
8378
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.7", type: TooltipComponent, isStandalone: true, selector: "lib-tooltip", inputs: { text: "text", position: "position" }, host: { properties: { "class": "this.positionClass" } }, ngImport: i0, template: "<div class=\"c-tooltip\">\n <span class=\"c-tooltip__text\">{{ text }}</span>\n</div>\n", styles: [":host{display:block;pointer-events:none;filter:drop-shadow(0 4px 8px rgba(0,0,0,.2))}.c-tooltip{position:relative;border-radius:var(--size-border-radius-sm);background:var(--color-core-background-surface-floating);display:flex;max-width:9rem;padding:var(--space-component-padding-md);flex-direction:column;justify-content:center;align-items:flex-start;color:var(--color-core-content-soft);font-family:var(--typography-body-xs-family);font-size:var(--typography-body-xs-size);font-style:normal;font-weight:var(--typography-body-xs-weight);line-height:var(--typography-body-xs-line-height);letter-spacing:var(--typography-body-xs-letter-spacing);animation:fadeIn .2s ease-in-out}.c-tooltip__text{max-width:280px}:host.position-top .c-tooltip{left:50%;transform:translate(-50%) translateY(-.5rem)}:host.position-top .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-left:16px solid transparent;border-right:16px solid transparent;border-top:16px solid var(--color-core-background-surface-floating);bottom:-.5rem;left:50%;transform:translate(-50%)}:host.position-bottom .c-tooltip{left:50%;transform:translate(-50%) translateY(.5rem)}:host.position-bottom .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-left:16px solid transparent;border-right:16px solid transparent;border-bottom:16px solid var(--color-core-background-surface-floating);top:-.5rem;left:50%;transform:translate(-50%)}:host.position-left .c-tooltip{top:50%;transform:translate(-.5rem) translateY(-50%)}:host.position-left .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-top:16px solid transparent;border-bottom:16px solid transparent;border-left:16px solid var(--color-core-background-surface-floating);right:-.5rem;top:50%;transform:translateY(-50%)}:host.position-right .c-tooltip{top:50%;transform:translate(.5rem) translateY(-50%)}:host.position-right .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-top:16px solid transparent;border-bottom:16px solid transparent;border-right:16px solid var(--color-core-background-surface-floating);left:-.5rem;top:50%;transform:translateY(-50%)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"] });
8292
8379
  }
8293
8380
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.7", ngImport: i0, type: TooltipComponent, decorators: [{
8294
8381
  type: Component,
8295
- args: [{ selector: 'lib-tooltip', template: "<div class=\"c-tooltip\">\n <span class=\"c-tooltip__text\">{{ text }}</span>\n</div>\n", styles: [":host{display:block;filter:drop-shadow(0 4px 8px rgba(0,0,0,.2))}.c-tooltip{position:relative;border-radius:var(--size-border-radius-sm);background:var(--color-core-background-surface-floating);display:flex;max-width:9rem;padding:var(--space-component-padding-md);flex-direction:column;justify-content:center;align-items:flex-start;color:var(--color-core-content-soft);font-family:var(--typography-body-xs-family);font-size:var(--typography-body-xs-size);font-style:normal;font-weight:var(--typography-body-xs-weight);line-height:var(--typography-body-xs-line-height);letter-spacing:var(--typography-body-xs-letter-spacing);animation:fadeIn .2s ease-in-out}.c-tooltip__text{max-width:280px}:host.position-top .c-tooltip{left:50%;transform:translate(-50%) translateY(-.5rem)}:host.position-top .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-left:16px solid transparent;border-right:16px solid transparent;border-top:16px solid var(--color-core-background-surface-floating);bottom:-.5rem;left:50%;transform:translate(-50%)}:host.position-bottom .c-tooltip{left:50%;transform:translate(-50%) translateY(.5rem)}:host.position-bottom .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-left:16px solid transparent;border-right:16px solid transparent;border-bottom:16px solid var(--color-core-background-surface-floating);top:-.5rem;left:50%;transform:translate(-50%)}:host.position-left .c-tooltip{top:50%;transform:translate(-.5rem) translateY(-50%)}:host.position-left .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-top:16px solid transparent;border-bottom:16px solid transparent;border-left:16px solid var(--color-core-background-surface-floating);right:-.5rem;top:50%;transform:translateY(-50%)}:host.position-right .c-tooltip{top:50%;transform:translate(.5rem) translateY(-50%)}:host.position-right .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-top:16px solid transparent;border-bottom:16px solid transparent;border-right:16px solid var(--color-core-background-surface-floating);left:-.5rem;top:50%;transform:translateY(-50%)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"] }]
8382
+ args: [{ selector: 'lib-tooltip', template: "<div class=\"c-tooltip\">\n <span class=\"c-tooltip__text\">{{ text }}</span>\n</div>\n", styles: [":host{display:block;pointer-events:none;filter:drop-shadow(0 4px 8px rgba(0,0,0,.2))}.c-tooltip{position:relative;border-radius:var(--size-border-radius-sm);background:var(--color-core-background-surface-floating);display:flex;max-width:9rem;padding:var(--space-component-padding-md);flex-direction:column;justify-content:center;align-items:flex-start;color:var(--color-core-content-soft);font-family:var(--typography-body-xs-family);font-size:var(--typography-body-xs-size);font-style:normal;font-weight:var(--typography-body-xs-weight);line-height:var(--typography-body-xs-line-height);letter-spacing:var(--typography-body-xs-letter-spacing);animation:fadeIn .2s ease-in-out}.c-tooltip__text{max-width:280px}:host.position-top .c-tooltip{left:50%;transform:translate(-50%) translateY(-.5rem)}:host.position-top .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-left:16px solid transparent;border-right:16px solid transparent;border-top:16px solid var(--color-core-background-surface-floating);bottom:-.5rem;left:50%;transform:translate(-50%)}:host.position-bottom .c-tooltip{left:50%;transform:translate(-50%) translateY(.5rem)}:host.position-bottom .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-left:16px solid transparent;border-right:16px solid transparent;border-bottom:16px solid var(--color-core-background-surface-floating);top:-.5rem;left:50%;transform:translate(-50%)}:host.position-left .c-tooltip{top:50%;transform:translate(-.5rem) translateY(-50%)}:host.position-left .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-top:16px solid transparent;border-bottom:16px solid transparent;border-left:16px solid var(--color-core-background-surface-floating);right:-.5rem;top:50%;transform:translateY(-50%)}:host.position-right .c-tooltip{top:50%;transform:translate(.5rem) translateY(-50%)}:host.position-right .c-tooltip:before{content:\"\";position:absolute;width:0;height:0;border-top:16px solid transparent;border-bottom:16px solid transparent;border-right:16px solid var(--color-core-background-surface-floating);left:-.5rem;top:50%;transform:translateY(-50%)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"] }]
8296
8383
  }], propDecorators: { text: [{
8297
8384
  type: Input
8298
8385
  }], position: [{