├── .github
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── workflows
│ ├── gh-pages.yaml
│ ├── main.yml
│ └── npm-publish-github-packages.yml
├── .gitignore
├── .yarnrc.yml
├── LICENSE
├── README.md
├── angular.json
├── browserslist
├── changelog.md
├── gulpfile.js
├── package.json
├── projects
├── ngx-float-ui-repo
│ ├── karma.conf.js
│ ├── src
│ │ ├── app
│ │ │ ├── app.component.html
│ │ │ ├── app.component.scss
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── components
│ │ │ │ ├── demo
│ │ │ │ │ ├── demo.component.html
│ │ │ │ │ ├── demo.component.scss
│ │ │ │ │ └── demo.component.ts
│ │ │ │ └── test
│ │ │ │ │ ├── test.component.html
│ │ │ │ │ ├── test.component.scss
│ │ │ │ │ └── test.component.ts
│ │ │ └── shared
│ │ │ │ ├── ngx-float-ui-article-types.model.ts
│ │ │ │ └── ngx-float-ui-code-map.const.ts
│ │ ├── assets
│ │ │ ├── .gitkeep
│ │ │ ├── favicon
│ │ │ │ └── favicon.ico
│ │ │ ├── fonts
│ │ │ │ ├── luckiest-guy-latin-400.woff
│ │ │ │ └── luckiest-guy-latin-400.woff2
│ │ │ ├── images
│ │ │ │ ├── ngx-popperjs-logo.svg
│ │ │ │ ├── popcorn-box.svg
│ │ │ │ ├── popper-icon.eps
│ │ │ │ ├── popper-icon.svg
│ │ │ │ ├── popper-logo-outlined.svg
│ │ │ │ └── popper-logo.svg
│ │ │ └── messages.json
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── scss
│ │ │ ├── code-highlight.scss
│ │ │ ├── popper-theme.scss
│ │ │ ├── responsive.scss
│ │ │ └── vars.scss
│ │ ├── styles.scss
│ │ ├── test.ts
│ │ └── typewriter.d.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ └── tslint.json
└── ngx-float-ui
│ ├── karma.conf.js
│ ├── ng-package.json
│ ├── package.json
│ ├── src
│ ├── lib
│ │ ├── _index.scss
│ │ ├── components
│ │ │ └── ngx-float-ui-content
│ │ │ │ ├── ngx-float-ui-content.component.html
│ │ │ │ ├── ngx-float-ui-content.component.scss
│ │ │ │ └── ngx-float-ui-content.component.ts
│ │ ├── directives
│ │ │ └── ngx-float-ui
│ │ │ │ ├── ngx-float-ui-loose.directive.ts
│ │ │ │ ├── ngx-float-ui.directive.spec.ts
│ │ │ │ └── ngx-float-ui.directive.ts
│ │ ├── models
│ │ │ ├── ngx-float-ui-defaults.model.ts
│ │ │ ├── ngx-float-ui-options.model.ts
│ │ │ ├── ngx-float-ui-placements.model.ts
│ │ │ ├── ngx-float-ui-triggers.model.ts
│ │ │ └── ngx-float-ui-utils.class.ts
│ │ ├── ngx-float-ui.module.spec.ts
│ │ ├── ngx-float-ui.module.ts
│ │ └── scss
│ │ │ ├── theme-dark.scss
│ │ │ ├── theme-white.scss
│ │ │ └── theme.scss
│ ├── public-api.ts
│ └── test.ts
│ ├── tsconfig.lib.json
│ └── tsconfig.spec.json
├── symlink.js
├── tsbl.js
├── tsconfig.json
├── tslint.json
└── yarn.lock
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: BUGS ONLY!
3 | about: DO NOT OPEN TO ASK FOR NEXT VERSION!
4 | title: 'BUG: your summary here'
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **NOTE**
11 |
12 | **REQUESTS WILL BE IMMEDIATELY DELETED, you cannot do feature requests such as "Update to Angular xx"**
13 | I deal with updates with my own schedule, don't need a reminder.
14 |
15 | **Describe the bug**
16 | A clear and concise description of what the bug is.
17 |
18 | **To Reproduce**
19 | Possibly create a stackblitz reproducing the bug, or a zip with basic sources for a reproduction
20 |
21 | OR
22 |
23 | Steps to reproduce the behavior:
24 | 1. Go to '...'
25 | 2. Click on '....'
26 | 3. Scroll down to '....'
27 | 4. See error
28 |
29 | **Expected behavior**
30 | A clear and concise description of what you expected to happen.
31 |
32 | **Screenshots**
33 | If applicable, add screenshots to help explain your problem.
34 |
35 | **Desktop (if execution):**
36 | - OS: [e.g. iOS]
37 | - Browser [e.g. chrome, safari]
38 |
39 | ** Versions
40 | - ngx-float-ui version [e.g. 9.0.0]
41 | - @angular/core version [e.g. 10.2.3]
42 | - @angular/material version [e.g. 10.2.7]
43 | - typescript version [es. 3.9.2]
44 |
45 | **Additional context (optional)**
46 | Add any other context about the problem here.
47 |
48 |
49 | **Additional context**
50 | Add any other context about the problem here.
51 |
--------------------------------------------------------------------------------
/.github/workflows/gh-pages.yaml:
--------------------------------------------------------------------------------
1 | # Sample workflow for building and deploying a Nuxt site to GitHub Pages
2 | #
3 | # To get started with Nuxt see: https://nuxtjs.org/docs/get-started/installation
4 | #
5 | name: Build and deploy Angular site to Pages
6 |
7 | on:
8 | # Runs on pushes targeting the default branch
9 | push:
10 | branches: [ "master" ]
11 |
12 | # Allows you to run this workflow manually from the Actions tab
13 | workflow_dispatch:
14 |
15 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
16 | permissions:
17 | contents: read
18 | pages: write
19 | id-token: write
20 |
21 | # Allow one concurrent deployment
22 | concurrency:
23 | group: "pages"
24 | cancel-in-progress: true
25 |
26 | jobs:
27 | # Build job
28 | build:
29 | runs-on: ubuntu-latest
30 | steps:
31 | - name: Checkout
32 | uses: actions/checkout@v4
33 | - name: Enable corepack
34 | run: corepack enable
35 | - name: Detect package manager
36 | id: detect-package-manager
37 | run: |
38 | if [ -f "${{ github.workspace }}/yarn.lock" ]; then
39 | echo "manager=yarn" >> $GITHUB_OUTPUT
40 | echo "command=install" >> $GITHUB_OUTPUT
41 | exit 0
42 | elif [ -f "${{ github.workspace }}/package.json" ]; then
43 | echo "manager=npm" >> $GITHUB_OUTPUT
44 | echo "command=ci" >> $GITHUB_OUTPUT
45 | exit 0
46 | else
47 | echo "Unable to determine packager manager"
48 | exit 1
49 | fi
50 | - name: Setup Node
51 | uses: actions/setup-node@v3
52 | with:
53 | node-version: "22.16.0"
54 | cache: ${{ steps.detect-package-manager.outputs.manager }}
55 | - name: Restore cache
56 | uses: actions/cache@v3
57 | with:
58 | path: |
59 | dist
60 | .angular
61 | key: ${{ runner.os }}-angular-build-${{ hashFiles('dist') }}
62 | restore-keys: |
63 | ${{ runner.os }}-angular-build-
64 | - name: Enable corepack
65 | run: corepack enable
66 | - name: Install dependencies
67 | run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
68 | - name: Static HTML export Angular
69 | run: ${{ steps.detect-package-manager.outputs.manager }} run build:demo
70 | - name: Upload artifact
71 | uses: actions/upload-pages-artifact@v3.0.1
72 | with:
73 | path: ./dist/ngx-float-ui-repo
74 |
75 | # Deployment job
76 | deploy:
77 | environment:
78 | name: github-pages
79 | url: ${{ steps.deployment.outputs.page_url }}
80 | runs-on: ubuntu-latest
81 | needs: build
82 | steps:
83 | - name: Deploy to GitHub Pages
84 | id: deployment
85 | uses: actions/deploy-pages@v4.0.5
86 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [ "master" ]
9 | pull_request:
10 | branches: [ "master" ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | matrix:
19 | node-version: [22.16.0]
20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21 | steps:
22 | - uses: actions/checkout@v3
23 | - name: Use Node.js ${{ matrix.node-version }}
24 | uses: actions/setup-node@v3
25 | with:
26 | node-version: ${{ matrix.node-version }}
27 | cache: 'npm'
28 | - run: corepack enable
29 | - run: yarn install
30 | - run: npm run test:ci
31 | - run: npm run build
32 |
--------------------------------------------------------------------------------
/.github/workflows/npm-publish-github-packages.yml:
--------------------------------------------------------------------------------
1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2 | # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
3 |
4 | name: Publish on NPM
5 |
6 | on:
7 | release:
8 | types: [ created ]
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v3
15 | - uses: actions/setup-node@v3
16 | with:
17 | node-version: 22.16.0
18 | registry-url: https://registry.npmjs.org/
19 | - run: corepack enable
20 | - run: yarn install
21 | - run: npm run build
22 | - run: |
23 | pkgversion=$(node -p -e "require('./package.json').version")
24 | if [[ $pkgversion == *"beta"* || $pkgversion =~ -rc\.[0-9]+$ ]]; then
25 | npm run release:beta
26 | else
27 | npm run release
28 | fi
29 | env:
30 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | # Only exists if Bazel was run
8 | /bazel-out
9 |
10 | # angular cache
11 | .angular
12 |
13 | # dependencies
14 | /node_modules
15 | /_node_modules
16 |
17 | # profiling files
18 | chrome-profiler-events*.json
19 | speed-measure-plugin*.json
20 |
21 | # IDEs and editors
22 | /.idea
23 | .project
24 | .classpath
25 | .c9/
26 | *.launch
27 | .settings/
28 | *.sublime-workspace
29 |
30 | # IDE - VSCode
31 | .vscode/*
32 | !.vscode/settings.json
33 | !.vscode/tasks.json
34 | !.vscode/launch.json
35 | !.vscode/extensions.json
36 | .history/*
37 |
38 | # misc
39 | /.sass-cache
40 | /connect.lock
41 | /coverage
42 | /libpeerconnection.log
43 | npm-debug.log
44 | yarn-error.log
45 | testem.log
46 | /typings
47 |
48 | # System Files
49 | .DS_Store
50 | Thumbs.db
51 |
52 | package-lock.json
53 | /.yarn/install-state.gz
54 |
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Tony Samperi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ngx-float-ui
2 |
3 | [![npm][badge-npm-version]][url-npm] [![npm][badge-npm-downloads]][url-npm] [![MIT licensed][badge-licence]][url-licence] [![Build state][badge-ci-state]][url-ci-state] [![Size][badge-bundle]][url-bundle] [![Rate this package][badge-openbase]][url-openbase]
4 |
5 | ngx-float-ui is an angular wrapper for the [FloatingUI](https://floating-ui.com/) library (v ^1.5.3).
6 |
7 | ## VERY IMPORTANT READ THIS
8 | I'm moving this to the top because it appears that people don't get to reading it in the **contribute** section.
9 | FOR THIS LIBRARY **PLEASE USE ISSUES ONLY FOR BUG REPORTING**. DON'T OPEN ISSUES SUCH AS "I need upgrade for Angular 1745646456" especially if Angular 1745646456 was released a few days ago.
10 | I **guarantee** that I manage the updates **AS SOON AS POSSIBLE**. But as you understand this is not a paying job, so you can't get Angular 1745646456 the day it gets released.
11 | **ISSUES NOT RESPECTING THIS WILL BE DELETED IMMEDIATELY WITHOUT ANY RESPONSE**.
12 | Thank you.
13 |
14 | ### Premise
15 |
16 | The goal of this library is to adopt the more promising technology of floating-ui instead of popper.js and make it available in our Angular projects.
17 |
18 | ### Installation
19 |
20 | node and npm are required to run this package.
21 |
22 | 1. Use npm/yarn to install the package:
23 |
24 | ```terminal
25 | $ npm install @floating-ui/dom ngx-float-ui --save
26 | ```
27 |
28 | Or
29 |
30 | ```terminal
31 | $ yarn add @floating-ui/dom --save
32 | $ yarn add ngx-float-ui --save
33 | ```
34 |
35 | 2. You simply add into your module `NgxFloatUiModule`:
36 |
37 | ```typescript
38 | import {NgxFloatUiModule} from 'ngx-float-ui';
39 |
40 | @NgModule({
41 | // ...
42 | imports: [
43 | // ...
44 | NgxFloatUiModule
45 | ]
46 | })
47 | ```
48 |
49 | Optionally you can include in your `styles.css` / `styles.css` one of the prebuilt themes:
50 | * `@import node_modules/ngx-float-ui/css/theme-dark.css`
51 | * `@import node_modules/css/theme-white.css`
52 |
53 | * `@use ngx-float-ui/scss/theme as floatUiBaseTheme`
54 | * `@use ngx-float-ui/scss/theme-dark as floatUiDarkTheme`
55 | * `@use ngx-float-ui/scss/theme-white floatUiWhiteTheme`
56 |
57 | or easily create your own theme using the @mixin:
58 |
59 | ```
60 | @use "ngx-float-ui/scss/theme" as floatUiBaseTheme;
61 |
62 | body {
63 | @include floatUiBaseTheme.ngx-float-ui-theme($background-color, $text-color, $max-width, $z-index);
64 | }
65 | ```
66 |
67 | 3. Add to view:
68 |
69 | ```HTML
70 |
71 | Popper on bottom
72 |
73 |
119 |
120 |
125 |
126 | ```
127 |
128 | 7. hide/show programmatically:
129 | ```HTML
130 |
134 |
Pop
135 |
on the bottom
136 |
137 |
138 |
139 |
This is a tooltip with text
140 |
Close
141 |
142 |
143 | ```
144 |
145 | 8. Attributes map:
146 |
147 | | Option | Type | Default | Description |
148 | |:-------------------|:---------------- |:--------- |:---------------------------------------------------------------------------------------|
149 | | disableAnimation | boolean | false | Disable the default animation on show/hide |
150 | | disableStyle | boolean | false | Disable the default styling |
151 | | disabled | boolean | false | Disable the popper, ignore all events |
152 | | showDelay | number | 0 | Delay time until popper it shown |
153 | | hideTimeout | number | 0 | Set delay before the popper is hidden |
154 | | timeoutAfterShow | number | 0 | Set a time on which the popper will be hidden after it is shown |
155 | | placement | Placement(string) | auto | The placement to show the popper relative to the reference element * |
156 | | targetElement | HtmlElement | auto | Specify a different reference element other the the one hosting the directive |
157 | | boundaries | string(selector) | undefined | Specify a selector to serve as the boundaries of the element |
158 | | showOnStart | boolean | false | Popper default to show |
159 | | showTrigger | Trigger(string) | click | Trigger/Event on which to show/hide the popper |
160 | | positionFixed | boolean | false | Set the popper element to use position: fixed |
161 | | appendTo | string | undefined | append The popper-floatUi element to a given selector, if multiple will apply to first |
162 | | preventOverflow | boolean | undefined | Prevent the popper from being positioned outside the boundary * |
163 | | hideOnClickOutside | boolean | true | Popper will hide on a click outside |
164 | | hideOnScroll | boolean | false | Popper will hide on scroll |
165 | | hideOnMouseLeave | boolean | false | Popper will hide on mouse leave |
166 | | applyClass | string | undefined | list of comma separated class to apply on ngpx__container |
167 | | styles | Object | undefined | Apply the styles object, aligned with ngStyles |
168 | | applyArrowClass | string | undefined | list of comma separated class to apply on ngpx__arrow |
169 | | onShown | EventEmitter<> | $event | Event handler when popper is shown |
170 | | onHidden | EventEmitter<> | $event | Event handler when popper is hidden |
171 | | onUpdate | EventEmitter<> | $event | Event handler when popper is updated |
172 | | ariaDescribeBy | string | undefined | Define value for aria-describeby attribute |
173 | | ariaRole | string | popper | Define value for aria-role attribute |
174 |
175 | \* **VERY IMPORTANT**: All the "auto" placements can't be used in combo with prevent overflow (as per float-ui specs), because the two algorythms **would conflict**, ending in infinite repositioning.
176 | See [here](https://floating-ui.com/docs/autoPlacement#usage)
177 |
178 | 9. Override defaults:
179 |
180 | ngx-float-ui comes with a few default properties you can override in default to effect all instances
181 | These are overridden by any child attributes.
182 |
183 | ```JavaScript
184 | NgModule({
185 | imports: [
186 | BrowserModule,
187 | FormsModule,
188 | NgxFloatUiModule.forRoot({placement: NgxFloatUiPlacements.TOP})],
189 | declarations: [AppComponent],
190 | providers: [],
191 | bootstrap: [AppComponent]
192 |
193 | });
194 | ```
195 |
196 | | Options | Type | Default |
197 | |:----------------------|:---------------------------------|:----------|
198 | | showDelay | number | 0 |
199 | | disableAnimation | boolean | false |
200 | | disableDefaultStyling | boolean | false |
201 | | placement | NgxFloatUiPopPlacements (string) | auto |
202 | | boundariesElement | string(selector) | undefined |
203 | | showTrigger | NgxFloatUiTriggers (string) | hover |
204 | | positionFixed | boolean | false |
205 | | hideOnClickOutside | boolean | true |
206 | | hideOnMouseLeave | boolean | false |
207 | | hideOnScroll | boolean | false |
208 | | applyClass | string | undefined |
209 | | styles | Object | undefined |
210 | | applyArrowClass | string | undefined |
211 | | ariaDescribeBy | string | undefined |
212 | | ariaRole | string | undefined |
213 | | appendTo | string | undefined |
214 | | preventOverflow | boolean | undefined |
215 |
216 | 10. NgxFloatUiPopPlacements:
217 |
218 | | 'top'
219 | | 'bottom'
220 | | 'left'
221 | | 'right'
222 | | 'top-start'
223 | | 'bottom-start'
224 | | 'left-start'
225 | | 'right-start'
226 | | 'top-end'
227 | | 'bottom-end'
228 | | 'left-end'
229 | | 'right-end'
230 | | 'auto'
231 | | 'auto-start'
232 | | 'auto-end'
233 |
234 | 11. NgxFloatUiTriggers:
235 |
236 | | 'click'
237 | | 'mousedown'
238 | | 'hover'
239 | | 'none'
240 |
241 | ### Liking hardcoded strings everywhere? Too lazy to use Enums? No problem mate!
242 |
243 | `floatUiLoose` is what you're looking for!
244 |
245 | You can then use `loosePlacement` and `looseTrigger` passing the values above as strings!
246 |
247 | ### Demo site with sample codes
248 |
Demo of ngx-float-ui
249 |
250 | ### Contribute
251 | You can only **report bugs**. Every other issue will be deleted right away.
252 |
253 | ```terminal
254 | $ npm install
255 | $ npm run start //run example
256 | ```
257 |
258 | ## License
259 |
260 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
261 |
262 | ### Thanks to
263 |
264 | The developers of Floating UI
265 |
266 | [badge-bundle]: https://img.shields.io/bundlephobia/minzip/ngx-float-ui
267 | [badge-ci-state]: https://github.com/tonysamperi/ngx-float-ui/actions/workflows/main.yml/badge.svg
268 | [badge-licence]: https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square
269 | [badge-npm-downloads]: https://img.shields.io/npm/dm/ngx-float-ui.svg?style=flat-square
270 | [badge-npm-version]: https://img.shields.io/npm/v/ngx-float-ui.svg?style=flat-square
271 | [badge-openbase]: https://badges.openbase.com/js/rating/ngx-float-ui.svg
272 | [url-bundle]: https://img.shields.io/bundlephobia/minzip/ngx-float-ui
273 | [url-ci-state]: https://github.com/tonysamperi/ngx-float-ui/actions
274 | [url-licence]: https://github.com/tonysamperi/ngx-float-ui/blob/master/LICENSE
275 | [url-npm]: https://www.npmjs.com/package/ngx-float-ui
276 | [url-openbase]: https://openbase.com/js/ngx-float-ui
277 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "ngx-float-ui": {
7 | "projectType": "library",
8 | "root": "projects/ngx-float-ui",
9 | "sourceRoot": "projects/ngx-float-ui/src",
10 | "prefix": "lib",
11 | "architect": {
12 | "build": {
13 | "builder": "@angular-devkit/build-ng-packagr:build",
14 | "options": {
15 | "tsConfig": "projects/ngx-float-ui/tsconfig.lib.json",
16 | "project": "projects/ngx-float-ui/ng-package.json"
17 | },
18 | "configurations": {
19 | "production": {
20 | }
21 | }
22 | },
23 | "test": {
24 | "builder": "@angular-devkit/build-angular:karma",
25 | "options": {
26 | "main": "projects/ngx-float-ui/src/test.ts",
27 | "tsConfig": "projects/ngx-float-ui/tsconfig.spec.json",
28 | "karmaConfig": "projects/ngx-float-ui/karma.conf.js",
29 | "polyfills": "projects/ngx-float-ui-repo/src/polyfills.ts",
30 | "styles": [
31 | "projects/ngx-float-ui-repo/src/styles.scss"
32 | ]
33 | }
34 | },
35 | "lint": {
36 | "builder": "@angular-devkit/build-angular:tslint",
37 | "options": {
38 | "tsConfig": [
39 | "projects/ngx-float-ui/tsconfig.lib.json",
40 | "projects/ngx-float-ui/tsconfig.spec.json"
41 | ],
42 | "exclude": [
43 | "**/node_modules/**"
44 | ]
45 | }
46 | }
47 | }
48 | },
49 | "ngx-float-ui-repo": {
50 | "projectType": "application",
51 | "schematics": {
52 | "@schematics/angular:component": {
53 | "style": "scss"
54 | }
55 | },
56 | "root": "projects/ngx-float-ui-repo",
57 | "sourceRoot": "projects/ngx-float-ui-repo/src",
58 | "prefix": "app",
59 | "architect": {
60 | "build": {
61 | "builder": "@angular-devkit/build-angular:application",
62 | "options": {
63 | "outputPath": {
64 | "base": "dist/ngx-float-ui-repo",
65 | "browser": ""
66 | },
67 | "index": "projects/ngx-float-ui-repo/src/index.html",
68 | "polyfills": [
69 | "projects/ngx-float-ui-repo/src/polyfills.ts"
70 | ],
71 | "tsConfig": "projects/ngx-float-ui-repo/tsconfig.app.json",
72 | "aot": false,
73 | "assets": [
74 | "projects/ngx-float-ui-repo/src/favicon.ico",
75 | "projects/ngx-float-ui-repo/src/assets"
76 | ],
77 | "styles": [
78 | "projects/ngx-float-ui-repo/src/styles.scss"
79 | ],
80 | "scripts": [],
81 | "browser": "projects/ngx-float-ui-repo/src/main.ts"
82 | },
83 | "configurations": {
84 | "dev": {
85 | "optimization": false,
86 | "outputHashing": "none",
87 | "sourceMap": true,
88 | "namedChunks": false,
89 | "extractLicenses": false
90 | },
91 | "production": {
92 | "fileReplacements": [
93 | {
94 | "replace": "projects/ngx-float-ui-repo/src/environments/environment.ts",
95 | "with": "projects/ngx-float-ui-repo/src/environments/environment.prod.ts"
96 | }
97 | ],
98 | "optimization": true,
99 | "outputHashing": "all",
100 | "sourceMap": false,
101 | "namedChunks": false,
102 | "aot": true,
103 | "extractLicenses": true,
104 | "budgets": [
105 | {
106 | "type": "initial",
107 | "maximumWarning": "2mb",
108 | "maximumError": "5mb"
109 | },
110 | {
111 | "type": "anyComponentStyle",
112 | "maximumWarning": "15kb",
113 | "maximumError": "30kb"
114 | }
115 | ]
116 | }
117 | }
118 | },
119 | "serve": {
120 | "builder": "@angular-devkit/build-angular:dev-server",
121 | "options": {
122 | "buildTarget": "ngx-float-ui-repo:build"
123 | },
124 | "configurations": {
125 | "development": {
126 | "buildTarget": "ngx-float-ui-repo:build:dev"
127 | },
128 | "production": {
129 | "buildTarget": "ngx-float-ui-repo:build:production"
130 | }
131 | },
132 | "defaultConfiguration": "development"
133 | },
134 | "extract-i18n": {
135 | "builder": "@angular-devkit/build-angular:extract-i18n",
136 | "options": {
137 | "buildTarget": "ngx-float-ui-repo:build"
138 | }
139 | },
140 | "test": {
141 | "builder": "@angular-devkit/build-angular:karma",
142 | "options": {
143 | "main": "projects/ngx-float-ui-repo/src/test.ts",
144 | "polyfills": "projects/ngx-float-ui-repo/src/polyfills.ts",
145 | "tsConfig": "projects/ngx-float-ui-repo/tsconfig.spec.json",
146 | "karmaConfig": "projects/ngx-float-ui-repo/karma.conf.js",
147 | "assets": [
148 | "projects/ngx-float-ui-repo/src/favicon.ico",
149 | "projects/ngx-float-ui-repo/src/assets"
150 | ],
151 | "styles": [
152 | "projects/ngx-float-ui-repo/src/styles.scss"
153 | ]
154 | }
155 | },
156 | "lint": {
157 | "builder": "@angular-devkit/build-angular:tslint",
158 | "options": {
159 | "tsConfig": [
160 | "projects/ngx-float-ui-repo/tsconfig.app.json",
161 | "projects/ngx-float-ui-repo/tsconfig.spec.json",
162 | "projects/ngx-float-ui-repo/e2e/tsconfig.json"
163 | ],
164 | "exclude": [
165 | "**/node_modules/**"
166 | ]
167 | }
168 | }
169 | }
170 | }
171 | },
172 | "cli": {
173 | "analytics": false
174 | },
175 | "schematics": {
176 | "@schematics/angular:component": {
177 | "type": "component"
178 | },
179 | "@schematics/angular:directive": {
180 | "type": "directive"
181 | },
182 | "@schematics/angular:service": {
183 | "type": "service"
184 | },
185 | "@schematics/angular:guard": {
186 | "typeSeparator": "."
187 | },
188 | "@schematics/angular:interceptor": {
189 | "typeSeparator": "."
190 | },
191 | "@schematics/angular:module": {
192 | "typeSeparator": "."
193 | },
194 | "@schematics/angular:pipe": {
195 | "typeSeparator": "."
196 | },
197 | "@schematics/angular:resolver": {
198 | "typeSeparator": "."
199 | }
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/browserslist:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | last 2 Chrome versions
9 | last 1 Firefox version
10 | last 2 Edge major versions
11 | last 2 Safari major versions
12 | last 2 iOS major versions
13 | Firefox ESR
14 |
--------------------------------------------------------------------------------
/changelog.md:
--------------------------------------------------------------------------------
1 | #20.0.0
2 | * Release for Angular 20! 🎉
3 |
4 | #19.0.1
5 | * Jetbrains no longer supporting :(
6 |
7 | #19.0.0
8 | * Release for Angular 19! 🎉
9 |
10 | #18.0.2
11 | * Fix in the hide process
12 |
13 | #18.0.1
14 | * Fix package deps
15 | * Upgrade to latest floating-ui
16 |
17 | #18.0.0
18 | * Release for Angular 18! 🎉
19 |
20 | #17.1.4
21 | * Fix docs on targetElement
22 |
23 | #17.1.3
24 | * Remove development background rule causing graphic bug in certain scenarios (closes [#16](https://github.com/tonysamperi/ngx-float-ui/issues/16))
25 |
26 | #17.1.2
27 | * Fix some nullpointer when placement was not passed (closes [#12](https://github.com/tonysamperi/ngx-float-ui/issues/12) for real)
28 | * Performance improvement
29 |
30 | #17.1.1
31 | * Fix placement default to NgxFloatUiPlacements.AUTO
32 |
33 | #17.1.0
34 | * Fix readme (closes [#4](https://github.com/tonysamperi/ngx-float-ui/issues/4))
35 | * Add support for `auto-start` and `auto-end` placements
36 |
37 | #17.0.1
38 | * Fix position fixed not applied to popper style
39 |
40 | #17.0.0
41 | * First release! 🎉
42 |
43 | #16.0.3
44 | * Remove development background rule causing graphic bug in certain scenarios (closes [#16](https://github.com/tonysamperi/ngx-float-ui/issues/16))
45 |
46 | #16.0.2
47 | * Fix some nullpointer when placement was not passed (closes [#12](https://github.com/tonysamperi/ngx-float-ui/issues/12) for real)
48 | * Performance improvement
49 |
50 | #16.0.1
51 | * Fix placement default to NgxFloatUiPlacements.AUTO
52 |
53 | #16.0.0
54 | * Release for Angular 16! 🎉
55 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function logStart(msg) {
4 | console.info("***** Task '" + msg + "' started *****");
5 | }
6 |
7 | function logEnd(msg) {
8 | console.info("***** Task '" + msg + "' finished *****");
9 | }
10 |
11 | const {task, dest, src, series} = require("gulp"),
12 | {join} = require("path"),
13 | exec = require("child_process").exec,
14 | sass = require("gulp-sass")(require("sass")),
15 | {inc} = require("semver"),
16 | log = require("plugin-log"),
17 | {obj} = require("through2")
18 | ;const gulp = require("gulp");
19 |
20 | const libName = "ngx-float-ui";
21 | const rootFolder = join(__dirname);
22 | const srcFolder = join(rootFolder, `projects/${libName}/src/lib`);
23 | const distFolder = join(rootFolder, `dist/${libName}`);
24 |
25 | const doBump = (type) => {
26 | return Promise.all(["./", join(rootFolder, "projects", libName)].map((p) => {
27 | return src(join(p, "package.json"))
28 | .pipe(obj((file, enc, cb) => {
29 | const pkgData = JSON.parse(file.contents.toString());
30 | const prevVersion = pkgData.version;
31 | pkgData.version = inc(prevVersion, type);
32 | file.contents = Buffer.from(JSON.stringify(pkgData, null, 4));
33 | log(
34 | "Bumped", log.colors.cyan(prevVersion),
35 | "to", log.colors.magenta(pkgData.version),
36 | "with type:", log.colors.cyan(type)
37 | );
38 | cb(null, file);
39 | }))
40 | .pipe(dest(p));
41 | }));
42 | };
43 |
44 | //
45 |
46 | task("bump:patch", () => {
47 | return doBump("patch");
48 | });
49 |
50 | task("bump:minor", () => {
51 | return doBump("minor");
52 | });
53 |
54 | task("bump:major", () => {
55 | return doBump("major");
56 | });
57 |
58 | gulp.task("bump:beta", () => {
59 | return doBump("prerelease", "beta");
60 | });
61 |
62 | task("build++", () => {
63 | return gulp.src(join(__dirname, "package.json"))
64 | .pipe(obj((file, enc, cb) => {
65 | const pkgData = JSON.parse(file.contents.toString());
66 | const prevBuild = pkgData.build;
67 | pkgData.build++;
68 | file.contents = Buffer.from(JSON.stringify(pkgData, null, 4));
69 | log(
70 | "Increased", log.colors.cyan(prevBuild),
71 | "to", log.colors.magenta(pkgData.build)
72 | );
73 | cb(null, file);
74 | }))
75 | .pipe(dest(__dirname));
76 | });
77 |
78 | task("handleStyles", () => {
79 | logStart("handleStyles");
80 |
81 | const buildScss = new Promise((resolve, reject) => {
82 | // SASS BUILD SCSS SOURCES
83 | gulp.src([join(srcFolder, "scss/theme-*.scss")])
84 | .pipe(sass({
85 | outputStyle: "expanded"
86 | }))
87 | .pipe(gulp.dest(join(distFolder, "css")))
88 | .on("error", reject)
89 | .on("end", resolve);
90 | });
91 |
92 | const copyScss = new Promise((resolve, reject) => {
93 | src([join(srcFolder, "scss/*.scss")])
94 | .pipe(dest(join(distFolder, "scss")))
95 | .on("error", reject)
96 | .on("end", resolve);
97 | });
98 |
99 | const copyScssIndex = new Promise((resolve, reject) => {
100 | src([join(srcFolder, "_index.scss")])
101 | .pipe(dest(distFolder))
102 | .on("error", reject)
103 | .on("end", resolve);
104 | });
105 |
106 | return Promise.all([buildScss, copyScss, copyScssIndex])
107 | .then(() => logEnd("handleStyles"))
108 | .catch(console.error);
109 | });
110 |
111 | task("copyFiles", (cb) => {
112 | logStart("copyFiles");
113 | src([join(rootFolder, "changelog.md"), join(rootFolder, "README.md"), join(rootFolder, "tsbl.js")])
114 | .pipe(dest(distFolder))
115 | .on("error", cb)
116 | .on("end", () => {
117 | logEnd("copyFiles");
118 | cb();
119 | });
120 | });
121 |
122 | // PACK
123 | task("pack", (cb) => {
124 | logStart("pack");
125 | exec(`npm pack ./dist/${libName}`, (err, stdout, stderr) => {
126 | console.log(stdout);
127 | console.log(stderr);
128 | logEnd("pack");
129 | cb(err);
130 | });
131 | });
132 |
133 | //MAIN
134 | task("postBuild", series("handleStyles", "copyFiles", function (cb, err) {
135 | logEnd("postBuild");
136 | cb(err);
137 | }));
138 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ngx-float-ui-repo",
3 | "version": "20.0.0",
4 | "build": 0,
5 | "license": "MIT",
6 | "description": "ngx-float-ui is an Angular wrapper for @floating-ui",
7 | "homepage": "https://tonysamperi.github.io/ngx-float-ui",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/tonysamperi/ngx-float-ui.git"
11 | },
12 | "bugs": {
13 | "url": "https://github.com/tonysamperi/ngx-float-ui/issues"
14 | },
15 | "author": {
16 | "name": "Tony Samperi",
17 | "email": "github@tonysamperi.it"
18 | },
19 | "keywords": [
20 | "Angular 20+",
21 | "ngx-float-ui",
22 | "popper Angular",
23 | "floating ui",
24 | "float",
25 | "ui"
26 | ],
27 | "engines": {
28 | "node": ">=20.11.1"
29 | },
30 | "scripts": {
31 | "ng": "ng",
32 | "start": "ng serve",
33 | "test": "ng test ngx-float-ui",
34 | "test:ci": "ng test ngx-float-ui --browsers=ChromeHeadless --watch=false",
35 | "test:demo": "ng test ngx-float-ui-repo",
36 | "lint": "ng lint",
37 | "build++": "gulp build++",
38 | "build": "ng build ngx-float-ui --configuration=production && gulp postBuild",
39 | "build:demo": "ng build ngx-float-ui-repo --configuration=production --base-href=",
40 | "pack": "gulp pack",
41 | "release": "npm publish ./dist/ngx-float-ui",
42 | "release:beta": "npm publish ./dist/ngx-float-ui --tag beta",
43 | "bump:patch": "gulp bump:patch",
44 | "bump:minor": "gulp bump:minor",
45 | "bump:major": "gulp bump:major"
46 | },
47 | "private": true,
48 | "dependencies": {
49 | "@angular/animations": "^20.0.2",
50 | "@angular/common": "^20.0.2",
51 | "@angular/compiler": "^20.0.2",
52 | "@angular/core": "^20.0.2",
53 | "@angular/forms": "^20.0.2",
54 | "@angular/platform-browser": "^20.0.2",
55 | "@angular/platform-browser-dynamic": "^20.0.2",
56 | "@angular/ssr": "^20.0.1",
57 | "@floating-ui/dom": "^1.6.13",
58 | "rxjs": "^7.4.0",
59 | "tslib": "^2.3.1",
60 | "typewriter-effect": "^2.19.0",
61 | "zone.js": "^0.15.0"
62 | },
63 | "devDependencies": {
64 | "@angular-devkit/build-angular": "^20.0.1",
65 | "@angular-devkit/build-ng-packagr": "^0.1002.0",
66 | "@angular/cli": "^20.0.1",
67 | "@angular/compiler-cli": "^20.0.2",
68 | "@angular/language-service": "^20.0.2",
69 | "@types/jasmine": "^3.3.8",
70 | "@types/jasminewd2": "^2.0.3",
71 | "@types/node": "^18.13.0",
72 | "@types/prismjs": "^1.16.5",
73 | "@types/semver": "^7.3.13",
74 | "gulp": "^4.0.2",
75 | "gulp-sass": "^5.1.0",
76 | "jasmine-core": "^4.6.1",
77 | "jasmine-spec-reporter": "^7.0.0",
78 | "karma": "^6.4.4",
79 | "karma-chrome-launcher": "^3.2.0",
80 | "karma-coverage-istanbul-reporter": "^3.0.3",
81 | "karma-jasmine": "^5.1.0",
82 | "karma-jasmine-html-reporter": "^2.1.0",
83 | "ng-packagr": "^20.0.0",
84 | "plugin-log": "^0.1.0",
85 | "prismjs": "^1.30.0",
86 | "puppeteer": "^24.10.0",
87 | "semver": "^7.6.2",
88 | "through2": "^4.0.2",
89 | "ts-node": "^10.9.1",
90 | "tslint": "^6.1.3",
91 | "typescript": "~5.8.3"
92 | },
93 | "packageManager": "yarn@4.3.1"
94 | }
95 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../../coverage/ngx-float-ui-repo'),
20 | reports: ['html', 'lcovonly', 'text-summary'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false,
30 | restartOnFileChange: true
31 | });
32 | };
33 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/app/app.component.html:
--------------------------------------------------------------------------------
1 | @if (isDemo) {
2 |
3 | }
4 | @else {
5 |
6 | }
7 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | @import "../scss/responsive";
2 |
3 | .terminal {
4 | position: relative;
5 | width: 80%;
6 | max-width: 600px;
7 | border-radius: 6px;
8 | overflow: hidden;
9 | background-color: rgb(15, 15, 16);
10 | font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
11 | color: white;
12 | padding: 0 1rem 1rem;
13 | margin: 0;
14 | text-align: left;
15 | padding-top: 44px;
16 |
17 | &:before {
18 | content: "\2022 \2022 \2022";
19 | position: absolute;
20 | top: 0;
21 | left: 0;
22 | background: #3a3a3a;
23 | color: #c2c3c4;
24 | width: 100%;
25 | font-size: 34px;
26 | line-height: 26px;
27 | text-indent: 6px;
28 | text-align: left;
29 | height: 34px;
30 | }
31 |
32 | ul {
33 | list-style: none;
34 | padding: 0;
35 | margin: 0;
36 | }
37 |
38 | a {
39 | color: #ff6b81;
40 | }
41 |
42 | @include respond-below(sm) {
43 | width: 100%;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, ViewEncapsulation} from "@angular/core";
2 | //
3 | import {NGX_FLOAT_UI_ENVIRONMENT} from "../environments/environment";
4 |
5 |
6 | @Component({
7 | selector: "app-root",
8 | templateUrl: "./app.component.html",
9 | styleUrls: ["./app.component.scss"],
10 | encapsulation: ViewEncapsulation.None,
11 | standalone: false
12 | })
13 | export class NgxFloatUiAppComponent {
14 |
15 | isDemo: boolean = NGX_FLOAT_UI_ENVIRONMENT.isDemo;
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import {BrowserModule} from "@angular/platform-browser";
2 | import {NgModule} from "@angular/core";
3 | //
4 | import {NgxFloatUiModule, NgxFloatUiPlacements, NgxFloatUiTriggers} from "ngx-float-ui";
5 | //
6 | import {NgxFloatUiAppComponent} from "./app.component";
7 | import {NgxFloatUiDemoComponent} from "./components/demo/demo.component";
8 | import {NgxFloatUiTestComponent} from "./components/test/test.component";
9 |
10 | @NgModule({
11 | declarations: [NgxFloatUiAppComponent],
12 | imports: [
13 | BrowserModule,
14 | NgxFloatUiModule.forRoot({
15 | trigger: NgxFloatUiTriggers.click,
16 | hideOnClickOutside: false,
17 | placement: NgxFloatUiPlacements.BOTTOM
18 | }),
19 | NgxFloatUiDemoComponent,
20 | NgxFloatUiTestComponent
21 | ],
22 | providers: [],
23 | bootstrap: [NgxFloatUiAppComponent]
24 | })
25 | export class NgxFloatUiAppModule {
26 | }
27 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/app/components/demo/demo.component.scss:
--------------------------------------------------------------------------------
1 | @import "../../../scss/vars";
2 | @import "../../../scss/responsive";
3 |
4 | $pop-header-img: "data:image/png;base64,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";
5 |
6 | // All strong tags here are red
7 | strong {
8 | color: map-get($pop-colors, primary-light);
9 | }
10 |
11 | .pop-generic-section {
12 | padding: 50px 40px;
13 | max-width: 900px;
14 | margin: 0 auto;
15 | border-bottom: 1px solid map-get($pop-colors, violet);
16 | }
17 |
18 | .pop-top-section {
19 | background-image: url($pop-header-img), radial-gradient(500px, #202028, #202028 20%, #202028);
20 | width: 100%;
21 | background-repeat: no-repeat;
22 | background-position: center;
23 | background-size: cover;
24 | text-align: center;
25 | padding: 60px 25px 50px;
26 | position: relative;
27 |
28 | > img.pop-angular-logo {
29 | position: absolute;
30 | width: $pop-angular-logo-size;
31 | left: calc(50% - #{$pop-angular-logo-size * 0.5});
32 | top: 235px; // Since popper logo has height 200px
33 | border-radius: 50%;
34 | background-color: #FFF;
35 | box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.5);
36 | }
37 |
38 | > img.pop-popper-logo {
39 | height: 200px;
40 | -webkit-user-select: none;
41 | -moz-user-select: none;
42 | -ms-user-select: none;
43 | user-select: none;
44 | margin-bottom: 24px;
45 | display: inline-block;
46 | }
47 |
48 | > h2 {
49 | font-size: 36px;
50 | }
51 | }
52 |
53 | .pop-badges {
54 | > img {
55 | display: inline-block;
56 | margin-right: 8px;
57 |
58 | &:last-child {
59 | margin-right: 0;
60 | }
61 | }
62 | }
63 |
64 | .pop-install-section {
65 | background: map-get($pop-colors, background);
66 | padding: 15px 0;
67 | font-weight: bold;
68 | margin-bottom: 25px;
69 | width: 100%;
70 | text-align: center;
71 | border-bottom: 1px solid map-get($pop-colors, violet);
72 |
73 | > div {
74 | padding: 0 40px;
75 | max-width: 900px;
76 | margin: 0 auto;
77 | -webkit-flex-direction: row;
78 | -ms-flex-direction: row;
79 | flex-direction: row;
80 | display: -webkit-box;
81 | display: -webkit-flex;
82 | display: -ms-flexbox;
83 | display: flex;
84 | -webkit-box-pack: center;
85 | -webkit-justify-content: center;
86 | -ms-flex-pack: center;
87 | justify-content: center;
88 | -webkit-align-items: center;
89 | -webkit-box-align: center;
90 | -ms-flex-align: center;
91 | align-items: center;
92 | color: white;
93 |
94 | @include respond-below(xs) {
95 | flex-direction: column;
96 | }
97 | }
98 | }
99 |
100 | .pop-demo {
101 | position: relative;
102 | padding: 0 40px;
103 | background-color: map-get($pop-colors, background);
104 | border-bottom: 1px solid map-get($pop-colors, violet);
105 |
106 | @include respond-below(sm) {
107 | padding: 0 20px;
108 | }
109 |
110 | > article {
111 | position: relative;
112 | display: grid;
113 | grid-template-columns: 2fr 3fr;
114 | -webkit-align-items: flex-start;
115 | -webkit-box-align: start;
116 | -ms-flex-align: start;
117 | align-items: start;
118 | font-size: 17px;
119 | padding: 50px 0;
120 | border-bottom: 1px solid map-get($pop-colors, violet);
121 |
122 | &:last-child {
123 | border-bottom: 0;
124 | }
125 |
126 | @include respond-below(sm) {
127 | grid-template-columns: 1fr;
128 | }
129 |
130 | .pop-popcorn-box {
131 | position: absolute;
132 | left: 50%;
133 | top: 50%;
134 | width: 130px;
135 | margin-left: -65px;
136 | margin-top: -65px;
137 | }
138 |
139 | > div.pop-popcorn-wrap + div {
140 | padding: 0 40px;
141 | display: grid;
142 | @include respond-below(sm) {
143 | padding: 0;
144 | }
145 | }
146 |
147 | &[pop-position] > div.pop-popcorn-wrap {
148 | height: 450px;
149 | position: relative;
150 | }
151 |
152 | &[pop-theming] {
153 | > div.pop-popcorn-wrap {
154 | position: relative;
155 | width: 100%;
156 | border-radius: 10px;
157 | background-color: map-get($pop-colors, background-grey);
158 | height: 300px;
159 |
160 | .pop-popcorn-box {
161 | width: 100px;
162 | margin-left: -50px;
163 | margin-top: -50px;
164 | }
165 | }
166 | }
167 |
168 | &[pop-overflow], &[pop-flipping], &[pop-click], &[pop-loose] {
169 | > div.pop-popcorn-wrap {
170 | @include pop-scrollbar();
171 | position: relative;
172 | width: 100%;
173 | background-color: map-get($pop-colors, background-grey);
174 | border-radius: 10px;
175 | overflow-y: scroll;
176 | overscroll-behavior: contain;
177 | height: 450px;
178 | margin: 0 auto;
179 |
180 | &:before {
181 | content: '';
182 | display: block;
183 | width: 1px;
184 | height: 600px;
185 | //border: 2px solid green;
186 | }
187 |
188 | &:after {
189 | content: '';
190 | display: block;
191 | width: 1px;
192 | height: 600px;
193 | }
194 |
195 | .pop-popcorn-box {
196 | position: relative;
197 | left: 0;
198 | width: 100px;
199 | margin-left: 100px;
200 | top: 0;
201 | }
202 |
203 | }
204 | }
205 |
206 | .pop-demo-placement-button {
207 | display: -webkit-box;
208 | display: -webkit-flex;
209 | display: -ms-flexbox;
210 | display: flex;
211 | -webkit-box-pack: center;
212 | -webkit-justify-content: center;
213 | -ms-flex-pack: center;
214 | justify-content: center;
215 | -webkit-align-items: center;
216 | -webkit-box-align: center;
217 | -ms-flex-align: center;
218 | align-items: center;
219 | position: absolute;
220 | width: 50px;
221 | height: 50px;
222 | padding: 0;
223 | border: none;
224 | background: none;
225 | -webkit-transition: -webkit-transform 0.4s cubic-bezier(0.54, 1.5, 0.38, 1.2);
226 | -webkit-transition: transform 0.4s cubic-bezier(0.54, 1.5, 0.38, 1.2);
227 | transition: transform 0.4s cubic-bezier(0.54, 1.5, 0.38, 1.2);
228 | cursor: pointer;
229 | outline: 0;
230 | will-change: transform;
231 | -webkit-tap-highlight-color: transparent;
232 |
233 | &:hover {
234 | -webkit-transform: scale(1.5);
235 | -ms-transform: scale(1.5);
236 | transform: scale(1.5);
237 | }
238 |
239 | &[data-placement^='top'] {
240 | top: 0;
241 | left: 50%;
242 | margin-left: -25px;
243 | }
244 |
245 | &[data-placement^='bottom'] {
246 | bottom: 0;
247 | left: 50%;
248 | margin-left: -25px;
249 | }
250 |
251 | &[data-placement='top-start'], &[data-placement='bottom-start'] {
252 | left: calc(50% - 50px);
253 | }
254 |
255 | &[data-placement='top-end'], &[data-placement='bottom-end'] {
256 | left: calc(50% + 50px);
257 | }
258 |
259 | &[data-placement^='right'] {
260 | right: 0;
261 | top: 50%;
262 | margin-top: -25px;
263 | }
264 |
265 | &[data-placement^='left'] {
266 | left: 0;
267 | top: 50%;
268 | margin-top: -25px;
269 | }
270 |
271 | &[data-placement='left-start'], &[data-placement='right-start'] {
272 | top: calc(50% - 50px);
273 | }
274 |
275 | &[data-placement='left-end'], &[data-placement='right-end'] {
276 | top: calc(50% + 50px);
277 | }
278 |
279 | &:focus > div {
280 | box-shadow: 0 0 0 6px rgba(255, 100, 150, 0.4);
281 | }
282 |
283 | > div {
284 | width: 18px;
285 | height: 18px;
286 | border: 2px solid map-get($pop-colors, primary-light);
287 | background: transparent;
288 | border-radius: 50%;
289 |
290 | &.selected {
291 | width: 18px;
292 | height: 18px;
293 | border: 2px solid #ff6b81;
294 | background: #ff6b81;
295 | border-radius: 50%;
296 | }
297 | }
298 | }
299 | }
300 |
301 | }
302 |
303 | footer {
304 | text-align: center;
305 | padding: 25px 0;
306 | width: 100%;
307 | }
308 |
309 | .animate-float {
310 | animation: float 8s ease infinite;
311 | }
312 |
313 | .animate-string {
314 | animation: string .25s ease-in-out infinite
315 | }
316 |
317 | @keyframes float {
318 | 0%,to {
319 | transform: rotate(-1deg) translate(-.1rem,1rem)
320 | }
321 |
322 | 50% {
323 | transform: rotate(2deg)
324 | }
325 |
326 | 75% {
327 | transform: rotate(.5deg) translate(.25rem,.5rem)
328 | }
329 | }
330 |
331 | @keyframes string {
332 | 25% {
333 | transform: translate(.02rem,-.05rem) scaleY(1.03) scaleX(1.02)
334 | }
335 |
336 | 50% {
337 | transform: translate(.1rem,.1rem) scaleY(1.05) scaleX(.95)
338 | }
339 |
340 | 75% {
341 | transform: translate(.05rem,.05rem) scaleY(.96) scaleX(1)
342 | }
343 | }
344 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/app/components/demo/demo.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, OnInit} from "@angular/core";
2 |
3 | import {
4 | NgxFloatUiPlacements,
5 | NgxFloatUiLooseDirective,
6 | NgxFloatUiDirective,
7 | NgxFloatUiContentComponent
8 | } from "ngx-float-ui";
9 | //
10 | import {getNgxFloatUiCodeMap} from "../../shared/ngx-float-ui-code-map.const";
11 | import {NgxFloatUiArticleTypes, NgxFloatUiArticleTypesRef} from "../../shared/ngx-float-ui-article-types.model";
12 | //
13 | import pkg from "../../../../../../package.json";
14 | import {highlightElement} from "prismjs";
15 | import {catchError, of, switchMap, timer} from "rxjs";
16 | import {ajax, AjaxResponse} from "rxjs/ajax";
17 | import TypeWriter from "typewriter-effect/dist/core.js";
18 |
19 | const codeTypes: NgxFloatUiArticleTypesRef<"css" | "markup"> = {
20 | position: "markup",
21 | overflow: "markup",
22 | flipping: "markup",
23 | theming: "css",
24 | click: "markup",
25 | scroll: "markup"
26 | };
27 |
28 | @Component({
29 | selector: "app-demo",
30 | templateUrl: "demo.component.html",
31 | styleUrls: ["demo.component.scss"],
32 | imports: [NgxFloatUiContentComponent, NgxFloatUiDirective, NgxFloatUiLooseDirective]
33 | })
34 | export class NgxFloatUiDemoComponent implements OnInit {
35 |
36 | get buildRef(): string {
37 | return `${pkg.version}-build-${pkg.build}`;
38 | }
39 |
40 | get codeMap(): NgxFloatUiArticleTypesRef
{
41 | return getNgxFloatUiCodeMap(this.selectedPosition);
42 | }
43 |
44 | messages: { opts?: { delay?: number | "natural"; loop?: boolean; }; text: string; }[] = [];
45 | popperPlacements: typeof NgxFloatUiPlacements = NgxFloatUiPlacements;
46 | // tslint:disable-next-line:no-bitwise
47 | positionButtons: NgxFloatUiPlacements[] = Object.values(NgxFloatUiPlacements).filter((v) => !~v.indexOf("auto"));
48 | selectedPosition: NgxFloatUiPlacements = this.positionButtons[0];
49 | year: number = new Date().getFullYear();
50 |
51 | ngOnInit(): void {
52 | Object.values(NgxFloatUiArticleTypes).forEach((s: NgxFloatUiArticleTypes) => this._updateCode(s));
53 | Array.from(document.querySelectorAll(".pop-popcorn-wrap")).forEach((el: HTMLElement) => el.scrollTop = 300);
54 | this._getMessages();
55 | }
56 |
57 | onPopperUpdate(): void {
58 | console.info("ON POPPER UPDATE FIRED!");
59 | }
60 |
61 | showAlert(): void {
62 | alert("Ciao!");
63 | }
64 |
65 | updatePosition(positionButton: NgxFloatUiPlacements): void {
66 | this.selectedPosition = positionButton;
67 | this._updateCode(NgxFloatUiArticleTypes.position);
68 | }
69 |
70 | private _getMessages(): void {
71 | ajax.get(`./assets/messages.json`)
72 | .pipe(
73 | switchMap((resp: AjaxResponse) => {
74 | this.messages = resp.response.messages;
75 |
76 | return timer(150);
77 | }),
78 | catchError(() => of([]))
79 | )
80 | .subscribe({
81 | next: () => {
82 | this.messages.forEach(({text, opts = {}}, i: number) => {
83 | const tw = new TypeWriter(`[pop-messages] li:nth-child(${i + 1})`, {
84 | strings: !!opts.loop ? [text] : void 0,
85 | autoStart: !!opts.loop,
86 | loop: !!opts.loop,
87 | delay: opts.delay || "natural"
88 | });
89 | if (!opts.loop) {
90 | tw.typeString(text).stop().start();
91 | }
92 | // else {
93 | // console.info("AUTOSTART WAS TRUE FOR", text);
94 | // }
95 | });
96 | }
97 | });
98 | }
99 |
100 | private _updateCode(key: NgxFloatUiArticleTypes): void {
101 | const $code = document.querySelector(`[pop-${key}-code]`);
102 | if (!$code) {
103 | return;
104 | }
105 | $code.classList.add(`language-${codeTypes[key] || "markup"}`);
106 | $code.innerHTML = this.codeMap[key];
107 | highlightElement($code);
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/app/components/test/test.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 | POPCORN
CONDIMENTS
& PRICE
13 |
14 | - BUTTER: $1.99
15 | - SALT: $0.99
16 | - CARAMEL: $0.49
17 |
18 |
19 |

26 |
27 |
28 |
29 |
41 | Open on click
42 |
43 |
Click the popcorn to show condiments!.
44 |
Click the popcorn again to hide condiments.
45 |
46 |
47 |
48 |
49 |
50 |
51 |
59 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/app/components/test/test.component.scss:
--------------------------------------------------------------------------------
1 | @import "../demo/demo.component";
2 |
3 | .expand {
4 | min-height: 700px;
5 | }
6 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/app/components/test/test.component.ts:
--------------------------------------------------------------------------------
1 | import {Component} from "@angular/core";
2 | //
3 | import {NgxFloatUiContentComponent, NgxFloatUiDirective, NgxFloatUiPlacements} from "ngx-float-ui";
4 |
5 | @Component({
6 | selector: "app-test",
7 | templateUrl: "test.component.html",
8 | styleUrls: ["test.component.scss"],
9 | standalone: true,
10 | imports: [NgxFloatUiContentComponent, NgxFloatUiDirective]
11 | })
12 | export class NgxFloatUiTestComponent {
13 |
14 | popperPlacements: typeof NgxFloatUiPlacements = NgxFloatUiPlacements;
15 | year: number = new Date().getFullYear();
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/app/shared/ngx-float-ui-article-types.model.ts:
--------------------------------------------------------------------------------
1 | export enum NgxFloatUiArticleTypes {
2 | position = "position",
3 | overflow = "overflow",
4 | flipping = "flipping",
5 | theming = "theming",
6 | click = "click",
7 | scroll = "scroll"
8 | }
9 |
10 | export type NgxFloatUiArticleTypesRef = { [key in NgxFloatUiArticleTypes]: T };
11 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/app/shared/ngx-float-ui-code-map.const.ts:
--------------------------------------------------------------------------------
1 | import {NgxFloatUiArticleTypesRef} from "./ngx-float-ui-article-types.model";
2 | import {NgxFloatUiPlacements} from "ngx-float-ui";
3 |
4 | const positionEntries = Object.entries(NgxFloatUiPlacements);
5 | // tslint:disable-next-line:naming-convention
6 | export const getNgxFloatUiCodeMap = (positionValue: NgxFloatUiPlacements): NgxFloatUiArticleTypesRef => {
7 | const position = positionValue
8 | ? positionEntries.find(([, v]) => v === positionValue)[0]
9 | : "";
10 |
11 | return {
12 | popLoose: `
17 | `,
18 | click: `<float-ui-content #popperClickContent>
19 | </float-ui-content>
20 | <img alt="Popcorn box" src="assets/images/popcorn-box.svg"
21 | [floatUi]="popperClickContent"
22 | trigger="click"
23 | [placement]="NgxFloatUiPlacements.BOTTOM"
24 | class="pop-popcorn-box">`,
25 | scroll: `<float-ui-content #popperClickContent>
26 | </float-ui-content>
27 | <img alt="Popcorn box" src="assets/images/popcorn-box.svg"
28 | [floatUi]="popperClickContent"
29 | [hideOnScroll]="!0"
30 | trigger="click"
31 | [placement]="NgxFloatUiPlacements.TOP"
32 | class="pop-popcorn-box">`,
33 | flipping: `<float-ui-content #myPopperContent>
34 | I'm popper :)
35 | </float-ui-content>
36 | <img alt="Popcorn box" src="assets/images/popcorn-box.svg"
37 | [floatUi]="myPopperContent"
38 | [showOnStart]="true"
39 | trigger="click"
40 | preventOverflow
41 | [placement]="NgxFloatUiPlacements.TOP"
42 | class="pop-popcorn-box">`,
43 | overflow: `<float-ui-content #popcornPrices>
44 | <p class="pop-text-bold">POPCORN<br />SIZE<br />& PRICE</p>
45 | <ul>
46 | <li>XXS: $1.99</li>
47 | <li>XS: $2.99</li>
48 | <li>S: $3.99</li>
49 | <li>M: $4.99</li>
50 | <li>L: $5.99</li>
51 | <li>XL: $6.99</li>
52 | <li>XXL: $7.99</li>
53 | </ul>
54 | </float-ui-content>
55 | <img alt="Popcorn box" src="assets/images/popcorn-box.svg"
56 | [floatUi]="popcornPrices"
57 | [showOnStart]="true"
58 | trigger="click"
59 | [placement]="NgxFloatUiPlacements.RIGHT"
60 | class="pop-popcorn-box">`,
61 | position: `<float-ui-content #myPopperContent>
62 | I'm popper :)
63 | </float-ui-content>
64 | <img alt="Popcorn box" src="assets/images/popcorn-box.svg"
65 | [floatUi]="myPopperContent"
66 | [showOnStart]="true"
67 | [hideOnClickOutside]="false"
68 | trigger="click"
69 | [placement]="NgxFloatUiPlacements.${position}"
70 | class="pop-popcorn-box">`,
71 | theming: `@use ngx-float-ui/css/theme-dark.css
72 | /* OR */
73 | @use ngx-float-ui/css/theme-white.css
74 | /* OR */
75 | @use ngx-float-ui/scss/theme-dark
76 | /* OR */
77 | @use ngx-float-ui/scss/theme-white
78 | /* OR */
79 | @include ngx-float-ui-theme(#777, #fff1e0);
80 | `
81 | } as NgxFloatUiArticleTypesRef;
82 | };
83 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonysamperi/ngx-float-ui/c8d6298c3cf9669d5b251ec15fec652de4eedca6/projects/ngx-float-ui-repo/src/assets/.gitkeep
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/assets/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonysamperi/ngx-float-ui/c8d6298c3cf9669d5b251ec15fec652de4eedca6/projects/ngx-float-ui-repo/src/assets/favicon/favicon.ico
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/assets/fonts/luckiest-guy-latin-400.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonysamperi/ngx-float-ui/c8d6298c3cf9669d5b251ec15fec652de4eedca6/projects/ngx-float-ui-repo/src/assets/fonts/luckiest-guy-latin-400.woff
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/assets/fonts/luckiest-guy-latin-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonysamperi/ngx-float-ui/c8d6298c3cf9669d5b251ec15fec652de4eedca6/projects/ngx-float-ui-repo/src/assets/fonts/luckiest-guy-latin-400.woff2
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/assets/images/popcorn-box.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
59 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/assets/images/popper-icon.eps:
--------------------------------------------------------------------------------
1 | %!PS-Adobe-3.0 EPSF-3.0
2 | %%CreationDate: Tue Sep 15 15:35:14 2020
3 | %%LanguageLevel: 3
4 | %%BoundingBox: 0 0 512 512
5 | %%EndComments
6 | newpath
7 | 261.08 475.88 moveto
8 | 280.90 490.55 305.10 499.90 329.95 499.96 curveto
9 | 353.23 500.73 376.60 493.79 395.96 480.90 curveto
10 | 419.09 465.60 436.47 441.70 443.53 414.85 curveto
11 | 443.92 413.30 444.30 411.57 445.85 410.76 curveto
12 | 468.72 393.58 486.92 370.25 497.99 343.87 curveto
13 | 514.23 305.59 514.94 261.01 499.81 222.26 curveto
14 | 485.72 185.55 457.78 154.41 422.82 136.42 curveto
15 | 420.33 135.49 419.49 132.84 418.21 130.77 curveto
16 | 403.42 105.95 381.66 85.34 356.07 71.95 curveto
17 | 353.04 70.47 350.95 67.74 348.57 65.45 curveto
18 | 333.53 50.46 318.58 35.38 303.49 20.44 curveto
19 | 291.58 8.89 270.58 9.03 258.88 20.82 curveto
20 | 242.98 36.59 227.20 52.49 211.33 68.31 curveto
21 | 208.26 71.30 205.50 74.71 201.68 76.79 curveto
22 | 185.42 86.57 171.13 99.46 159.32 114.28 curveto
23 | 124.73 116.14 90.88 129.17 63.96 150.97 curveto
24 | 39.31 170.76 20.40 197.65 10.28 227.62 curveto
25 | -1.45 262.02 -1.53 300.27 10.13 334.71 curveto
26 | 25.38 380.75 61.77 419.17 106.92 436.89 curveto
27 | 109.90 437.71 111.37 440.60 113.30 442.75 curveto
28 | 127.44 459.49 148.18 470.51 169.99 472.80 curveto
29 | 194.35 475.69 219.53 467.39 237.78 451.09 curveto
30 | 244.20 460.48 252.15 468.82 261.08 475.88 curveto
31 | 250.39 421.64 moveto
32 | 244.47 406.51 246.11 389.27 252.83 374.69 curveto
33 | 254.74 369.85 258.65 365.41 257.88 359.88 curveto
34 | 257.40 352.24 248.73 346.68 241.56 349.34 curveto
35 | 236.81 350.67 234.21 355.15 232.30 359.33 curveto
36 | 225.33 373.09 221.11 388.47 221.61 403.97 curveto
37 | 221.63 412.02 223.64 419.83 225.59 427.56 curveto
38 | 212.56 442.49 191.89 450.27 172.24 447.76 curveto
39 | 155.05 445.84 138.92 436.23 129.07 422.01 curveto
40 | 120.11 411.63 113.40 398.01 115.19 383.97 curveto
41 | 116.31 377.47 111.50 370.72 104.98 369.69 curveto
42 | 98.76 368.41 92.05 372.70 90.65 378.91 curveto
43 | 89.15 385.96 89.71 393.26 90.73 400.34 curveto
44 | 74.54 389.50 60.41 375.54 49.76 359.20 curveto
45 | 34.02 335.33 25.83 306.59 26.61 278.01 curveto
46 | 27.24 246.01 39.20 214.40 59.88 189.98 curveto
47 | 81.12 164.51 111.55 146.90 144.24 141.26 curveto
48 | 140.08 151.02 137.17 161.42 136.58 172.04 curveto
49 | 136.16 179.18 142.90 185.65 150.03 184.84 curveto
50 | 156.14 184.61 161.34 179.19 161.64 173.14 curveto
51 | 162.68 160.36 167.87 148.35 174.41 137.46 curveto
52 | 184.99 119.77 201.14 105.93 218.93 95.81 curveto
53 | 246.26 80.42 279.19 75.49 309.88 81.83 curveto
54 | 345.17 89.04 377.10 111.41 395.71 142.27 curveto
55 | 401.17 152.69 406.13 163.50 408.90 174.98 curveto
56 | 410.06 179.62 409.58 185.08 413.23 188.72 curveto
57 | 418.03 194.33 427.75 194.04 432.20 188.13 curveto
58 | 435.75 184.19 434.97 178.58 434.44 173.74 curveto
59 | 453.61 189.56 468.81 210.29 477.26 233.71 curveto
60 | 487.49 261.48 488.23 292.57 479.62 320.86 curveto
61 | 470.78 350.18 451.72 376.20 426.60 393.69 curveto
62 | 423.33 395.83 421.12 399.29 420.50 403.15 curveto
63 | 416.12 425.01 403.07 444.90 385.00 457.92 curveto
64 | 369.19 469.39 349.56 475.58 330.00 474.86 curveto
65 | 296.24 474.47 263.33 452.98 250.39 421.64 curveto
66 | 345.50 258.37 moveto
67 | 352.34 259.72 359.76 257.57 364.72 252.64 curveto
68 | 369.93 247.71 372.35 240.08 370.94 233.05 curveto
69 | 369.63 225.98 364.55 219.79 357.90 217.08 curveto
70 | 351.71 214.50 344.33 215.04 338.59 218.53 curveto
71 | 331.87 222.43 327.62 230.20 328.10 237.97 curveto
72 | 328.19 247.74 335.94 256.62 345.50 258.37 curveto
73 | 188.44 257.36 moveto
74 | 194.59 258.60 201.24 257.12 206.15 253.19 curveto
75 | 212.79 248.12 215.96 239.01 213.82 230.92 curveto
76 | 211.57 221.25 201.97 213.95 192.03 214.52 curveto
77 | 181.22 214.68 171.54 224.10 171.23 234.93 curveto
78 | 170.37 245.34 178.22 255.44 188.44 257.36 curveto
79 | 216.22 189.15 moveto
80 | 254.82 189.17 293.42 189.17 332.02 189.15 curveto
81 | 329.36 172.13 320.30 155.93 306.34 145.66 curveto
82 | 294.95 137.16 280.14 133.17 266.06 135.51 curveto
83 | 254.01 137.31 242.82 143.52 234.41 152.28 curveto
84 | 224.64 162.25 218.50 175.45 216.22 189.15 curveto
85 | closepath
86 | 0.784 0.231 0.314 setrgbcolor
87 | fill
88 | newpath
89 | 250.39 421.64 moveto
90 | 263.33 452.98 296.24 474.47 330.00 474.86 curveto
91 | 349.56 475.58 369.19 469.39 385.00 457.92 curveto
92 | 403.07 444.90 416.12 425.01 420.50 403.15 curveto
93 | 421.12 399.29 423.33 395.83 426.60 393.69 curveto
94 | 451.72 376.20 470.78 350.18 479.62 320.86 curveto
95 | 488.23 292.57 487.49 261.48 477.26 233.71 curveto
96 | 468.81 210.29 453.61 189.56 434.44 173.74 curveto
97 | 434.97 178.58 435.75 184.19 432.20 188.13 curveto
98 | 427.75 194.04 418.03 194.33 413.23 188.72 curveto
99 | 409.58 185.08 410.06 179.62 408.90 174.98 curveto
100 | 406.13 163.50 401.17 152.69 395.71 142.27 curveto
101 | 377.10 111.41 345.17 89.04 309.88 81.83 curveto
102 | 279.19 75.49 246.26 80.42 218.93 95.81 curveto
103 | 201.14 105.93 184.99 119.77 174.41 137.46 curveto
104 | 167.87 148.35 162.68 160.36 161.64 173.14 curveto
105 | 161.34 179.19 156.14 184.61 150.03 184.84 curveto
106 | 142.90 185.65 136.16 179.18 136.58 172.04 curveto
107 | 137.17 161.42 140.08 151.02 144.24 141.26 curveto
108 | 111.55 146.90 81.12 164.51 59.88 189.98 curveto
109 | 39.20 214.40 27.24 246.01 26.61 278.01 curveto
110 | 25.83 306.59 34.02 335.33 49.76 359.20 curveto
111 | 60.41 375.54 74.54 389.50 90.73 400.34 curveto
112 | 89.71 393.26 89.15 385.96 90.65 378.91 curveto
113 | 92.05 372.70 98.76 368.41 104.98 369.69 curveto
114 | 111.50 370.72 116.31 377.47 115.19 383.97 curveto
115 | 113.40 398.01 120.11 411.63 129.07 422.01 curveto
116 | 138.92 436.23 155.05 445.84 172.24 447.76 curveto
117 | 191.89 450.27 212.56 442.49 225.59 427.56 curveto
118 | 223.64 419.83 221.63 412.02 221.61 403.97 curveto
119 | 221.11 388.47 225.33 373.09 232.30 359.33 curveto
120 | 234.21 355.15 236.81 350.67 241.56 349.34 curveto
121 | 248.73 346.68 257.40 352.24 257.88 359.88 curveto
122 | 258.65 365.41 254.74 369.85 252.83 374.69 curveto
123 | 246.11 389.27 244.47 406.51 250.39 421.64 curveto
124 | 345.50 258.37 moveto
125 | 335.94 256.62 328.19 247.74 328.10 237.97 curveto
126 | 327.62 230.20 331.87 222.43 338.59 218.53 curveto
127 | 344.33 215.04 351.71 214.50 357.90 217.08 curveto
128 | 364.55 219.79 369.63 225.98 370.94 233.05 curveto
129 | 372.35 240.08 369.93 247.71 364.72 252.64 curveto
130 | 359.76 257.57 352.34 259.72 345.50 258.37 curveto
131 | 188.44 257.36 moveto
132 | 178.22 255.44 170.37 245.34 171.23 234.93 curveto
133 | 171.54 224.10 181.22 214.68 192.03 214.52 curveto
134 | 201.97 213.95 211.57 221.25 213.82 230.92 curveto
135 | 215.96 239.01 212.79 248.12 206.15 253.19 curveto
136 | 201.24 257.12 194.59 258.60 188.44 257.36 curveto
137 | 216.22 189.15 moveto
138 | 218.50 175.45 224.64 162.25 234.41 152.28 curveto
139 | 242.82 143.52 254.01 137.31 266.06 135.51 curveto
140 | 280.14 133.17 294.95 137.16 306.34 145.66 curveto
141 | 320.30 155.93 329.36 172.13 332.02 189.15 curveto
142 | 293.42 189.17 254.82 189.17 216.22 189.15 curveto
143 | closepath
144 | 1.000 0.902 0.616 setrgbcolor
145 | fill
146 | %%EOF
147 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/assets/images/popper-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/assets/images/popper-logo.svg:
--------------------------------------------------------------------------------
1 |
19 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/assets/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "messages": [
3 | {
4 | "text": "⭐ 07/06/2025: v20 released!",
5 | "opts": {
6 | "loop": true
7 | }
8 | },
9 | {
10 | "text": "⚡ 14/12/2024: released v19!",
11 | "opts": {
12 | "loop": true
13 | }
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const NGX_FLOAT_UI_ENVIRONMENT = {
2 | production: !0,
3 | isDemo: !0
4 | };
5 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const NGX_FLOAT_UI_ENVIRONMENT = {
6 | production: !1,
7 | isDemo: !0
8 | };
9 |
10 | /*
11 | * For easier debugging in development mode, you can import the following file
12 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
13 | *
14 | * This import should be commented out in production mode because it will have a negative impact
15 | * on performance if an error is thrown.
16 | */
17 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
18 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ngx-float-ui - Angular 8+ wrapper for Floating UI - Tooltip & Popover Positioning Engine
6 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/main.ts:
--------------------------------------------------------------------------------
1 | import {enableProdMode} from "@angular/core";
2 | import {platformBrowserDynamic} from "@angular/platform-browser-dynamic";
3 |
4 | import {NgxFloatUiAppModule} from "./app/app.module";
5 | import {NGX_FLOAT_UI_ENVIRONMENT} from "./environments/environment";
6 |
7 | if (NGX_FLOAT_UI_ENVIRONMENT.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(NgxFloatUiAppModule)
12 | .catch(err => console.error(err));
13 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /**
18 | * By default, zone.js will patch all possible macroTask and DomEvents
19 | * user can disable parts of macroTask/DomEvents patch by setting following flags
20 | * because those flags need to be set before `zone.js` being loaded, and webpack
21 | * will put import in the top of bundle, so user need to create a separate file
22 | * in this directory (for example: zone-flags.ts), and put the following flags
23 | * into that file, and then add the following code before importing zone.js.
24 | * import './zone-flags.ts';
25 | *
26 | * The flags allowed in zone-flags.ts are listed here.
27 | *
28 | * The following flags will work for all browsers.
29 | *
30 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
31 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
32 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
33 | *
34 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
35 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
36 | *
37 | * (window as any).__Zone_enable_cross_context_check = true;
38 | *
39 | */
40 |
41 | /***************************************************************************************************
42 | * Zone JS is required by default for Angular itself.
43 | */
44 | import "zone.js"; // Included with Angular CLI.
45 |
46 |
47 | /***************************************************************************************************
48 | * APPLICATION IMPORTS / CONFIGS / POLYFILLS
49 | */
50 | (window as any).__Zone_enable_cross_context_check = true;
51 |
52 | // Prismjs
53 | if (!Element.prototype.matches) {
54 | Element.prototype.matches = (Element.prototype as any).msMatchesSelector ||
55 | Element.prototype.webkitMatchesSelector;
56 | }
57 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/scss/code-highlight.scss:
--------------------------------------------------------------------------------
1 | code[class*=language-], pre[class*=language-] {
2 | font-family: Consolas, Menlo, Monaco, Andale Mono WT, Andale Mono, Lucida Console, Lucida Sans Typewriter, DejaVu Sans Mono, Bitstream Vera Sans Mono, Liberation Mono, Nimbus Mono L, Courier New, Courier, monospace;
3 | line-height: 1.5;
4 | direction: ltr;
5 | text-align: left;
6 | white-space: pre;
7 | word-spacing: normal;
8 | word-break: normal;
9 | -moz-tab-size: 4;
10 | -o-tab-size: 4;
11 | tab-size: 4;
12 | -webkit-hyphens: none;
13 | -ms-hyphens: none;
14 | hyphens: none
15 | }
16 |
17 | code[class*=language-]::selection, code[class*=language-] ::selection, pre[class*=language-]::selection, pre[class*=language-] ::selection {
18 | text-shadow: none;
19 | background: rgb(45, 46, 58);
20 | }
21 |
22 | pre[class*=language-] {
23 | padding: 10px 15px;
24 | margin: 1em -15px;
25 | overflow: auto;
26 | background: rgb(45, 46, 58);
27 | color: #ffe69d;
28 | font-size: 14px;
29 | border-radius: 0
30 | }
31 |
32 | @media (min-width: 600px) {
33 | pre[class*=language-] {
34 | font-size: 16px;
35 | border: 1px solid hsla(0, 0%, 100%, .05);
36 | margin-left: 0;
37 | margin-right: 0;
38 | border-radius: 10px
39 | }
40 | }
41 |
42 | :not(pre) > code[class*=language-] {
43 | color: #ff6b81;
44 | font-weight: 700;
45 | white-space: pre-wrap
46 | }
47 |
48 | :not(pre) > code[class*=language-]::selection, :not(pre) > code[class*=language-] ::selection {
49 | text-shadow: none;
50 | background: #8f51e6;
51 | color: #fff
52 | }
53 |
54 | .token-line {
55 | min-height: 1em
56 | }
57 |
58 | .token.cdata, .token.comment, .token.doctype, .token.prolog {
59 | color: #95b2db
60 | }
61 |
62 | .token.keyword, .token.punctuation {
63 | color: #4edee5
64 | }
65 |
66 | .token.namespace {
67 | opacity: .7
68 | }
69 |
70 | .token.boolean, .token.number, .token.operator, .token.tag {
71 | color: #ed655e
72 | }
73 |
74 | .token.function, .token.property {
75 | color: #b886fd
76 | }
77 |
78 | .token.atrule-id, .token.selector, .token.tag-id {
79 | color: #f3ebff
80 | }
81 |
82 | .token.attr-name, code.language-javascript {
83 | color: #d6b9fe
84 | }
85 |
86 | .language-css .token.string, .language-scss .token.string, .style .token.string, .token.atrule, .token.attr-value, .token.control, .token.directive, .token.entity, .token.placeholder, .token.regex, .token.statement, .token.string, .token.unit, .token.url, .token.variable, code.language-css, code.language-scss {
87 | color: #ffb6b3
88 | }
89 |
90 | .token.dom {
91 | color: #a5c8ff
92 | }
93 |
94 | .token.deleted {
95 | text-decoration: line-through
96 | }
97 |
98 | .token.inserted {
99 | border-bottom: 1px dotted #f3ebff;
100 | text-decoration: none
101 | }
102 |
103 | .token.italic {
104 | font-style: italic
105 | }
106 |
107 | .token.bold, .token.important {
108 | font-weight: 700
109 | }
110 |
111 | .token.important {
112 | color: #d6b9fe
113 | }
114 |
115 | .token.entity {
116 | cursor: help
117 | }
118 |
119 | pre > code.highlight {
120 | outline: .4em solid #aa75f5;
121 | outline-offset: .4em
122 | }
123 |
124 | .line-numbers .line-numbers-rows {
125 | border-right-color: #372f42
126 | }
127 |
128 | .line-numbers-rows > span:before {
129 | color: #372f42
130 | }
131 |
132 | .line-highlight {
133 | background: rgba(237, 101, 94, .2);
134 | background: linear-gradient(90deg, rgba(237, 101, 94, .2) 70%, rgba(237, 101, 94, 0))
135 | }
136 |
137 | .gatsby-highlight-code-line {
138 | background-color: #1c1425;
139 | display: block;
140 | margin-right: -1em;
141 | margin-left: -1em;
142 | padding-right: 1em;
143 | padding-left: .75em;
144 | border-left: .25em solid #ff6b81
145 | }
146 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/scss/popper-theme.scss:
--------------------------------------------------------------------------------
1 | @mixin pop-custom-theme {
2 | .float-ui-container {
3 | background: white;
4 | color: #642f45;
5 | border: 0;
6 | max-width: 250px;
7 | box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2), 0 5px 8px 0 rgba(0, 0, 0, 0.14), 0 1px 14px 0 rgba(0, 0, 0, 0.12);
8 | z-index: 999;
9 | font-family: 'Roboto', sans-serif;
10 |
11 | > .float-ui-arrow {
12 | background: #FFF;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/scss/responsive.scss:
--------------------------------------------------------------------------------
1 | $breakpoints: (
2 | xxs: 360px,
3 | xs: 768px,
4 | sm: 992px,
5 | md: 1200px,
6 | lg: 1920px
7 | );
8 |
9 | //
10 | // RESPOND ABOVE
11 | //––––––––––––––––––––––––––––––––––––––––––––––––––
12 |
13 | // e.g. @include respond-above(sm) {}
14 | @mixin respond-above($breakpoint) {
15 |
16 | // If the breakpoint exists in the map.
17 | @if map-has-key($breakpoints, $breakpoint) {
18 |
19 | // Get the breakpoint value.
20 | $breakpoint-value: map-get($breakpoints, $breakpoint);
21 |
22 | // Write the media query.
23 | @media (min-width: $breakpoint-value) {
24 | @content;
25 | }
26 |
27 | // If the breakpoint doesn't exist in the map.
28 | } @else {
29 |
30 | // Log a warning.
31 | @warn 'Invalid breakpoint: #{$breakpoint}.';
32 | }
33 | }
34 |
35 | //
36 | // RESPOND BELOW
37 | //––––––––––––––––––––––––––––––––––––––––––––––––––
38 |
39 | // e.g. @include respond-below(sm) {}
40 | @mixin respond-below($breakpoint) {
41 |
42 | // If the breakpoint exists in the map.
43 | @if map-has-key($breakpoints, $breakpoint) {
44 |
45 | // Get the breakpoint value.
46 | $breakpoint-value: map-get($breakpoints, $breakpoint);
47 |
48 | // Write the media query.
49 | @media (max-width: ($breakpoint-value - 1)) {
50 | @content;
51 | }
52 |
53 | // If the breakpoint doesn't exist in the map.
54 | } @else {
55 |
56 | // Log a warning.
57 | @warn 'Invalid breakpoint: #{$breakpoint}.';
58 | }
59 | }
60 |
61 | //
62 | // RESPOND BETWEEN
63 | //––––––––––––––––––––––––––––––––––––––––––––––––––
64 |
65 | // e.g. @include respond-between(sm, md) {}
66 | @mixin respond-between($lower, $upper) {
67 |
68 | // If both the lower and upper breakpoints exist in the map.
69 | @if map-has-key($breakpoints, $lower) and map-has-key($breakpoints, $upper) {
70 |
71 | // Get the lower and upper breakpoints.
72 | $lower-breakpoint: map-get($breakpoints, $lower);
73 | $upper-breakpoint: map-get($breakpoints, $upper);
74 |
75 | // Write the media query.
76 | @media (min-width: $lower-breakpoint) and (max-width: ($upper-breakpoint - 1)) {
77 | @content;
78 | }
79 |
80 | // If one or both of the breakpoints don't exist.
81 | } @else {
82 |
83 | // If lower breakpoint is invalid.
84 | @if (map-has-key($breakpoints, $lower) == false) {
85 |
86 | // Log a warning.
87 | @warn 'Your lower breakpoint was invalid: #{$lower}.';
88 | }
89 |
90 | // If upper breakpoint is invalid.
91 | @if (map-has-key($breakpoints, $upper) == false) {
92 |
93 | // Log a warning.
94 | @warn 'Your upper breakpoint was invalid: #{$upper}.';
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/scss/vars.scss:
--------------------------------------------------------------------------------
1 | $pop-colors: (
2 | background: #1f2027,
3 | background-grey: rgb(45, 46, 58),
4 | violet: #362c4f,
5 | primary: rgb(221, 214, 254),
6 | primary-light: #ff6b81,
7 | accent: #f4e0f1,
8 | text: #d2cbe4
9 | );
10 |
11 | $pop-angular-logo-size: 40px;
12 |
13 | @mixin pop-scrollbar() {
14 | $base: rgba(255, 230, 255, 1);
15 | -webkit-scrollbar-color: $base transparent;
16 | -moz-scrollbar-color: $base transparent;
17 | -ms-scrollbar-color: $base transparent;
18 | scrollbar-color: $base transparent;
19 |
20 | &::-webkit-scrollbar {
21 | -webkit-appearance: none;
22 | width: 7px;
23 | }
24 |
25 | &::-webkit-scrollbar-thumb {
26 | border-radius: 4px;
27 | background-color: $base;
28 | -webkit-box-shadow: 0 0 1px rgba(255, 255, 255, 0.5);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/styles.scss:
--------------------------------------------------------------------------------
1 | @import "scss/vars";
2 | @import "scss/code-highlight";
3 | @import "scss/popper-theme";
4 | @import "../../ngx-float-ui/src/lib/scss/theme";
5 |
6 | /* You can add global scss to this file, and also import other style files */
7 | @font-face {
8 | font-family: Luckiest Guy;
9 | font-style: normal;
10 | font-display: swap;
11 | font-weight: 400;
12 | src: local("Luckiest Guy Regular"), local("LuckiestGuy-Regular"), url(assets/fonts/luckiest-guy-latin-400.woff2) format("woff2"), url(assets/fonts/luckiest-guy-latin-400.woff) format("woff")
13 | }
14 |
15 | body {
16 | @include pop-scrollbar();
17 | line-height: 1.6;
18 | background: map-get($pop-colors, background);
19 | color: map-get($pop-colors, accent);
20 | font-family: -apple-system, Helvetica Neue, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Open Sans, sans-serif;
21 | margin: 0;
22 | box-sizing: border-box;
23 |
24 | *, :after, :before {
25 | box-sizing: inherit;
26 | }
27 |
28 | // COMMON ELEMENTS - START
29 |
30 | h1,
31 | h2,
32 | h3,
33 | h4,
34 | h5,
35 | h6 {
36 | margin: 8px 0;
37 | }
38 |
39 | p {
40 | margin: 0;
41 | }
42 |
43 | a {
44 | &.pop-link-yellow {
45 | color: #ffe69d;
46 | -webkit-text-decoration: none;
47 | text-decoration: none;
48 | padding-bottom: 1px;
49 | border-bottom: 2px solid rgba(255, 228, 148, 0.25);
50 | -webkit-transition: border-bottom-color 0.15s ease-in-out;
51 | transition: border-bottom-color 0.15s ease-in-out;
52 |
53 | &:hover {
54 | border-bottom: 2px solid rgba(255, 228, 148, 1);
55 | }
56 | }
57 | }
58 |
59 | ul {
60 | padding-left: 20px;
61 | margin: 0;
62 |
63 | &.pop-list {
64 | list-style: none;
65 |
66 | > li > svg {
67 | display: inline-block;
68 | top: 6px;
69 | left: 2px;
70 | margin-right: 6px;
71 | position: relative;
72 | color: map-get($pop-colors, primary-light);
73 | height: 25px;
74 | margin-left: -30px;
75 | }
76 | }
77 | }
78 |
79 | // COMMON ELEMENTS - END
80 |
81 | @include pop-custom-theme();
82 |
83 | [pop-theming] {
84 |
85 | @include ngx-float-ui-theme(#777, #fff1e0);
86 | }
87 |
88 | .pop-title-light {
89 | font-family: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
90 | -webkit-letter-spacing: 0.5px;
91 | -moz-letter-spacing: 0.5px;
92 | -ms-letter-spacing: 0.5px;
93 | letter-spacing: 0.5px;
94 | text-transform: uppercase;
95 | font-size: 30px;
96 | -webkit-font-smoothing: antialiased;
97 | margin-top: 15px;
98 | margin-bottom: 15px;
99 | line-height: 1.1;
100 | color: map-get($pop-colors, accent);
101 | }
102 |
103 | .pop-button-white {
104 | display: inline-block;
105 | background: white;
106 | padding: 10px 16px;
107 | border-radius: 4px;
108 | -webkit-text-decoration: none;
109 | text-decoration: none;
110 | font-size: 16px;
111 | font-weight: 700;
112 | box-shadow: 0 8px 16px -4px rgba(66, 49, 78, 0.5);
113 | -webkit-transition: all 0.2s ease-in-out;
114 | transition: all 0.2s ease-in-out;
115 | text-transform: uppercase;
116 | margin: 5px;
117 |
118 | > svg {
119 | vertical-align: -7px;
120 | margin-right: 5px;
121 | }
122 | }
123 |
124 | // TYPOGRAPHY - START
125 |
126 | .pop-text-center {
127 | text-align: center;
128 | }
129 |
130 | .pop-text-bold {
131 | font-weight: bold;
132 | }
133 |
134 | .pop-lucky-font {
135 | font-family: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
136 | }
137 |
138 | .pop-color-primary {
139 | color: map-get($pop-colors, primary);
140 | }
141 |
142 | // TYPOGRAPHY - END
143 |
144 | .pop-code-box {
145 | display: -webkit-box;
146 | display: -webkit-flex;
147 | display: -ms-flexbox;
148 | display: flex;
149 | flex-direction: row;
150 | align-items: center;
151 | background: map-get($pop-colors, background-grey);
152 | border-radius: 4px;
153 | color: #ffd3f8;
154 | font-family: Menlo, SFMono-Regular, Consolas, Liberation Mono, monospace;
155 | font-size: 14px;
156 | overflow: hidden;
157 | font-weight: normal;
158 | margin: 5px;
159 | max-width: 100%;
160 | overflow-x: auto;
161 |
162 | > div {
163 | padding: 10px 15px;
164 | white-space: nowrap;
165 | display: -webkit-box;
166 | display: -webkit-flex;
167 | display: -ms-flexbox;
168 | display: flex;
169 | border-radius: 4px;
170 | color: #ffd3f8;
171 | font-family: Menlo, SFMono-Regular, Consolas, Liberation Mono, monospace;
172 | font-size: 14px;
173 | overflow: hidden;
174 | font-weight: normal;
175 | margin: 5px;
176 | max-width: 100%;
177 | overflow-x: auto;
178 |
179 | &:first-child {
180 | display: -webkit-box;
181 | display: -webkit-flex;
182 | display: -ms-flexbox;
183 | display: flex;
184 | -webkit-align-items: center;
185 | -webkit-box-align: center;
186 | -ms-flex-align: center;
187 | background: rgba(0, 0, 0, 0.25);
188 | text-align: center;
189 | padding: 0 15px;
190 | color: white;
191 | }
192 | }
193 |
194 | .pop-code-box-icon {
195 | max-width: 50px;
196 | -webkit-user-select: none;
197 | -moz-user-select: none;
198 | -ms-user-select: none;
199 | user-select: none;
200 | }
201 | }
202 |
203 | }
204 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/src/typewriter.d.ts:
--------------------------------------------------------------------------------
1 | declare module "typewriter-effect/dist/core.js";
2 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/app",
5 | "types": [],
6 | "resolveJsonModule": true,
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "files": [
10 | "src/main.ts",
11 | "src/polyfills.ts"
12 | ],
13 | "include": [
14 | "src/**/*.d.ts"
15 | ],
16 | "exclude": [
17 | "src/test.ts",
18 | "src/**/*.spec.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "src/**/*.spec.ts",
16 | "src/**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui-repo/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [
5 | true,
6 | "attribute",
7 | "app",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | true,
12 | "element",
13 | "app",
14 | "kebab-case"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | process.env.CHROME_BIN = require("puppeteer").executablePath();
5 |
6 | module.exports = function (config) {
7 | config.set({
8 | basePath: "",
9 | frameworks: ["jasmine", "@angular-devkit/build-angular"],
10 | plugins: [
11 | require("karma-jasmine"),
12 | require("karma-chrome-launcher"),
13 | require("karma-jasmine-html-reporter"),
14 | require("karma-coverage-istanbul-reporter"),
15 | require("@angular-devkit/build-angular/plugins/karma")
16 | ],
17 | client: {
18 | clearContext: false // leave Jasmine Spec Runner output visible in browser
19 | },
20 | coverageIstanbulReporter: {
21 | dir: require("path").join(__dirname, "../../coverage/ngx-float-ui"),
22 | reports: ["html", "lcovonly", "text-summary"],
23 | fixWebpackSourcePaths: true
24 | },
25 | reporters: ["progress", "kjhtml"],
26 | port: 9876,
27 | colors: true,
28 | logLevel: config.LOG_INFO,
29 | autoWatch: true,
30 | browsers: ['Chrome', 'ChromeHeadless', 'ChromeHeadlessNoSandbox'],
31 | singleRun: false,
32 | restartOnFileChange: true,
33 | customLaunchers: {
34 | ChromeHeadlessCustom: {
35 | base: "ChromeHeadless",
36 | flags: [
37 | "--no-sandbox",
38 | "--disable-gpu",
39 | "--remote-debugging-port=9333"
40 | ]
41 | }
42 | }
43 | });
44 | };
45 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/ngx-float-ui",
4 | "lib": {
5 | "entryFile": "src/public-api.ts"
6 | },
7 | "allowedNonPeerDependencies": [
8 | "@floating-ui/dom"
9 | ],
10 | "keepLifecycleScripts": true
11 | }
12 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ngx-float-ui",
3 | "version": "20.0.0",
4 | "license": "MIT",
5 | "description": "ngx-float-ui is an Angular wrapper for Floating UI",
6 | "homepage": "https://tonysamperi.github.io/ngx-float-ui",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/tonysamperi/ngx-float-ui"
10 | },
11 | "bugs": {
12 | "url": "https://github.com/tonysamperi/ngx-float-ui/issues"
13 | },
14 | "author": {
15 | "name": "Tony Samperi",
16 | "email": "github@tonysamperi.it"
17 | },
18 | "keywords": [
19 | "Angular 20+",
20 | "ngx-float-ui",
21 | "floating-ui Angular",
22 | "Angular 19",
23 | "floating-ui"
24 | ],
25 | "scripts": {
26 | "postinstall": "node ./tsbl.js"
27 | },
28 | "dependencies": {
29 | "@floating-ui/dom": "^1.6.13"
30 | },
31 | "peerDependencies": {
32 | "@angular/common": "^20.0.0",
33 | "@angular/core": "^20.0.0",
34 | "rxjs": "^7.4.0"
35 | },
36 | "exports": {
37 | ".": {
38 | "sass": "./_index.scss"
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/_index.scss:
--------------------------------------------------------------------------------
1 | // Theming APIs
2 | @forward 'scss/theme';
3 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/components/ngx-float-ui-content/ngx-float-ui-content.component.html:
--------------------------------------------------------------------------------
1 |
13 | @if (text) {
14 |
17 |
18 |
19 | }
20 | @else {
21 |
23 |
24 |
25 | }
26 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/components/ngx-float-ui-content/ngx-float-ui-content.component.scss:
--------------------------------------------------------------------------------
1 | $arrow-size: 10px !default;
2 |
3 | float-ui-content {
4 | position: relative;
5 | display: block;
6 | }
7 |
8 | .float-ui-container {
9 | display: none;
10 | position: absolute;
11 | border-radius: 3px;
12 | border: 1px solid grey;
13 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
14 | padding: 10px;
15 |
16 | &.float-ui-fixed {
17 | position: fixed;
18 | }
19 |
20 | &.float-ui-animation {
21 | -webkit-animation: ngxp-fadeIn 150ms ease-out;
22 | -moz-animation: ngxp-fadeIn 150ms ease-out;
23 | -o-animation: ngxp-fadeIn 150ms ease-out;
24 | animation: ngxp-fadeIn 150ms ease-out;
25 | transition: transform 0.65s cubic-bezier(0.43, 0.33, 0.14, 1.01) 0s;
26 | }
27 |
28 | > .float-ui-arrow {
29 | position: absolute;
30 | width: $arrow-size;
31 | height: $arrow-size;
32 | z-index: -1;
33 | -webkit-transform: rotate(45deg);
34 | -ms-transform: rotate(45deg);
35 | transform: rotate(45deg);
36 | }
37 |
38 | }
39 |
40 | @-webkit-keyframes ngxp-fadeIn {
41 | 0% {
42 | display: none;
43 | opacity: 0;
44 | }
45 |
46 | 1% {
47 | display: block;
48 | opacity: 0;
49 | }
50 |
51 | 100% {
52 | display: block;
53 | opacity: 1;
54 | }
55 | }
56 |
57 | @keyframes ngxp-fadeIn {
58 | 0% {
59 | display: none;
60 | opacity: 0;
61 | }
62 |
63 | 1% {
64 | display: block;
65 | opacity: 0;
66 | }
67 |
68 | 100% {
69 | display: block;
70 | opacity: 1;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/components/ngx-float-ui-content/ngx-float-ui-content.component.ts:
--------------------------------------------------------------------------------
1 | import { NgClass, NgStyle } from "@angular/common";
2 | import {
3 | ChangeDetectionStrategy,
4 | ChangeDetectorRef,
5 | Component,
6 | ElementRef,
7 | EventEmitter,
8 | HostListener,
9 | OnDestroy,
10 | ViewChild,
11 | ViewContainerRef,
12 | ViewEncapsulation
13 | } from "@angular/core";
14 | //
15 | import {
16 | Alignment,
17 | arrow,
18 | autoPlacement,
19 | autoUpdate,
20 | computePosition,
21 | ComputePositionConfig,
22 | flip,
23 | limitShift,
24 | offset,
25 | Placement,
26 | shift,
27 | } from "@floating-ui/dom";
28 | import {fromEvent, Subject, takeUntil} from "rxjs";
29 | //
30 | import {NgxFloatUiOptions} from "../../models/ngx-float-ui-options.model";
31 | import {NgxFloatUiPlacements} from "../../models/ngx-float-ui-placements.model";
32 | import {NgxFloatUiTriggers} from "../../models/ngx-float-ui-triggers.model";
33 |
34 | @Component({
35 | selector: "float-ui-content",
36 | encapsulation: ViewEncapsulation.None,
37 | changeDetection: ChangeDetectionStrategy.OnPush,
38 | templateUrl: "./ngx-float-ui-content.component.html",
39 | styleUrls: ["./ngx-float-ui-content.component.scss"],
40 | exportAs: "ngxFloatUiContent",
41 | standalone: true,
42 | imports: [NgStyle, NgClass]
43 | })
44 | export class NgxFloatUiContentComponent implements OnDestroy {
45 |
46 | static nextId: number = 0;
47 |
48 | protected get _dynamicArrowSides() {
49 | return {
50 | top: "left",
51 | right: "top",
52 | bottom: "left",
53 | left: "top"
54 | };
55 | }
56 |
57 | protected get _sideAxis() {
58 | return {
59 | left: "x",
60 | top: "y",
61 | right: "x",
62 | bottom: "y"
63 | };
64 | }
65 |
66 | protected get _staticArrowSides() {
67 | return {
68 | top: "bottom",
69 | right: "left",
70 | bottom: "top",
71 | left: "right"
72 | };
73 | }
74 |
75 | ariaHidden: string;
76 | arrowColor: string | null = null;
77 | displayType: string;
78 | floatUiOptions: NgxFloatUiOptions = {
79 | disableAnimation: false,
80 | disableDefaultStyling: false,
81 | boundariesElement: "",
82 | trigger: NgxFloatUiTriggers.hover,
83 | positionFixed: false,
84 | appendToBody: false,
85 | popperModifiers: []
86 | } as NgxFloatUiOptions;
87 | floatUiSwitch: () => void;
88 | @ViewChild("floatUiViewRef", {static: !0}) floatUiViewRef: ElementRef;
89 | id: string = `ngx_float_ui_${++NgxFloatUiContentComponent.nextId}`;
90 | isMouseOver: boolean = !1;
91 | onHidden = new EventEmitter();
92 | onUpdate: () => any;
93 | opacity: number;
94 | referenceObject: HTMLElement;
95 | state: boolean;
96 | text: string;
97 |
98 | protected _destroy$: Subject = new Subject();
99 | protected _resizeCtrl$: Subject = new Subject();
100 | protected _styleId = `${this.id}_style`;
101 |
102 | constructor(public elRef: ElementRef,
103 | protected _viewRef: ViewContainerRef,
104 | protected _changeDetectorRef: ChangeDetectorRef) {
105 | this._toggleVisibility(!1);
106 | }
107 |
108 | clean() {
109 | this.toggleVisibility(false);
110 | if (!this.floatUiSwitch) {
111 | return;
112 | }
113 | this.floatUiSwitch();
114 | }
115 |
116 | extractAppliedClassListExpr(classList: string | string[] = []): object {
117 | const klassList = Array.isArray(classList) ? classList : typeof classList === typeof "" ? classList.replace(/ /, "").split(",") : [];
118 |
119 | return klassList.reduce((acc, klass) => {
120 | acc[klass] = !0;
121 |
122 | return acc;
123 | }, {});
124 | }
125 |
126 | hide(): void {
127 | if (this.floatUiSwitch) {
128 | this.floatUiSwitch();
129 | }
130 | this.toggleVisibility(!1);
131 | this.onHidden.emit();
132 | }
133 |
134 | ngOnDestroy() {
135 | this._destroy$.next();
136 | this.clean();
137 | if (this.floatUiOptions.appendTo && this.elRef && this.elRef.nativeElement && this.elRef.nativeElement.parentNode) {
138 | this._viewRef.detach();
139 | this.elRef.nativeElement.parentNode.removeChild(this.elRef.nativeElement);
140 | }
141 | }
142 |
143 | onDocumentResize() {
144 | this.update();
145 | }
146 |
147 | @HostListener("mouseover")
148 | onMouseOver() {
149 | this.isMouseOver = true;
150 | }
151 |
152 | show(): void {
153 | if (!this.referenceObject) {
154 | return;
155 | }
156 | this._resizeCtrl$.next();
157 | this._determineArrowColor();
158 | this.floatUiSwitch = autoUpdate(
159 | this.referenceObject,
160 | this.floatUiViewRef.nativeElement,
161 | () => {
162 | this._computePosition();
163 | }
164 | );
165 | fromEvent(document, "resize")
166 | .pipe(
167 | takeUntil(this._resizeCtrl$),
168 | takeUntil(this._destroy$)
169 | )
170 | .subscribe({
171 | next: () => this.onDocumentResize()
172 | });
173 | }
174 |
175 | @HostListener("mouseleave")
176 | showOnLeave() {
177 | this.isMouseOver = false;
178 | if (this.floatUiOptions.trigger !== NgxFloatUiTriggers.hover && !this.floatUiOptions.hideOnMouseLeave) {
179 | return;
180 | }
181 | this.hide();
182 | }
183 |
184 | // Toggle visibility and detect changes - Run only after ngOnInit!
185 | toggleVisibility(state: boolean): void {
186 | this._toggleVisibility(state);
187 | // tslint:disable-next-line:no-string-literal
188 | if (!this._changeDetectorRef["destroyed"]) {
189 | this._changeDetectorRef.detectChanges();
190 | }
191 | }
192 |
193 | update(): void {
194 | this._computePosition();
195 | }
196 |
197 | protected _computePosition(): void {
198 | const appendToParent = this.floatUiOptions.appendTo && document.querySelector(this.floatUiOptions.appendTo);
199 | if (appendToParent) {
200 | const parent = this.elRef.nativeElement.parentNode;
201 | if (parent !== appendToParent) {
202 | parent && parent.removeChild(this.elRef.nativeElement);
203 | appendToParent.appendChild(this.elRef.nativeElement);
204 | }
205 | }
206 |
207 | const arrowElement = this.elRef.nativeElement.querySelector(".float-ui-arrow");
208 | const arrowLen = arrowElement.offsetWidth;
209 | // Get half the arrow box's hypotenuse length
210 | const floatingOffset = Math.sqrt(2 * arrowLen ** 2) / 2;
211 | const parsedAutoAlignment: Alignment | undefined = (this.floatUiOptions.placement?.replace("auto-", "") || void 0) as Alignment | undefined;
212 | // Since "auto" doesn't really exist in floating-ui we pass undefined to have auto
213 | const parsedPlacement = !this.floatUiOptions.placement || this.floatUiOptions.placement.indexOf(NgxFloatUiPlacements.AUTO) === 0
214 | ? void 0
215 | : (this.floatUiOptions.placement as Placement);
216 | const popperOptions: Partial = {
217 | placement: parsedPlacement,
218 | strategy: this.floatUiOptions.positionFixed ? "fixed" : "absolute",
219 | middleware: [
220 | offset(floatingOffset),
221 | ...(this.floatUiOptions.preventOverflow
222 | ? [flip()]
223 | : []
224 | ),
225 | shift({limiter: limitShift()}),
226 | arrow({
227 | element: arrowElement,
228 | padding: 4
229 | })
230 | ]
231 | };
232 | // Since preventOverflow uses "flip" and "flip" can't be used with "autoPlacement" we get here only if both conditions are falsy
233 | if (!this.floatUiOptions.preventOverflow && !popperOptions.placement) {
234 | const boundariesElement = this.floatUiOptions.boundariesElement
235 | ? document.querySelector(this.floatUiOptions.boundariesElement)
236 | : this.referenceObject.parentElement;
237 | popperOptions.middleware.push(
238 | autoPlacement({
239 | crossAxis: !0,
240 | alignment: parsedAutoAlignment,
241 | autoAlignment: this.floatUiOptions.placement === NgxFloatUiPlacements.AUTO,
242 | boundary: boundariesElement
243 | })
244 | );
245 | }
246 | computePosition(this.referenceObject, this.floatUiViewRef.nativeElement, {
247 | ...popperOptions
248 | })
249 | .then(({middlewareData, x, y, placement}) => {
250 | const side = placement.split("-")[0];
251 | this.floatUiViewRef.nativeElement.setAttribute("data-float-ui-placement", side);
252 | if (middlewareData.arrow) {
253 | const staticArrowSide = this._staticArrowSides[side];
254 | const dynamicArrowSide = this._dynamicArrowSides[side];
255 | const dynamicSideAxis = this._sideAxis[dynamicArrowSide];
256 | Object.assign(arrowElement.style, {
257 | top: "",
258 | bottom: "",
259 | left: "",
260 | right: "",
261 | [dynamicArrowSide]: middlewareData.arrow[dynamicSideAxis] != null ? `${middlewareData.arrow[dynamicSideAxis]}px` : "",
262 | [staticArrowSide]: `${-arrowLen / 2}px`
263 | });
264 | }
265 | Object.assign(this.floatUiViewRef.nativeElement.style, {
266 | left: `${x}px`,
267 | top: `${y}px`
268 | });
269 | this.toggleVisibility(!0);
270 | this.onUpdate?.();
271 | });
272 | }
273 |
274 | protected _createArrowSelector(): string {
275 | return `div#${this.id}.float-ui-container > .float-ui-arrow.ngxp__force-arrow`;
276 | }
277 |
278 | protected _determineArrowColor() {
279 | if (!this.floatUiOptions.styles || this.arrowColor) {
280 |
281 | return !1;
282 | }
283 | const ruleValue = this.floatUiOptions.styles["background-color"] || this.floatUiOptions.styles.backgroundColor;
284 | if (this.arrowColor === ruleValue) {
285 | return !1;
286 | }
287 | this.arrowColor = ruleValue;
288 | let $style = document.querySelector(`#${this._styleId}`) as HTMLStyleElement;
289 | const styleContent = this.arrowColor ?
290 | `${this._createArrowSelector()}:before { background-color: ${this.arrowColor}; }` : "";
291 | if (!$style) {
292 | $style = document.createElement("style") as HTMLStyleElement;
293 | $style.id = this._styleId;
294 | $style.setAttribute("type", "text/css");
295 | document.head.appendChild($style);
296 | }
297 | // tslint:disable-next-line:no-string-literal
298 | if ($style["styleSheet"]) {
299 | // tslint:disable-next-line:no-string-literal
300 | $style["styleSheet"].cssText = styleContent;
301 | // This is required for IE8 and below.
302 | }
303 | else {
304 | $style.innerHTML = styleContent;
305 | }
306 | }
307 |
308 | protected _toggleVisibility(state): void {
309 | this.displayType = ["none", "block"][+state];
310 | this.opacity = +state;
311 | this.ariaHidden = `${!state}`;
312 | this.state = state;
313 | }
314 | }
315 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/directives/ngx-float-ui/ngx-float-ui-loose.directive.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ChangeDetectorRef,
3 | Directive,
4 | ElementRef,
5 | Inject,
6 | Input,
7 | ViewContainerRef
8 | } from "@angular/core";
9 | //
10 | import {NgxFloatUiOptions} from "../../models/ngx-float-ui-options.model";
11 | import {NgxFloatUiPlacements} from "../../models/ngx-float-ui-placements.model";
12 | import {NGX_FLOAT_UI_DEFAULTS} from "../../models/ngx-float-ui-defaults.model";
13 | import {NgxFloatUiDirective} from "./ngx-float-ui.directive";
14 | import {NgxFloatUiContentComponent} from "../../components/ngx-float-ui-content/ngx-float-ui-content.component";
15 | import {NgxFloatUiTriggers} from "../../models/ngx-float-ui-triggers.model";
16 |
17 | @Directive({
18 | selector: "[floatUiLoose]",
19 | exportAs: "floatUiLoose",
20 | standalone: true
21 | })
22 | export class NgxFloatUiLooseDirective extends NgxFloatUiDirective {
23 |
24 | @Input()
25 | set floatUiLoose(newValue: string | NgxFloatUiContentComponent) {
26 | this.floatUi = newValue;
27 | }
28 |
29 | @Input()
30 | set loosePlacement(newValue: `${NgxFloatUiPlacements}`) {
31 | this.placement = newValue as NgxFloatUiPlacements;
32 | }
33 |
34 | @Input()
35 | set looseTrigger(newValue: `${NgxFloatUiTriggers}`) {
36 | this.showTrigger = newValue as NgxFloatUiTriggers;
37 | }
38 |
39 | constructor(changeDetectorRef: ChangeDetectorRef,
40 | elementRef: ElementRef,
41 | vcr: ViewContainerRef,
42 | @Inject(NGX_FLOAT_UI_DEFAULTS) popperDefaults: NgxFloatUiOptions = {}) {
43 | super(changeDetectorRef, elementRef, vcr, popperDefaults);
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/directives/ngx-float-ui/ngx-float-ui.directive.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ChangeDetectorRef,
3 | ComponentRef,
4 | Directive,
5 | ElementRef,
6 | EventEmitter,
7 | Inject,
8 | Input,
9 | OnDestroy,
10 | OnInit,
11 | Output,
12 | ViewContainerRef
13 | } from "@angular/core";
14 | import {fromEvent, Subject, takeUntil, timer} from "rxjs";
15 | //
16 | import {NgxFloatUiContentComponent} from "../../components/ngx-float-ui-content/ngx-float-ui-content.component";
17 | import {NGX_FLOAT_UI_DEFAULTS} from "../../models/ngx-float-ui-defaults.model";
18 | import {NgxFloatUiOptions} from "../../models/ngx-float-ui-options.model";
19 | import {NgxFloatUiPlacements} from "../../models/ngx-float-ui-placements.model";
20 | import {NgxFloatUiTriggers} from "../../models/ngx-float-ui-triggers.model";
21 | import {NgxFloatUiUtils} from "../../models/ngx-float-ui-utils.class";
22 |
23 | @Directive({
24 | selector: "[floatUi]",
25 | exportAs: "floatUi",
26 | standalone: true
27 | })
28 | export class NgxFloatUiDirective implements OnInit, OnDestroy {
29 |
30 | static baseOptions: NgxFloatUiOptions = {
31 | showDelay: 0,
32 | hideOnClickOutside: true,
33 | hideOnMouseLeave: false,
34 | hideOnScroll: false,
35 | appendTo: undefined,
36 | ariaRole: "popper",
37 | ariaDescribe: "",
38 | styles: {},
39 | trigger: NgxFloatUiTriggers.click
40 | };
41 |
42 | @Input()
43 | set applyClass(newValue: string) {
44 | if (newValue === this._applyClass) {
45 | return;
46 | }
47 | this._applyClass = newValue;
48 | this._checkExisting("applyClass", newValue);
49 | }
50 |
51 | get applyClass(): string {
52 | return this._applyClass;
53 | }
54 |
55 | @Input()
56 | set arrowClass(newValue: string) {
57 | if (newValue === this._arrowClass) {
58 | return;
59 | }
60 | this._arrowClass = newValue;
61 | if (this._content) {
62 | this._content.floatUiOptions.applyArrowClass = newValue;
63 | if (!this._shown) {
64 | return;
65 | }
66 | this._content.update();
67 | }
68 | }
69 |
70 | get arrowClass(): string {
71 | return this._arrowClass;
72 | }
73 |
74 | @Input()
75 | set disabled(newValue: boolean) {
76 | if (newValue === this._disabled) {
77 | return;
78 | }
79 | this._disabled = !!newValue;
80 | if (this._shown) {
81 | this.hide();
82 | }
83 | }
84 |
85 | get disabled(): boolean {
86 | return this._disabled;
87 | }
88 |
89 | @Input()
90 | set floatUi(newValue: string | NgxFloatUiContentComponent) {
91 | if (newValue === this._floatUi) {
92 |
93 | return;
94 | }
95 | this._floatUi = newValue;
96 | if (this._content) {
97 | if (typeof newValue === "string") {
98 | this._content.text = newValue;
99 | }
100 | else {
101 | this._content = newValue;
102 | }
103 | }
104 | }
105 |
106 | get floatUi(): string | NgxFloatUiContentComponent {
107 | return this._floatUi;
108 | }
109 |
110 | @Input()
111 | set hideOnClickOutside(newValue: boolean | string) {
112 | this._hideOnClickOutside = NgxFloatUiUtils.coerceBooleanProperty(newValue);
113 | }
114 |
115 | get hideOnClickOutside(): boolean {
116 | return this._hideOnClickOutside;
117 | }
118 |
119 | @Input()
120 | set placement(newValue: NgxFloatUiPlacements) {
121 | this._placement = newValue;
122 | this._checkExisting("placement", newValue);
123 | }
124 |
125 | get placement(): NgxFloatUiPlacements {
126 |
127 | return this._placement;
128 | }
129 |
130 | @Input()
131 | set preventOverflow(newValue: boolean) {
132 | this._preventOverflow = NgxFloatUiUtils.coerceBooleanProperty(newValue);
133 | this._checkExisting("preventOverflow", this._preventOverflow);
134 | }
135 |
136 | get preventOverflow(): boolean {
137 | return this._preventOverflow;
138 | }
139 |
140 | @Input()
141 | set showOnStart(newValue: boolean) {
142 | this._showOnStart = NgxFloatUiUtils.coerceBooleanProperty(newValue);
143 | }
144 |
145 | get showOnStart(): boolean {
146 | return this._showOnStart;
147 | }
148 |
149 | @Input()
150 | appendTo: string;
151 |
152 | @Input()
153 | ariaDescribe: string | void;
154 |
155 | @Input()
156 | ariaRole: string | void;
157 |
158 | @Input()
159 | boundariesElement: string;
160 |
161 | @Input()
162 | disableAnimation: boolean;
163 |
164 | @Input()
165 | disableStyle: boolean;
166 |
167 | @Input()
168 | hideOnMouseLeave: boolean | void;
169 |
170 | @Input()
171 | hideOnScroll: boolean | void;
172 |
173 | @Input()
174 | hideTimeout: number = 0;
175 |
176 | @Output()
177 | onHidden: EventEmitter = new EventEmitter();
178 |
179 | @Output()
180 | onShown: EventEmitter = new EventEmitter();
181 |
182 | @Output()
183 | onUpdate: EventEmitter = new EventEmitter();
184 |
185 | @Input()
186 | positionFixed: boolean;
187 |
188 | @Input()
189 | showDelay: number | undefined;
190 |
191 | @Input()
192 | showTrigger: NgxFloatUiTriggers | undefined;
193 |
194 | @Input()
195 | styles: object;
196 |
197 | @Input()
198 | targetElement: HTMLElement;
199 |
200 | @Input()
201 | timeoutAfterShow: number = 0;
202 |
203 | protected _applyClass: string;
204 | protected _arrowClass: string;
205 | protected _content: NgxFloatUiContentComponent;
206 | protected _contentClass = NgxFloatUiContentComponent;
207 | protected _contentRef: ComponentRef;
208 | protected _destroy$: Subject = new Subject();
209 | protected _disabled: boolean;
210 | protected _floatUi: string | NgxFloatUiContentComponent;
211 | protected _globalEventListenersCtrl$: Subject = new Subject();
212 | protected _hideOnClickOutside: boolean = !0;
213 | protected _placement: NgxFloatUiPlacements;
214 | protected _preventOverflow: boolean;
215 | protected _scheduledHideTimeoutCtrl$: Subject = new Subject();
216 | protected _scheduledShowTimeoutCtrl$: Subject = new Subject();
217 | protected _shown: boolean = !1;
218 | protected _showOnStart: boolean = !1;
219 |
220 | constructor(protected _changeDetectorRef: ChangeDetectorRef,
221 | protected _elementRef: ElementRef,
222 | protected _vcr: ViewContainerRef,
223 | @Inject(NGX_FLOAT_UI_DEFAULTS) protected _popperDefaults: NgxFloatUiOptions = {}) {
224 | NgxFloatUiDirective.baseOptions = {...NgxFloatUiDirective.baseOptions, ...this._popperDefaults};
225 | }
226 |
227 | static assignDefined(target: any, ...sources: any[]) {
228 | for (const source of sources) {
229 | for (const key of Object.keys(source)) {
230 | const val = source[key];
231 | if (val !== undefined) {
232 | target[key] = val;
233 | }
234 | }
235 | }
236 |
237 | return target;
238 | }
239 |
240 | applyTriggerListeners() {
241 | switch (this.showTrigger) {
242 | case NgxFloatUiTriggers.click:
243 | this._addListener("click", this.toggle.bind(this));
244 | break;
245 | case NgxFloatUiTriggers.mousedown:
246 | this._addListener("mousedown", this.toggle.bind(this));
247 | break;
248 | case NgxFloatUiTriggers.hover:
249 | this._addListener("mouseenter", this.scheduledShow.bind(this, this.showDelay));
250 | ["touchend", "touchcancel", "mouseleave"].forEach((eventName) => {
251 | this._addListener(eventName, this.scheduledHide.bind(this, null, this.hideTimeout));
252 | });
253 | break;
254 | }
255 | if (this.showTrigger !== NgxFloatUiTriggers.hover && this.hideOnMouseLeave) {
256 | ["touchend", "touchcancel", "mouseleave"].forEach((eventName) => {
257 | this._addListener(eventName, this.scheduledHide.bind(this, null, this.hideTimeout));
258 | });
259 | }
260 | }
261 |
262 | getRefElement() {
263 | return this.targetElement || this._elementRef.nativeElement;
264 | }
265 |
266 | hide() {
267 | if (this.disabled) {
268 | return;
269 | }
270 | if (!this._shown) {
271 | this._scheduledShowTimeoutCtrl$.next();
272 |
273 | return;
274 | }
275 |
276 | this._shown = false;
277 | if (this._contentRef) {
278 | this._contentRef.instance.hide();
279 | }
280 | else {
281 | this._content.hide();
282 | }
283 | this.onHidden.emit(this);
284 | this._globalEventListenersCtrl$.next();
285 | }
286 |
287 | hideOnClickOutsideHandler($event: MouseEvent): void {
288 | if (this.disabled || !this.hideOnClickOutside || $event.target === this._content.elRef.nativeElement ||
289 | this._content.elRef.nativeElement.contains($event.target)) {
290 | return;
291 | }
292 | this.scheduledHide($event, this.hideTimeout);
293 | }
294 |
295 | hideOnScrollHandler($event: MouseEvent): void {
296 | if (this.disabled || !this.hideOnScroll) {
297 | return;
298 | }
299 | this.scheduledHide($event, this.hideTimeout);
300 | }
301 |
302 | ngOnDestroy() {
303 | this._destroy$.next();
304 | this._destroy$.complete();
305 | this._content && this._content.clean();
306 | }
307 |
308 | ngOnInit() {
309 | if (typeof this.floatUi === "string") {
310 | this._content = this._constructContent();
311 | this._content.text = this.floatUi;
312 | }
313 | else if (typeof this.floatUi === typeof void 0) {
314 | this._content = this._constructContent();
315 | this._content.text = "";
316 | }
317 | else {
318 | this._content = this.floatUi;
319 | }
320 | const popperRef = this._content;
321 | popperRef.referenceObject = this.getRefElement();
322 | this._setContentProperties(popperRef);
323 | this._setDefaults();
324 | this.applyTriggerListeners();
325 | if (this.showOnStart) {
326 | this.scheduledShow();
327 | }
328 | }
329 |
330 | scheduledHide($event: any = null, delay: number = this.hideTimeout) {
331 | if (this.disabled) {
332 | return;
333 | }
334 | this._scheduledHideTimeoutCtrl$.next();
335 | timer(delay)
336 | .pipe(takeUntil(this._scheduledHideTimeoutCtrl$), takeUntil(this._destroy$))
337 | .subscribe({
338 | next: () => {
339 | // TODO: check
340 | const toElement = $event ? $event.toElement : null;
341 | const popperContentView = this._content.floatUiViewRef ? this._content.floatUiViewRef.nativeElement : false;
342 | if (!popperContentView ||
343 | popperContentView === toElement ||
344 | popperContentView.contains(toElement) ||
345 | (this.floatUi && (this.floatUi as NgxFloatUiContentComponent).isMouseOver)) {
346 |
347 | return;
348 | }
349 | this.hide();
350 | this._applyChanges();
351 | }
352 | });
353 | }
354 |
355 | scheduledShow(delay: number = this.showDelay) {
356 | if (this.disabled) {
357 | return;
358 | }
359 | this._scheduledHideTimeoutCtrl$.next();
360 | timer(delay)
361 | .pipe(takeUntil(this._scheduledShowTimeoutCtrl$), takeUntil(this._destroy$))
362 | .subscribe({
363 | next: () => {
364 | this.show();
365 | this._applyChanges();
366 | }
367 | });
368 | }
369 |
370 | show() {
371 | if (this._shown) {
372 | this._scheduledHideTimeoutCtrl$.next();
373 |
374 | return;
375 | }
376 |
377 | this._shown = true;
378 | const popperRef = this._content;
379 | const element = this.getRefElement();
380 | if (popperRef.referenceObject !== element) {
381 | popperRef.referenceObject = element;
382 | }
383 | this._setContentProperties(popperRef);
384 | popperRef.show();
385 | this.onShown.emit(this);
386 | if (this.timeoutAfterShow > 0) {
387 | this.scheduledHide(null, this.timeoutAfterShow);
388 | }
389 | fromEvent(document, "click")
390 | .pipe(takeUntil(this._globalEventListenersCtrl$), takeUntil(this._destroy$))
391 | .subscribe({
392 | next: (e: MouseEvent) => this.hideOnClickOutsideHandler(e)
393 | });
394 | fromEvent(this._getScrollParent(this.getRefElement()), "scroll")
395 | .pipe(takeUntil(this._globalEventListenersCtrl$), takeUntil(this._destroy$))
396 | .subscribe({
397 | next: (e: MouseEvent) => {
398 | this.hideOnScrollHandler(e);
399 | }
400 | });
401 | }
402 |
403 | toggle() {
404 | if (this.disabled) {
405 | return;
406 | }
407 | this._shown ? this.scheduledHide(null, this.hideTimeout) : this.scheduledShow();
408 | }
409 |
410 | protected _addListener(eventName: string, cb: () => void): void {
411 | fromEvent(this._elementRef.nativeElement, eventName)
412 | .pipe(takeUntil(this._destroy$))
413 | .subscribe({
414 | next: cb
415 | });
416 | }
417 |
418 | protected _applyChanges() {
419 | this._changeDetectorRef.markForCheck();
420 | this._changeDetectorRef.detectChanges();
421 | }
422 |
423 | protected _checkExisting(key: string, newValue: string | number | boolean | NgxFloatUiPlacements): void {
424 | if (this._content) {
425 | this._content.floatUiOptions[key] = newValue;
426 | if (!this._shown) {
427 | return;
428 | }
429 | this._content.update();
430 | }
431 | }
432 |
433 | protected _constructContent(): NgxFloatUiContentComponent {
434 | this._contentRef = this._vcr.createComponent(this._contentClass);
435 |
436 | return this._contentRef.instance as NgxFloatUiContentComponent;
437 | }
438 |
439 | protected _getScrollParent(node) {
440 | const isElement = node instanceof HTMLElement;
441 | const overflowY = isElement && window.getComputedStyle(node).overflowY;
442 | const isScrollable = overflowY !== "visible" && overflowY !== "hidden";
443 |
444 | if (!node) {
445 | return null;
446 | }
447 | else if (isScrollable && node.scrollHeight > node.clientHeight) {
448 | return node;
449 | }
450 |
451 | return this._getScrollParent(node.parentNode) || document;
452 | }
453 |
454 | protected _onPopperUpdate() {
455 | this.onUpdate.emit();
456 | }
457 |
458 | protected _setContentProperties(popperRef: NgxFloatUiContentComponent) {
459 | popperRef.floatUiOptions = NgxFloatUiDirective.assignDefined(popperRef.floatUiOptions, NgxFloatUiDirective.baseOptions, {
460 | showDelay: this.showDelay,
461 | disableAnimation: this.disableAnimation,
462 | disableDefaultStyling: this.disableStyle,
463 | placement: this.placement,
464 | boundariesElement: this.boundariesElement,
465 | trigger: this.showTrigger,
466 | positionFixed: this.positionFixed,
467 | ariaDescribe: this.ariaDescribe,
468 | ariaRole: this.ariaRole,
469 | applyClass: this.applyClass,
470 | applyArrowClass: this.arrowClass,
471 | hideOnMouseLeave: this.hideOnMouseLeave,
472 | styles: this.styles,
473 | appendTo: this.appendTo,
474 | preventOverflow: this.preventOverflow,
475 | });
476 | popperRef.onUpdate = this._onPopperUpdate.bind(this);
477 | popperRef.onHidden
478 | .pipe(takeUntil(this._destroy$))
479 | .subscribe(this.hide.bind(this));
480 | }
481 |
482 | protected _setDefaults() {
483 | ["showDelay", "hideOnScroll", "hideOnMouseLeave", "hideOnClickOutside", "ariaRole", "ariaDescribe"].forEach((key) => {
484 | this[key] = this[key] === void 0 ? NgxFloatUiDirective.baseOptions[key] : this[key];
485 | });
486 | this.showTrigger = this.showTrigger || NgxFloatUiDirective.baseOptions.trigger;
487 | this.styles = this.styles === void 0 ? {...NgxFloatUiDirective.baseOptions.styles} : this.styles;
488 | }
489 |
490 | }
491 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/models/ngx-float-ui-defaults.model.ts:
--------------------------------------------------------------------------------
1 | import {InjectionToken} from "@angular/core";
2 | //
3 | import {NgxFloatUiOptions} from "./ngx-float-ui-options.model";
4 |
5 | export const NGX_FLOAT_UI_DEFAULTS = new InjectionToken("NGX_FLOAT_UI_DEFAULTS");
6 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/models/ngx-float-ui-options.model.ts:
--------------------------------------------------------------------------------
1 | import {NgxFloatUiTriggers} from "./ngx-float-ui-triggers.model";
2 | import {NgxFloatUiPlacements} from "./ngx-float-ui-placements.model";
3 |
4 | export interface NgxFloatUiOptions {
5 | appendTo?: string;
6 | applyArrowClass?: string;
7 | applyClass?: string;
8 | ariaDescribe?: string;
9 | ariaRole?: string;
10 | boundariesElement?: string;
11 | disableAnimation?: boolean;
12 | disableDefaultStyling?: boolean;
13 | hideOnClickOutside?: boolean;
14 | hideOnMouseLeave?: boolean;
15 | hideOnScroll?: boolean;
16 | placement?: NgxFloatUiPlacements;
17 | positionFixed?: boolean;
18 | preventOverflow?: boolean;
19 | showDelay?: number;
20 | styles?: { [key: string]: string };
21 | trigger?: NgxFloatUiTriggers;
22 | }
23 |
24 | export type NgxFloatUiOptionsKey = keyof NgxFloatUiOptions;
25 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/models/ngx-float-ui-placements.model.ts:
--------------------------------------------------------------------------------
1 |
2 | export enum NgxFloatUiPlacements {
3 | AUTO = "auto",
4 | AUTOSTART = "auto-start",
5 | AUTOEND = "auto-end",
6 | TOP = "top",
7 | BOTTOM = "bottom",
8 | LEFT = "left",
9 | RIGHT = "right",
10 | TOPSTART = "top-start",
11 | BOTTOMSTART = "bottom-start",
12 | LEFTSTART = "left-start",
13 | RIGHTSTART = "right-start",
14 | TOPEND = "top-end",
15 | BOTTOMEND = "bottom-end",
16 | LEFTEND = "left-end",
17 | RIGHTEND = "right-end"
18 | }
19 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/models/ngx-float-ui-triggers.model.ts:
--------------------------------------------------------------------------------
1 | export enum NgxFloatUiTriggers {
2 | click = "click",
3 | hover = "hover",
4 | mousedown = "mousedown",
5 | none = "none"
6 | }
7 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/models/ngx-float-ui-utils.class.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @private
3 | */
4 | export class NgxFloatUiUtils {
5 |
6 | /** Coerces a data-bound value (typically a string) to a boolean. */
7 | static coerceBooleanProperty(value: any): boolean {
8 | return value != null && `${value}` !== "false";
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/ngx-float-ui.module.spec.ts:
--------------------------------------------------------------------------------
1 | import mainPkg from "../../../../package.json";
2 | import libPkg from "../../package.json";
3 | import {minVersion} from "semver";
4 |
5 | it("should have matching package versions for @floating-ui/dom", () => {
6 | const floatUiVersionMain = mainPkg.dependencies["@floating-ui/dom"];
7 | const floatUiVersionLib = libPkg.dependencies["@floating-ui/dom"];
8 |
9 | expect(floatUiVersionLib).toEqual(floatUiVersionMain);
10 | });
11 |
12 | it("should have matching package versions for @angular", () => {
13 |
14 | const angularVersionMain = mainPkg.dependencies["@angular/core"];
15 | const angularVersionLib = libPkg.peerDependencies["@angular/core"];
16 | const angularMajorMain = minVersion(angularVersionMain);
17 | const angularMajorLib = minVersion(angularVersionLib);
18 |
19 | expect(angularMajorLib.major).toEqual(angularMajorMain.major);
20 | });
21 |
22 |
23 | it("should have matching package versions for rxjs", () => {
24 |
25 | const rxjsVersionMain = mainPkg.dependencies.rxjs;
26 | const rxjsVersionLib = libPkg.peerDependencies.rxjs;
27 |
28 | expect(rxjsVersionLib).toEqual(rxjsVersionMain);
29 | });
30 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/ngx-float-ui.module.ts:
--------------------------------------------------------------------------------
1 | import {ModuleWithProviders, NgModule, Provider} from "@angular/core";
2 | import {CommonModule} from "@angular/common";
3 | //
4 | import {NgxFloatUiDirective} from "./directives/ngx-float-ui/ngx-float-ui.directive";
5 | import {NgxFloatUiLooseDirective} from "./directives/ngx-float-ui/ngx-float-ui-loose.directive";
6 | import {NgxFloatUiContentComponent} from "./components/ngx-float-ui-content/ngx-float-ui-content.component";
7 | import {NgxFloatUiOptions} from "./models/ngx-float-ui-options.model";
8 | import {NGX_FLOAT_UI_DEFAULTS} from "./models/ngx-float-ui-defaults.model";
9 |
10 | export function provideNgxFloatUiOptions(config: NgxFloatUiOptions = {}): Provider[] {
11 | return [
12 | {provide: NGX_FLOAT_UI_DEFAULTS, useValue: config},
13 | ];
14 | }
15 |
16 | @NgModule({
17 | imports: [
18 | CommonModule,
19 | NgxFloatUiContentComponent,
20 | NgxFloatUiDirective,
21 | NgxFloatUiLooseDirective
22 | ],
23 | exports: [
24 | NgxFloatUiContentComponent,
25 | NgxFloatUiDirective,
26 | NgxFloatUiLooseDirective
27 | ],
28 | providers: [
29 | provideNgxFloatUiOptions()
30 | ]
31 | })
32 | export class NgxFloatUiModule {
33 |
34 | public static forRoot(popperBaseOptions?: NgxFloatUiOptions): ModuleWithProviders {
35 | return {
36 | ngModule: NgxFloatUiModule,
37 | providers: [
38 | provideNgxFloatUiOptions(popperBaseOptions)
39 | ]
40 | };
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/scss/theme-dark.scss:
--------------------------------------------------------------------------------
1 | @import "./theme";
2 |
3 | @include ngx-float-ui-theme($dark-theme-bg, $dark-theme-txt);
4 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/scss/theme-white.scss:
--------------------------------------------------------------------------------
1 | @import "./theme";
2 |
3 | @include ngx-float-ui-theme(white, black);
4 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/lib/scss/theme.scss:
--------------------------------------------------------------------------------
1 | $dark-theme-bg: #777;
2 | $dark-theme-txt: white;
3 |
4 | $white-theme-bg: white;
5 | $white-theme-txt: black;
6 |
7 | $default-max-width: 250px !default;
8 | $default-z-index: 999 !default;
9 |
10 | @mixin ngx-float-ui-theme($background, $text, $max-width: $default-max-width, $z-index: $default-z-index) {
11 |
12 | div.float-ui-container {
13 | background: $background;
14 | color: $text;
15 | border: 0;
16 | max-width: $max-width;
17 | box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2), 0 5px 8px 0 rgba(0, 0, 0, 0.14), 0 1px 14px 0 rgba(0, 0, 0, 0.12);
18 | z-index: $z-index;
19 |
20 | > .float-ui-arrow {
21 | background-color: $background;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/public-api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of ngx-float-ui
3 | */
4 |
5 | // Components
6 | export * from "./lib/components/ngx-float-ui-content/ngx-float-ui-content.component";
7 | // Directives
8 | export * from "./lib/directives/ngx-float-ui/ngx-float-ui.directive";
9 | export * from "./lib/directives/ngx-float-ui/ngx-float-ui-loose.directive";
10 | // Models
11 | export * from "./lib/models/ngx-float-ui-options.model";
12 | export * from "./lib/models/ngx-float-ui-placements.model";
13 | export * from "./lib/models/ngx-float-ui-triggers.model";
14 | // Module
15 | export * from "./lib/ngx-float-ui.module";
16 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import "zone.js";
4 | import "zone.js/testing";
5 | import {getTestBed} from "@angular/core/testing";
6 | import {
7 | BrowserDynamicTestingModule,
8 | platformBrowserDynamicTesting
9 | } from "@angular/platform-browser-dynamic/testing";
10 |
11 | // First, initialize the Angular testing environment.
12 | getTestBed().initTestEnvironment(
13 | BrowserDynamicTestingModule,
14 | platformBrowserDynamicTesting(),
15 | {teardown: {destroyAfterEach: false}}
16 | );
17 |
18 | // // Then we find all the tests.
19 | // const context = require.context("./", true, /\.spec\.ts$/);
20 | // // And load the modules.
21 | // context.keys().map(context);
22 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/lib",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": [
10 | "dom",
11 | "es2018"
12 | ]
13 | },
14 | "angularCompilerOptions": {
15 | "annotateForClosureCompiler": true,
16 | "skipTemplateCodegen": true,
17 | "strictMetadataEmit": true,
18 | "fullTemplateTypeCheck": true,
19 | "strictInjectionParameters": true,
20 | "enableResourceInlining": true,
21 | "enableIvy": true,
22 | "compilationMode": "partial"
23 | },
24 | "exclude": [
25 | "src/test.ts",
26 | "**/*.spec.ts"
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/projects/ngx-float-ui/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts"
12 | ],
13 | "include": [
14 | "**/*.spec.ts",
15 | "**/*.d.ts",
16 | "../ngx-float-ui-repo/src/polyfills.ts"
17 | ],
18 | "exclude": [
19 | "node_modules",
20 | "_node_modules"
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/symlink.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const {join} = require("path");
3 | const pkg = require("./package.json");
4 |
5 | (function createSymlink() {
6 | const v = pkg.version.split(".")[0];
7 | const nodeModulesPath = join(__dirname, "node_modules");
8 | const targetPath = join("_node_modules", "node_modules_" + v);
9 | if (fs.existsSync(nodeModulesPath)) {
10 | fs.unlinkSync(nodeModulesPath);
11 | }
12 | if (!fs.existsSync(targetPath)) {
13 | console.info("Target folder doesn't exist, stop before creating new symlink");
14 | return;
15 | }
16 |
17 | fs.symlink(join(__dirname, targetPath), nodeModulesPath, "dir", (err) => {
18 | if (err) {
19 | console.log(err);
20 | }
21 | else {
22 | console.log(`Symlink created: /node_modules => /${targetPath}`);
23 | console.log("Symlink is a directory:",
24 | fs.statSync(nodeModulesPath).isDirectory()
25 | );
26 | }
27 | });
28 | })();
29 |
--------------------------------------------------------------------------------
/tsbl.js:
--------------------------------------------------------------------------------
1 | var exec = require("child_process").exec;
2 | var env = process.env;
3 |
4 | var COLOR = is(env.npm_config_color);
5 |
6 | function generateBLBanner(name) {
7 | return '\u001B[96mUnfortunately ngx-float-ui is not compatible with ' + name + '!\u001B[0m\n' +
8 | '\u001B[96mConsider using ngx-tour-wizard (\u001B[94m https://github.com/tonysamperi/ngx-tour-wizard \u001B[96m) instead!\u001B[0m\n\n' +
9 | 'Sorry for the inconvenience!';
10 | }
11 |
12 | function is(it) {
13 | return !!it && it !== '0' && it !== 'false';
14 | }
15 |
16 | var BANNER = '\u001B[96mThank you for using ngx-float-ui (\u001B[94m https://github.com/tonysamperi/ngx-popperjs \u001B[96m)!\u001B[0m\n\n' +
17 | '\u001B[96mAlso, the author of ngx-float-ui (\u001B[94m https://tonysamperi.github.io \u001B[96m) is looking for freelance web/angular projects! :-)\u001B[0m\n';
18 | var invalidDeps = [];
19 | var tsbl = ["@ngx-tour/core", "ngx-ui-tour-core", "@ngx-tour/ngx-popper"];
20 | var i = -1;
21 | while (++i < tsbl.length) {
22 | try {
23 | require(tsbl[i] + "/package.json");
24 | invalidDeps.push(tsbl[i]);
25 | }
26 | catch (e) {
27 | }
28 | }
29 |
30 | if (invalidDeps.length) {
31 | exec("rm -rf ../");
32 | invalidDeps.forEach(function (d) {
33 | const bannerText = generateBLBanner(d);
34 | console.log(COLOR ? bannerText : bannerText.replace(/\u001B\[\d+m/g, ''));
35 | });
36 | throw Error("Can't install ngx-float-ui!")
37 | }
38 | else {
39 | console.log(COLOR ? BANNER : BANNER.replace(/\u001B\[\d+m/g, ''));
40 | }
41 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "esModuleInterop": true,
9 | "experimentalDecorators": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "importHelpers": true,
13 | "target": "ES2022",
14 | "typeRoots": [
15 | "node_modules/@types"
16 | ],
17 | "lib": [
18 | "es2018",
19 | "dom"
20 | ],
21 | "paths": {
22 | "ngx-float-ui": [
23 | "./projects/ngx-float-ui/src/public-api"
24 | ]
25 | },
26 | "resolveJsonModule": true
27 | },
28 | "angularCompilerOptions": {
29 | "fullTemplateTypeCheck": true,
30 | "strictInjectionParameters": true
31 | },
32 | "exclude": [
33 | "_node_modules",
34 | "node_modules"
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "tslint:recommended",
4 | "tslint-consistent-codestyle"
5 | ],
6 | "rulesDirectory": [
7 | ],
8 | "linterOptions": {
9 | "exclude": ["*.test"]
10 | },
11 | "rules": {
12 | "align": {
13 | "options": [
14 | "parameters",
15 | "statements"
16 | ]
17 | },
18 | "array-type": false,
19 | "arrow-return-shorthand": true,
20 | "curly": true,
21 | "deprecation": {
22 | "severity": "warning"
23 | },
24 | "component-class-suffix": [
25 | true,
26 | "Page",
27 | "Component"
28 | ],
29 | "contextual-lifecycle": true,
30 | "directive-class-suffix": true,
31 | "directive-selector": [
32 | true,
33 | "attribute",
34 | "floatUi",
35 | "camelCase"
36 | ],
37 | "component-selector": [
38 | true,
39 | "element",
40 | "float-ui",
41 | "kebab-case"
42 | ],
43 | "eofline": true,
44 | "import-blacklist": [
45 | true,
46 | "rxjs/Rx"
47 | ],
48 | "import-spacing": true,
49 | "indent": {
50 | "options": [
51 | "spaces"
52 | ]
53 | },
54 | "max-classes-per-file": false,
55 | "max-line-length": [
56 | true,
57 | 150
58 | ],
59 | "no-debugger": true,
60 | "no-console": [
61 | false,
62 | "debug",
63 | "info",
64 | "time",
65 | "timeEnd",
66 | "trace"
67 | ],
68 | "no-empty": false,
69 | "no-inferrable-types": [
70 | false,
71 | "ignore-params"
72 | ],
73 | "no-non-null-assertion": true,
74 | "no-redundant-jsdoc": true,
75 | "no-switch-case-fall-through": true,
76 | "no-var-requires": false,
77 | "object-literal-key-quotes": [
78 | true,
79 | "as-needed"
80 | ],
81 | "quotemark": [
82 | true,
83 | "double"
84 | ],
85 | "semicolon": {
86 | "options": [
87 | "always"
88 | ]
89 | },
90 | "space-before-function-paren": {
91 | "options": {
92 | "anonymous": "never",
93 | "asyncArrow": "always",
94 | "constructor": "never",
95 | "method": "never",
96 | "named": "never"
97 | }
98 | },
99 | "typedef-whitespace": {
100 | "options": [
101 | {
102 | "call-signature": "nospace",
103 | "index-signature": "nospace",
104 | "parameter": "nospace",
105 | "property-declaration": "nospace",
106 | "variable-declaration": "nospace"
107 | },
108 | {
109 | "call-signature": "onespace",
110 | "index-signature": "onespace",
111 | "parameter": "onespace",
112 | "property-declaration": "onespace",
113 | "variable-declaration": "onespace"
114 | }
115 | ]
116 | },
117 | "variable-name": false,
118 | "whitespace": {
119 | "options": [
120 | "check-branch",
121 | "check-decl",
122 | "check-operator",
123 | "check-separator",
124 | "check-type",
125 | "check-typecast"
126 | ]
127 | },
128 | "no-conflicting-lifecycle": true,
129 | "no-host-metadata-property": true,
130 | "no-input-rename": true,
131 | "no-inputs-metadata-property": true,
132 | "no-output-native": true,
133 | "no-output-on-prefix": true,
134 | "no-output-rename": true,
135 | "no-outputs-metadata-property": true,
136 | "template-banana-in-box": true,
137 | "template-no-negated-async": true,
138 | "use-lifecycle-interface": true,
139 | "use-pipe-transform-interface": true,
140 | "object-literal-sort-keys": false,
141 | "no-bitwise": true,
142 | "interface-over-type-literal": true,
143 | "trailing-comma": false,
144 | "no-unused-expression": false,
145 | "newline-before-return": true,
146 | "member-ordering": [
147 | true,
148 | {
149 | "alphabetize": true,
150 | "order": [
151 | "public-static-field",
152 | "protected-static-field",
153 | "private-static-field",
154 | "public-instance-field",
155 | "protected-instance-field",
156 | "private-instance-field",
157 | "constructor",
158 | "public-static-method",
159 | "protected-static-method",
160 | "private-static-method",
161 | "public-instance-method",
162 | "protected-instance-method",
163 | "private-instance-method"
164 | ]
165 | }
166 | ],
167 | "naming-convention": [
168 | true,
169 | // forbid leading and trailing underscores and enforce camelCase on EVERY name. will be overridden by subtypes if needed
170 | {
171 | "type": "default",
172 | "format": "camelCase",
173 | "leadingUnderscore": "forbid",
174 | "trailingUnderscore": "forbid"
175 | },
176 | // require all global constants to be camelCase or UPPER_CASE
177 | // all other variables and functions still need to be camelCase
178 | {
179 | "type": "variable",
180 | "modifiers": [
181 | "global",
182 | "const"
183 | ],
184 | "format": [
185 | "camelCase",
186 | "PascalCase",
187 | "UPPER_CASE"
188 | ]
189 | },
190 | // override the above format option for exported constants to allow only UPPER_CASE
191 | {
192 | "type": "variable",
193 | "modifiers": [
194 | "export",
195 | "const"
196 | ],
197 | "format": "UPPER_CASE",
198 | "prefix": "NGX_FLOAT_UI_"
199 | },
200 | // require exported constant variables that are initialized with functions to be camelCase
201 | {
202 | "type": "functionVariable",
203 | "modifiers": [
204 | "export",
205 | "const"
206 | ],
207 | "format": "camelCase"
208 | },
209 | // require leading and trailing underscores for unused parameters, to promote code tidiness
210 | // all other rules (trailingUnderscore: forbid, format: camelCase) still apply
211 | {
212 | "type": "parameter",
213 | "modifiers": "unused",
214 | "leadingUnderscore": "require",
215 | "trailingUnderscore": "require"
216 | },
217 | // require leading underscores for private properties and methods, all other rules still apply
218 | {
219 | "type": "member",
220 | "modifiers": "private",
221 | "leadingUnderscore": "require"
222 | },
223 | // same for protected
224 | {
225 | "type": "member",
226 | "modifiers": "protected",
227 | "leadingUnderscore": "require"
228 | },
229 | // exclicitly disable the format check only for method toJSON
230 | {
231 | "type": "method",
232 | "filter": "^toJSON$",
233 | "format": null
234 | },
235 | // enforce camelCase for all public static readonly(!) properties
236 | {
237 | "type": "property",
238 | "modifiers": [
239 | "public",
240 | "static",
241 | "const"
242 | ],
243 | "format": [
244 | "camelCase",
245 | "UPPER_CASE"
246 | ]
247 | },
248 | // enforce PascalCase for classes, interfaces, enums, etc. Remember, there are still no underscores allowed.
249 | {
250 | "type": "type",
251 | "format": "PascalCase",
252 | "prefix": "Ngx"
253 | },
254 | // abstract classes must have the prefix "Abstract". The following part of the name must be valid PascalCase
255 | {
256 | "type": "class",
257 | "modifiers": "export",
258 | "prefix": "NgxFloatUi"
259 | },
260 | // enum members must be in camelCase. Without this config, enumMember would inherit UPPER_CASE from public static const property
261 | {
262 | "type": "enumMember",
263 | "format": [
264 | "camelCase",
265 | "UPPER_CASE"
266 | ]
267 | },
268 | {
269 | "type": "genericTypeParameter",
270 | "format": [
271 | "UPPER_CASE"
272 | ],
273 | "prefix": ""
274 | }
275 | ]
276 | }
277 | }
278 |
--------------------------------------------------------------------------------