{"id":16785537,"url":"https://github.com/joaom00/selection-popover","last_synced_at":"2025-03-17T02:31:50.255Z","repository":{"id":90268003,"uuid":"606111656","full_name":"joaom00/selection-popover","owner":"joaom00","description":"Easy-to-use, unstyled, composable react selection popover.","archived":false,"fork":false,"pushed_at":"2024-03-28T11:02:01.000Z","size":401,"stargazers_count":60,"open_issues_count":3,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-11T19:49:19.936Z","etag":null,"topics":["medium","popover","radix-ui","react","selection","share","text-selection","unstyled"],"latest_commit_sha":null,"homepage":"https://selection-popover.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joaom00.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-02-24T16:15:56.000Z","updated_at":"2025-02-08T08:28:53.000Z","dependencies_parsed_at":null,"dependency_job_id":"a34545f7-6b02-472c-b1f7-4861b60d41ea","html_url":"https://github.com/joaom00/selection-popover","commit_stats":{"total_commits":19,"total_committers":1,"mean_commits":19.0,"dds":0.0,"last_synced_commit":"7cba6ed9ea90cd37d0aa09d1c9da5f1571b5fac8"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joaom00%2Fselection-popover","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joaom00%2Fselection-popover/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joaom00%2Fselection-popover/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joaom00%2Fselection-popover/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joaom00","download_url":"https://codeload.github.com/joaom00/selection-popover/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243841202,"owners_count":20356441,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["medium","popover","radix-ui","react","selection","share","text-selection","unstyled"],"created_at":"2024-10-13T08:09:38.798Z","updated_at":"2025-03-17T02:31:50.249Z","avatar_url":"https://github.com/joaom00.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca align='center' href=\"https://selection-popover.vercel.app\"\u003e\n    \u003cimg src=\"./website/public/og.png\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Easy-to-use, composable react selection popover\n\u003c/p\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n\u003ca href=\"https://www.npmjs.com/package/selection-popover\"\u003e![npm version](https://img.shields.io/npm/v/selection-popover.svg)\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/selection-popover\"\u003e![npm downloads](https://img.shields.io/npm/dm/selection-popover.svg)\u003c/a\u003e\n\n\u003c/div\u003e\n\n## Install\n\n```bash\nnpm install selection-popover\n```\n\n## Content\n\n- [Anatomy](#anatomy)\n- [API Reference](#api-reference)\n  - [Root](#root)\n  - [Trigger](#trigger)\n  - [Portal](#portal)\n  - [Content](#content)\n  - [Arrow](#arrow)\n- [Examples](#examples)\n  - [Origin-aware animations](#origin-aware-animations)\n  - [Collision-aware animations](#collision-aware-animations)\n  - [Unmount animations](#unmount-animations)\n  - [Use with Radix Toolbar](#use-with-radix-toolbar)\n- [Acknowledgements](#acknowledgements)\n\n## Anatomy\n\nImport all parts and piece them together.\n\n```jsx\nimport * as Selection from 'selection-popover'\n\nexport default () =\u003e (\n  \u003cSelection.Root\u003e\n    \u003cSelection.Trigger /\u003e\n    \u003cSelection.Portal\u003e\n      \u003cSelection.Content\u003e\n        \u003cSelection.Arrow /\u003e\n      \u003c/Selection.Content\u003e\n    \u003c/Selection.Portal\u003e\n  \u003c/Selection.Root\u003e\n)\n```\n\n## API Reference\n\n### Root\n\nContains all the parts of a selection.\n\n| Prop           | Type                      | Default | Description                                                                                                         |\n| -------------- | ------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------- |\n| `defaultOpen`  | `boolean`                 | -       | The open state of the hover card when it is initially rendered. Use when you do not need to control its open state. |\n| `open`         | `boolean`                 | -       | The controlled open state of the popover. Must be used in conjunction with `onOpenChange`.                          |\n| `onOpenChange` | `(open: boolean) =\u003e void` | -       | Event handler called when the open state of the popover changes.                                                    |\n| `whileSelect`  | `boolean`                 | `false` | When `true`, the popover will open while the text is selected, otherwise only when the mouse up.                    |\n| `disabled`     | `boolean`                 | `false` | When true, the popover won't open when text is selected.                                                            |\n| `openDelay`    | `number`                  | `0`     | The duration from when release the mouse until the content opens. In `whileSelect` is when you start the selection. |\n| `closeDelay`   | `number`                  | `0`     | The duration from when you click outside of the content until the content closes.                                   |\n\n### Trigger\n\nThe area that opens the popover. Wrap it around the target you want the popover to open when a text is selected.\n\n| Prop      | Type      | Default | Description                                                                                                                                                                                                   |\n| --------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `asChild` | `boolean` | `false` | Change the component to the HTML tag or custom component of the only child. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node. |\n\n### Portal\n\nWhen used, portals the content part into the `body`.\n\n| Prop         | Type          | Default         | Description                                                                                                                                                                              |\n| ------------ | ------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `forceMount` | `boolean`     | -               | Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. If used on this part, it will be inherited by `Selection.Content`. |\n| `container`  | `HTMLElement` | `document.body` | Specify a container element to portal the content into.                                                                                                                                  |\n\n### Content\n\nThe component that pops out when a text is selected.\n\n| Prop                   | Type                                                            | Default   | Description                                                                                                                                                                                                               |\n| ---------------------- | --------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `asChild`              | `boolean`                                                       | false     | Change the component to the HTML tag or custom component of the only child. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.             |\n| `forceMount`           | `boolean`                                                       | -         | Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. It inherits from `Selection.Portal`.                                                                |\n| `side`                 | `\"top\" \\| \"right\" \\| \"bottom\" \\| \"left\"`                        | `top`     | The preferred side of the selection to render against when open. Will be reversed when collisions occur and `avoidCollisions` is enabled.                                                                                 |\n| `sideOffset`           | `number`                                                        | `0`       | The distance in pixels from the selection.                                                                                                                                                                                |\n| `align`                | `\"start\" \\| \"center\" \\| \"end\"`                                  | `center`  | The preferred alignment against the selection. May change when collisions occur.                                                                                                                                          |\n| `alignOffset`          | `number`                                                        | `0`       | An offset in pixels from the `\"start\"` or `\"end\"` alignment options.                                                                                                                                                      |\n| `avoidCollisions`      | `boolean`                                                       | `true`    | When `true`, overrides the `side` and `align` preferences to prevent collisions with boundary edges.                                                                                                                      |\n| `collisionBoundary`    | `Element \\| null \\| Array\u003cElement \\| null\u003e`                     | `[]`      | The element used as the collision boundary. By default this is the viewport, though you can provide additional element(s) to be included in this check.                                                                   |\n| `collisionPadding`     | `number \\| Partial\u003cRecord\u003cSide, number\u003e\u003e`                       | `0`       | The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: `{ top: 20, left: 20 }`.                          |\n| `arrowPadding`         | `number`                                                        | `0`       | The padding between the arrow and the edges of the content. If your content has `border-radius`, this will prevent it from overflowing the corners.                                                                       |\n| `sticky`               | `\"partial\" \\| \"always\"`                                         | `partial` | The sticky behavior on the align axis. `\"partial\"` will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst `\"always\"` will keep the content in the boundary regardless. |\n| `hideWhenDetached`     | `boolean`                                                       | `false`   | Whether to hide the content when the text becomes fully occluded.                                                                                                                                                         |\n| `onEscapeKeyDown`      | `(event: KeyboardEvent) =\u003e void`                                | -         | Event handler called when the escape key is down. It can be prevented by calling `event.preventDefault`.                                                                                                                  |\n| `onPointerDownOutside` | `(event: PointerDownOutsideEvent) =\u003e void`                      | -         | Event handler called when a pointer event occurs outside the bounds of the component. It can be prevented by calling `event.preventDefault`.                                                                              |\n| `onFocusOutside`       | `(event: FocusOutsideEvent) =\u003e void`                            | -         | Event handler called when focus moves outside the bounds of the component. It can be prevented by calling `event.preventDefault`.                                                                                         |\n| `onInteractOutside`    | `(event: PointerDownOutsideEvent \\| FocusOutsideEvent) =\u003e void` | -         | Event handler called when an interaction (pointer or focus event) happens outside the bounds of the component. It can be prevented by calling `event.preventDefault`.                                                     |\n\n| Data Attribute | Values                                   |\n| -------------- | ---------------------------------------- |\n| `[data-state]` | `\"open\" \\| \"closed\"`                     |\n| `[data-side]`  | `\"left\" \\| \"right\" \\| \"bottom\" \\| \"top\"` |\n| `[data-align]` | `\"start\" \\| \"end\" \\| \"center\"`           |\n\n| CSS Variable                                   | Description                                                                   |\n| ---------------------------------------------- | ----------------------------------------------------------------------------- |\n| `--selection-popover-content-transform-origin` | The `transform-origin` computed from the content and arrow positions/offsets. |\n| `--selection-popover-select-width`             | The width of the select.                                                      |\n| `--selection-popover-select-height`            | The height of the select.                                                     |\n\n### Arrow\n\nAn optional arrow element to render alongside the popover. This can be used to help visually link the selected text with the `Selection.Content`. Must be rendered inside `Selection.Content`.\n\n| Prop      | Type      | Default | Description                                                                                                                                                                                                   |\n| --------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `asChild` | `boolean` | `false` | Change the component to the HTML tag or custom component of the only child. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node. |\n| `width`   | `number`  | `10`    | The width of the arrow in pixels.                                                                                                                                                                             |\n| `height`  | `number`  | `5`     | The height of the arrow in pixels.                                                                                                                                                                            |\n\n## Examples\n\n### Origin-aware animations\n\n```jsx\n// index.jsx\nimport * as Selection from 'selection-popover'\nimport './styles.css'\n\nexport default () =\u003e (\n  \u003cSelection.Root\u003e\n    \u003cSelection.Trigger\u003e...\u003c/Selection.Trigger\u003e\n    \u003cSelection.Portal\u003e\n      \u003cSelection.Content className=\"SelectionContent\"\u003e...\u003c/Selection.Content\u003e\n    \u003c/Selection.Portal\u003e\n  \u003c/Selection.Root\u003e\n)\n```\n\n```css\n/* styles.css */\n.SelectionContent {\n  transform-origin: var(--selection-popover-content-transform-origin);\n  animation: scaleIn 500ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n@keyframes scaleIn {\n  from {\n    opacity: 0;\n    transform: scale(0);\n  }\n  to {\n    opacity: 1;\n    transform: scale(1);\n  }\n}\n```\n\n### Collision-aware animations\n\n```jsx\n// index.jsx\nimport * as Selection from 'selection-popover'\nimport './styles.css'\n\nexport default () =\u003e (\n  \u003cSelection.Root\u003e\n    \u003cSelection.Trigger\u003e...\u003c/Selection.Trigger\u003e\n    \u003cSelection.Portal\u003e\n      \u003cSelection.Content className=\"SelectionContent\"\u003e...\u003c/Selection.Content\u003e\n    \u003c/Selection.Portal\u003e\n  \u003c/Selection.Root\u003e\n)\n```\n\n```css\n/* styles.css */\n.SelectionContent {\n  animation-duration: 400ms;\n  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);\n}\n.SelectionContent[data-state='open'][data-side='top'] {\n  animation-name: slideDownAndFade;\n}\n.SelectionContent[data-state='open'][data-side='bottom'] {\n  animation-name: slideUpAndFade;\n}\n\n@keyframes slideDownAndFade {\n  from {\n    opacity: 0;\n    transform: translateY(-2px);\n  }\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n\n@keyframes slideUpAndFade {\n  from {\n    opacity: 0;\n    transform: translateY(2px));\n  }\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n```\n\n### Unmount animations\n\n```jsx\n// index.jsx\nimport * as Selection from 'selection-popover'\nimport './styles.css'\n\nexport default () =\u003e (\n  \u003cSelection.Root\u003e\n    \u003cSelection.Trigger\u003e...\u003c/Selection.Trigger\u003e\n    \u003cSelection.Portal\u003e\n      \u003cSelection.Content className=\"SelectionContent\"\u003e...\u003c/Selection.Content\u003e\n    \u003c/Selection.Portal\u003e\n  \u003c/Selection.Root\u003e\n)\n```\n\n```css\n/* styles.css */\n.SelectionContent {\n  animation-duration: 400ms;\n  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);\n}\n.SelectionContent[data-state='open'] {\n  animation-name: slideDownAndFade;\n}\n.SelectionContent[data-state='closed'] {\n  animation-name: slideUpAndFade;\n}\n\n@keyframes slideDownAndFade {\n  from {\n    opacity: 0;\n    transform: translateY(-2px);\n  }\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n\n@keyframes slideUpAndFade {\n  from {\n    opacity: 1;\n    transform: translateY(0));\n  }\n  to {\n    opacity: 0;\n    transform: translateY(-2px);\n  }\n}\n```\n\n### Use with [Radix Toolbar](https://www.radix-ui.com/docs/primitives/components/toolbar)\n\n```jsx\nimport * as Selection from 'selection-popover'\nimport * as Toolbar from '@radix-ui/react-toolbar'\n\nexport default () =\u003e (\n  \u003cSelection.Root\u003e\n    \u003cSelection.Trigger\u003e...\u003c/Selection.Trigger\u003e\n    \u003cSelection.Portal\u003e\n      \u003cSelection.Content asChild\u003e\n        \u003cToolbar.Root\u003e...\u003c/Toolbar.Root\u003e\n        \u003cSelection.Arrow /\u003e\n      \u003c/Selection.Content\u003e\n    \u003c/Selection.Portal\u003e\n  \u003c/Selection.Root\u003e\n)\n```\n\n## Acknowledgements\n\n- API heavily inspired on [Radix UI](https://www.radix-ui.com/)\n- Inspired by this [tweet](https://twitter.com/TobiasWhetton/status/1612821266242715648) from [Tobias Whetton](https://twitter.com/TobiasWhetton)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoaom00%2Fselection-popover","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoaom00%2Fselection-popover","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoaom00%2Fselection-popover/lists"}