Giter Club home page Giter Club logo

nanopop's Introduction

Logo

Ultra Tiny, Opinionated Positioning Engine

gzip size brotli size Build Status Download count No dependencies JSDelivr download count Current version Support me


Nanopop is an ultra-tiny positioning engine. Hold up, isn't there PopperJS? Yeah - and PopperJS is great! But there are tons of features that, in most cases, you just might not need. This library is less than a third of PopperJS.

When should I use Nanopop and not PopperJS?

  1. Situations where you want full control over positioning, including handling events such as scrolling, and manual resizing.
  2. Performance-critical cases with lots of elements [...] nanopop will only makes changes if you say so.
  3. Poppers with minimal footprint such as drop-downs and tooltips which don't require that much configurability.
  4. You might have some special needs about how your popper behaves. Index exposes a function for the sole purpose of positioning something, use it in your own library!

This library was originally part of pickr - now ported to TS with tests and a few updates / bug-fixes.

Heads up! This is the readme for v2 - if you're looking for the first version head over here (v1 is not maintained anymore).

Getting Started

Install via npm:

$ npm install nanopop

Install via yarn:

$ yarn add nanopop

Include directly via jsdelivr:

<script src="https://cdn.jsdelivr.net/npm/nanopop/dist/nanopop.umd.js"></script>

Using JavaScript Modules:

import {
    reposition,   // Core, stateless function to reposition an element
    createPopper, // Stateful function which keeps track of your configuration
    defaults,     // A subset of nanopops options used as default values
    version       // Current version
} from 'https://cdn.jsdelivr.net/npm/nanopop/lib/nanopop.min.mjs'

๐ŸŒŸ Index is fully tree-shakable! E.g. if you only use reposition you'll probably end up with less than 500B code!

Usage

reposition(
    /* reference: */ document.querySelector('.btn'),
    /* popper: */ document.querySelector('.dropdown'),
    /* We're using the default options */
);

โš  The popper-element must have set position to fixed.

โ„น Because the default-container is document.documentElement you might have to increase the height of the html element to make room for your popper (e.g. html {height: 100vh;})

All options

import {reposition, createPopper} from 'nanopop';

// Using a object and reposition directly
const nanopop = reposition(reference, popper, {

    // The DOMRect of the container, it used the html-element as default.
    // You could also create your own boundary using a custon DOMRect (https://developer.mozilla.org/en-US/docs/Web/API/DOMRect)!
    container: document.documentElement.getBoundingClientRect(),

    // Optional arrow element that is positioned between the popper and the reference element.
    arrow: undefined,

    // Margin between the popper element and the reference
    margin: 8,

    // Minimum space between the popper and the container
    padding: 0,

    // Preferred position, any combination of [top|right|bottom|left]-[start|middle|end] is valid.
    // 'middle' is used as default-variant if you leave it out.
    position: 'bottom-middle',

    // In case the variant-part (start, middle or end) cannot be applied you can specify what (and if)
    // should be tried next.
    variantFlipOrder: {
        start: 'sme', // In case of -start try 'start' first, if that fails 'middle' and 'end' if both doesn't work.
        middle: 'mse',
        end: 'ems'
    },

    // The same as variantFlipOrder, but if all variants fail you might want to try other positions.
    positionFlipOrder: {
        top: 'tbrl', // Try 'top' first, 'bottom' second, 'right' third and 'left' as latest position.
        right: 'rltb',
        bottom: 'btrl',
        left: 'lrbt'
    }
});

/**
 * Using the createPopper function to create a stateful wrapper
 *
 * Correct ways of calling it are:
 * createPopper(reference: HTMLElement, popper: HTMLElement, options?: NanoPopOptions)
 * createPopper(options?: NanoPopOptions)
 * โš  If you omit options entierly you'll have to set both the reference and the popper later when calling .update!
 */
const popper = createPopper({...});
popper.update(); // You can pass an object to update which will get merged with the existing config.

Calling popper.update(...) or reposition(...) both returns a position-pair (For example te for Top-End) or null based on if it was possible to find a position for the popper without clipping it._

Tip: The returned position-pair is perfect for tool-tips to give them a little arrow!

Caveats

  1. The popper-element must have position set to fixed.
  2. If nanopop cannot find a position without clipping your popper it'll revert its top and left values - you can use css / js to handle this case.

nanopop's People

Contributors

atmos4 avatar dependabot[bot] avatar fitsum avatar mojabyte avatar nateseymour avatar panphora avatar simonwep avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

nanopop's Issues

suggestion: position relative to mouse pointer

Love this lib, and would like to see a feature for positioning by mouse and not by element

This is for tooltips which I want to show when hovering some elements, but I want the tooltip to follow the mouse as long as it is hovered on the element (which I will manage myself using mouseenter, mouseleave & mousemove events)

Suggested API:

NanoPop.reposition( 'pointer', tipElm )

Usage with React

Hey there. Is it safe to use with react lifecycle hooks? As I noticed nanopop has no method to destroy an instance and react will try to create another one every re-render. Thanks

fix position for reference node inside an iframe and popper outside

