@ovhcloud/ods-cdk v17.2.2
OvhCloud Component Development Kit
See all documentation in doc directory.
Overlay Introduction
The OVHcloud Component Development Kit helps you to make overlay easier.
It can be useful to build this kind of components:
- select: a component that allows the user to select a value of a list. Usually, the content of the list will be displayed just below.
- tooltip: if you want to develop a component that displays a help text when the user hovers an element
- menu: you want to build a menu with items displayed in the foreground and the user can choose an item from the list
You can read the Usage page if you want to start quickly, but we recommend to understand the system under the hood
in the corresponding page too.
Documentation:
- System: understand how works the positioning system
- Usage: follow the guide to build overlays
- Symmetry position strategy: default embedded position strategy
Example of uses:
- Basic: basic usage in vanilla
- Stencil Select: how to use it for a stencil's made Select component
- Stencil Tooltip: how to use it for a stencil's made Tooltip component
Overlay System
The overlay system of ODS CDK is based on a web component we call surface.
A surface is used to display content in the foreground, such as menu, select or tooltip, etc.:
this is the overlay concept. It is very common to all User Interface components.
This is why the overlay system is implemented in ODS CDK: it helps developers to make overlays simpler!
The surface will be displayed after a user action or programmatically.
Principles
In order to make it works, it needs to be anchored to an element: this is the trigger and it represents an anchor for
the surface.
Depending on how you configure the surface, it will be displayed at some computed coordinates (x,y) relatively to
this anchor.
The overlay (the surface) position is computed by placing two references at the same coordinates:
- an anchor cornerreference (that corresponds to the trigger)
- a surface cornerreference (that corresponds to the content of the overlay)
A corner can be placed to the different corner of an HTML
element: top left, top right, bottom left, bottom right.
As the overlay system is compatible with the Right To Left HTML directionality,
we can define the corner references also with: top start, top end, bottom start, bottom end.
The start or end notions will be replaced by the correct left or right depending on the directionality you defined
in your app.
At the end, the surface is linked to his anchor via their corner references.
All the positions are computed thanks to position strategies.
By default, ODS CDK has a built-in strategy called symmetry but you can define your own strategy.
The system works with two different margins:
- a comfort margin: for instance, in comfort mode, the surfacepositioning avoids the edges of the viewport
- a limit margin: for instance, in limit mode, we allow the surfaceto overlap the comfort margin up to the margin limit
the meaning and behavior of the margins are defined inside the strategy.
By default, the system will use these references:
- triggerat- bottom-startcorner
- surfaceat- top-startcorner

When the system will compute the position of the surface,
it will use one of the four corner of the anchor as the zero reference
in order to display at a certain coordinate, relatively to the surface.
You have all the control over the width of the surface in your component:

Directionality
The system is compatible with the RTL directionality.
It detects automatically the direction.
You can find here some examples:

An RTL conversion table is used to translate the wanted position into the real positioning.
Each position corresponds to a specific bit and allows us to change easily the result,
depending on whether we are in RTL mode or not.
For instance, if you indicate Top start for the surface corner and you are in LTR mode,
the system will translate it to Top left:

Position table
The overlay management works with a position table.
Each combination of anchor corner reference and surface corner reference can be described via a position table.
The position strategy uses and completes this table.
The embedded default strategy is called symmetry and works with this base position table:

Position computing
The overlay system starts with a position that you want to use or a default one. The default positions are:
- Bottom startfor the- trigger
- Top startfor the- surface
1. RTL conversion
The first step is to normalize the wanted position thanks to the RTL conversion table.
Once the start or end are converted into the correct direction left or right,
it will call the inspectors.
2. Standard inspections
The inspectors are here to get the available space around the trigger, in the page.
Starting here, this is the position strategy which will define the inspectors
and returns the available computed spaces.
3. Compute position
Once the inspections are done, the overlay system calls the compute function of the position strategy.
The system will check the returned result:
- returns undefined: it keep the position
- returns a fallback: it will apply the fallback position
- returns a different position (switching): it will attempt a second time the workflow with the new position
4. Fallback inspections
When the returned result of compute is a fallback, it will determine another available spacing
through the fallback inspectors.
The result are computed by the position strategy.
5. Standard appliers
When there is no fallback position to apply, the system will call the standard appliers in order to
get the final verticalOffset, verticalALignment, HorizontalOffset, HorizontalAlignment and maxHeight.
This is the nominal case when the overlay can be entirely displayed into the page.
6. Fallback appliers
When we are in a fallback positioning, we had determinate that there is not enought space for displaying.
It will call different appliers for verticalOffset, verticalALignment, HorizontalOffset,
HorizontalAlignment and maxHeight.
Accordingly with the position strategy, the position will be different and in a degraded mode.
7. Apply position
At the end, the final position will be applied on the surface container.

