├── src
├── demo
│ ├── assets
│ │ └── .gitkeep
│ ├── favicon.ico
│ ├── app
│ │ ├── anchor-reuse
│ │ │ ├── anchor-reuse.component.scss
│ │ │ └── anchor-reuse.component.ts
│ │ ├── positioning
│ │ │ ├── positioning.component.scss
│ │ │ └── positioning.component.ts
│ │ ├── scroll-strategies
│ │ │ ├── scroll-strategies.component.scss
│ │ │ └── scroll-strategies.component.ts
│ │ ├── demo.component.scss
│ │ ├── select-trigger
│ │ │ ├── select-trigger.component.scss
│ │ │ └── select-trigger.component.ts
│ │ ├── transitions
│ │ │ ├── transitions.component.scss
│ │ │ └── transitions.component.ts
│ │ ├── focus
│ │ │ ├── focus.component.scss
│ │ │ └── focus.component.ts
│ │ ├── tooltip
│ │ │ ├── tooltip.component.scss
│ │ │ └── tooltip.component.ts
│ │ ├── speed-dial
│ │ │ ├── speed-dial.component.scss
│ │ │ └── speed-dial.component.ts
│ │ ├── action-api
│ │ │ ├── action-api.component.scss
│ │ │ └── action-api.component.ts
│ │ ├── interactive-close
│ │ │ ├── interactive-close.component.scss
│ │ │ └── interactive-close.component.ts
│ │ ├── demo.module.ts
│ │ └── demo.component.ts
│ ├── browserslist
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── tslint.json
│ ├── tsconfig.spec.json
│ ├── tsconfig.app.json
│ ├── main.ts
│ ├── test.ts
│ ├── index.html
│ ├── karma.conf.js
│ ├── styles.scss
│ └── polyfills.ts
└── lib
│ ├── tsconfig.lib.prod.json
│ ├── ng-package.prod.json
│ ├── ng-package.json
│ ├── tslint.json
│ ├── tsconfig.spec.json
│ ├── package.json
│ ├── popover
│ ├── tokens.ts
│ ├── popover.component.html
│ ├── popover.animations.ts
│ ├── types.ts
│ ├── popover.module.ts
│ ├── popover.component.scss
│ ├── popover.errors.ts
│ ├── popover-hover.directive.ts
│ ├── popover.component.ts
│ ├── popover-anchoring.service.ts
│ └── popover.spec.ts
│ ├── test.ts
│ ├── public_api.ts
│ ├── tsconfig.lib.json
│ └── karma.conf.js
├── .prettierignore
├── tools
├── tsconfig.json
├── constants.ts
├── release.ts
└── prepare-package.ts
├── .github
└── ISSUE_TEMPLATE.md
├── .prettierrc
├── .travis.yml
├── .editorconfig
├── .gitignore
├── tsconfig.json
├── CONTRIBUTING.md
├── .eslintrc.json
├── LICENSE
├── package.json
├── angular.json
├── README.md
└── CHANGELOG.md
/src/demo/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist
2 | LICENSE
3 | node_modules
4 |
--------------------------------------------------------------------------------
/src/demo/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ncstate-sat/popover/HEAD/src/demo/favicon.ico
--------------------------------------------------------------------------------
/tools/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "module": "es2022"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/demo/app/anchor-reuse/anchor-reuse.component.scss:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | background: black;
3 | color: white;
4 | padding: 8px;
5 | font-size: 16px;
6 | }
7 |
--------------------------------------------------------------------------------
/src/lib/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.lib.json",
3 | "angularCompilerOptions": {
4 | "compilationMode": "partial"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/demo/browserslist:
--------------------------------------------------------------------------------
1 | last 2 Chrome versions
2 | last 1 Firefox version
3 | last 2 Edge major versions
4 | last 2 Safari major versions
5 | last 2 iOS major versions
6 | Firefox ESR
7 |
--------------------------------------------------------------------------------
/src/demo/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | import * as pkg from '../../../package.json';
2 |
3 | export const environment = {
4 | production: true,
5 | version: pkg.version
6 | };
7 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/lib/ng-package.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/popover",
4 | "lib": {
5 | "entryFile": "./public_api.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/demo/app/positioning/positioning.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: block;
3 | }
4 |
5 | .config {
6 | margin-bottom: 16px;
7 | }
8 |
9 | .popover {
10 | background: lightgray;
11 | padding: 32px;
12 | }
13 |
--------------------------------------------------------------------------------
/src/demo/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "demo", "camelCase"],
5 | "component-selector": [true, "element", "demo", "kebab-case"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/lib/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/popover",
4 | "deleteDestPath": false,
5 | "lib": {
6 | "entryFile": "./public_api.ts"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/lib/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "sat", "camelCase"],
5 | "component-selector": [true, "element", "sat", "kebab-case"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 120,
3 | "singleQuote": true,
4 | "useTabs": false,
5 | "tabWidth": 2,
6 | "semi": true,
7 | "bracketSpacing": true,
8 | "htmlWhitespaceSensitivity": "strict",
9 | "trailingComma": "none"
10 | }
11 |
--------------------------------------------------------------------------------
/src/demo/app/scroll-strategies/scroll-strategies.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: block;
3 | }
4 |
5 | .anchor {
6 | margin: 48px;
7 | }
8 |
9 | .popover {
10 | padding: 48px;
11 | color: white;
12 | background: black;
13 | }
14 |
--------------------------------------------------------------------------------
/src/lib/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": ["jasmine", "node"]
6 | },
7 | "files": ["test.ts"],
8 | "include": ["**/*.spec.ts", "**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/src/demo/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "types": ["jasmine", "node"]
6 | },
7 | "files": ["test.ts", "polyfills.ts"],
8 | "include": ["**/*.spec.ts", "**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/src/demo/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "importHelpers": true,
5 | "outDir": "../../out-tsc/app",
6 | "types": []
7 | },
8 | "files": ["main.ts", "polyfills.ts"],
9 | "include": ["src/demo/**/*.d.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/src/lib/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ncstate/sat-popover",
3 | "version": "AUTOGENERATED",
4 | "description": "Popover component for Angular",
5 | "author": "Will Howell",
6 | "license": "MIT",
7 | "peerDependencies": {
8 | "AUTOGENERATED": "AUTOGENERATED"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/lib/popover/tokens.ts:
--------------------------------------------------------------------------------
1 | import { InjectionToken } from '@angular/core';
2 |
3 | // See http://cubic-bezier.com/#.25,.8,.25,1 for reference.
4 | // const DEFAULT_TRANSITION = '200ms cubic-bezier(0.25, 0.8, 0.25, 1)';
5 | export const DEFAULT_TRANSITION = new InjectionToken('DefaultTransition');
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: trusty
2 | language: node_js
3 | node_js:
4 | - '22'
5 | - 'lts/*'
6 |
7 | before_install:
8 | - export CHROME_BIN=chromium-browser
9 | - export DISPLAY=:99.0
10 | - sh -e /etc/init.d/xvfb start
11 |
12 | before_script:
13 | - npm install
14 |
15 | script: npm run test:once
16 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/src/lib/popover/popover.component.html:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/demo/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { DemoModule } from './app/demo.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic()
12 | .bootstrapModule(DemoModule)
13 | .catch((err) => console.log(err));
14 |
--------------------------------------------------------------------------------
/src/demo/app/demo.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: block;
3 | min-height: 100%;
4 | }
5 |
6 | .mat-toolbar {
7 | justify-content: space-between;
8 | }
9 |
10 | .version {
11 | padding-left: 8px;
12 | }
13 |
14 | .page-content {
15 | padding: 48px;
16 |
17 | & > * {
18 | // separate each example
19 | margin-bottom: 32px;
20 | }
21 | }
22 |
23 | .repo-link {
24 | color: white;
25 | text-decoration: none;
26 | margin: 0;
27 | }
28 |
--------------------------------------------------------------------------------
/src/demo/app/select-trigger/select-trigger.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: block;
3 | }
4 |
5 | .fancy-caption {
6 | background: yellow;
7 | border: dashed 20px orange;
8 | border-radius: 50%;
9 | font-size: 40px;
10 | height: 64px;
11 | width: 64px;
12 | line-height: 64px;
13 | text-align: center;
14 |
15 | transform: rotate(360deg);
16 | transition: transform 600ms ease-out;
17 |
18 | &.opened {
19 | transform: rotate(0deg);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/demo/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/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
6 |
7 | // First, initialize the Angular testing environment.
8 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
9 |
--------------------------------------------------------------------------------
/src/lib/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'core-js/es7/reflect';
4 | import 'zone.js';
5 | import 'zone.js/testing';
6 | import { getTestBed } from '@angular/core/testing';
7 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
8 |
9 | // First, initialize the Angular testing environment.
10 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
11 |
--------------------------------------------------------------------------------
/src/demo/app/transitions/transitions.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: block;
3 | }
4 |
5 | .indicators {
6 | display: flex;
7 | margin-bottom: 32px;
8 | }
9 |
10 | .indicator {
11 | margin-right: 8px;
12 | padding: 8px;
13 | background: pink;
14 |
15 | &.active {
16 | background: red;
17 | }
18 | }
19 |
20 | .anchor {
21 | background: darkblue;
22 | cursor: pointer;
23 | height: 48px;
24 | width: 48px;
25 | }
26 |
27 | .popover {
28 | background: lightgreen;
29 | color: rgba(0, 0, 0, 0.87);
30 | padding: 24px;
31 | }
32 |
--------------------------------------------------------------------------------
/src/lib/public_api.ts:
--------------------------------------------------------------------------------
1 | export { SatPopoverModule } from './popover/popover.module';
2 | export { SatPopoverAnchoringService } from './popover/popover-anchoring.service';
3 | export { SatPopoverComponent, SatPopoverAnchorDirective } from './popover/popover.component';
4 | export { SatPopoverHoverDirective } from './popover/popover-hover.directive';
5 | export { DEFAULT_TRANSITION } from './popover/tokens';
6 | export {
7 | SatPopoverHorizontalAlign,
8 | SatPopoverVerticalAlign,
9 | SatPopoverScrollStrategy,
10 | SatPopoverOpenOptions
11 | } from './popover/types';
12 |
--------------------------------------------------------------------------------
/src/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SAT Popover
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/demo/app/focus/focus.component.scss:
--------------------------------------------------------------------------------
1 | @use '@angular/material' as mat;
2 |
3 | :host {
4 | display: block;
5 | }
6 |
7 | .results {
8 | background: rgba(0, 0, 0, 0.06);
9 | display: inline-block;
10 | padding: 32px;
11 | position: relative;
12 |
13 | p {
14 | margin: 4px 0;
15 | }
16 | }
17 |
18 | .edit {
19 | position: absolute;
20 | right: 0;
21 | top: 0;
22 | margin: 4px;
23 | color: rgba(0, 0, 0, 0.54);
24 | }
25 |
26 | .form {
27 | @include mat.elevation(8);
28 | display: flex;
29 | flex-direction: column;
30 | padding: 24px;
31 | background: white;
32 | }
33 |
--------------------------------------------------------------------------------
/tools/constants.ts:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 |
3 | // paths
4 | export const SOURCE_PACKAGE_PATH = join(__dirname, '..', 'package.json');
5 | export const SOURCE_README_PATH = join(__dirname, '..', 'README.md');
6 | export const DIST_PATH = join(__dirname, '..', 'dist', 'popover');
7 | export const DIST_PACKAGE_PATH = join(DIST_PATH, 'package.json');
8 | export const DIST_README_PATH = join(DIST_PATH, 'README.md');
9 |
10 | // config
11 | export const PEER_DEPENDENCIES = ['@angular/common', '@angular/core', '@angular/cdk'];
12 | export const PACKAGE_PROPERTIES = ['keywords', 'repository', 'bugs', 'homepage'];
13 |
--------------------------------------------------------------------------------
/.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 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | yarn-error.log
34 | testem.log
35 | /typings
36 |
37 | # System Files
38 | .DS_Store
39 | Thumbs.db
40 |
41 | .angular
42 |
--------------------------------------------------------------------------------
/src/lib/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/lib",
5 | "declarationMap": true,
6 | "target": "es2022",
7 | "module": "es2022",
8 | "useDefineForClassFields": true,
9 | "declaration": true,
10 | "sourceMap": true,
11 | "inlineSources": true,
12 | "esModuleInterop": true,
13 | "experimentalDecorators": true,
14 | "importHelpers": true,
15 | "types": [],
16 | "lib": ["dom", "es2022"]
17 | },
18 | "angularCompilerOptions": {
19 | "skipTemplateCodegen": true,
20 | "strictMetadataEmit": true,
21 | "fullTemplateTypeCheck": true,
22 | "strictInjectionParameters": true
23 | },
24 | "exclude": ["test.ts", "**/*.spec.ts"]
25 | }
26 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "module": "es2022",
6 | "outDir": "./dist/out-tsc",
7 | "sourceMap": true,
8 | "declaration": false,
9 | "stripInternal": true,
10 | "moduleResolution": "bundler",
11 | "esModuleInterop": true,
12 | "experimentalDecorators": true,
13 | "target": "es2022",
14 | "typeRoots": ["node_modules/@types"],
15 | "lib": ["es2022", "dom"],
16 | "paths": {
17 | "@ncstate/sat-popover": ["dist/popover"],
18 | "core-js/es7/reflect": ["node_modules/core-js/proposals/reflect-metadata", "node_modules/core-js/es/reflect"],
19 | "core-js/es6/*": ["node_modules/core-js/es"]
20 | },
21 | "resolveJsonModule": true
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/demo/app/tooltip/tooltip.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: block;
3 | }
4 |
5 | .mat-card-content {
6 | display: flex;
7 | flex-direction: column;
8 | align-items: flex-start;
9 | }
10 |
11 | .anchor {
12 | cursor: default;
13 | display: inline-block;
14 | background: rgba(0, 0, 0, 0.1);
15 | padding: 8px;
16 | margin: 16px;
17 | }
18 |
19 | .tooltip-wrapper {
20 | padding: 8px;
21 | background: rgba(50, 50, 50, 0.9);
22 | color: white;
23 | border-radius: 2px;
24 | margin: 8px;
25 | font-size: 12px;
26 | }
27 |
28 | .seagreen {
29 | color: lightseagreen;
30 | }
31 |
32 | .hover-text {
33 | display: inline-block;
34 | padding: 4px 8px;
35 | background: rgba(0, 0, 0, 0.1);
36 |
37 | &:hover {
38 | background: rgb(152, 194, 223);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/demo/environments/environment.ts:
--------------------------------------------------------------------------------
1 | import * as pkg from '../../../package.json';
2 |
3 | // This file can be replaced during build by using the `fileReplacements` array.
4 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
5 | // The list of file replacements can be found in `angular.json`.
6 |
7 | export const environment = {
8 | production: false,
9 | version: pkg.version
10 | };
11 |
12 | /*
13 | * In development mode, to ignore zone related error stack frames such as
14 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
15 | * import the following file, but please comment it out in production mode
16 | * because it will have performance impact when throw error
17 | */
18 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
19 |
--------------------------------------------------------------------------------
/src/lib/popover/popover.animations.ts:
--------------------------------------------------------------------------------
1 | import { trigger, state, style, animate, transition, AnimationTriggerMetadata } from '@angular/animations';
2 |
3 | export const transformPopover: AnimationTriggerMetadata = trigger('transformPopover', [
4 | state('enter', style({ opacity: 1, transform: 'scale(1)' }), { params: { startAtScale: 0.3 } }),
5 | state('void, exit', style({ opacity: 0, transform: 'scale({{endAtScale}})' }), { params: { endAtScale: 0.5 } }),
6 | transition('* => enter', [
7 | style({ opacity: 0, transform: 'scale({{endAtScale}})' }),
8 | animate('{{openTransition}}', style({ opacity: 1, transform: 'scale(1)' }))
9 | ]),
10 | transition('* => void, * => exit', [
11 | animate('{{closeTransition}}', style({ opacity: 0, transform: 'scale({{endAtScale}})' }))
12 | ])
13 | ]);
14 |
--------------------------------------------------------------------------------
/src/demo/app/speed-dial/speed-dial.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: block;
3 | position: fixed;
4 | right: 32px;
5 | bottom: 32px;
6 | // Remove default margin given to each demo
7 | margin-bottom: 0 !important;
8 | }
9 |
10 | // Position the icon in the center so multiple
11 | // ones will overlap
12 | .mat-fab .mat-icon {
13 | position: absolute;
14 | top: 16px;
15 | left: 16px;
16 | }
17 |
18 | .dial {
19 | margin-bottom: 8px;
20 |
21 | // Tab direction moves away from anchor
22 | display: flex;
23 | flex-direction: column-reverse;
24 |
25 | .mat-mini-fab {
26 | margin: 8px 0;
27 | }
28 | }
29 |
30 | .tooltip {
31 | padding: 4px 8px;
32 | background: rgba(50, 50, 50, 0.9);
33 | color: white;
34 | border-radius: 2px;
35 | margin: 8px;
36 | font-size: 12px;
37 | }
38 |
--------------------------------------------------------------------------------
/src/demo/app/action-api/action-api.component.scss:
--------------------------------------------------------------------------------
1 | @use '@angular/material' as mat;
2 |
3 | $info-panel-bg: rgb(83, 83, 83);
4 |
5 | :host {
6 | display: block;
7 | }
8 |
9 | .avatar {
10 | @include mat.elevation(1);
11 | background: rgba(77, 204, 255, 0.8);
12 | color: white;
13 | display: inline-block;
14 | height: 48px;
15 | width: 48px;
16 | line-height: 48px;
17 | text-align: center;
18 | border-radius: 50%;
19 | font-size: 20px;
20 | }
21 |
22 | .info {
23 | @include mat.elevation(4);
24 | background: $info-panel-bg;
25 | color: white;
26 | border-radius: 4px;
27 | position: relative;
28 | margin-left: 6px;
29 | padding: 12px;
30 | }
31 |
32 | .caret {
33 | height: 8px;
34 | width: 8px;
35 | position: absolute;
36 | left: -4px;
37 | top: 50%;
38 | transform: translateY(-50%) rotate(45deg);
39 | background: $info-panel-bg;
40 | }
41 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Build the library
4 |
5 | ```bash
6 | npm run build
7 | ```
8 |
9 | ## Run the demo server
10 |
11 | ```bash
12 | npm run demo
13 | ```
14 |
15 | Open the server [using this link to the localhost](http://localhost:4200)
16 |
17 | ## Testing
18 |
19 | ```bash
20 | npm run test
21 | npm run test:once
22 | ```
23 |
24 | ## Releases
25 |
26 | - Check out a branch and edit package version and add changelog entry
27 | - Run `npm install` again to update `package-lock.json`
28 | - Open PR and merge into `master`
29 | - Run `git checkout master && git pull origin master`
30 | - Make sure everything is 👌
31 | - Run this script via `npm run release`
32 |
33 | > Note: If you have 2FA configured for npm.js (and you should), run: `npm run release --otp=XXXXXX`
34 |
35 | - Build and publish the demo app `npm run build:demo && npm run gh-pages`
36 | - Update all the official StackBlitz demos
37 | - Edit release on Github
38 |
--------------------------------------------------------------------------------
/src/demo/app/interactive-close/interactive-close.component.scss:
--------------------------------------------------------------------------------
1 | @use '@angular/material' as mat;
2 |
3 | $error-text: #e63922;
4 |
5 | :host {
6 | display: block;
7 | }
8 |
9 | .options {
10 | @include mat.elevation(8);
11 | display: flex;
12 | flex-direction: column;
13 | padding: 24px;
14 | background: white;
15 |
16 | .error {
17 | color: $error-text;
18 | }
19 |
20 | .mat-button {
21 | margin-top: 8px;
22 | }
23 | }
24 |
25 | .shake {
26 | animation: shake 300ms ease-in-out;
27 | }
28 |
29 | @keyframes shake {
30 | 0% {
31 | transform: translateX(0);
32 | }
33 | 12.5% {
34 | transform: translateX(-6px) rotateY(-5deg);
35 | }
36 | 37.5% {
37 | transform: translateX(5px) rotateY(4deg);
38 | }
39 | 62.5% {
40 | transform: translateX(-3px) rotateY(-2deg);
41 | }
42 | 87.5% {
43 | transform: translateX(2px) rotateY(1deg);
44 | }
45 | 100% {
46 | transform: translateX(0);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/lib/popover/types.ts:
--------------------------------------------------------------------------------
1 | export type SatPopoverScrollStrategy = 'noop' | 'block' | 'reposition' | 'close';
2 | export const VALID_SCROLL: SatPopoverScrollStrategy[] = ['noop', 'block', 'reposition', 'close'];
3 |
4 | export type SatPopoverHorizontalAlign = 'before' | 'start' | 'center' | 'end' | 'after';
5 | export const VALID_HORIZ_ALIGN: SatPopoverHorizontalAlign[] = ['before', 'start', 'center', 'end', 'after'];
6 |
7 | export type SatPopoverVerticalAlign = 'above' | 'start' | 'center' | 'end' | 'below';
8 | export const VALID_VERT_ALIGN: SatPopoverVerticalAlign[] = ['above', 'start', 'center', 'end', 'below'];
9 |
10 | export interface SatPopoverOpenOptions {
11 | /**
12 | * Whether the popover should return focus to the previously focused element after
13 | * closing. Defaults to true.
14 | */
15 | restoreFocus?: boolean;
16 |
17 | /** Whether the first focusable element should be focused on open. Defaults to true. */
18 | autoFocus?: boolean;
19 | }
20 |
--------------------------------------------------------------------------------
/src/demo/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 |
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../coverage'),
20 | reports: ['html', 'lcovonly'],
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 | });
31 | };
32 |
--------------------------------------------------------------------------------
/src/lib/popover/popover.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { OverlayModule } from '@angular/cdk/overlay';
4 | import { A11yModule } from '@angular/cdk/a11y';
5 | import { BidiModule } from '@angular/cdk/bidi';
6 |
7 | import { SatPopoverComponent, SatPopoverAnchorDirective } from './popover.component';
8 | import { SatPopoverHoverDirective } from './popover-hover.directive';
9 | import { DEFAULT_TRANSITION } from './tokens';
10 |
11 | @NgModule({
12 | imports: [
13 | CommonModule,
14 | OverlayModule,
15 | A11yModule,
16 | BidiModule,
17 | SatPopoverComponent,
18 | SatPopoverAnchorDirective,
19 | SatPopoverHoverDirective
20 | ],
21 | providers: [
22 | // See http://cubic-bezier.com/#.25,.8,.25,1 for reference.
23 | { provide: DEFAULT_TRANSITION, useValue: '200ms cubic-bezier(0.25, 0.8, 0.25, 1)' }
24 | ],
25 | exports: [SatPopoverComponent, SatPopoverAnchorDirective, SatPopoverHoverDirective, BidiModule]
26 | })
27 | export class SatPopoverModule {}
28 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "ignorePatterns": [
4 | "projects/**/*"
5 | ],
6 | "overrides": [
7 | {
8 | "files": [
9 | "*.ts"
10 | ],
11 | "extends": [
12 | "eslint:recommended",
13 | "plugin:@typescript-eslint/recommended",
14 | "plugin:@angular-eslint/recommended",
15 | "plugin:@angular-eslint/template/process-inline-templates"
16 | ],
17 | "rules": {
18 | "@angular-eslint/directive-selector": [
19 | "error",
20 | {
21 | "type": "attribute",
22 | "prefix": "",
23 | "style": "camelCase"
24 | }
25 | ],
26 | "@angular-eslint/component-selector": [
27 | "error",
28 | {
29 | "type": "element",
30 | "prefix": "",
31 | "style": "kebab-case"
32 | }
33 | ]
34 | }
35 | },
36 | {
37 | "files": [
38 | "*.html"
39 | ],
40 | "extends": [
41 | "plugin:@angular-eslint/template/recommended",
42 | "plugin:@angular-eslint/template/accessibility"
43 | ],
44 | "rules": {}
45 | }
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 North Carolina State University Security Applications & Technologies
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 |
--------------------------------------------------------------------------------
/src/demo/styles.scss:
--------------------------------------------------------------------------------
1 | @use '@angular/material' as mat;
2 |
3 | // Material theming
4 | $ncstate-primary: mat.m2-define-palette(mat.$m2-red-palette, 700);
5 | $ncstate-accent: mat.m2-define-palette(mat.$m2-blue-grey-palette);
6 | $ncstate-warn: mat.m2-define-palette(mat.$m2-deep-orange-palette);
7 |
8 | $ncstate-theme: mat.m2-define-light-theme(
9 | (
10 | color: (
11 | primary: $ncstate-primary,
12 | accent: $ncstate-accent,
13 | warn: $ncstate-warn
14 | ),
15 | typography: mat.m2-define-typography-config(),
16 | density: 0
17 | )
18 | );
19 |
20 | $gradient-opacity: 0.48;
21 |
22 | @include mat.core();
23 | @include mat.all-component-themes($ncstate-theme);
24 |
25 | html,
26 | body {
27 | height: 100%;
28 | margin: 0;
29 | }
30 |
31 | .demo-background-rainbow {
32 | background: linear-gradient(
33 | to right,
34 | rgba(255, 165, 0, $gradient-opacity),
35 | rgba(255, 255, 0, $gradient-opacity),
36 | rgba(0, 128, 0, $gradient-opacity),
37 | rgba(0, 255, 255, $gradient-opacity),
38 | rgba(0, 0, 255, $gradient-opacity),
39 | rgba(238, 130, 238, $gradient-opacity)
40 | );
41 | }
42 |
43 | .demo-background-dark {
44 | background: rgba(0, 0, 0, 0.6);
45 | }
46 |
--------------------------------------------------------------------------------
/src/lib/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 | var configuration = {
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 |
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../../coverage'),
20 | reports: ['html', 'lcovonly'],
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 | customLaunchers: {
31 | Chrome_travis_ci: {
32 | base: 'Chrome',
33 | flags: ['--no-sandbox']
34 | }
35 | }
36 | };
37 |
38 | // Run with --no-sandbox mode on Travis
39 | if (process.env.TRAVIS) {
40 | configuration.browsers = ['Chrome_travis_ci'];
41 | }
42 |
43 | config.set(configuration);
44 | };
45 |
--------------------------------------------------------------------------------
/src/lib/popover/popover.component.scss:
--------------------------------------------------------------------------------
1 | @use '@angular/cdk' as cdk;
2 |
3 | // Include overlay styles
4 | @include cdk.overlay();
5 |
6 | .sat-popover-container {
7 | &.sat-popover-before.sat-popover-above {
8 | transform-origin: right bottom;
9 |
10 | [dir='rtl'] & {
11 | transform-origin: left bottom;
12 | }
13 | }
14 |
15 | &.sat-popover-before.sat-popover-center {
16 | transform-origin: right center;
17 |
18 | [dir='rtl'] & {
19 | transform-origin: left center;
20 | }
21 | }
22 |
23 | &.sat-popover-before.sat-popover-below {
24 | transform-origin: right top;
25 |
26 | [dir='rtl'] & {
27 | transform-origin: left top;
28 | }
29 | }
30 |
31 | &.sat-popover-center.sat-popover-above {
32 | transform-origin: center bottom;
33 | }
34 |
35 | &.sat-popover-center.sat-popover-below {
36 | transform-origin: center top;
37 | }
38 |
39 | &.sat-popover-after.sat-popover-above {
40 | transform-origin: left bottom;
41 |
42 | [dir='rtl'] & {
43 | transform-origin: right bottom;
44 | }
45 | }
46 |
47 | &.sat-popover-after.sat-popover-center {
48 | transform-origin: left center;
49 |
50 | [dir='rtl'] & {
51 | transform-origin: right center;
52 | }
53 | }
54 |
55 | &.sat-popover-after.sat-popover-below {
56 | transform-origin: left top;
57 |
58 | [dir='rtl'] & {
59 | transform-origin: right top;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/demo/app/action-api/action-api.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { MatCardModule } from '@angular/material/card';
3 | import { SatPopoverModule } from '../../../lib/public_api';
4 |
5 | @Component({
6 | imports: [MatCardModule, SatPopoverModule],
7 | selector: 'demo-action-api',
8 | styleUrls: ['./action-api.component.scss'],
9 | template: `
10 |
11 | Action API
12 |
13 | W
14 |
15 |
16 |
17 |
Messages: 12
18 |
Friends since: 12/21/2012
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | `
32 | })
33 | export class DemoActionAPIComponent {}
34 |
--------------------------------------------------------------------------------
/src/lib/popover/popover.errors.ts:
--------------------------------------------------------------------------------
1 | import { VALID_HORIZ_ALIGN, VALID_VERT_ALIGN, VALID_SCROLL } from './types';
2 |
3 | export function getUnanchoredPopoverError(): Error {
4 | return Error('SatPopover does not have an anchor.');
5 | }
6 |
7 | export function getInvalidPopoverAnchorError(): Error {
8 | return Error('SatPopover#anchor must be an instance of SatPopoverAnchor, ElementRef, or HTMLElement.');
9 | }
10 |
11 | export function getInvalidPopoverError(): Error {
12 | return Error('SatPopoverAnchor#satPopoverAnchor must be an instance of SatPopover.');
13 | }
14 |
15 | export function getInvalidSatPopoverAnchorError(): Error {
16 | return Error(
17 | `SatPopoverAnchor must be associated with a ` +
18 | `SatPopover component. ` +
19 | `Examples: or ` +
20 | `