Giter Site home page Giter Site logo

oleggrishechkin / react-viewport-list Goto Github PK

View Code? Open in Web Editor NEW
211.0 9.0 20.0 807 KB

πŸ“œ Virtualization for lists with dynamic item size

Home Page: https://codesandbox.io/s/react-viewport-list-xw2rt

License: MIT License

TypeScript 99.82% Shell 0.18%
react react-virtual react-virtualized react-window virtualization

react-viewport-list's Introduction

React ViewPort List

NPM version typescript NPM license NPM total downloads NPM monthly downloads

If your application renders long lists of data (hundreds or thousands of rows), we recommended using a technique known as β€œwindowing”. This technique only renders a small subset of your rows at any given time, and can dramatically reduce the time it takes to re-render the components as well as the number of DOM nodes created.

- React.js documentation

πŸ“œ Virtualization for lists with dynamic item size

Features πŸ”₯

  • Simple API like Array.Prototype.map()
  • Created for dynamic item height or width (if you don't know item size)
  • Works perfectly with Flexbox (unlike other libraries with position: absolute)
  • Supports scroll to index
  • Supports initial index
  • Supports vertical ↕ and horizontal ↔ lists️️
  • Tiny (about 2kb minified+gzipped)

Try 100k list demo

Getting Started

  • Installation:

    npm install --save react-viewport-list
  • Basic Usage:

    import { useRef } from 'react';
    import { ViewportList } from 'react-viewport-list';
    
    const ItemList = ({
      items,
    }: {
      items: { id: string; title: string }[];
    }) => {
      const ref = useRef<HTMLDivElement | null>(
        null,
      );
    
      return (
        <div className="scroll-container" ref={ref}>
          <ViewportList
            viewportRef={ref}
            items={items}
          >
            {(item) => (
              <div key={item.id} className="item">
                {item.title}
              </div>
            )}
          </ViewportList>
        </div>
      );
    };
    
    export { ItemList };

MutableRefObject<HTMLElement / null> / RefObject<HTMLElement / null> / { current: HTMLElement / null } / null

Props

name type default description
viewportRef MutableRefObject<HTMLElement / null> / RefObject<HTMLElement / null> / { current: HTMLElement / null } / null required Viewport and scroll container.
document.documentElement will be used if viewportRef not provided.
items T[] [] Array of items.
itemSize number 0 Item average (estimated) size (height for axis="y" and width for axis="x") in px.
Size should be greater or equal zero.
Size will be computed automatically if itemMinSize not provided or equal zero.
itemMargin number -1 Item margin (margin-bottom for axis="y" and margin-right for axis="x") in px.
Margin should be greater or equal -1.
Margin will be computed automatically if margin not provided or equal -1.
You should still set margin in item styles
overscan number 1 Count of "overscan" items.
axis "y" / "x" 'y' Scroll axis:
  • "y" - vertical
  • "x" - horizontal
initialIndex number -1 Initial item index in viewport.
initialAlignToTop boolean true scrollIntoView param.
Used with initialIndex
initialOffset number 0 Offset after scrollIntoView call.
Used with initialIndex.
This value will be added to the scroll after scroll to index.
initialDelay number -1 setTimeout delay for initial scrollToIndex.
Used with initialIndex.
initialPrerender number 0 Used with initialIndex.
This value will modify initial start index and initial end index like [initialIndex - initialPrerender, initialIndex + initialPrerender].
You can use it to avoid blank screen with only one initial item rendered
children (item: T, index: number, array: T[]) => ReactNode required Item render function.
Similar to Array.Prototype.map().
onViewportIndexesChange (viewportIndexes: [number, number]) => void optional Will be called on rendered in viewport indexes change.
overflowAnchor "none" / "auto" "auto" Compatibility for overflow-anchor: none.
Set it to "none" if you use overflow-anchor: none in your parent container styles.
withCache boolean true Cache rendered item heights.
scrollThreshold number 0 If scroll diff more than scrollThreshold setting indexes was skipped. It's can be useful for better fast scroll UX.
renderSpacer (props: { ref: MutableRefObject; style: CSSProperties; type: 'top' / 'bottom' }) => ReactNode ({ ref, style }) => <div ref={ref} style={style} /> In some rare cases you can use specific elements/styles instead of default spacers
count number optional You can use items count instead of items directly. Use should use different children: (index: number) => ReactNode
indexesShift number 0 Every time you unshift (prepend items) you should increase indexesShift by prepended items count. If you shift items (remove items from top of the list you should decrease indexesShift by removed items count).
getItemBoundingClientRect (element: Element) => DOMRect / { bottom: number; left: number; right: number; top: number; width: number; height: number; } (element) => element.getBoundingClientRect() You can use custom rect getter to support display: contents or other cases when element.getBoundingClientRect() returns "bad" data

Methods

scrollToIndex

scrollToIndex method has only one param - options;

Options param

name type default description
index number -1 Item index for scroll.
alignToTop boolean true scrollIntoView param. Only boolean option supported.
offset number 0 Offset after scrollIntoView call.
This value will be added to the scroll after scroll to index.
delay number -1 setTimeout delay for initial scrollToIndex.
prerender number 0 This value will modify initial start index and initial end index like [index - initialPrerender, index + initialPrerender].
You can use it to avoid blank screen with only one initial item rendered

Usage

import { useRef } from 'react';
import { ViewportList } from 'react-viewport-list';

const ItemList = ({
  items,
}: {
  items: { id: string; title: string }[];
}) => {
  const ref = useRef(null);
  const listRef = useRef(null);

  return (
    <div className="scroll-container" ref={ref}>
      <ViewportList
        ref={listRef}
        viewportRef={ref}
        items={items}
      >
        {(item) => (
          <div key={item.id} className="item">
            {item.title}
          </div>
        )}
      </ViewportList>
      <button
        className="up-button"
        onClick={() =>
          listRef.current.scrollToIndex({
            index: 0,
          })
        }
      />
    </div>
  );
};

export { ItemList };

getScrollPosition

getScrollPosition returns an object with scroll position: { index: number, offset: number }

Returns

name type description
index number Item index for scroll.
offset number Offset after scrollIntoView call.
This value will be added to the scroll after scroll to index.

If items=[] or count=0 getScrollPosition returns { index: -1; offset: 0 }

Usage

import { useEffect, useRef } from 'react';
import { ViewportList } from 'react-viewport-list';

const ItemList = ({
  items,
}: {
  items: { id: string; title: string }[];
}) => {
  const ref = useRef(null);
  const listRef = useRef(null);

  useEffect(
    () => () => {
      window.sessionStorage.setItem(
        'lastScrollPosition',
        JSON.stringify(
          listRef.current.getScrollPosition(),
        ),
      );
    },
    [],
  );

  return (
    <div className="scroll-container" ref={ref}>
      <ViewportList
        ref={listRef}
        viewportRef={ref}
        items={items}
      >
        {(item) => (
          <div key={item.id} className="item">
            {item.title}
          </div>
        )}
      </ViewportList>
      <button className="up-button" />
    </div>
  );
};

export { ItemList };

Performance

If you have performance issues, you can add will-change: transform to a scroll container.

You should remember that in some situations will-change: transform can cause performance issues instead of fixing them.

.scroll-container {
  will-change: transform;
}

Children pseudo-classes

ViewportList render two elements (spacers) before first rendered item and after last rendered item. That's why children pseudo-classes like :nth-child(), :last-child, :first-child may work incorrectly.

Margin

If you want more accurate virtualizing you should use equal margin for all items. Also, you should use margin-top or margin-bottom (not both) for axis="y" and margin-right or margin-left (not both) for axis="x".

If you want to use different margins and stil want more accurate virtualizing you can wrap your items in some element like <div> and use padding instead of margin.

Non-keyed

You should avoid non-keyed usage of list. You should provide unique key prop for each list items. If you have issues with scroll in Safari and other browsers without overflow-anchor support, check item's key prop.

Advanced Usage

  • Grouping

    ViewportList render Fragment with items in viewport. So, grouping just work.

    import { useRef } from 'react';
    import { ViewportList } from 'react-viewport-list';
    
    const GroupedItemList = ({
      keyItems,
      items,
    }: {
      keyItems: { id: string; title: string }[];
      items: { id: string; title: string }[];
    }) => {
      const ref = useRef(null);
    
      return (
        <div className="scroll-container" ref={ref}>
          <span className="group-title">
            Key Items
          </span>
          <ViewportList
            viewportRef={ref}
            items={keyItems}
          >
            {(item) => (
              <div
                key={item.id}
                className="key-item"
              >
                {item.title}
              </div>
            )}
          </ViewportList>
          <span className="group-title">Items</span>
          <ViewportList
            viewportRef={ref}
            items={items}
          >
            {(item) => (
              <div key={item.id} className="item">
                {item.title}
              </div>
            )}
          </ViewportList>
        </div>
      );
    };
    export { GroupedItemList };
  • Sorting

    You can use React Sortable HOC

    import { useRef } from 'react';
    import {
      SortableContainer,
      SortableElement,
    } from 'react-sortable-hoc';
    import { ViewportList } from 'react-viewport-list';
    
    const SortableList = SortableContainer(
      ({ innerRef, ...rest }) => (
        <div {...rest} ref={innerRef} />
      ),
    );
    
    const SortableItem = SortableElement(
      (props) => <div {...props} />,
    );
    
    const SortableItemList = ({
      items,
      onSortEnd,
    }) => {
      const ref = useRef(null);
    
      return (
        <SortableList
          innerRef={ref}
          className="scroll-container"
          onSortEnd={onSortEnd}
        >
          <ViewportList
            viewportRef={ref}
            items={items}
          >
            {(item, index) => (
              <SortableItem
                key={index}
                index={index}
                className="item"
              >
                {item.title}
              </SortableItem>
            )}
          </ViewportList>
        </SortableList>
      );
    };
    
    export { SortableItemList };
  • Scroll to position

    Scroll to position may work incorrectly because scrollHeight and scrollTop (or scrollWidth and scrollLeft) changed automatically while scrolling. But you can scroll to position with scrollToIndex method with { index: 0, offset: scrollPosition }. For initial scroll to position you can use initialIndex={0} and initialOffset={scrollPosition}. You should remember that after scroll happened scroll position can be not equal to specified offset.

    import { useRef } from 'react';
    import { ViewportList } from 'react-viewport-list';
    
    const ItemList = ({
      items,
      savedScroll,
    }: {
      items: { id: string; title: string }[];
      savedScroll: number;
    }) => {
      const ref = useRef(null);
      const listRef = useRef(null);
    
      return (
        <div className="scroll-container" ref={ref}>
          <ViewportList
            ref={listRef}
            viewportRef={ref}
            items={items}
            initialIndex={0}
            initialOffset={savedScroll}
          >
            {(item) => (
              <div key={item.id} className="item">
                {item.title}
              </div>
            )}
          </ViewportList>
          <button
            className="up-button"
            onClick={() => {
              // this sets scrollTop of "scroll-container" to 1000
              listRef.current.scrollToIndex({
                index: 0,
                offset: 1000,
              });
            }}
          />
        </div>
      );
    };
    
    export { ItemList };
  • Tests

    You can mock ViewportList for unit tests:

    import {
      useImperativeHandle,
      forwardRef,
    } from 'react';
    
    export const ViewportListMock = forwardRef(
      ({ items = [], children }, ref) => {
        useImperativeHandle(
          ref,
          () => ({
            scrollToIndex: () => {},
          }),
          [],
        );
    
        return (
          <>
            <div />
            {items.map(children)}
            <div />
          </>
        );
      },
    );
    
    export default ViewportListMock;

react-viewport-list's People

Contributors

artsur avatar dependabot[bot] avatar oleggrishechkin avatar zerosoul 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  avatar  avatar  avatar  avatar

react-viewport-list's Issues

Is infinite scroll pagination supported?

Hi, I've just found this library and I am trying to integrate it in my infinite scroll page (using Capacitor with <IonPage />, i don't know if that is relevant 😁 ). The items prop is expected to increase as the user scrolls more and more into the view since I am fetching more and more data from the server.
Currently as I fetch more and more data (I merge all the pages I get from the server in one array) and I pass it to the items prop. But for same reason the component doesn't even iterate through my array. I confirmed and the array is not empty.

