{"id":13627295,"url":"https://github.com/ngneat/helipopper","last_synced_at":"2025-05-14T19:01:51.901Z","repository":{"id":37489119,"uuid":"271583427","full_name":"ngneat/helipopper","owner":"ngneat","description":"🚁 A Powerful Tooltip and Popover for Angular Applications","archived":false,"fork":false,"pushed_at":"2025-03-31T06:38:46.000Z","size":3247,"stargazers_count":431,"open_issues_count":32,"forks_count":39,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-05-07T18:15:35.927Z","etag":null,"topics":["angular","popover","tippyjs","tooltip"],"latest_commit_sha":null,"homepage":"https://ngneat.github.io/helipopper/","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/ngneat.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"ngneat"}},"created_at":"2020-06-11T15:35:15.000Z","updated_at":"2025-05-02T13:27:46.000Z","dependencies_parsed_at":"2025-05-07T18:15:38.701Z","dependency_job_id":"b080088c-07e5-459d-ae23-cef928a13141","html_url":"https://github.com/ngneat/helipopper","commit_stats":{"total_commits":232,"total_committers":16,"mean_commits":14.5,"dds":0.5991379310344828,"last_synced_commit":"f8983120e624823b808106742756957bde6c94b7"},"previous_names":[],"tags_count":73,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngneat%2Fhelipopper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngneat%2Fhelipopper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngneat%2Fhelipopper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngneat%2Fhelipopper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ngneat","download_url":"https://codeload.github.com/ngneat/helipopper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253873546,"owners_count":21977119,"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":["angular","popover","tippyjs","tooltip"],"created_at":"2024-08-01T22:00:32.565Z","updated_at":"2025-05-14T19:01:50.873Z","avatar_url":"https://github.com/ngneat.png","language":"TypeScript","funding_links":["https://github.com/sponsors/ngneat"],"categories":["Projects by main language","Third Party Components"],"sub_categories":["angular","Tooltips"],"readme":"\u003cp align=\"center\"\u003e\n \u003cimg width=\"20%\" height=\"20%\" src=\"./logo.svg\"\u003e\n\u003c/p\u003e\n\n\u003cbr /\u003e\n\n[![MIT](https://img.shields.io/packagist/l/doctrine/orm.svg?style=flat-square)]()\n[![commitizen](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg?style=flat-square)]()\n[![PRs](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)]()\n[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)\n[![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors-)\n[![ngneat](https://img.shields.io/badge/@-ngneat-383636?style=flat-square\u0026labelColor=8f68d4)](https://github.com/ngneat/)\n[![spectator](https://img.shields.io/badge/tested%20with-spectator-2196F3.svg?style=flat-square)]()\n[![@ngneat/helipopper](https://github.com/ngneat/helipopper/workflows/@ngneat/helipopper/badge.svg)]()\n\n\u003e A Powerful Tooltip and Popover for Angular Applications\n\n[Tippy.js](https://atomiks.github.io/tippyjs/v6/getting-started/) is the complete tooltip, popover, dropdown, and menu\nsolution for the web, powered by Popper.js.\n\nIt is an abstraction over Popper that provides the logic and optionally the styling involved in all types of elements\nthat pop out from the flow of the document and get overlaid on top of the UI, positioned next to a reference element.\n\nThis is a lightweight wrapper with additional features that lets you use it declaratively in Angular. Tippy has\nvirtually no restrictions over Popper and gives you limitless control while providing useful behavior and defaults.\n\nIf you're using v1 and don't want to migrate, you can find it [here](https://github.com/ngneat/helipopper/tree/v1).\n\n## Features\n\n✅ Position Tooltips, Menus, Dropdowns, and Popovers \u003cbr\u003e\n✅ Predefined Variations \u003cbr\u003e\n✅ TemplateRef/Component Support\u003cbr\u003e\n✅ Lazy Registration\u003cbr\u003e\n✅ Manual Trigger Support\u003cbr\u003e\n✅ Text Overflow Support\u003cbr\u003e\n✅ Context Menu Support\u003cbr\u003e\n\n### Installation\n\n```sh\n$ npm i @ngneat/helipopper\n# Or if you're using yarn\n$ yarn add @ngneat/helipopper\n# Or if you're using pnpm\n$ pnpm i @ngneat/helipopper\n```\n\nConfigure it as shown below:\n\n```ts\nimport { provideTippyLoader, provideTippyConfig, tooltipVariation, popperVariation } from '@ngneat/helipopper/config';\n\nbootstrapApplication(AppComponent, {\n  providers: [\n    provideTippyLoader(() =\u003e import('tippy.js')),\n    provideTippyConfig({\n      defaultVariation: 'tooltip',\n      variations: {\n        tooltip: tooltipVariation,\n        popper: popperVariation,\n      },\n    }),\n  ],\n});\n```\n\nPlease note that the `provideTippyLoader` is required, as it specifies how Tippy is loaded - either synchronously or asynchronously. When dynamic import is used, the library will load only when the first Tippy directive is rendered. If we want it to load synchronously, we use the following:\n\n```ts\nimport tippy from 'tippy.js';\n\nprovideTippyLoader(() =\u003e tippy);\n```\n\nAdd the styles you want to `styles.scss`:\n\n```scss\n@import 'tippy.js/dist/tippy.css';\n@import 'tippy.js/themes/light.css';\n@import 'tippy.js/animations/scale.css';\n```\n\nYou have the freedom to [customize](https://atomiks.github.io/tippyjs/v6/themes/) it if you need to.\n\nImport the standalone `TippyDirective` in your components:\n\n```ts\nimport { TippyDirective } from '@ngneat/helipopper';\n\n@Component({\n  standalone: true,\n  imports: [TippyDirective],\n})\nclass ExampleComponent {}\n```\n\nAnd use it in your templates:\n\n```html\n\u003cbutton tp=\"Helpful Message\"\u003eI have a tooltip\u003c/button\u003e\n```\n\nThe library exposes default variations for `tooltip` and `popper`. You can use them, extend them, or pass your own\nvariations. A `variation` is a set of predefined `tippy` properties. For example, here's how the built-in `tooltip`\nvariation looks like:\n\n```ts\nexport const tooltipVariation = {\n  theme: null,\n  arrow: false,\n  animation: 'scale',\n  trigger: 'mouseenter',\n  offset: [0, 5],\n};\n```\n\n### Use `TemplateRef` as content\n\n```html\n\u003cbutton [tp]=\"tpl\" tpVariation=\"popper\"\u003eClick Me\u003c/button\u003e\n\n\u003cng-template #tpl let-hide\u003e\n  \u003ch6\u003ePopover title\u003c/h6\u003e\n  \u003cp\u003eAnd here's some amazing content. It's very engaging. Right?\u003c/p\u003e\n\u003c/ng-template\u003e\n```\n\n### Use `Component` as content\n\n```ts\nimport type { TippyInstance } from '@ngneat/helipopper/config';\nimport { injectTippyRef } from '@ngneat/helipopper';\n\n@Component()\nclass MyComponent {\n  tippy = injectTippyRef();\n}\n```\n\n```html\n\u003cbutton [tp]=\"MyComponent\"\u003eClick Me\u003c/button\u003e\n```\n\n### Text Overflow\n\nYou can pass the `onlyTextOverflow` input to show the tooltip only when the host overflows its container:\n\n```html\n\u003cdiv style=\"max-width: 100px;\" class=\"overflow-hidden flex\"\u003e\n  \u003cp class=\"ellipsis\" [tp]=\"text\" tpPlacement=\"right\" [tpOnlyTextOverflow]=\"true\"\u003e\n    {{ text }}\n  \u003c/p\u003e\n\u003c/div\u003e\n```\n\nNote: this feature is using [`ResizeObserver`](https://caniuse.com/resizeobserver) api.\n\nYou might have cases where the host has a static width and the content is dynamic. In this case, use the `tpStaticWidthHost` input with combination with `tpOnlyTextOverflow`:\n\n```html\n\u003cdiv style=\"max-width: 100px;\" class=\"overflow-hidden flex\"\u003e\n  \u003cp\n    style=\"width: 100px\"\n    class=\"ellipsis\"\n    [tp]=\"dynamicText\"\n    tpPlacement=\"right\"\n    [tpOnlyTextOverflow]=\"true\"\n    tpStaticWidthHost\n  \u003e\n    {{ dynamicText }}\n  \u003c/p\u003e\n\u003c/div\u003e\n```\n\nNote: when using `tpStaticWidthHost` you can't use `tpUseTextContent`, you need to explicitly pass the content to `tp` in order to trigger content change.\n\n### Use Text Content\n\nYou can instruct tippy to use the element textContent as the tooltip content:\n\n```html\n\u003cp tp tpUseTextContent\u003e{{ text }}\u003c/p\u003e\n```\n\n### Lazy\n\nYou can pass the `tpIsLazy` input when you want to defer the creation of tippy only when the element is in the view:\n\n```html\n\u003cdiv *ngFor=\"let item of items\" [tp]=\"item.label\" [tpIsLazy]=\"true\"\u003e{{ item.label }}\u003c/div\u003e\n```\n\nNote that it's using [`IntersectionObserver`](https://caniuse.com/intersectionobserver) api.\n\n### Context Menu\n\nFirst, define the `contextMenu` variation:\n\n```ts\nimport {\n  popperVariation,\n  tooltipVariation,\n  provideTippyConfig,\n  withContextMenuVariation,\n} from '@ngneat/helipopper/config';\n\nbootstrapApplication(AppComponent, {\n  providers: [\n    provideTippyConfig({\n      defaultVariation: 'tooltip',\n      variations: {\n        tooltip: tooltipVariation,\n        popper: popperVariation,\n        contextMenu: withContextMenuVariation(popperVariation),\n      },\n    }),\n  ],\n});\n```\n\nNow you can use it in your template:\n\n```html\n\u003cng-template #contextMenu let-hide let-item=\"data\"\u003e\n  \u003cul\u003e\n    \u003cli (click)=\"copy(item); hide()\"\u003eCopy\u003c/li\u003e\n    \u003cli (click)=\"duplicate(item); hide()\"\u003eDuplicate\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/ng-template\u003e\n\n\u003cul\u003e\n  \u003cli\n    *ngFor=\"let item of list\"\n    [tp]=\"contextMenu\"\n    [tpData]=\"item\"\n    tpVariation=\"contextMenu\"\n  \u003e\n    {{ item.label }}\n  \u003c/li\u003e\n\u003c/ul\u003e\n```\n\n### Manual Trigger\n\n```html\n\u003cdiv tp=\"Helpful Message\" tpTrigger=\"manual\" #tooltip=\"tippy\"\u003eClick Open to see me\u003c/div\u003e\n\n\u003cbutton (click)=\"tooltip.show()\"\u003eOpen\u003c/button\u003e\n\u003cbutton (click)=\"tooltip.hide()\"\u003eClose\u003c/button\u003e\n```\n\n### Declarative show/hide\n\nUse isVisible to trigger show and hide. Set trigger to manual.\n\n```html\n\u003cdiv tp=\"Helpful Message\" tpTrigger=\"manual\" [tpIsVisible]=\"visibility\"\u003e\n  Click Open to see me\n\u003c/div\u003e\n\n\u003cbutton (click)=\"visibility = true\"\u003eOpen\u003c/button\u003e\n\u003cbutton (click)=\"visibility = false\"\u003eClose\u003c/button\u003e\n```\n\nYou can see more examples in\nour [playground](https://github.com/ngneat/helipopper/blob/master/src/app/app.component.html), or\nlive [here](https://ngneat.github.io/helipopper/).\n\n### Inputs\n\n```ts\ntp: string | TemplateRef\u003cany\u003e | Type\u003cany\u003e | undefined | null;\ntpAppendTo: TippyProps['appendTo'];\ntpDelay: TippyProps['delay'];\ntpDuration: TippyProps['duration'];\ntpHideOnClick: TippyProps['hideOnClick'];\ntpInteractive: TippyProps['interactive'];\ntpInteractiveBorder: TippyProps['interactiveBorder'];\ntpMaxWidth: TippyProps['maxWidth'];\ntpOffset: TippyProps['offset'];\ntpPlacement: TippyProps['placement'];\ntpPopperOptions: TippyProps['popperOptions'];\ntpShowOnCreate: TippyProps['showOnCreate'];\ntpTrigger: TippyProps['trigger'];\ntpTriggerTarget: TippyProps['triggerTarget'];\ntpZIndex: TippyProps['zIndex'];\ntpAnimation: TippyProps['animation'];\ntpUseTextContent: boolean;\ntpIsLazy: boolean;\ntpVariation: string;\ntpIsEnabled: boolean;\ntpIsVisible: boolean;\ntpClassName: string;\ntpOnlyTextOverflow: boolean;\ntpData: any;\ntpUseHostWidth: boolean;\ntpHideOnEscape: boolean;\ntpDetectChangesComponent: boolean;\ntpPopperWidth: number | string;\ntpHost: HTMLElement;\ntpIsVisible: boolean;\n```\n\n### Outputs\n\n```ts\ntpVisible = new EventEmitter\u003cboolean\u003e();\n```\n\n### Global Config\n\n- You can pass any `tippy` option at global config level.\n- `beforeRender` - Hook that'll be called before rendering the tooltip content ( applies only for string )\n\n### Create `tippy` Programmatically\n\n```typescript\nimport type { TippyInstance } from '@ngneat/helipopper/config';\nimport { TippyService } from '@ngneat/helipopper';\n\nclass Component {\n  @ViewChild('inputName') inputName: ElementRef;\n  tippy: TippyInstance;\n  private tippyService = inject(TippyService);\n\n  async show() {\n    if (!this.tippy) {\n      this.tippy = await firstValueFrom(\n        this.tippyService.create(this.inputName, 'this field is required')\n      );\n    }\n\n    this.tippy.show();\n  }\n\n  ngOnDestroy() {\n    this.tippy?.destroy();\n  }\n}\n```\n\n## Contributors ✨\n\nThank goes to all these wonderful [people who contributed](https://github.com/ngneat/helipopper/graphs/contributors) ❤️\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fngneat%2Fhelipopper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fngneat%2Fhelipopper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fngneat%2Fhelipopper/lists"}