reposition(
    /* reference: */ document.querySelector('.btn'),
    /* popper: */ document.querySelector('.dropdown'),
    /* We're using the default options */
)

I have situation where an element inside an iframe injects a popup outside of the iframe and shows it, and I need to position this popup near the element which triggered it, but nanopop cannot position it:

https://jsbin.com/duwohoz/2/edit?html,js,output

the click event data is unhelpful because it is relative to the iframe document, but I think nanopop can try and check if the reference element is inside an iframe (of course same-origin one) and if so, get the iframe position

In my demo above I show how to find the correct iframe which hosts the reference node and through that, find the iframes position on the parent document and now nanopop should theoretically detect all that and offset the position of the popper accordingly

Thanks for this awesome lightweight script!


This is for my open-source knobs project which is encapsulated inside an iframe but I am adding a color picker that should open outside (there's no visual space for it inside the iframe)

Node version issue

image

I changed nothing. But when ci/cd runs cmd gives me above error. What should I do? I can not upgrade node version. Because others libs contains version error.

Alignment when body height is not 100%

This isn't a bug as such, but it did catch me by surprise, so I thought I'd document it and you can decide if it is a bug or not.

The alignment offered by nanopop fails (return null) when the height of the window is large enough for the content to align correctly, but the body is not (i.e. it might have a small amount of content, or a position fixed element which is what I was using).

Here is a screenshot showing the body being short:

image

Edit: Bother, the firefox highlighting of the element didn't get included in the screenshot. The body tag height ends just after the little CloudTables logo.

Perhaps when the container is the body, it should use the window height / width rather than the body? I worked around it in my case with a min height of 100% for the body.

Truncate when no viable match

It would be great if this could be extended such that in the case of failure to find any fit we could (optionally) discover the position-variant fit that has the smallest overflow and use that position but reduce the height / width of the popper so that it still fits on the screen.

Seems like in the case of a total miss, top & left of 0,0 is almost never what you want to happen.

Boundary padding

Is there any way I could add a padding to the container rect so that my popper elements don't stick to the very edge of the screen?

forceApplyOnFailure missed in v2? How now to handle manually positions of element?

Hi, I see that forceApplyOnFailure missed in v2
How now to handle manually positions of element?
For example, I now that an element need to have position 'bottom-end'.
if the element does not fit into the calculated area to take the desired positioning, your plugin uses positionFlipOrder.
if the element does not fit in any of the existing positionFlipOrder variant - the element receive top: 0; left: 0;
Is it possible to somehow do so that the element in this case simply does not have anything? That is, to remove any inline styles? In this case, I would be able to set position with css only.

DEMO: https://codesandbox.io/s/practical-pond-zsump?file=/src/App.js

As a progressive improvement, I would consider not removing / resetting to 0 - all positioning styles, but only those that are not compromised. For example if position is 'bottom-end', and vertically element anyway is in normal rythm, but only have problems with horizontally position, that solution is only to remove left: XX from inline styles, but leave top: YY.
Because in this case, I could simply set a fallback using css: left: 0; right: 0; and element is fullwidth pisitioned.

Thanks!

Position "bottom-middle" not behaving as expected

When using an inline-block button and a wide dropdown element, with Nanopop configured to have the dropdown appear in the "bottom-middle", I instead see the dropdown appearing on the right side of the button.

NanoPop(reference, popper, {
  position: "bottom-middle"
});

Screen Shot 2020-05-29 at 11 41 38 AM

I created an example to showcase the code: https://codepen.io/panphora/pen/RwWXwWw

Can you help me figure out how to get the dropdown to appear below the button and in the middle?

suggestion: add position information to the positioned node

I need the reposition method to add information regarding the placement of the popper element because I want to use it as tooltips and I need to know where are the poppers places so I could render the correct triangle tip which will point to the side which triggered the tooltip

Wrong arrow positioning in transformed container

When the reference, popper, and arrow elements are descendants of an element with transform style, the arrow positioning will break, while the popper position is correct. It happens only when the container with the transform style is smaller than the reference size + popper size.

To reproduce, use this test that applies a transform: scale(1); style to a parent with no visual changes:

<div id="container">
    <div id="transform">
        <div id="reference"></div>
        <div id="popper"></div>
        <div id="arrow"></div>
    </div>
</div>

<style>
    @import '_reset.css';

    #container {
        display: flex;
        align-items: center;
        justify-content: center;
    }

    #transform {
        transform: scale(1);
        display: flex;
        align-items: center;
        justify-content: center;
    }

    #reference {
        width: 40px;
        height: 40px;
    }

    #popper {
        width: 100px;
        height: 100px;
    }

    #arrow{
        position: fixed;
        width: 10px;
        height: 10px;
        background: black;
        transform: translate(-50%, -50%) rotate(45deg);
    }
</style>

<script type="module">
    import {reposition} from '/dist/nanopop.mjs';

    const reference = document.querySelector('#reference');
    const popper = document.querySelector('#popper');
    const arrow = document.querySelector('#arrow');

    reposition(reference, popper, {
        position: location.hash.slice(1),
        arrow: arrow
    });
</script>

the result would be:

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.