Is such behavior supposed to work with this component, or is my approach of passing the total data fetched to the component wrong (should I be passing only the current page?)

Right now I can't reproduce this, but I will be thankful if I get your feedback!

Scroll position

Hi, thanks for your component.

It would be really cool if we could directly define the position of the scroll rather than going through the indexes of "viewport". This would return the user to the exact location of their scroll.

Or to have the Y position of each child.

I remain available if it is not clear to you,
Thanks.

Unexpected scroll jump with initialIndex set to last item

Hello! First of all, I would like to express my gratitude for your library - it's the best virtualization tool I've ever worked with. However, I came across a visual issue: when the initialIndex is set as the last item in the list, there's a noticeable jump in scrolling. This is particularly noticeable when using initialDelay. At the moment, we've addressed this with the visibility: hidden hack for the container until all elements are fully rendered. Do you perhaps have a more optimal solution?

          <ViewportList
            viewportRef={scrollAreaRef}
            items={data}
            overscan={20}
            initialIndex={data.length - 1}
            initialPrerender={10}
            initialDelay={1000}
            initialAlignToTop={false}
            indexesShift={indexesShift}
            onViewportIndexesChange={handleViewportIndexesChange}
          >
            {handleRenderItem}
          </ViewportList>

first video without the visibility: hidden hack
second with the visibility: hidden hack