More:
- Symmetry Position strategy: default strategy used
Symmetry strategy
This position strategy is the default built-in strategy used inside the overlay system.
The main principles are:
- always positioning the surfacearound thetrigger, without any overlap (except if not enough space)
- when there is not enough space, in a direction, we display the surfacein the opposite direction. This is why it is calledsymmetry
- When there is not enough space in the opposite direction too, we must fall back the position with something different.
In this case, we stay as much as possible at the original position but never overlapping the limit margin.
And we could limit the surfacesize to a smaller size.
Symmetry position table
Positions that correspond to a vertical and horizontal positioning are defined (green tick). The surface can be centered at the top or bottom of the trigger.
Some positions are not available because of the overlapping of the surface container on top of the anchor (red cross).
The orange arrows shows the position switching when there is not enough space on the vertical Y axis.
The blue arrows are here for horizontal axis.

Symmetry position strategy
The symmetry strategy starts with the default position, except if the user specified another one.
1. RTL conversion
The process is internal to the overlay system and is applied for all strategies.
2. Standard inspections
The standard mode checks the space between the border of the anchor and the border of the comfort margin.
3. Compute position
When there is not enough space and this is the first computation,
it switches to the opposite position, as with symmetry, and it starts again with standard inspections.
When it already switched off, it will apply a fallback system.
Indeed, there is not enough space on the both sides of the trigger.
It will call the fallback inspectors.
4. Fallback inspections
The fallback mode check the space between the border of the anchor and the border of the limit margin.
Then, it will call the fallback appliers.
5. Standard appliers
When there is no fallback position to apply, the system will call the standard appliers in order to
get the final verticalOffset, verticalALignment, HorizontalOffset, HorizontalAlignment and maxHeight.
This is the nominal case when the overlay can be entirely displayed between the anchor and the comfort margin.
6. Fallback appliers
When we are in a fallback positioning, we had determinate that there is not enough space for displaying on both sides.
If the surface size is inferior to the space between the opposites comfort margin of both sides,
it starts by staying sticky to the original side of the position, but overlapping the anchor.
If there is not enough space between comfort margin, it displays the surface vertically
or horizontally centered between limit margins and it won't grow anymore.
In this case, if the viewport is very small, the content will always stay in this configuration.
In all cases, the content will be scrollable if needed.
7. Apply position
At the end, the final position will be applied on the surface container.

Usage
- Setup CDK
- Define the trigger and the surface
- Set the position
- Set a custom position strategy
- Specifications
If you have something to display in an overlay, you can use the surface component of ODS CDK.
We strongly recommend to read the documentation about the principles of the system.
Setup CDK
First of all we have to call the CDK setup in order to define custom elements of the CDK.
It will define the web component named ocdk-surface.
In both vanilla or stencilJs, we have to call it at least one time.
import { ocdkDefineCustomElements } from '@ovhcloud/ods-cdk';
// define custom elements from CDK
ocdkDefineCustomElements()Define the trigger and the surface
First, you need to define an HTML element (that can be a web component) as the anchor for the surface:
it is the trigger element.
Then, describe the content of your overlay in a surface.
These two elements must be in a container element with two css properties position: relative and text-align: initial,
in order to display the surface relatively to this container (at the end relatively to the trigger).
Vanilla
In vanilla, you can do this with the following:
<style>
  #container {
    /* overlay important properties */
    position: relative; /* must be here to make the positioning working well */
    text-align: initial; /* must be here to make the positioning working well for RTL alignment */
    padding: 0; /* must be here to make the computing works. If you need one padding, apply it on trigger or surface */
    /* end overlay important properties */
    display: inline-block; /* recommended */
  }
</style>
<div id="container">
  <div id="trigger">my trigger</div>
  <ocdk-surface id="surface">My surface</ocdk-surface>
</div>Then, we have to link them together, so we need references of these two elements.
import { OcdkSurface } from '@ovhcloud/ods-cdk';
const trigger = document.getElementById('trigger');
const surface = document.getElementById('surface') as OcdkSurface | null;Once we have both references from these two elements, we need to inform the surface what is the anchor.
We must do this when the trigger and the surface elements are defined.
// synchronize the trigger element (anchor) with the surface when both are available
if (trigger && surface) {
  surface.setAnchorElement(trigger);
}StencilJs
In a stencil web component this is similar to the vanilla way.
We can create the web component with the reference feature which is very useful to inform
the surface what is the anchor element:
import { OcdkSurface, ocdkDefineCustomElements, ocdkIsSurface } from '@ovhcloud/ods-cdk';
import { Component, Host, h } from '@stencil/core';
// define custom elements from CDK
ocdkDefineCustomElements();
@Component({
  tag: 'my-overlay',
  styleUrl: 'my-overlay.scss',
  shadow: true,
})
export class MyOverlay {
  /** anchor reference. can be considered as fulfilled by the stencil ref system */
  private anchor!: HTMLElement;
  /** surface reference. initialized when cdk is */
  private surface?: OcdkSurface = undefined;
  /** ... */
  render() {
    return (
      <Host>
        <div class="trigger"
          ref={(el?: HTMLElement) => {
            this.anchor = el as HTMLDivElement;
            this.syncReferences();
          }}>
          My select trigger
        </div>
        <ocdk-surface ref={(el: HTMLElement) => {
          if (ocdkIsSurface(el)) {
            this.surface = el as OcdkSurface;
            this.syncReferences();
          }
        }}>
          Content
        </ocdk-surface>
      </Host>
    );
  }
  /**
   * when ready, set the anchor of the surface as the trigger element
   */
  private syncReferences() {x
    if (this.surface && this.anchor) {
      this.surface.setAnchorElement(this.anchor);
    }
  }
}The css is similar as in Vanilla:
:host {
  /* overlay important properties */
  display: inline-block; /* best practice for web component */
  position: relative; /* must be here to make the positioning working well */
  text-align: initial; /* must be here to make the positioning working well for RTL alignment */
  padding: 0; /* must be here to make the computing works. If you need padding, apply it on trigger or surface */
  /* end overlay important properties */
}Set the position
By default, the system uses the symmetry strategy. You can read more in the corresponding page.
You can change the position you want for your overlay by setting the corner reference of the anchor first and
then the corner reference of the surface:
Vanilla:
// set corners on the previous found surface element (anchor and then surface)
surface.corners = [OcdkSurfaceCorner.BOTTOM_START, OcdkSurfaceCorner.TOP_START];Stencil:
import { OcdkSurfaceCorner } from '@ovhcloud/ods-cdk';
import { Component, Host, h } from '@stencil/core';
/** ... */
@Component({
  tag: 'my-overlay',
  styleUrl: 'my-overlay.scss',
  shadow: true,
})
export class MyOverlay {
  /** ... */
  render() {
    return (
      <Host>
        {/* ... */}
      <ocdk-surface corners={[OcdkSurfaceCorner.BOTTOM_START, OcdkSurfaceCorner.TOP_START]}>
    </ocdk-surface>
    </Host>
  );
  }
}Set a custom position strategy
By default, the system uses the symmetry strategy. You can read more in the corresponding page.
If you want to change how the system apply a position to the overlay, you can change the embedded computing by your own. In that case, you have to write your custom position strategy.
You must define the strategy itself:
import {
  OcdkLogger,
  OcdkSurfaceNormalizedCorner,
  OcdkSurfaceOnePositionStrategy,
  OcdkSurfaceStrategyDefiner,
  OcdkSurfaceStrategyDefinerConfig,
} from '@ovhcloud/ods-cdk';
/**
 * global config to implement for the `custom` example strategy
 */
export interface OcdkSurfaceCustomStrategyConfigExample extends OcdkSurfaceStrategyDefinerConfig {
}
/**
 * `custom` positioning strategy example for the CDK overlay
 */