Screen.Recording.2023-10-17.at.10.34.28.mov
Screen.Recording.2023-10-17.at.10.51.52.mov

Having trouble getting axis="x" to work

Hello!

I continue to use this project and it has been working great for me. Today I have attempted to make my first horizontally scrolling list using <ViewportList>. To start, I just added axis={"x"}, and on reload it still scrolls vertically. I've looked through the docs and its unclear if there's something else that is needed to get it to work? Are there any specific conditions that the individual items need to meet for it to work?

For my use case, I specify the fixed size of the items, both their height and width (they're square images).

Scroll to bottom on initial render

Currently, the behavior is the list stays at top on initial render, I'd like it to be at bottom.
I've tried initialIndex, initialAlignToTop with no luck.

Getting currently rendered item index on viewport

Hi, thanks for creating this wonderful package! This package suits my needs perfectly, but it lacks one feature that makes it perfect: an event emitter to tell which index is being rendered on viewport. It would be nice if you could add this feature πŸ˜ƒ

Endless Scrolling Loop

Sometimes the list will get stuck in a scroll loop. Where it renders another item but this changes the dimensions which means that it then doesn't render the item and so it keeps doing this. Also throws a maximum update depth exceeded error.

I have created a code sandbox which demonstrates this. It's not a realistic example but I was struggling to get the data that makes the columns change size in such a way that it loops.

https://codesandbox.io/s/blue-cloud-lpzrw7?file=/src/App.js

Question regarding CPU usage when idle

I'm currently looking at different virtualization libraries and so far really like this one. But after experimenting a little more with it, I noticed that there is always some CPU usage going on even when nothing is being scrolled or changed. I noticed that this is caused by requestAnimationframe continuously firing and doing work.
Now my question is whether this library could be optimized to skip this work when nothing is changing or whether this is somehow inherent to the technical approach of this library. I haven't looked into the implementation details yet, but haven't run into the same issue with other virtualization libraries.

Is SSR supported?

Hi, just want to double-check that this library will not impede rendering components when JavaScript is disabled (i.e., on SSR apps to ensure that SEO is not impacted).

Thanks!

Spacer Element Types

Any chance you can look into allowing for a different element type for the hidden spacer elements? The reason I ask is because I would like to use this component for an html table. The tr is the permitted content element for tbody. Please see tbody.

Virtual list doesn't work when scrollToIndex = 0

I'm passing a value to scrollToIndex from the first render, since I'm giving the user control to move through the list with the keyboard, but at the beginning, when this value is 0, the library didn't update the list items on scroll. After moving once with the keyboard, the library starts working as intended.
Checking the code, I found that when this value is 0, the frame function gets stuck on this line.
At the beginning the value of variables.current.scrolledToIndex is -1, the comparison gives true, but then since scrollToIndex is 0 and the initial value of startIndex is 1, there's no point where the variables.current.scrolledToIndex variable gets updated, and the function returns.

To workaround this, I prevented the value I'm passing to scrollToIndex to be 0. The virtual list works, but it won't scroll to the first item when navigating with the keyboard.

Thank you

I was fighting with react-window for a while until I found react-viewport-list.
It was easy to install and worked exactly as I expected without inserting unwanted DOM elements or requiring me to specify the height and item size.

Thanks for making this

Scroll to position

I’m using react router and was wondering is there a way in that when a user goes back to a route it will keep the same scroll position? I understand there is a scroll to index and scroll to position using initial offset but I’m having trouble understanding how to calculate the correct offset in this scenario to get the screen to the correct previous scroll position.

Using the following (where variable y i'm storing the previous scrollTop position of the scrollable div) doesn't scroll back to the correct position:
listRef.current.scrollToIndex({
index: 0,
offset: y
});

New items added to bottom of list. After refresh, appeared first.

Hi, thank you for this wonderful library. I'm loving the performance.

I'm rendering a ViewportList like this:

<ViewportList
        ref={listRef}
        viewportRef={ref}
        items={**items**}
        initialIndex={0}
        initialOffset={savedScroll}
      >

items is an array of objects in reverse-chronilogical order. They appear in correct order on first load.

When, a new item is pushed to the front of the items array (It's powered by a firebase query snapshot update), the new item appears in the ViewportList at the bottom of the list, which is unexpected.

After a refresh, the new item appears at the top, as expected.

Is there a way to handle inserting elements to the beginning of the items list, and having it refresh correctly?

Thanks

justify-content: flex-end; hides the scroll bar.

I was trying to make it so, the list starts from bottom of the container, and pushes the items top and once it goes beyond the container height, it starts scrolling.
Everything works expect when I add "justify-content: flex-end;" this to the css, it just doesn't show the scroll bar anymore.

You can try this in here: https://codesandbox.io/p/sandbox/react-viewport-list-forked-txvfgg?file=%2Fsrc%2FApp.js%3A6%2C31
that's the same code base, just added justify-content: flex-end; on the list class on app.css and updated the number of items, and it just stops showing the scroll bar.

Can you please help me with this? Looking forward to hearing from you.
Thank you ~

Grid like list

Screen Shot 2022-09-05 at 00 44 24

I have list of products that are displayed in a grid container 4 items per row for desktop and 2 items per row for mobile. The rows are dynamic so container height depends on product count. Is there a way to set up this behavior?

Styling is breaking if used in material ui table

I have a material ui table which renders 1000+ of rows and when I am placing scroll container div just after styling for and is breaking. Is there a way to handle when it comes to implementing in material ui table thereby ensuring that styling is not breaking. I also tried by changing div element display property as table-row. but still we are not getting default table styling.
And if I make as scroll container then only row is rendering.

When using react-viewport-list, children not being rendered in test.

I'm using Jest and React Testing Library. Windowing works fine in browser, but list items are not being rendered in tests.
Test was able to find only one item of whole list, all other items were not being rendered.

How can I mock this ViewPortList, so that whole list gets rendered at once?.

Demo application drag based scrolling issues with Chrome and Edge on Mac

Hello:

I was trying out the demo application using various browsers (latest versions) in Mac (macOS 12.6) and Windows (10). I noticed that I am not able to easily drag the scrollbar to any desired position using Chrome and Edge for Mac. It is feels like it is stuck or really lags behind whereby it is unusable. The other browsers seem to function reasonably well. In my adventure, I was also able to position the scrollbar such that it would cause to the top and bottom spacer elements to swap height values back and forth repeatedly (infinite loop) until I managed to change position to a place where it would break out of this behavior. This too was found in Chrome/Edge on Mac. Anyway, I will try to provide more details on this issue if I can get the actual drag scrolling to work. With that said, I would like to help troubleshoot these issues further and was wondering if you have any advice please.

Regards,

Bradley

scroll window

hi πŸ‘‹πŸ»
when I scroll the window, I want the viewport list to start scrolling that means synchronize both

flew wrap not work

i have row include col-6 classname
but it always show list not card side card i want like this picture

image

Safari skips elements and scroll with lags

Hi, nice library. Glad to see someone keep trying to create something great in terms of virtualisation of lists.

Have you tested your library on Safari? No matter what kind of content I put into ViewportList, Safari scrolls them incorrectly. Browser ALWAYS skip elements and throws you around the list in some unexpected places. Is there a way to fix it? It happens when you not provide ViewportRef

Wrong indexes onViewportIndexesChange

Hi,

Either I don't understand the behavior or there is a bug.
With "onViewportIndexesChange" the starting index does not coincide with what is actually displayed.
Here it should be 0-1 then 1-2 then 2-3 etc.
indexes-issue

Thanks in advance

Items with position: sticky break the virtual list

If the list has headers items styled with position: sticky, the list breaks.
This is caused due the use of .getBoundingClientRect() on the first element of the list, whose top value doesn't change when positioned as sticky, causing the internals to not recognize the scrolling and preventing the update of the list items.
To try to make it work, I passed the indexes of the sticky items and made the component ignore them when passing the innerRef to the child function, and also made it render these item at all times to prevent the stickied item from disappearing if the list is too long.
My patched version of the component is currently on this repo, but I'm not very confident on the quality of the code, so I decided to make this issue instead of a PR.

BTW, this is the first react-virtual-list component I find that can handle undetermined heights correctly. Great job.

Usage with display: grid

First of all thanks for this package, really nice to have an alternative for react-virtualized.

I'm trying to create a simple two column grid with this css applied to scroll-container, but it completely brakes everything:

{
    display: grid;
    grid-template-columns: 50% 50%;
    gap: 16px 8px;
}

I've try to add it to your example and got the same result.

Can you please provide correct way of css grid usage? Thx.

Any way to show a dynamic number of columns?

Hello!

This is more a question than an issue. I'd love to have a list where the number of columns is specific to the size of the window. Right now with react-viewport-list I seem to only be able to create a list with a single column. Any suggestions on how I might be able to make dynamic? I know how I might go about making it a fixed number of columns (e.g., 2), but curious if there's any way to make it dynamic.

useEffect values are loading up on every re-render. Is there anyway to preserve the new changes?

May I know if there is any way to skip re-render the list of functional components whenever the component is added to the view when scrolling and preserve the changes to that component?

For better explanation, consider this sample code example.

https://codesandbox.io/s/restless-thunder-ekkvne?file=/src/App.js

Here is the problem I am facing -

  1. Whenever a component is rendered to the view, the useEffect gets called again, which takes care of setting initial values for me. So whenever the component is scrolled again into the view, the useEffect get's called and all the new changes are gone as useEffect is getting called and set's the initial values again.
  2. I just like to know if there is any way I can skip this and persist the value that I get from server and skip updating the cache. Will onViewportIndexesChange solve my problem?

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.