export class OcdkSurfaceCustomStrategyExample implements OcdkSurfaceStrategyDefiner<OcdkSurfaceCustomStrategyConfigExample> {
  logger = new OcdkLogger('OcdkSurfaceCustomStrategyExample');
  getConfig(): OcdkSurfaceCustomStrategyConfigExample {
    return {};
  }
  getPositions(): [OcdkSurfaceOnePositionStrategy<OcdkSurfaceCustomStrategyConfigExample>, ...OcdkSurfaceOnePositionStrategy<OcdkSurfaceCustomStrategyConfigExample>[]] {
    return [
      // ### anchored at bottom
      {
        cornerPoints: { anchor: OcdkSurfaceNormalizedCorner.BOTTOM_LEFT, origin: OcdkSurfaceNormalizedCorner.TOP_LEFT },
        STRATEGIES: {
          // main mode : standard
          standard: {
            inspectors: {
              // analyze spaces in your own way (comfort margin mode)
              comfort: {
                availableBottom: () => 0,
                availableLeft: () => 0,
                availableRight: () => 0,
                availableTop: () => 0,
              },
              // analyze spaces in your own way (limit margin mode)
              limit: {
                availableBottom: () => 0,
                availableLeft: () => 0,
                availableRight: () => 0,
                availableTop: () => 0,
              },
            },
            // use analyzed spaces (limit and margin) and fill final values
            appliers: {
              maxHeight: (opt) => opt.measurements.surfaceSize.height,
              maxWidth: (opt) => opt.measurements.surfaceSize.width,
              verticalOffset: () => 0,
              verticalAlignment: 'top',
              horizontalOffset: (opt) => -opt.measurements.surfaceSize.width,
              horizontalAlignment: 'right',
            },
          },
          // fallback mode
          FALLBACK: {
            inspectors: {
              // analyze comfort spaces in your own alternative way (standard mode has no enough space)
              comfort: {
                availableBottom: () => 0,
                availableLeft: () => 0,
                availableRight: () => 0,
                availableTop: () => 0,
              },
              // analyze limit spaces in your own alternative way (standard mode has no enough space)
              limit: {
                availableBottom: () => 0,
                availableLeft: () => 0,
                availableRight: () => 0,
                availableTop: () => 0,
              },
            },
            // use analyzed spaces (limit and margin) and fill final values
            appliers: {
              maxHeight: () => 100,
              maxWidth: () => 200,
              verticalOffset: () => 0,
              verticalAlignment: 'top',
              horizontalOffset: () => 0,
              horizontalAlignment: 'left',
            },
          },
          // make a choice of position: keep, switch or fallback
          COMPUTE: () => {
            this.logger.log('[COMPUTE] position BOTTOM_LEFT TOP_LEFT');
            return undefined; // no position switching: apply the current one
          },
        },
      },
      // write here all the other positions of the strategy table position
    ];
  }
}and then, apply it by calling the surface:
import { OcdkSurfaceCustomStrategyExample } from '@ovhcloud/ods-cdk/dev';
const customStrategyInstance = new OcdkSurfaceCustomStrategyExample();
// set strategy on the previous found surface element
surface.setCustomStrategy(customStrategyInstance);Specifications
ocdk-surface component specifications.
properties
| property | reflected | type | default | description | 
|---|---|---|---|---|
| corners | no | [OcdkSurfaceCorner, OcdkSurfaceCorner] | [OcdkSurfaceCorner.TOP_START, OcdkSurfaceCorner.BOTTOM_START] | property to set the corner references for the anchorand thesurface | 
| opened | yes | boolean | false | attribute to open or not the surface | 
| animated | yes | boolean | true | the surface appears with an animation by default. If you want to disable animation, you must set to falsevia the property way likemySurface.animated = falseor.animated={false}(lit-html) | 
| animation | yes | OcdkSurfaceAnimation / 'none' | OcdkSurfaceAnimation.SCALING | the animation for the appearing. You can set noneif you don't want animation. You can setundefinedto reset the default animation. | 
CSS variables
| name | css type | default | description | 
|---|---|---|---|
| --ocdk-surface-z-index | z-index | 8 | custom z-index if needed | 
methods
setAnchorElement
setAnchorElement(element: Element): void
Set up the surface with an anchor element.
The position will be computed depending the position of the element in the page.
open
open(): void
Display the surface overlay and display it with the selected position strategy.
close
close(): void
Hide the surface overlay.
setAnchorCorner
setAnchorCorner(anchorCorner: OcdkSurfaceCorner): void
Set the anchor corner reference that corresponds to the trigger element.
setCornerPoints
setCornerPoints(cornerPoints: Partial<OcdkSurfaceCornerPoints>): void
Equivalent of the corners property.
Set the corner references for the anchor and the surface.
setOriginCorner
setOriginCorner(originCorner: OcdkSurfaceCorner): void
Set the surface corner reference.
setAnchorMargin
setAnchorMargin(margin: Partial<OcdkSurfaceDistance>): void
Set the margin around the anchor.
The overlay will be displayed at a distance that corresponds to these margins.
setCustomStrategy
setCustomStrategy<StrategyConfig extends OcdkSurfaceStrategyDefinerConfig>(strategy: OcdkSurfaceStrategyDefiner<StrategyConfig>): void
Set your own position strategy. When you define a custom strategy, you must use OcdkSurfaceStrategyDefiner. You have to define a config and the different positions with all the inspectors and appliers.
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago