├── .build ├── check-public-exports │ └── index.ts ├── commitlint │ └── index.js ├── common │ ├── github │ │ └── index.js │ └── index.js └── publish-gh-pages │ └── index.js ├── .ci └── templates │ └── steps │ ├── create-bundle.yml │ ├── initialize-environment.yml │ ├── install-dependencies.yml │ └── test.yml ├── .codeclimate.yml ├── .compodocrc.json ├── .editorconfig ├── .eslintrc.json ├── .github └── workflows │ └── publish.yml ├── .gitignore ├── .npmrc ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── angular.json ├── azure-pipelines.yml ├── commitlint.config.js ├── eslint-configs ├── angular-html.js ├── angular.js ├── base.js └── rxjs.js ├── favicon.ico ├── import-sorter.json ├── logo.png ├── package-lock.json ├── package.json ├── projects ├── angular │ ├── .eslintrc.json │ ├── _uipath-angular.theme.scss │ ├── a11y │ │ ├── ng-package.json │ │ └── src │ │ │ ├── public_api.ts │ │ │ ├── queued-announcer │ │ │ └── queued-announcer.ts │ │ │ └── ui-auto-accessible-label │ │ │ ├── ui-auto-accessible-label.directive.spec.ts │ │ │ ├── ui-auto-accessible-label.directive.ts │ │ │ └── ui-auto-accessible-label.module.ts │ ├── axe-helper.ts │ ├── components │ │ ├── styles │ │ │ └── _ellipse.scss │ │ ├── ui-file-picker │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── _ui-file-picker.theme.scss │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-file-picker.component.html │ │ │ │ ├── ui-file-picker.component.scss │ │ │ │ ├── ui-file-picker.component.spec.ts │ │ │ │ ├── ui-file-picker.component.ts │ │ │ │ ├── ui-file-picker.intl.ts │ │ │ │ └── ui-input-file-drop-zone │ │ │ │ ├── ui-input-file-drop-zone.component.html │ │ │ │ ├── ui-input-file-drop-zone.component.scss │ │ │ │ ├── ui-input-file-drop-zone.component.spec.ts │ │ │ │ └── ui-input-file-drop-zone.component.ts │ │ ├── ui-grid │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── _ui-grid-variables.scss │ │ │ │ ├── _ui-grid.theme.scss │ │ │ │ ├── body │ │ │ │ ├── ui-grid-column.directive.spec.ts │ │ │ │ ├── ui-grid-column.directive.ts │ │ │ │ ├── ui-grid-expanded-row.directive.ts │ │ │ │ ├── ui-grid-loading.directive.ts │ │ │ │ ├── ui-grid-no-content.directive.ts │ │ │ │ ├── ui-grid-row-action.directive.ts │ │ │ │ ├── ui-grid-row-card-view.directive.ts │ │ │ │ └── ui-grid-row-config.directive.ts │ │ │ │ ├── components │ │ │ │ ├── ui-grid-custom-paginator │ │ │ │ │ ├── ui-grid-custom-paginator.component.html │ │ │ │ │ ├── ui-grid-custom-paginator.component.scss │ │ │ │ │ ├── ui-grid-custom-paginator.component.spec.ts │ │ │ │ │ ├── ui-grid-custom-paginator.component.ts │ │ │ │ │ └── ui-grid-custom-paginator.module.ts │ │ │ │ ├── ui-grid-search │ │ │ │ │ ├── _ui-grid-search.theme.scss │ │ │ │ │ ├── ui-grid-custom-search.directive.ts │ │ │ │ │ ├── ui-grid-search.component.html │ │ │ │ │ ├── ui-grid-search.component.scss │ │ │ │ │ ├── ui-grid-search.component.spec.ts │ │ │ │ │ ├── ui-grid-search.component.ts │ │ │ │ │ └── ui-grid-search.module.ts │ │ │ │ └── ui-grid-toggle-columns │ │ │ │ │ ├── ui-grid-toggle-columns.component.html │ │ │ │ │ ├── ui-grid-toggle-columns.component.scss │ │ │ │ │ ├── ui-grid-toggle-columns.component.ts │ │ │ │ │ └── ui-grid-toggle-columns.module.ts │ │ │ │ ├── events │ │ │ │ └── page-change-event.ts │ │ │ │ ├── filters │ │ │ │ ├── ui-grid-dropdown-filter.directive.ts │ │ │ │ ├── ui-grid-filter.ts │ │ │ │ └── ui-grid-search-filter.directive.ts │ │ │ │ ├── footer │ │ │ │ └── ui-grid-footer.directive.ts │ │ │ │ ├── header │ │ │ │ ├── ui-grid-header-button.directive.ts │ │ │ │ └── ui-grid-header.directive.ts │ │ │ │ ├── managers │ │ │ │ ├── data-manager.spec.ts │ │ │ │ ├── data-manager.ts │ │ │ │ ├── filter-manager.spec.ts │ │ │ │ ├── filter-manager.ts │ │ │ │ ├── index.ts │ │ │ │ ├── live-announcer-manager.spec.ts │ │ │ │ ├── live-announcer-manager.ts │ │ │ │ ├── performance.ts │ │ │ │ ├── resize-strategy-token.ts │ │ │ │ ├── resize │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── resize-manager.constants.spec.ts │ │ │ │ │ ├── resize-manager.constants.ts │ │ │ │ │ ├── resize-manager.factory.ts │ │ │ │ │ ├── resize-manager.ts │ │ │ │ │ ├── strategies │ │ │ │ │ │ ├── aggresive-neighbour-push-resizer.ts │ │ │ │ │ │ ├── immediate-neighbour-halt-resizer.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── scrollable-grid-resizer.ts │ │ │ │ │ └── types │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── resizableGrid.ts │ │ │ │ │ │ ├── resizeDefinition.ts │ │ │ │ │ │ ├── resizeDirection.ts │ │ │ │ │ │ ├── resizeEvent.ts │ │ │ │ │ │ ├── resizeInfo.ts │ │ │ │ │ │ ├── resizeState.ts │ │ │ │ │ │ └── resizeStrategy.ts │ │ │ │ ├── selection-manager.spec.ts │ │ │ │ ├── selection-manager.ts │ │ │ │ ├── sort-manager.spec.ts │ │ │ │ ├── sort-manager.ts │ │ │ │ └── visibility-manager.ts │ │ │ │ ├── models │ │ │ │ ├── dataModel.ts │ │ │ │ ├── filterModel.ts │ │ │ │ ├── index.ts │ │ │ │ ├── selectionDiff.ts │ │ │ │ ├── sortModel.ts │ │ │ │ └── visibleModel.ts │ │ │ │ ├── public_api.ts │ │ │ │ ├── test │ │ │ │ ├── column.ts │ │ │ │ ├── data.ts │ │ │ │ ├── generics.ts │ │ │ │ ├── index.ts │ │ │ │ └── testEntity.ts │ │ │ │ ├── ui-grid.component.html │ │ │ │ ├── ui-grid.component.scss │ │ │ │ ├── ui-grid.component.spec.ts │ │ │ │ ├── ui-grid.component.ts │ │ │ │ ├── ui-grid.intl.ts │ │ │ │ └── ui-grid.module.ts │ │ ├── ui-password-indicator │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── _ui-password-indicator.theme.scss │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-password-indicator.animations.ts │ │ │ │ ├── ui-password-indicator.component.html │ │ │ │ ├── ui-password-indicator.component.scss │ │ │ │ ├── ui-password-indicator.component.spec.ts │ │ │ │ ├── ui-password-indicator.component.ts │ │ │ │ ├── ui-password-indicator.intl.ts │ │ │ │ ├── ui-password-indicator.module.ts │ │ │ │ ├── ui-password-indicator.validator.spec.ts │ │ │ │ └── ui-password-indicator.validator.ts │ │ ├── ui-password-toggle │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-password-toggle.component.html │ │ │ │ ├── ui-password-toggle.component.spec.ts │ │ │ │ ├── ui-password-toggle.component.ts │ │ │ │ ├── ui-password-toggle.intl.ts │ │ │ │ └── ui-password-toggle.module.ts │ │ ├── ui-snackbar │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── _ui-snackbar.theme.scss │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-snackbar.component.html │ │ │ │ ├── ui-snackbar.component.scss │ │ │ │ ├── ui-snackbar.component.spec.ts │ │ │ │ ├── ui-snackbar.component.ts │ │ │ │ ├── ui-snackbar.intl.spec.ts │ │ │ │ ├── ui-snackbar.intl.ts │ │ │ │ └── ui-snackbar.module.ts │ │ ├── ui-suggest │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── _ui-suggest.theme.scss │ │ │ │ ├── models │ │ │ │ ├── index.ts │ │ │ │ ├── maxSelectionConfig.ts │ │ │ │ ├── suggestDirection.ts │ │ │ │ ├── suggestDisplayPriority.ts │ │ │ │ ├── suggestValue.ts │ │ │ │ ├── suggestValueData.ts │ │ │ │ └── suggestValues.ts │ │ │ │ ├── public_api.ts │ │ │ │ ├── test │ │ │ │ ├── index.ts │ │ │ │ ├── suggestionItem.ts │ │ │ │ └── ui-suggest-assert.ts │ │ │ │ ├── ui-suggest.animations.ts │ │ │ │ ├── ui-suggest.component.html │ │ │ │ ├── ui-suggest.component.scss │ │ │ │ ├── ui-suggest.component.spec.ts │ │ │ │ ├── ui-suggest.component.ts │ │ │ │ ├── ui-suggest.intl.ts │ │ │ │ ├── ui-suggest.mat-form-field.ts │ │ │ │ ├── ui-suggest.module.ts │ │ │ │ └── utils │ │ │ │ ├── index.ts │ │ │ │ └── items.utils.ts │ │ └── ui-tree-select │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ ├── models │ │ │ └── tree.models.ts │ │ │ ├── public_api.ts │ │ │ ├── ui-tree-item │ │ │ ├── ui-tree-item.component.html │ │ │ ├── ui-tree-item.component.spec.ts │ │ │ └── ui-tree-item.component.ts │ │ │ ├── ui-tree-select.component.html │ │ │ ├── ui-tree-select.component.scss │ │ │ ├── ui-tree-select.component.spec.ts │ │ │ ├── ui-tree-select.component.ts │ │ │ └── utils │ │ │ └── tree.utils.ts │ ├── directives │ │ ├── internal │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-format.ts │ │ │ │ └── ui-loader-button.ts │ │ ├── keyboard-shortcut │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── keyboard-shortcut.directive.spec.ts │ │ │ │ ├── keyboard-shortcut.directive.ts │ │ │ │ ├── keyboard-shortcut.module.ts │ │ │ │ └── public_api.ts │ │ ├── ui-autofocus │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-autofocus.directive.spec.ts │ │ │ │ ├── ui-autofocus.directive.ts │ │ │ │ └── ui-autofocus.module.ts │ │ ├── ui-click-outside │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-click-outside.directive.spec.ts │ │ │ │ ├── ui-click-outside.directive.ts │ │ │ │ └── ui-click-outside.module.ts │ │ ├── ui-clipboard │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-clipboard.directive.spec.ts │ │ │ │ ├── ui-clipboard.directive.ts │ │ │ │ └── ui-clipboard.module.ts │ │ ├── ui-content-loader │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── internal │ │ │ │ ├── ui-content-spinner.component.html │ │ │ │ ├── ui-content-spinner.component.scss │ │ │ │ └── ui-content-spinner.component.ts │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-content-loader.directive.spec.ts │ │ │ │ ├── ui-content-loader.directive.ts │ │ │ │ └── ui-content-loader.module.ts │ │ ├── ui-dateformat │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-dateformat.directive.spec.ts │ │ │ │ ├── ui-dateformat.directive.ts │ │ │ │ └── ui-dateformat.module.ts │ │ ├── ui-drag-and-drop-file │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-drag-and-drop-file.directive.spec.ts │ │ │ │ ├── ui-drag-and-drop-file.directive.ts │ │ │ │ └── ui-drag-and-drop-file.module.ts │ │ ├── ui-file-drop-zone │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── file-drop-zone.utils.ts │ │ │ │ ├── file-reader.service.ts │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-file-drop-zone.directive.spec.ts │ │ │ │ └── ui-file-drop-zone.directive.ts │ │ ├── ui-matformfield-required │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-matformfield-required.directive.intl.ts │ │ │ │ ├── ui-matformfield-required.directive.spec.ts │ │ │ │ ├── ui-matformfield-required.directive.ts │ │ │ │ └── ui-matformfield-required.module.ts │ │ ├── ui-ng-let │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-ng-let.directive.spec.ts │ │ │ │ ├── ui-ng-let.directive.ts │ │ │ │ └── ui-ng-let.module.ts │ │ ├── ui-progress-button │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── internal │ │ │ │ ├── ui-button-progress-bar.component.html │ │ │ │ ├── ui-button-progress-bar.component.scss │ │ │ │ └── ui-button-progress-bar.component.ts │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-progress-button.directive.spec.ts │ │ │ │ ├── ui-progress-button.directive.ts │ │ │ │ └── ui-progress-button.module.ts │ │ ├── ui-scroll-into-view │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-scroll-into-view.directive.spec.ts │ │ │ │ ├── ui-scroll-into-view.directive.ts │ │ │ │ └── ui-scroll-into-view.module.ts │ │ ├── ui-secondformat │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-secondformat.directive.spec.ts │ │ │ │ ├── ui-secondformat.directive.ts │ │ │ │ └── ui-secondformat.module.ts │ │ ├── ui-spinner-button │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── internal │ │ │ │ ├── ui-button-progress-spinner.component.html │ │ │ │ ├── ui-button-progress-spinner.component.scss │ │ │ │ └── ui-button-progress-spinner.component.ts │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-spinner-button.directive.spec.ts │ │ │ │ ├── ui-spinner-button.directive.ts │ │ │ │ └── ui-spinner-button.module.ts │ │ ├── ui-virtual-scroll-range-loader │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ │ ├── public_api.ts │ │ │ │ ├── ui-virtual-scroll-range-loader.directive.spec.ts │ │ │ │ ├── ui-virtual-scroll-range-loader.directive.ts │ │ │ │ └── ui-virtual-scroll-range-loader.module.ts │ │ └── ui-virtual-scroll-viewport-resize │ │ │ ├── ng-package.json │ │ │ └── src │ │ │ ├── public_api.ts │ │ │ ├── ui-virtual-scroll-viewport-resize.directive.spec.ts │ │ │ ├── ui-virtual-scroll-viewport-resize.directive.ts │ │ │ └── ui-virtual-scroll-viewport-resize.module.ts │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── pipes │ │ ├── ng-package.json │ │ └── src │ │ │ ├── file-size │ │ │ ├── file-size.intl.ts │ │ │ ├── file-size.pipe.spec.ts │ │ │ └── file-size.pipe.ts │ │ │ ├── nl2br │ │ │ ├── nl2br.pipe.spec.ts │ │ │ └── nl2br.pipe.ts │ │ │ ├── pipe.module.ts │ │ │ └── public_api.ts │ ├── public_api.ts │ ├── test.theme.ts │ ├── test.ts │ ├── testing │ │ ├── ng-package.json │ │ └── src │ │ │ ├── component-utils │ │ │ ├── grid-testing-utils.ts │ │ │ ├── index.ts │ │ │ └── suggest-testing-utils.ts │ │ │ ├── public_api.ts │ │ │ └── utilities │ │ │ ├── event-generator.ts │ │ │ ├── events │ │ │ ├── drop-event.ts │ │ │ └── index.ts │ │ │ ├── fake-file-list.ts │ │ │ ├── fixture-testing-utils.ts │ │ │ ├── html-testing-utils.ts │ │ │ ├── index.ts │ │ │ ├── internal │ │ │ ├── cursor.ts │ │ │ └── index.ts │ │ │ └── key.ts │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ ├── tsconfig.spec.json │ └── utilities │ │ ├── ng-package.json │ │ └── src │ │ ├── array │ │ ├── index.ts │ │ ├── sort.spec.ts │ │ └── sort.ts │ │ ├── browser │ │ ├── extract-cookies.spec.ts │ │ ├── extract-cookies.ts │ │ ├── has-support-for-prefers-color-scheme.spec.ts │ │ ├── has-support-for-prefers-color-scheme.ts │ │ ├── index.ts │ │ ├── is-browser-platform.ts │ │ ├── is-edge.spec.ts │ │ ├── is-edge.ts │ │ ├── is-internet-explorer.spec.ts │ │ └── is-internet-explorer.ts │ │ ├── luxon │ │ ├── index.ts │ │ └── use-luxon.ts │ │ ├── public_api.ts │ │ ├── rxjs │ │ ├── async-of.spec.ts │ │ ├── async-of.ts │ │ ├── concat-join.spec.ts │ │ ├── concat-join.ts │ │ ├── index.ts │ │ ├── internal │ │ │ └── observed-value-of.ts │ │ ├── repeat-stream.spec.ts │ │ └── repeat-stream.ts │ │ └── string │ │ ├── file-extension.ts │ │ ├── identifier.ts │ │ └── index.ts └── playground │ ├── .eslintrc.json │ ├── karma.conf.js │ ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── pages │ │ │ ├── file-picker │ │ │ │ ├── file-picker.module.ts │ │ │ │ ├── file-picker.page.html │ │ │ │ ├── file-picker.page.scss │ │ │ │ └── file-picker.page.ts │ │ │ ├── grid │ │ │ │ ├── component │ │ │ │ │ ├── grid.component.html │ │ │ │ │ ├── grid.component.scss │ │ │ │ │ └── grid.component.ts │ │ │ │ ├── grid.intl.ts │ │ │ │ ├── grid.models.ts │ │ │ │ ├── grid.page.html │ │ │ │ ├── grid.page.module.ts │ │ │ │ ├── grid.page.scss │ │ │ │ └── grid.page.ts │ │ │ ├── home │ │ │ │ ├── home.page.html │ │ │ │ ├── home.page.module.ts │ │ │ │ ├── home.page.scss │ │ │ │ └── home.page.ts │ │ │ ├── progress-button │ │ │ │ ├── progress-button.module.ts │ │ │ │ ├── progress-button.page.html │ │ │ │ ├── progress-button.page.scss │ │ │ │ └── progress-button.page.ts │ │ │ ├── snackbar │ │ │ │ ├── snackbar-content.component.ts │ │ │ │ ├── snackbar.module.ts │ │ │ │ ├── snackbar.page.html │ │ │ │ ├── snackbar.page.scss │ │ │ │ └── snackbar.page.ts │ │ │ ├── suggest │ │ │ │ ├── suggest.module.ts │ │ │ │ ├── suggest.page.html │ │ │ │ ├── suggest.page.scss │ │ │ │ ├── suggest.page.spec.ts │ │ │ │ └── suggest.page.ts │ │ │ └── tree-select │ │ │ │ ├── tree-select.page.html │ │ │ │ ├── tree-select.page.scss │ │ │ │ └── tree-select.page.ts │ │ └── utils │ │ │ └── settings-store.ts │ ├── assets │ │ └── .gitkeep │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.scss │ └── test.ts │ ├── tsconfig.app.json │ └── tsconfig.spec.json ├── scss-bundle.config.json ├── test.theme.scss ├── tsconfig.json ├── tsconfig.node.json └── version-bump.js /.build/commitlint/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Adapted from: https://github.com/fathyb/commitlint-circle 3 | */ 4 | const { lint, load } = require('@commitlint/core') 5 | const path = require('path') 6 | const { red, yellow, blue, green } = require('chalk'); 7 | 8 | const { getPullRequestHead, getCommits, statusReporterFactory } = require('../common/github'); 9 | 10 | const _handleUnexpectedError = (err) => { 11 | console.error(err) 12 | process.exit(1) 13 | } 14 | 15 | const reportStatus = statusReporterFactory('commitlint') 16 | 17 | const run = async () => { 18 | const head = await getPullRequestHead() 19 | 20 | console.log(blue('👮‍ Calling Git Police...')) 21 | 22 | await reportStatus('pending', head.sha, 'Checking commits...') 23 | 24 | const commits = await getCommits() 25 | 26 | const { rules } = await load(path.resolve('commitlint.config.js')) 27 | 28 | const lintResultList = await Promise.all( 29 | commits.map(({ commit }) => lint(commit.message, rules)) 30 | ) 31 | 32 | const isConventionalCommitGuidelineRespected = lintResultList.every(result => result.valid) 33 | const isAnyFixupCommit = commits.some(({ commit }) => commit.message.startsWith('fixup!')) 34 | 35 | if ( 36 | isConventionalCommitGuidelineRespected && 37 | !isAnyFixupCommit 38 | ) { 39 | console.log(green(`💯 Good to go!`)) 40 | 41 | await reportStatus('success', head.sha, '✔ Good to go!') 42 | .catch(_handleUnexpectedError) 43 | 44 | return; 45 | } 46 | 47 | if (isAnyFixupCommit) { 48 | console.warn(yellow(`😬 There are still some fixup commits.`)) 49 | 50 | await reportStatus('error', head.sha, 'Please rebase and apply the fixup commits!') 51 | .catch(_handleUnexpectedError) 52 | 53 | return; 54 | } 55 | 56 | console.error(red(`⛔ Something doesn't check out`)) 57 | 58 | await reportStatus('error', head.sha, 'We use conventional commits (╯°□°)╯︵ ┻━┻', 'https://www.conventionalcommits.org/en/v1.0.0-beta.4/') 59 | .catch(_handleUnexpectedError) 60 | } 61 | 62 | (async () => { 63 | await run() 64 | .catch(_handleUnexpectedError) 65 | })() 66 | -------------------------------------------------------------------------------- /.build/common/index.js: -------------------------------------------------------------------------------- 1 | const { red } = require('chalk') 2 | 3 | const extract = (token) => { 4 | const value = process.env[token] 5 | if (!value) { 6 | console.warn(`Could not read environment variable: ${red(token)}`) 7 | return '' 8 | } 9 | 10 | return value; 11 | } 12 | 13 | const owner = extract('GH_OWNER') 14 | const repo = extract('GH_REPO') 15 | const pull = extract('GH_PULL_ID') 16 | const token = extract('GH_TOKEN') 17 | 18 | const variables = { 19 | owner, 20 | repo, 21 | pull, 22 | token, 23 | } 24 | 25 | module.exports = { 26 | variables, 27 | } 28 | -------------------------------------------------------------------------------- /.build/publish-gh-pages/index.js: -------------------------------------------------------------------------------- 1 | const ghpages = require('gh-pages') 2 | const { variables } = require('../common') 3 | const { owner, repo, token } = variables; 4 | 5 | const PUBLISH_CONFIG = { 6 | repo: `https://${token}@github.com/${owner}/${repo}.git`, 7 | silent: true, 8 | } 9 | 10 | console.log('Publishing docs to branch `gh-pages`...') 11 | 12 | ghpages.publish( 13 | 'documentation', 14 | PUBLISH_CONFIG, 15 | (err) => { 16 | if (err) { 17 | console.error(err) 18 | } else { 19 | console.log('Docs have been published succesfully...') 20 | } 21 | }) 22 | -------------------------------------------------------------------------------- /.ci/templates/steps/create-bundle.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | npm run build:prod 4 | env: 5 | GH_NPM_REGISTRY_TOKEN: '' 6 | displayName: 'Bundle Library' 7 | -------------------------------------------------------------------------------- /.ci/templates/steps/initialize-environment.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | echo '##vso[task.setvariable variable=CC_TEST_REPORTER_ID]$(system.ccToken)' 4 | echo '##vso[task.setvariable variable=GIT_COMMIT_SHA]$(Build.SourceVersion)' 5 | echo '##vso[task.setvariable variable=GIT_BRANCH]$(Build.SourceBranchName)' 6 | echo '##vso[task.setvariable variable=GH_TOKEN]$(system.githubToken)' 7 | echo '##vso[task.setvariable variable=GH_PULL_ID]$(System.PullRequest.PullRequestNumber)' 8 | echo '##vso[task.setvariable variable=GH_OWNER]UiPath' 9 | echo '##vso[task.setvariable variable=GH_REPO]angular-components' 10 | displayName: 'Configure Environment Variables' 11 | 12 | - script: | 13 | echo $(GIT_COMMIT_SHA) 14 | echo $(GIT_BRANCH) 15 | displayName: 'Echo Commit SHA and Branch' 16 | 17 | - script: | 18 | echo $(GH_PULL_ID) 19 | displayName: 'Echo PR Number' 20 | condition: eq(variables['GIT_BRANCH'], 'merge') 21 | 22 | - task: NodeTool@0 23 | inputs: 24 | versionSpec: '16.x' 25 | displayName: 'Install Node.js' 26 | -------------------------------------------------------------------------------- /.ci/templates/steps/install-dependencies.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | npm ci 4 | env: 5 | GH_NPM_REGISTRY_TOKEN: '' 6 | displayName: 'Install NPM Dependencies' 7 | -------------------------------------------------------------------------------- /.ci/templates/steps/test.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | codeCoverage: 'false' 3 | 4 | steps: 5 | - ${{ if eq(parameters.codeCoverage, 'true') }}: 6 | - script: | 7 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 8 | chmod +x ./cc-test-reporter 9 | ./cc-test-reporter before-build -d 10 | displayName: 'Hook Code Climate Coverage Status' 11 | condition: ne(variables['GIT_BRANCH'], 'merge') 12 | 13 | - script: | 14 | npm run test:coverage 15 | env: 16 | GH_NPM_REGISTRY_TOKEN: '' 17 | displayName: 'Run Tests and Generate Coverage Report' 18 | 19 | - script: | 20 | ./cc-test-reporter format-coverage -d -t lcov ./coverage/lcov.info 21 | ./cc-test-reporter upload-coverage -d 22 | displayName: 'Publish LCOV result to Code Climate' 23 | condition: ne(variables['GIT_BRANCH'], 'merge') 24 | 25 | - task: PublishTestResults@2 26 | inputs: 27 | testResultsFormat: 'JUnit' 28 | testResultsFiles: '**/*.test.xml' 29 | failTaskOnFailedTests: true 30 | displayName: 'Publish Test Results' 31 | condition: always() 32 | 33 | - ${{ if eq(parameters.codeCoverage, 'false') }}: 34 | - script: | 35 | npm run test:headless 36 | displayName: 'Run Tests' 37 | 38 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | exclude_patterns: 2 | - '**/test/' 3 | - '**/tests/' 4 | - '**/*.spec.ts' 5 | - '**/*.d.ts' 6 | - '**/node_modules/' 7 | - '**/.build/' 8 | 9 | checks: 10 | method-lines: 11 | config: 12 | threshold: 50 13 | 14 | file-lines: 15 | enabled: false 16 | -------------------------------------------------------------------------------- /.compodocrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@compodoc/compodoc/src/config/schema.json", 3 | "tsconfig": "./projects/angular/tsconfig.lib.json", 4 | "name": "UiPath Angular Components", 5 | "customFavicon": "./favicon.ico", 6 | "customLogo": "./logo.png", 7 | "disablePrivate": true, 8 | "disableProtected": true 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Manual Publish 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build-and-publish: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | contents: read 11 | packages: write 12 | 13 | steps: 14 | - name: Set up Node.js 15 | uses: actions/setup-node@v2 16 | with: 17 | node-version: '16.18.0' 18 | 19 | - name: Checkout repository 20 | uses: actions/checkout@v2 21 | 22 | - name: Install dependencies 23 | run: npm ci --force 24 | 25 | - name: Build for production 26 | run: npm run build:prod 27 | 28 | - name: Publish to GitHub Packages 29 | env: 30 | GH_NPM_REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | run: npm run publish:gh 32 | 33 | - name: Authenticate to NPMJS 34 | run: echo -e '//registry.npmjs.org/:_authToken=${NPMJS_PUBLISH_TOKEN}' > .npmrc 35 | 36 | - name: Publish to npm 37 | env: 38 | NPMJS_PUBLISH_TOKEN: ${{ secrets.NPMJS_PUBLISH_TOKEN }} 39 | run: npm run publish:npm 40 | -------------------------------------------------------------------------------- /.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 | /documentation 8 | 9 | # dependencies 10 | /node_modules 11 | **/node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events.json 15 | speed-measure-plugin.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.angular/cache 36 | /.sass-cache 37 | /connect.lock 38 | /coverage 39 | /libpeerconnection.log 40 | npm-debug.log 41 | yarn-error.log 42 | testem.log 43 | /typings 44 | 45 | # System Files 46 | .DS_Store 47 | Thumbs.db 48 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | //npm.pkg.github.com/:_authToken=${GH_NPM_REGISTRY_TOKEN} 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | // List of extensions which should be recommended for users of this workspace. 5 | "recommendations": [ 6 | "natewallace.angular2-inline", 7 | "eamodio.gitlens", 8 | "Angular.ng-template", 9 | "christian-kohler.npm-intellisense", 10 | "christian-kohler.path-intellisense", 11 | "ghaschel.vscode-angular-html", 12 | "alexiv.vscode-angular2-files", 13 | "mrmlnc.vscode-scss", 14 | "sibiraj-s.vscode-scss-formatter", 15 | "dozerg.tsimportsorter", 16 | "dbaeumer.vscode-eslint", 17 | "johnpapa.vscode-peacock" 18 | ], 19 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 20 | "unwantedRecommendations": [ 21 | "ms-vscode.vscode-typescript-tslint-plugin", 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.insertFinalNewline": true, 3 | "files.trimTrailingWhitespace": true, 4 | "breadcrumbs.enabled": true, 5 | "extensions.ignoreRecommendations": false, 6 | "typescript.updateImportsOnFileMove.enabled": "always", 7 | "editor.codeActionsOnSave": { 8 | "source.fixAll.eslint": true, 9 | }, 10 | "javascript.preferences.importModuleSpecifier": "non-relative", 11 | "javascript.preferences.quoteStyle": "single", 12 | "typescript.preferences.importModuleSpecifier": "non-relative", 13 | "typescript.preferences.quoteStyle": "single", 14 | "html.format.wrapAttributes": "force-aligned", 15 | "typescript.tsdk": "node_modules/typescript/lib", 16 | "workbench.colorCustomizations": { 17 | "activityBar.activeBackground": "#600215", 18 | "activityBar.activeBorder": "#1b7d03", 19 | "activityBar.background": "#600215", 20 | "activityBar.foreground": "#e7e7e7", 21 | "activityBar.inactiveForeground": "#e7e7e799", 22 | "activityBarBadge.background": "#1b7d03", 23 | "activityBarBadge.foreground": "#e7e7e7", 24 | "statusBar.background": "#2e010a", 25 | "statusBar.foreground": "#e7e7e7", 26 | "statusBarItem.hoverBackground": "#600215", 27 | "titleBar.activeBackground": "#2e010a", 28 | "titleBar.activeForeground": "#e7e7e7", 29 | "titleBar.inactiveBackground": "#2e010a99", 30 | "titleBar.inactiveForeground": "#e7e7e799", 31 | "sash.hoverBorder": "#600215", 32 | "statusBarItem.remoteBackground": "#2e010a", 33 | "statusBarItem.remoteForeground": "#e7e7e7", 34 | "commandCenter.border": "#e7e7e799" 35 | }, 36 | "peacock.color": "#2e010a", 37 | "editor.bracketPairColorization.enabled": true, 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 UiPath Inc. 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 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'scope-enum': () => { 5 | const scopeList = [ 6 | 'a11y', 7 | 'autofocus', 8 | 'click-outside', 9 | 'clipboard', 10 | 'content-loader', 11 | 'dateformat', 12 | 'deps', 13 | 'drag-and-drop-file', 14 | 'file-picker', 15 | 'grid', 16 | 'ng-let', 17 | 'nl2br', 18 | 'password-indicator', 19 | 'password-toggle', 20 | 'playground', 21 | 'progress-button', 22 | 'scroll-into-view', 23 | 'secondformat', 24 | 'snackbar', 25 | 'spinner-button', 26 | 'suggest', 27 | 'testing', 28 | 'virtual-scroll-range-loader', 29 | 'virtual-scroll-viewport-resize', 30 | 'matformfield-required', 31 | 'keyboard-shortcut', 32 | 'tree-select', 33 | ]; 34 | 35 | return [2, 'always', scopeList]; 36 | } 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /eslint-configs/angular-html.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | '@angular-eslint/template', 4 | ], 5 | extends: [ 6 | 'plugin:@angular-eslint/template/recommended', 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /eslint-configs/rxjs.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | 'rxjs', 4 | ], 5 | rules: { 6 | 'rxjs/no-compat': 'error', 7 | 'rxjs/no-nested-subscribe': 'error', 8 | 'rxjs/no-subject-unsubscribe': 'error', 9 | 'rxjs/no-unsafe-switchmap': 'error', 10 | 'rxjs/no-unsafe-takeuntil': 'error', 11 | 'rxjs/no-create': 'error', 12 | 'rxjs/finnish': [ 13 | 'error', 14 | { 15 | 'functions': false, 16 | 'methods': false, 17 | 'parameters': false, 18 | 'properties': true, 19 | 'variables': true, 20 | }, 21 | ], 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UiPath/angular-components/d5660a9805152dae8c1290f322db07a9d8abad26/favicon.ico -------------------------------------------------------------------------------- /import-sorter.json: -------------------------------------------------------------------------------- 1 | { 2 | "groupRules": [ 3 | {}, 4 | "^[@]", 5 | "^[.]" 6 | ], 7 | "sortRules": { 8 | "paths": [ 9 | "aA", 10 | "_" 11 | ], 12 | "names": [ 13 | "aA", 14 | "_" 15 | ] 16 | }, 17 | "tabSize": 4, 18 | "quoteMark": "single", 19 | "trailingComma": "multiLine" 20 | } 21 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UiPath/angular-components/d5660a9805152dae8c1290f322db07a9d8abad26/logo.png -------------------------------------------------------------------------------- /projects/angular/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json", 3 | "ignorePatterns": [ 4 | "!**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "projects/angular/tsconfig.lib.json", 14 | "projects/angular/tsconfig.spec.json" 15 | ], 16 | "createDefaultProgram": true 17 | }, 18 | "plugins": [ 19 | "sonarjs", 20 | "eslint-plugin-rxjs" 21 | ], 22 | "rules": {} 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /projects/angular/_uipath-angular.theme.scss: -------------------------------------------------------------------------------- 1 | @use "@angular/material" as mat; 2 | @import "./components/ui-suggest/src/ui-suggest.theme"; 3 | @import "./components/ui-grid/src/ui-grid.theme"; 4 | @import "./components/ui-snackbar/src/ui-snackbar.theme"; 5 | @import "./components/ui-password-indicator/src/ui-password-indicator.theme"; 6 | @import "./components/ui-file-picker/src/ui-file-picker.theme"; 7 | 8 | @mixin uipath-angular-theme($theme) { 9 | @include ui-suggest-theme($theme); 10 | @include ui-grid-theme($theme); 11 | @include ui-password-indicator-theme($theme); 12 | @include ui-file-picker-theme($theme); 13 | } 14 | 15 | @mixin uipath-snackbar-theme($theme, $snackbar-theme: null) { 16 | @include ui-snackbar-theme($theme, $snackbar-theme); 17 | } 18 | -------------------------------------------------------------------------------- /projects/angular/a11y/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/a11y/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { QueuedAnnouncer } from './queued-announcer/queued-announcer'; 2 | export { UiAutoAccessibleLabelModule } from './ui-auto-accessible-label/ui-auto-accessible-label.module'; 3 | export { UiAutoAccessibleLabelDirective } from './ui-auto-accessible-label/ui-auto-accessible-label.directive'; 4 | -------------------------------------------------------------------------------- /projects/angular/a11y/src/queued-announcer/queued-announcer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | firstValueFrom, 3 | of, 4 | } from 'rxjs'; 5 | import { delay } from 'rxjs/operators'; 6 | 7 | import { LiveAnnouncer } from '@angular/cdk/a11y'; 8 | import { Injectable } from '@angular/core'; 9 | 10 | @Injectable({ 11 | providedIn: 'root', 12 | }) 13 | export class QueuedAnnouncer { 14 | private _msgQueue: string[] = []; 15 | private _isAnnouncing = false; 16 | 17 | constructor(private _liveAnnouncer: LiveAnnouncer) { } 18 | 19 | enqueue(msg: string | undefined) { 20 | if (!msg) { return; } 21 | this._msgQueue.push(msg); 22 | 23 | if (!this._isAnnouncing) { 24 | this._isAnnouncing = true; 25 | this._announceNext(); 26 | } 27 | } 28 | 29 | private _announceNext = () => { 30 | if (!this._msgQueue.length) { 31 | this._isAnnouncing = false; 32 | return; 33 | } 34 | 35 | this._liveAnnouncer.announce(this._msgQueue.shift()!) 36 | // announcements end up in an aria-live element 37 | // a delay is needed before setting the next announcement so they are both picked up by the screen reader 38 | .then(() => firstValueFrom(of(void 0).pipe(delay(50)))) 39 | .then(this._announceNext) 40 | .catch(this._announceNext); 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /projects/angular/a11y/src/ui-auto-accessible-label/ui-auto-accessible-label.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { UiAutoAccessibleLabelDirective } from './ui-auto-accessible-label.directive'; 4 | 5 | @NgModule({ 6 | declarations: [ 7 | UiAutoAccessibleLabelDirective, 8 | ], 9 | exports: [ 10 | UiAutoAccessibleLabelDirective, 11 | ], 12 | }) 13 | export class UiAutoAccessibleLabelModule { } 14 | -------------------------------------------------------------------------------- /projects/angular/axe-helper.ts: -------------------------------------------------------------------------------- 1 | import { RunOptions } from 'axe-core'; 2 | import * as jasmineAxe from 'jasmine-axe'; 3 | 4 | /* eslint-disable */ 5 | export const axe = jasmineAxe.configureAxe({ 6 | globalOptions: { 7 | standards: { 8 | ariaRoles: { 9 | combobox: { 10 | type: 'widget', 11 | requiredOwned: [], 12 | requiredAttrs: ['aria-expanded'], 13 | allowedAttrs: [ 14 | 'aria-controls', 15 | 'aria-autocomplete', 16 | 'aria-readonly', 17 | 'aria-required', 18 | 'aria-activedescendant', 19 | 'aria-orientation', 20 | ], 21 | }, 22 | }, 23 | }, 24 | }, 25 | }); 26 | 27 | let _specFn: typeof it = () => { }; 28 | let _focusedSpecFn: typeof fit = () => { }; 29 | let _excludedSpecFn: typeof xit = () => { }; 30 | 31 | export const setSpecFn = (_it: typeof it, _fit: typeof fit, _xit: typeof xit) => { 32 | _specFn = _it.bind(null); 33 | _focusedSpecFn = _fit.bind(null); 34 | _excludedSpecFn = _xit.bind(null); 35 | }; 36 | 37 | export const a11y = { 38 | fit: function (...args: Parameters) { 39 | _focusedSpecFn?.call(null, ...args); 40 | }, 41 | xit: function (...args: Parameters) { 42 | _excludedSpecFn?.call(null, ...args); 43 | }, 44 | it: function (...args: Parameters) { 45 | _specFn?.call(null, ...args); 46 | }, 47 | suite: function (cb: (customSpecOptions: RunOptions) => void) { 48 | cb(customSpecOptions); 49 | }, 50 | }; 51 | 52 | const customSpecOptions: RunOptions = { 53 | rules: { 54 | 'color-contrast': { enabled: false }, 55 | }, 56 | }; 57 | -------------------------------------------------------------------------------- /projects/angular/components/styles/_ellipse.scss: -------------------------------------------------------------------------------- 1 | %ellipse { 2 | text-overflow: ellipsis; 3 | overflow: hidden; 4 | white-space: nowrap; 5 | } 6 | -------------------------------------------------------------------------------- /projects/angular/components/ui-file-picker/ng-package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /projects/angular/components/ui-file-picker/src/_ui-file-picker.theme.scss: -------------------------------------------------------------------------------- 1 | @mixin ui-file-picker-theme($theme) { 2 | $background: map-get($theme, "background"); 3 | $foreground: map-get($theme, "foreground"); 4 | $primary: map-get($theme, "primary"); 5 | 6 | $color-primary: mat.get-color-from-palette($primary); 7 | $color-background: mat.get-color-from-palette($background, "background"); 8 | $border-color: mat.get-color-from-palette($foreground, "divider"); 9 | $color-hint: mat.get-color-from-palette($foreground, "hint-text"); 10 | 11 | .ui-file-picker { 12 | .upload-wrapper { 13 | border-color: $border-color; 14 | &.ui-file-drop-zone-highlight { 15 | border-color: $color-primary; 16 | } 17 | .upload-input-wrapper { 18 | border-top-color: $border-color; 19 | .primary { 20 | color: $color-primary; 21 | .placeholder { 22 | color: $color-hint; 23 | } 24 | } 25 | .mat-stroked-button { 26 | background: $color-background; 27 | } 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /projects/angular/components/ui-file-picker/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiInputFileDropZoneComponent } from './ui-input-file-drop-zone/ui-input-file-drop-zone.component'; 2 | export { UiFilePickerComponent } from './ui-file-picker.component'; 3 | export { UiFilePickerIntl } from './ui-file-picker.intl'; 4 | -------------------------------------------------------------------------------- /projects/angular/components/ui-file-picker/src/ui-file-picker.intl.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { of } from 'rxjs'; 3 | 4 | @Injectable({ providedIn: 'root' }) 5 | export class UiFilePickerIntl { 6 | clickUploadDragDrop$ = of('Click to upload or drag and drop'); 7 | deleteAll$ = of('Delete all'); 8 | fileName$ = of('File name'); 9 | size$ = of('Size'); 10 | type$ = of('Type'); 11 | 12 | deleteFile$ = (fileName: string) => of(`Delete file ${fileName}`); 13 | errorReadingFiles$ = (directory: string, errorName: string, errorMsg: string) => of(`Error reading directory: ${directory}. ${errorName}: ${errorMsg}`); 14 | } 15 | -------------------------------------------------------------------------------- /projects/angular/components/ui-file-picker/src/ui-input-file-drop-zone/ui-input-file-drop-zone.component.html: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /projects/angular/components/ui-file-picker/src/ui-input-file-drop-zone/ui-input-file-drop-zone.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | position: relative; 4 | 5 | input { 6 | position: absolute; 7 | width: 100%; 8 | height: 100%; 9 | top: 0; 10 | left: 0; 11 | opacity: 0; 12 | cursor: pointer; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/_ui-grid-variables.scss: -------------------------------------------------------------------------------- 1 | $ui-grid-cell-background-color: var(--color-background, #ffffff); 2 | $ui-grid-border-color: var(--color-border-de-emp, #cfd8dd); 3 | $ui-grid-cell-bottom-border: 1px solid $ui-grid-border-color; 4 | $ui-grid-row-hover-color: var(--color-data-grid-hover, #F1F2F3); 5 | $ui-grid-header-pressed-color: var(--color-data-grid-pressed, #EAECED); 6 | $header-background-color: var(--color-background-secondary, #f4f5f7); 7 | $grid-actions-box-shadow: var(--color-background, #ffffff); 8 | $highlighted-entity-color: var(--color-primary, #0067df); 9 | $scroll-margin-shadow-color: var(--color-background-inverse, #182027); 10 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/body/ui-grid-expanded-row.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ContentChild, 3 | Directive, 4 | TemplateRef, 5 | } from '@angular/core'; 6 | 7 | /** 8 | * Expanded row definition directive. 9 | * 10 | * @export 11 | */ 12 | @Directive({ 13 | selector: '[uiGridExpandedRow], ui-grid-expanded-row', 14 | }) 15 | export class UiGridExpandedRowDirective { 16 | /** 17 | * @internal 18 | * @ignore 19 | */ 20 | @ContentChild(TemplateRef, { 21 | static: true, 22 | }) 23 | html?: TemplateRef; 24 | } 25 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/body/ui-grid-loading.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ContentChild, 3 | Directive, 4 | TemplateRef, 5 | } from '@angular/core'; 6 | 7 | @Directive({ 8 | selector: '[uiGridLoading], ui-grid-loading', 9 | }) 10 | export class UiGridLoadingDirective { 11 | @ContentChild(TemplateRef, { 12 | static: true, 13 | }) 14 | html?: TemplateRef; 15 | } 16 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/body/ui-grid-no-content.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ContentChild, 3 | Directive, 4 | TemplateRef, 5 | } from '@angular/core'; 6 | 7 | @Directive({ 8 | selector: '[uiGridNoContent], ui-grid-no-content', 9 | }) 10 | export class UiGridNoContentDirective { 11 | @ContentChild(TemplateRef, { 12 | static: true, 13 | }) 14 | html?: TemplateRef; 15 | } 16 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/body/ui-grid-row-action.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ContentChild, 3 | Directive, 4 | TemplateRef, 5 | } from '@angular/core'; 6 | 7 | /** 8 | * The row action definition directive. 9 | * 10 | */ 11 | @Directive({ 12 | selector: '[uiGridRowAction], ui-grid-row-action', 13 | }) 14 | export class UiGridRowActionDirective { 15 | /** 16 | * @ignore 17 | */ 18 | @ContentChild(TemplateRef, { 19 | static: true, 20 | }) 21 | html?: TemplateRef; 22 | } 23 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/body/ui-grid-row-card-view.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ContentChild, 3 | Directive, TemplateRef, 4 | } from '@angular/core'; 5 | 6 | export interface IGridRowCardViewContext { 7 | index: number; 8 | last: boolean; 9 | data: T; 10 | } 11 | 12 | @Directive({ selector: '[uiGridRowCardView], ui-grid-row-card-view' }) 13 | export class UiGridRowCardViewDirective { 14 | @ContentChild(TemplateRef, { 15 | static: true, 16 | }) 17 | html?: TemplateRef>; 18 | } 19 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/body/ui-grid-row-config.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directive, 3 | Input, 4 | } from '@angular/core'; 5 | 6 | import { IGridDataEntry } from '../models'; 7 | 8 | /** 9 | * Row configuration directive. 10 | * 11 | * @export 12 | */ 13 | @Directive({ 14 | selector: '[uiGridRowConfig], ui-grid-row-config', 15 | }) 16 | export class UiGridRowConfigDirective { 17 | /** 18 | * Class function factory, used to apply `ngClass` on rows. 19 | * 20 | */ 21 | @Input() 22 | ngClassFn: (entry: T) => Record = (_) => ({} as Record); 23 | } 24 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/components/ui-grid-custom-paginator/ui-grid-custom-paginator.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { MatButtonModule } from '@angular/material/button'; 4 | import { MatSelectModule } from '@angular/material/select'; 5 | import { MatTooltipModule } from '@angular/material/tooltip'; 6 | 7 | import { UiGridCustomPaginatorComponent } from './ui-grid-custom-paginator.component'; 8 | 9 | @NgModule({ 10 | imports: [ 11 | CommonModule, 12 | MatSelectModule, 13 | MatButtonModule, 14 | MatTooltipModule, 15 | ], 16 | declarations: [UiGridCustomPaginatorComponent], 17 | exports: [UiGridCustomPaginatorComponent], 18 | }) 19 | export class UiGridCustomPaginatorModule { } 20 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/components/ui-grid-search/_ui-grid-search.theme.scss: -------------------------------------------------------------------------------- 1 | @mixin ui-grid-search-theme($theme) { 2 | $warn-palette: map-get($theme, "warn"); 3 | 4 | .ui-grid-search-cancel { 5 | &:hover, 6 | &:focus { 7 | color: map-get($warn-palette, "default"); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/components/ui-grid-search/ui-grid-custom-search.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ContentChild, 3 | Directive, 4 | TemplateRef, 5 | } from '@angular/core'; 6 | 7 | @Directive({ 8 | selector: '[uiGridCustomSearch], ui-grid-custom-search', 9 | }) 10 | export class UiGridCustomSearchDirective { 11 | @ContentChild(TemplateRef, { 12 | static: true, 13 | }) 14 | html?: TemplateRef; 15 | } 16 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/components/ui-grid-search/ui-grid-search.component.scss: -------------------------------------------------------------------------------- 1 | ui-grid-search { 2 | mat-form-field { 3 | width: 140px; 4 | .mat-mdc-form-field-flex { 5 | .mat-mdc-form-field-infix { 6 | display: flex; 7 | } 8 | } 9 | } 10 | 11 | .ui-grid-search-cancel { 12 | cursor: pointer; 13 | 14 | &:focus { 15 | outline: none; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/components/ui-grid-search/ui-grid-search.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { ReactiveFormsModule } from '@angular/forms'; 4 | import { MatButtonModule } from '@angular/material/button'; 5 | import { MatFormFieldModule } from '@angular/material/form-field'; 6 | import { MatIconModule } from '@angular/material/icon'; 7 | import { MatInputModule } from '@angular/material/input'; 8 | import { MatTooltipModule } from '@angular/material/tooltip'; 9 | 10 | import { UiGridSearchComponent } from './ui-grid-search.component'; 11 | 12 | @NgModule({ 13 | imports: [ 14 | CommonModule, 15 | MatIconModule, 16 | MatFormFieldModule, 17 | MatButtonModule, 18 | MatInputModule, 19 | MatTooltipModule, 20 | ReactiveFormsModule, 21 | ], 22 | declarations: [UiGridSearchComponent], 23 | exports: [UiGridSearchComponent], 24 | }) 25 | export class UiGridSearchModule { } 26 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/components/ui-grid-toggle-columns/ui-grid-toggle-columns.component.scss: -------------------------------------------------------------------------------- 1 | $toggle-col-width: 10em; 2 | 3 | .ui-grid-toggle-columns { 4 | margin-right: 6px; 5 | padding-left: 0; 6 | display: flex; 7 | 8 | mat-divider { 9 | margin-left: 6px; 10 | height: 32px; 11 | align-self: center; 12 | } 13 | 14 | mat-select { 15 | overflow: hidden; 16 | width: 0; 17 | min-width: 0; 18 | // adjustments in order to match trigger icon with optgroup-label icon 19 | position: relative; 20 | top: -10px; 21 | left: -67px; 22 | } 23 | } 24 | 25 | // overlay panel 26 | .ui-grid-toggle-panel { 27 | // IE fix - min-width with calc does not render properly so we add a fallback in case min-width is evaluated even lower 28 | width: 210px !important; 29 | 30 | .mat-mdc-optgroup-label { 31 | display: flex; 32 | padding-left: 12px; 33 | padding-right: 3px; 34 | justify-content: space-between; 35 | align-items: center; 36 | 37 | .mat-icon { 38 | margin-right: 0; 39 | flex-shrink: 0; 40 | } 41 | 42 | .mdc-list-item__primary-text { 43 | display: flex; 44 | align-items: center; 45 | max-width: 100%; 46 | } 47 | 48 | .ui-grid-toggle-title { 49 | white-space: nowrap; 50 | overflow: hidden; 51 | text-overflow: ellipsis; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/components/ui-grid-toggle-columns/ui-grid-toggle-columns.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { MatButtonModule } from '@angular/material/button'; 4 | import { MatDividerModule } from '@angular/material/divider'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatSelectModule } from '@angular/material/select'; 7 | import { MatTooltipModule } from '@angular/material/tooltip'; 8 | import { UiAutoAccessibleLabelModule } from '@uipath/angular/a11y'; 9 | 10 | import { UiGridToggleColumnsComponent } from './ui-grid-toggle-columns.component'; 11 | 12 | @NgModule({ 13 | imports: [ 14 | CommonModule, 15 | MatIconModule, 16 | MatSelectModule, 17 | MatButtonModule, 18 | MatTooltipModule, 19 | MatDividerModule, 20 | UiAutoAccessibleLabelModule, 21 | ], 22 | declarations: [UiGridToggleColumnsComponent], 23 | exports: [UiGridToggleColumnsComponent], 24 | }) 25 | export class UiGridToggleColumnsModule { } 26 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/events/page-change-event.ts: -------------------------------------------------------------------------------- 1 | import { PageEvent } from '@angular/material/paginator'; 2 | 3 | export type PageChangeEvent = Omit & { 4 | length?: number | null; 5 | }; 6 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/filters/ui-grid-filter.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directive, 3 | EventEmitter, 4 | Input, 5 | OnDestroy, 6 | Output, 7 | } from '@angular/core'; 8 | 9 | import { IFilterModel } from '../models'; 10 | 11 | /** 12 | * Filter definition directive. 13 | * 14 | * @export 15 | * @internal 16 | * @ignore 17 | */ 18 | @Directive() 19 | export abstract class UiGridFilterDirective implements OnDestroy { 20 | @Input() 21 | disabled?: boolean; 22 | 23 | @Input() 24 | method?: string; 25 | 26 | @Output() 27 | filterChange = new EventEmitter | null>(); 28 | 29 | ngOnDestroy() { 30 | this.filterChange.complete(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/header/ui-grid-header-button.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ContentChild, 3 | Directive, 4 | Input, 5 | TemplateRef, 6 | } from '@angular/core'; 7 | 8 | /** 9 | * Header button definition directive. 10 | * 11 | * @export 12 | */ 13 | @Directive({ 14 | selector: '[uiHeaderButton], ui-header-button', 15 | }) 16 | export class UiGridHeaderButtonDirective { 17 | /** 18 | * Configure if the button is the grid main action, or a selection action. 19 | * 20 | */ 21 | @Input() 22 | type?: 'action' | 'main' | 'inline'; 23 | 24 | /** 25 | * Configure if the button is visible or not. 26 | * 27 | */ 28 | @Input() 29 | visible = true; 30 | 31 | /** 32 | * @internal 33 | * @ignore 34 | */ 35 | @ContentChild(TemplateRef, { 36 | static: true, 37 | }) 38 | html?: TemplateRef; 39 | } 40 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/managers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sort-manager'; 2 | export * from './data-manager'; 3 | export * from './filter-manager'; 4 | export * from './selection-manager'; 5 | export * from './resize'; 6 | export * from './performance'; 7 | export * from './live-announcer-manager'; 8 | export * from './visibility-manager'; 9 | export * from './resize-strategy-token'; 10 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/managers/performance.ts: -------------------------------------------------------------------------------- 1 | import { Subject } from 'rxjs'; 2 | 3 | import { isDevMode } from '@angular/core'; 4 | 5 | /** 6 | * @internal 7 | * @ignore 8 | */ 9 | export class PerformanceMonitor { 10 | paintTime$ = new Subject(); 11 | 12 | get enabled() { 13 | return isDevMode(); 14 | } 15 | 16 | private _timestamp?: number; 17 | private _obsever?: MutationObserver; 18 | 19 | constructor(element: Element) { 20 | if (!this.enabled) { return; } 21 | 22 | this._obsever = new MutationObserver(this._onPaint); 23 | this._obsever.observe(element, { 24 | childList: true, 25 | subtree: true, 26 | }); 27 | } 28 | 29 | reset() { 30 | if (!this.enabled) { return; } 31 | 32 | this._timestamp = performance.now(); 33 | } 34 | 35 | destroy() { 36 | this.paintTime$.complete(); 37 | 38 | if (!this.enabled) { return; } 39 | 40 | this._obsever!.disconnect(); 41 | } 42 | 43 | private _onPaint = (record: MutationRecord[]) => { 44 | const isRowPaint = record 45 | .some(r => 46 | this._isRow(r.target) || 47 | [ 48 | ...Array.from(r.addedNodes), 49 | ...Array.from(r.removedNodes), 50 | ].some(this._isRow), 51 | ); 52 | 53 | if (!isRowPaint) { return; } 54 | 55 | this.paintTime$.next((performance.now() - this._timestamp!).toFixed(2)); 56 | }; 57 | 58 | private _isRow = (element: Node) => 59 | !!(element as Element).classList && 60 | (element as Element) 61 | .classList 62 | .contains('ui-grid-row'); 63 | } 64 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/managers/resize-strategy-token.ts: -------------------------------------------------------------------------------- 1 | import { BehaviorSubject } from 'rxjs'; 2 | 3 | import { InjectionToken } from '@angular/core'; 4 | 5 | import { ResizeStrategy } from './resize/types/resizeStrategy'; 6 | 7 | export const UI_GRID_RESIZE_STRATEGY_STREAM = new InjectionToken>('resize-strategy-stream'); 8 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/managers/resize/index.ts: -------------------------------------------------------------------------------- 1 | export * from './resize-manager'; 2 | export { ResizeStrategy } from './types'; 3 | export * from './resize-manager.factory'; 4 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/managers/resize/resize-manager.factory.ts: -------------------------------------------------------------------------------- 1 | import { IGridDataEntry } from '../../models'; 2 | import { ResizeManager } from './resize-manager'; 3 | import { 4 | AggresiveNeighbourPushResizer, 5 | ImmediateNeighbourHaltResizer, 6 | } from './strategies'; 7 | import { ScrollableGridResizer } from './strategies/scrollable-grid-resizer'; 8 | import { 9 | ResizableGrid, 10 | ResizeStrategy, 11 | } from './types'; 12 | 13 | /** 14 | * @internal 15 | * @ignore 16 | */ 17 | export const ResizeManagerFactory = 18 | (type: ResizeStrategy, grid: ResizableGrid): ResizeManager => { 19 | switch (type) { 20 | case ResizeStrategy.PassiveNeighbourPush: 21 | console.warn(`The ${ResizeStrategy[type] 22 | } strategy is not yet supported. It will default to ${ResizeStrategy[ResizeStrategy.AggresiveNeighbourPush] 23 | }`); 24 | return new AggresiveNeighbourPushResizer(grid); 25 | case ResizeStrategy.AggresiveNeighbourPush: 26 | return new AggresiveNeighbourPushResizer(grid); 27 | case ResizeStrategy.ImmediateNeighbourHalt: 28 | return new ImmediateNeighbourHaltResizer(grid); 29 | case ResizeStrategy.ScrollableGrid: 30 | return new ScrollableGridResizer(grid); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/managers/resize/strategies/index.ts: -------------------------------------------------------------------------------- 1 | export * from './immediate-neighbour-halt-resizer'; 2 | export * from './aggresive-neighbour-push-resizer'; 3 | export * from './scrollable-grid-resizer'; 4 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/managers/resize/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './resizeDefinition'; 2 | export * from './resizeDirection'; 3 | export * from './resizeEvent'; 4 | export * from './resizeInfo'; 5 | export * from './resizeState'; 6 | export * from './resizeStrategy'; 7 | export * from './resizableGrid'; 8 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/managers/resize/types/resizableGrid.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | Observable, 3 | Subject, 4 | } from 'rxjs'; 5 | 6 | import type { 7 | ChangeDetectorRef, 8 | ElementRef, 9 | EventEmitter, 10 | QueryList, 11 | SimpleChanges, 12 | } from '@angular/core'; 13 | 14 | import type { UiGridColumnDirective } from '../../../body/ui-grid-column.directive'; 15 | 16 | /** 17 | * @internal 18 | * @ignore 19 | */ 20 | export abstract class ResizableGrid { 21 | abstract columns: QueryList>; 22 | abstract rendered: EventEmitter; 23 | abstract toggleColumns: boolean; 24 | 25 | protected abstract _ref: ElementRef; 26 | protected abstract _cd: ChangeDetectorRef; 27 | 28 | protected abstract _destroyed$: Subject; 29 | protected abstract _columnChanges$: Observable; 30 | } 31 | 32 | export type ResizeEmission = Record; 33 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/managers/resize/types/resizeDefinition.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @internal 3 | * @ignore 4 | */ 5 | export type IResizeDefinition = Record; 6 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/managers/resize/types/resizeDirection.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @internal 3 | * @ignore 4 | */ 5 | export enum ResizeDirection { 6 | Left = -1, 7 | Right = 1, 8 | } 9 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/managers/resize/types/resizeEvent.ts: -------------------------------------------------------------------------------- 1 | import { IResizeState } from './resizeState'; 2 | 3 | /** 4 | * @internal 5 | * @ignore 6 | */ 7 | export interface IResizeEvent { 8 | current: IResizeState; 9 | previous: IResizeState; 10 | deltaPx: number; 11 | } 12 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/managers/resize/types/resizeInfo.ts: -------------------------------------------------------------------------------- 1 | import { UiGridColumnDirective } from '../../../body/ui-grid-column.directive'; 2 | 3 | /** 4 | * @internal 5 | * @ignore 6 | */ 7 | export interface IResizeInfo { 8 | column: UiGridColumnDirective; 9 | element: HTMLDivElement; 10 | cells: HTMLDivElement[]; 11 | index: number; 12 | dragInitX?: number; 13 | } 14 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/managers/resize/types/resizeState.ts: -------------------------------------------------------------------------------- 1 | import { ResizeDirection } from './resizeDirection'; 2 | import { IResizeInfo } from './resizeInfo'; 3 | 4 | /** 5 | * @internal 6 | * @ignore 7 | */ 8 | export interface IResizeState { 9 | resized: IResizeInfo; 10 | neighbour?: IResizeInfo; 11 | oppositeNeighbour?: IResizeInfo; 12 | offsetPx: number; 13 | offsetPercent: number; 14 | direction: ResizeDirection; 15 | event?: MouseEvent; 16 | } 17 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/managers/resize/types/resizeStrategy.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @internal 3 | * @ignore 4 | */ 5 | export enum ResizeStrategy { 6 | AggresiveNeighbourPush = 0, 7 | PassiveNeighbourPush, 8 | ImmediateNeighbourHalt, 9 | ScrollableGrid 10 | } 11 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/models/dataModel.ts: -------------------------------------------------------------------------------- 1 | import { ResizeStrategy } from '../managers'; 2 | 3 | /** 4 | * The grid entry schema. 5 | * 6 | * @export 7 | */ 8 | export interface IGridDataEntry { 9 | /** 10 | * The identifier associated to the current row item. 11 | * 12 | * Used to track multi-page selections / optimize rendering. 13 | */ 14 | id: number | string; 15 | } 16 | /** 17 | * Grid options. 18 | * `useLegacyDesign` if you still need the old layout 19 | * `collapsibleFilters` is deprecated, use `collapseFiltersCount` instead 20 | * `fetchStrategy` controls how searchable filters will emit queries on render 21 | * 22 | * @export 23 | */ 24 | export interface GridOptions { 25 | useCache?: boolean; 26 | fetchStrategy?: 'eager' | 'onOpen'; 27 | collapsibleFilters?: boolean; 28 | collapseFiltersCount?: number; 29 | idProperty?: keyof T; 30 | rowSize?: number; 31 | resizeStrategy?: ResizeStrategy; 32 | selectablePageIndex?: boolean; 33 | hasHighDensity?: boolean; 34 | } 35 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/models/filterModel.ts: -------------------------------------------------------------------------------- 1 | import { ISuggestValueData } from '@uipath/angular/components/ui-suggest'; 2 | 3 | /** 4 | * The filter model schema. 5 | * 6 | * @export 7 | */ 8 | export interface IFilterModel { 9 | /** 10 | * The targeted entity property. 11 | * 12 | */ 13 | property: keyof T | string; 14 | /** 15 | * Filter method metadata. 16 | * 17 | * eg: `equals`, `greaterThan`, etc. 18 | * 19 | */ 20 | method: string; 21 | /** 22 | * The current filter value. 23 | * 24 | */ 25 | value: string | number | Date | boolean | [] | undefined | null; 26 | /** 27 | * Type metadata. 28 | * 29 | * eg: `string`, `number`, etc. 30 | * 31 | */ 32 | type?: string; 33 | /** 34 | * The current filter meta. 35 | * This will be additional suggest data sent to search filters in case you need more than the id from suggest. 36 | * 37 | */ 38 | meta?: ISuggestValueData | ISuggestValueData[] | null; 39 | } 40 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sortModel'; 2 | export * from './filterModel'; 3 | export * from './dataModel'; 4 | export * from './visibleModel'; 5 | export * from './selectionDiff'; 6 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/models/selectionDiff.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Selection difference schema. 3 | * 4 | * Used for selection snapshot comparisons. 5 | * 6 | * @export 7 | */ 8 | export interface ISelectionDiff { 9 | /** 10 | * The added list. 11 | * 12 | */ 13 | add: Partial[]; 14 | /** 15 | * The removed list. 16 | * 17 | */ 18 | remove: Partial[]; 19 | } 20 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/models/sortModel.ts: -------------------------------------------------------------------------------- 1 | import { SortDirection } from '@angular/material/sort'; 2 | 3 | /** 4 | * Sort model schemal 5 | * 6 | * @export 7 | */ 8 | export interface ISortModel { 9 | /** 10 | * The sort direction. 11 | * 12 | */ 13 | direction: SortDirection; 14 | /** 15 | * The target sort field. 16 | * 17 | */ 18 | field: keyof T | string; 19 | /** 20 | * The sorted column title. 21 | * 22 | */ 23 | title: string; 24 | /** 25 | * Sort event as a result of direct user intent to sort by a column. 26 | * 27 | */ 28 | userEvent?: boolean; 29 | } 30 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/models/visibleModel.ts: -------------------------------------------------------------------------------- 1 | export interface IVisibleModel { 2 | property: keyof T | string; 3 | label: string; 4 | checked: boolean; 5 | disabled: boolean; 6 | } 7 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/test/data.ts: -------------------------------------------------------------------------------- 1 | import * as faker from 'faker'; 2 | 3 | import { ITestEntity } from './testEntity'; 4 | 5 | let ENTITY_COUNT = 0; 6 | 7 | export const generateEntity = (): ITestEntity => ({ 8 | id: ++ENTITY_COUNT, 9 | myNumber: faker.random.number(), 10 | myString: faker.random.alphaNumeric(16), 11 | myBool: faker.random.boolean(), 12 | myDate: faker.date.future(), 13 | myObj: { 14 | myObjString: faker.random.alphaNumeric(32), 15 | myObjNumber: faker.random.number(), 16 | myObjBool: faker.random.boolean(), 17 | myObjDate: faker.date.future(), 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/test/generics.ts: -------------------------------------------------------------------------------- 1 | import * as faker from 'faker'; 2 | 3 | export const generateListFactory = (mapper: () => T, wrapper: (fn: (...args: any[]) => T[]) => T[] = (fn) => fn()) => 4 | (count: number | 'random' = 5) => wrapper(() => 5 | Array(count === 'random' ? 6 | faker.random.number({ 7 | min: 5, 8 | max: 100, 9 | }) : 10 | count, 11 | ).fill(0).map(mapper)); 12 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/test/index.ts: -------------------------------------------------------------------------------- 1 | export * from './data'; 2 | export * from './testEntity'; 3 | export * from './column'; 4 | export * from './generics'; 5 | -------------------------------------------------------------------------------- /projects/angular/components/ui-grid/src/test/testEntity.ts: -------------------------------------------------------------------------------- 1 | export interface ITestEntity { 2 | id: number; 3 | myNumber: number; 4 | myString: string; 5 | myBool: boolean; 6 | myDate: Date; 7 | myObj: { 8 | myObjString: string; 9 | myObjNumber: number; 10 | myObjBool: boolean; 11 | myObjDate: Date; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /projects/angular/components/ui-password-indicator/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/components/ui-password-indicator/src/_ui-password-indicator.theme.scss: -------------------------------------------------------------------------------- 1 | @mixin ui-password-indicator-theme($theme) { 2 | $foreground: map-get($theme, "foreground"); 3 | $background: map-get($theme, "background"); 4 | $warn: map-get($theme, "warn"); 5 | $primary: map-get($theme, "primary"); 6 | 7 | $warn-color: mat.get-color-from-palette($warn); 8 | $warn-color-light: mat.get-color-from-palette($warn, lighter); 9 | 10 | $primary-color: mat.get-color-from-palette($primary); 11 | $disabled-color: mat.get-color-from-palette($foreground, "disabled-text"); 12 | $background-color: mat.get-color-from-palette($background, "background"); 13 | 14 | .mat-mdc-form-field.mat-form-field-invalid { 15 | ui-password-indicator { 16 | .mdc-linear-progress__buffer { 17 | background-color: $warn-color-light; 18 | } 19 | .mdc-linear-progress__bar-inner { 20 | border-color: $warn-color; 21 | } 22 | 23 | .mat-mdc-list { 24 | .mat-mdc-list-item { 25 | &.ui-password-rule-invalid { 26 | color: $warn-color; 27 | } 28 | } 29 | } 30 | } 31 | } 32 | 33 | ui-password-indicator { 34 | .ui-password-progress-container { 35 | border-top-color: $background-color; 36 | border-bottom-color: $background-color; 37 | } 38 | 39 | .mat-mdc-list { 40 | .mat-mdc-list-item { 41 | &.ui-password-rule-invalid { 42 | mat-icon { 43 | color: $warn-color; 44 | } 45 | } 46 | 47 | &.ui-password-rule-valid { 48 | color: $disabled-color; 49 | mat-icon { 50 | color: $primary-color; 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /projects/angular/components/ui-password-indicator/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiPasswordComplexityIntl } from './ui-password-indicator.intl'; 2 | export { UiPasswordIndicatorComponent } from './ui-password-indicator.component'; 3 | export { UiPasswordIndicatorModule } from './ui-password-indicator.module'; 4 | export { 5 | complexityValidator, 6 | IPasswordRuleSet, 7 | IPasswordValidationFn, 8 | IRegexLike, 9 | IRuleValidationState, 10 | } from './ui-password-indicator.validator'; 11 | -------------------------------------------------------------------------------- /projects/angular/components/ui-password-indicator/src/ui-password-indicator.animations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | animate, 3 | style, 4 | transition, 5 | trigger, 6 | } from '@angular/animations'; 7 | 8 | /** 9 | * Enter / Leave animation generator. 10 | * NOTE: keep exported as function in order for it to be hoisted! Else the AOT build will fail. 11 | * 12 | * @internal 13 | * @ignore 14 | */ 15 | export function inAndOut(inView: Record, outOfView: Record, timing: string) { 16 | return [ 17 | transition(':enter', [ 18 | style(outOfView), 19 | animate( 20 | timing, 21 | style(inView), 22 | ), 23 | ]), 24 | transition(':leave', [ 25 | style(inView), 26 | animate( 27 | timing, 28 | style(outOfView), 29 | ), 30 | ]), 31 | ]; 32 | } 33 | 34 | const ANIMATION_TIMING = '300ms cubic-bezier(0.55, 0, 0.55, 0.2)'; 35 | 36 | const INVALID_RULE_OUT = { 37 | width: 0, 38 | opacity: 0, 39 | }; 40 | const INVALID_RULE_IN = { 41 | width: '*', 42 | opacity: 1, 43 | }; 44 | 45 | export const RULE_ITEM_ANIMATION = trigger( 46 | 'ruleAnimation', 47 | inAndOut(INVALID_RULE_IN, INVALID_RULE_OUT, ANIMATION_TIMING), 48 | ); 49 | 50 | const RULE_LIST_OUT = { 51 | opacity: 0, 52 | transform: 'translateY(-25%)', 53 | }; 54 | 55 | const RULE_LIST_IN = { 56 | opacity: 1, 57 | transform: 'translateY(0)', 58 | }; 59 | 60 | export const RULE_LIST_ANIMATION = trigger( 61 | 'ruleListAnimation', 62 | inAndOut(RULE_LIST_IN, RULE_LIST_OUT, ANIMATION_TIMING), 63 | ); 64 | 65 | const PROGRESS_OUT = { 66 | opacity: 0, 67 | }; 68 | 69 | const PROGRESS_IN = { 70 | opacity: 1, 71 | }; 72 | 73 | export const PROGRESS_ANIMATION = trigger( 74 | 'progressAnimation', 75 | inAndOut(PROGRESS_IN, PROGRESS_OUT, ANIMATION_TIMING), 76 | ); 77 | -------------------------------------------------------------------------------- /projects/angular/components/ui-password-indicator/src/ui-password-indicator.component.html: -------------------------------------------------------------------------------- 1 | 8 | 41 | 42 | -------------------------------------------------------------------------------- /projects/angular/components/ui-password-indicator/src/ui-password-indicator.component.scss: -------------------------------------------------------------------------------- 1 | $progress-height: 1px; 2 | $progress-top: 30px; 3 | 4 | ui-password-indicator { 5 | display: block; 6 | position: absolute; 7 | width: 100%; 8 | top: $progress-top; 9 | z-index: 1; 10 | 11 | .ui-password-progress-container { 12 | /* Hide leaking glow from mat-form-field-underline */ 13 | border-top-width: 2px; 14 | border-top-style: solid; 15 | border-bottom-width: 2px; 16 | border-bottom-style: solid; 17 | 18 | .mat-mdc-progress-bar { 19 | height: $progress-height; 20 | } 21 | } 22 | 23 | .mat-mdc-list { 24 | padding-top: 0; 25 | display: inline-flex; 26 | 27 | $font-size: 75%; 28 | 29 | .mat-mdc-list-item { 30 | &.mat-2-line { 31 | height: $font-size; 32 | } 33 | .mdc-list-item__primary-text { 34 | flex-direction: row; 35 | align-items: center; 36 | 37 | .ui-password-rule-label { 38 | font-size: $font-size; 39 | } 40 | 41 | mat-icon { 42 | /* Force Font Smoothing (small sizes of icon fonts get edgy 😢) */ 43 | transform: rotate(0.03deg); 44 | font-size: $font-size; 45 | width: $font-size; 46 | height: $font-size; 47 | line-height: $font-size; 48 | margin-right: 2px; 49 | } 50 | } 51 | 52 | &:not(:last-child) { 53 | margin-right: 5px; 54 | } 55 | .mdc-list-item__content { 56 | padding: 0; 57 | } 58 | } 59 | } 60 | } 61 | 62 | .mat-mdc-form-field { 63 | &.mat-focused { 64 | ui-password-indicator { 65 | top: $progress-top - 1px; 66 | 67 | .ui-password-progress-container { 68 | .mat-mdc-progress-bar { 69 | height: $progress-height + 1px; 70 | } 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /projects/angular/components/ui-password-indicator/src/ui-password-indicator.intl.ts: -------------------------------------------------------------------------------- 1 | import startCase from 'lodash-es/startCase'; 2 | import { Subject } from 'rxjs'; 3 | 4 | import { Injectable } from '@angular/core'; 5 | 6 | @Injectable({ 7 | providedIn: 'root', 8 | }) 9 | export class UiPasswordComplexityIntl { 10 | // eslint-disable-next-line rxjs/finnish 11 | changes = new Subject(); 12 | notMet = 'Complexity rules not met:'; 13 | allMet = 'All complexity rules are met'; 14 | 15 | ruleLabel = (ruleKey: string) => startCase(ruleKey); 16 | percentageTitle = (percentage: number) => `${percentage}%`; 17 | } 18 | -------------------------------------------------------------------------------- /projects/angular/components/ui-password-indicator/src/ui-password-indicator.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { MatIconModule } from '@angular/material/icon'; 4 | import { MatListModule } from '@angular/material/list'; 5 | import { MatProgressBarModule } from '@angular/material/progress-bar'; 6 | import { UiNgLetModule } from '@uipath/angular/directives/ui-ng-let'; 7 | 8 | import { UiPasswordIndicatorComponent } from './ui-password-indicator.component'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | CommonModule, 13 | MatListModule, 14 | MatProgressBarModule, 15 | MatIconModule, 16 | UiNgLetModule, 17 | ], 18 | declarations: [ 19 | UiPasswordIndicatorComponent, 20 | ], 21 | exports: [ 22 | UiPasswordIndicatorComponent, 23 | ], 24 | }) 25 | export class UiPasswordIndicatorModule { } 26 | -------------------------------------------------------------------------------- /projects/angular/components/ui-password-indicator/src/ui-password-indicator.validator.ts: -------------------------------------------------------------------------------- 1 | import type { AbstractControl } from '@angular/forms'; 2 | 3 | export type IPasswordValidationFn = (value: string) => boolean; 4 | export interface IRegexLike { 5 | test: (value: string) => boolean; 6 | } 7 | export type IPasswordRuleSet = Record; 8 | export type IRuleValidationState = Record; 9 | export const VALIDATION_RULE_NAME = 'complexity'; 10 | 11 | const resolveFn = (obj: IRegexLike | IPasswordValidationFn) => { 12 | const isRegexLike = typeof obj === 'object' && !!obj.test; 13 | 14 | if (isRegexLike) { return (obj as IRegexLike).test.bind(obj); } 15 | 16 | if (typeof obj === 'function') { return obj; } 17 | 18 | throw Error('The validation member must be a Function or a Regex!'); 19 | }; 20 | 21 | export const complexityValidator = (rules: IPasswordRuleSet, required: boolean) => 22 | (control: AbstractControl) => { 23 | const value: string = control.value; 24 | 25 | if ( 26 | !required && 27 | (value == null || value === '') 28 | ) { return null; } 29 | 30 | const ruleKeys = Object.keys(rules); 31 | 32 | const validityMap = ruleKeys 33 | .reduce( 34 | (ruleStateMap, ruleKey) => { 35 | const validator = rules[ruleKey]; 36 | if ( 37 | value == null || 38 | !resolveFn(validator)(value) 39 | ) { 40 | ruleStateMap[ruleKey] = true; 41 | } 42 | 43 | return ruleStateMap; 44 | }, 45 | {} as IRuleValidationState, 46 | ); 47 | 48 | return !Object.keys(validityMap).length ? 49 | null : 50 | { 51 | [VALIDATION_RULE_NAME]: validityMap, 52 | }; 53 | }; 54 | -------------------------------------------------------------------------------- /projects/angular/components/ui-password-toggle/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/components/ui-password-toggle/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiPasswordToggleComponent } from './ui-password-toggle.component'; 2 | export { UiPasswordToggleModule } from './ui-password-toggle.module'; 3 | export { UiPasswordToggleIntl } from './ui-password-toggle.intl'; 4 | -------------------------------------------------------------------------------- /projects/angular/components/ui-password-toggle/src/ui-password-toggle.component.html: -------------------------------------------------------------------------------- 1 | 5 | 12 | 13 | -------------------------------------------------------------------------------- /projects/angular/components/ui-password-toggle/src/ui-password-toggle.intl.ts: -------------------------------------------------------------------------------- 1 | import { Subject } from 'rxjs'; 2 | 3 | import { Injectable } from '@angular/core'; 4 | 5 | @Injectable() 6 | export class UiPasswordToggleIntl { 7 | // eslint-disable-next-line rxjs/finnish 8 | changes = new Subject(); 9 | 10 | tooltipShow = 'Show'; 11 | tooltipHide = 'Hide'; 12 | } 13 | -------------------------------------------------------------------------------- /projects/angular/components/ui-password-toggle/src/ui-password-toggle.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { MatButtonModule } from '@angular/material/button'; 4 | import { MatIconModule } from '@angular/material/icon'; 5 | import { MatTooltipModule } from '@angular/material/tooltip'; 6 | import { UiAutoAccessibleLabelModule } from '@uipath/angular/a11y'; 7 | import { UiNgLetModule } from '@uipath/angular/directives/ui-ng-let'; 8 | 9 | import { UiPasswordToggleComponent } from './ui-password-toggle.component'; 10 | 11 | @NgModule({ 12 | imports: [ 13 | CommonModule, 14 | MatTooltipModule, 15 | MatIconModule, 16 | MatButtonModule, 17 | UiAutoAccessibleLabelModule, 18 | UiNgLetModule, 19 | ], 20 | declarations: [ 21 | UiPasswordToggleComponent, 22 | ], 23 | exports: [ 24 | UiPasswordToggleComponent, 25 | ], 26 | }) 27 | export class UiPasswordToggleModule { } 28 | -------------------------------------------------------------------------------- /projects/angular/components/ui-snackbar/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/components/ui-snackbar/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { 2 | SnackbarAction, 3 | SnackBarType, 4 | UiMatSnackBarConfig, 5 | UiSnackBarComponent, 6 | UiSnackBarService, 7 | UI_MAT_SNACK_BAR_DEFAULT_OPTIONS, 8 | UI_MAT_SNACK_BAR_PAYLOAD, 9 | } from './ui-snackbar.component'; 10 | 11 | export { UiSnackBarModule } from './ui-snackbar.module'; 12 | export { UiSnackbarIntl } from './ui-snackbar.intl'; 13 | -------------------------------------------------------------------------------- /projects/angular/components/ui-snackbar/src/ui-snackbar.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{data.icon}} 4 | 6 | 9 | 11 |
12 | 13 |
14 | 21 | 22 | 32 |
33 |
34 | -------------------------------------------------------------------------------- /projects/angular/components/ui-snackbar/src/ui-snackbar.component.scss: -------------------------------------------------------------------------------- 1 | .ui-snackbar { 2 | &-container { 3 | display: flex; 4 | justify-content: space-between; 5 | } 6 | 7 | &-message, 8 | &-dismiss { 9 | display: flex; 10 | align-items: center; 11 | } 12 | 13 | &-action { 14 | margin-right: 10px; 15 | } 16 | 17 | &-message { 18 | mat-icon { 19 | margin-right: 10px; 20 | } 21 | span { 22 | overflow-y: auto; 23 | max-height: calc(100vh - 80px); 24 | font-size: 14.5px; 25 | line-height: 1.35em; 26 | word-break: break-word; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /projects/angular/components/ui-snackbar/src/ui-snackbar.intl.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { UiSnackbarIntl } from './ui-snackbar.intl'; 4 | 5 | describe('Service: UiSnackbarIntlService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({ 7 | providers: [UiSnackbarIntl], 8 | })); 9 | 10 | it('should create', () => { 11 | const service: UiSnackbarIntl = TestBed.inject(UiSnackbarIntl); 12 | expect(service).toBeTruthy(); 13 | expect(service.closeSnackbarShortcut).toEqual('Close the snackbar using the shortcut: Delete + x'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /projects/angular/components/ui-snackbar/src/ui-snackbar.intl.ts: -------------------------------------------------------------------------------- 1 | import { Subject } from 'rxjs'; 2 | 3 | import { 4 | Injectable, 5 | OnDestroy, 6 | } from '@angular/core'; 7 | 8 | @Injectable() 9 | export class UiSnackbarIntl implements OnDestroy { 10 | closeAriaLabel = 'Close'; 11 | closeSnackbarShortcut = 'Close the snackbar using the shortcut: Delete + x'; 12 | protected _destroyed$ = new Subject(); 13 | 14 | ngOnDestroy() { 15 | this._destroyed$.next(); 16 | this._destroyed$.complete(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /projects/angular/components/ui-snackbar/src/ui-snackbar.module.ts: -------------------------------------------------------------------------------- 1 | import { PortalModule } from '@angular/cdk/portal'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NgModule } from '@angular/core'; 4 | import { MatButtonModule } from '@angular/material/button'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { 7 | MatSnackBarModule, 8 | MAT_SNACK_BAR_DEFAULT_OPTIONS, 9 | } from '@angular/material/snack-bar'; 10 | import { KeyboardShortcutModule } from '@uipath/angular/directives/keyboard-shortcut'; 11 | import { UiPipeModule } from '@uipath/angular/pipes'; 12 | 13 | import { UiSnackBarComponent } from './ui-snackbar.component'; 14 | 15 | const DEFAULT_DURATION = 10000; 16 | const DEFAULT_HORIZONTAL = 'center'; 17 | const DEFAULT_VERTICAL = 'top'; 18 | 19 | @NgModule({ 20 | imports: [ 21 | MatSnackBarModule, 22 | MatIconModule, 23 | MatButtonModule, 24 | PortalModule, 25 | UiPipeModule, 26 | CommonModule, 27 | KeyboardShortcutModule, 28 | ], 29 | declarations: [UiSnackBarComponent], 30 | providers: [{ 31 | provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, 32 | useValue: { 33 | verticalPosition: DEFAULT_VERTICAL, 34 | horizontalPosition: DEFAULT_HORIZONTAL, 35 | duration: DEFAULT_DURATION, 36 | }, 37 | }], 38 | }) 39 | export class UiSnackBarModule { } 40 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/_ui-suggest.theme.scss: -------------------------------------------------------------------------------- 1 | @mixin ui-suggest-theme($theme) { 2 | $foreground: map-get($theme, "foreground"); 3 | $background: map-get($theme, "background"); 4 | $is-dark: map-get($theme, "is-dark"); 5 | $primary-palette: map-get($theme, "primary"); 6 | $warn-palette: map-get($theme, "warn"); 7 | 8 | $ui-focus-overlay: rgba(map-get($foreground, "base"), 0.12); 9 | $ui-hover-overlay: rgba(map-get($foreground, "base"), 0.04); 10 | 11 | $componentName: "ui-suggest"; 12 | 13 | .mat-toolbar #{$componentName} { 14 | .mat-mdc-input-element { 15 | color: map-get($foreground, "text"); 16 | } 17 | } 18 | 19 | .ui-suggest-dropdown-item-list-container { 20 | background-color: map-get($background, "dialog"); 21 | .mat-mdc-list { 22 | &-item { 23 | &.selected { 24 | background-color: map-get($background, "disabled-button"); 25 | } 26 | 27 | &.selected.active, 28 | &.active, 29 | &:hover, 30 | &.selected:hover { 31 | background-color: map-get($background, "hover"); 32 | } 33 | } 34 | } 35 | } 36 | 37 | #{$componentName} { 38 | .display { 39 | mat-icon { 40 | color: inherit; 41 | } 42 | 43 | mat-icon.clear:hover, 44 | mat-icon.clear:focus { 45 | color: map-get($warn-palette, "default"); 46 | } 47 | } 48 | } 49 | 50 | mat-form-field.mat-form-field-hide-placeholder #{$componentName} { 51 | .mat-chip-grid { 52 | .mat-mdc-input-element::placeholder { 53 | color: currentColor !important; 54 | -webkit-text-fill-color: currentColor !important; 55 | } 56 | } 57 | } 58 | 59 | #{$componentName}:not(.form-control) { 60 | .display { 61 | &:focus { 62 | background-color: $ui-focus-overlay; 63 | } 64 | 65 | &:hover { 66 | background-color: $ui-hover-overlay; 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './suggestValue'; 2 | export * from './suggestValueData'; 3 | export * from './suggestValues'; 4 | export * from './suggestDisplayPriority'; 5 | export * from './suggestDirection'; 6 | export * from './maxSelectionConfig'; 7 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/models/maxSelectionConfig.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Suggest max selection config type definition. 3 | * count: Max number of selected items. 4 | * itemTooltip: Tooltip to be displayed on each unselected item. 5 | * footerMessage: Message to be displayed in the dropdown footer. 6 | * 7 | * @export 8 | */ 9 | export type SuggestMaxSelectionConfig = { 10 | count: number; 11 | itemTooltip: string; 12 | footerMessage: string; 13 | }; 14 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/models/suggestDirection.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Dropdown direction type definition. 3 | * 4 | * default: 'down' 5 | * 6 | * @export 7 | */ 8 | export type SuggestDirection = 'up' | 'down'; 9 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/models/suggestDisplayPriority.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Dropdown display priority type definition. 3 | * 4 | * @export 5 | */ 6 | export type SuggestDisplayPriority = 'default' | 'selected'; 7 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/models/suggestValue.ts: -------------------------------------------------------------------------------- 1 | import { 2 | VirtualScrollItem, 3 | VirtualScrollItemStatus, 4 | } from '@uipath/angular/directives/ui-virtual-scroll-range-loader'; 5 | 6 | /** 7 | * UiSuggest item schema. 8 | * 9 | * @export 10 | */ 11 | export interface ISuggestValue extends VirtualScrollItem { 12 | /** 13 | * Unique identifier associated to the entry. 14 | * 15 | */ 16 | id: number | string; 17 | /** 18 | * Text associated to the entry. 19 | * 20 | */ 21 | text: string; 22 | /** 23 | * Marks the current item state 24 | * 25 | * @internal 26 | * @ignore 27 | */ 28 | loading?: VirtualScrollItemStatus; 29 | /** 30 | * Flag that marks custom items. 31 | * 32 | * @internal 33 | * @ignore 34 | */ 35 | isCustom?: boolean; 36 | /** 37 | * Flag that marks if item is expandable. 38 | * Will be ignored if ui-suggest doesn't have drillDown and searchable. 39 | * On selection will trigger a new searchSource call, value will NOT updated yet. 40 | * The string input will be applied as `${item.text}:` 41 | */ 42 | expandable?: boolean; 43 | /** 44 | * Flag that marks if item is disabled. 45 | */ 46 | disabled?: boolean; 47 | /** 48 | * Optional icon that will be displayed to the left of the item. 49 | * 50 | */ 51 | icon?: { 52 | iconOnly?: boolean; 53 | svgIcon?: string; 54 | matIcon?: string; 55 | }; 56 | 57 | /** 58 | * Tooltip associated to the entry. 59 | */ 60 | tooltip?: string; 61 | } 62 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/models/suggestValueData.ts: -------------------------------------------------------------------------------- 1 | import { ISuggestValue } from './suggestValue'; 2 | 3 | /** 4 | * UiSuggest item with data schema. 5 | * 6 | * @export 7 | */ 8 | export interface ISuggestValueData extends ISuggestValue { 9 | /** 10 | * Data associated to the entry item. 11 | * 12 | */ 13 | data?: T; 14 | } 15 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/models/suggestValues.ts: -------------------------------------------------------------------------------- 1 | import { ISuggestValueData } from './suggestValueData'; 2 | 3 | /** 4 | * Fetch value schema. 5 | * 6 | * @export 7 | */ 8 | export interface ISuggestValues { 9 | /** 10 | * The response data. 11 | * 12 | */ 13 | data?: ISuggestValueData[]; 14 | /** 15 | * The total available item count. (used to determine chunks for lazy-loading) 16 | * 17 | */ 18 | total?: number; 19 | } 20 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiSuggestComponent } from './ui-suggest.component'; 2 | export { UiSuggestModule } from './ui-suggest.module'; 3 | export { UiSuggestIntl } from './ui-suggest.intl'; 4 | export { 5 | ISuggestValue, 6 | ISuggestValueData, 7 | ISuggestValues, 8 | SuggestDirection, 9 | SuggestDisplayPriority, 10 | } from './models'; 11 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/test/index.ts: -------------------------------------------------------------------------------- 1 | export * from './suggestionItem'; 2 | export * from './ui-suggest-assert'; 3 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/test/suggestionItem.ts: -------------------------------------------------------------------------------- 1 | import * as faker from 'faker'; 2 | 3 | import { ISuggestValue } from '../models'; 4 | 5 | export const generateSuggestionItem = (label = ''): ISuggestValue => { 6 | const value = `${label}${faker.random.words(1)}`; 7 | 8 | return { 9 | id: value, 10 | text: value, 11 | expandable: faker.random.boolean(), 12 | }; 13 | }; 14 | 15 | export const generateSuggetionItemList = 16 | (count: number | 'random' = 5, label?: string): ISuggestValue[] => 17 | Array( 18 | count === 'random' ? 19 | faker.random.number({ 20 | min: 5, 21 | max: 50, 22 | }) : 23 | count, 24 | ) 25 | .fill(0) 26 | .map(() => generateSuggestionItem(label)); 27 | 28 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/test/ui-suggest-assert.ts: -------------------------------------------------------------------------------- 1 | import { DebugElement } from '@angular/core'; 2 | import { By } from '@angular/platform-browser'; 3 | 4 | import { UiSuggestComponent } from '../ui-suggest.component'; 5 | 6 | export class UiSuggestAssert { 7 | constructor( 8 | private _root: DebugElement, 9 | private _suggest: UiSuggestComponent, 10 | ) { } 11 | 12 | isOpen(): void { 13 | this._assertOpenState('open'); 14 | } 15 | 16 | isClosed(): void { 17 | this._assertOpenState('closed'); 18 | } 19 | 20 | isDisabled(): void { 21 | this._assertDisableState('disabled'); 22 | } 23 | 24 | isEnabled(): void { 25 | this._assertDisableState('enabled'); 26 | } 27 | 28 | private _assertOpenState(expected: 'open' | 'closed'): void { 29 | const expectedIsOpen = expected === 'open'; 30 | 31 | expect(this._suggest.isOpen).toBe(expectedIsOpen); 32 | 33 | if (!this._suggest.multiple) { 34 | const combo = this._root.query(By.css('[role=combobox]')).nativeElement; 35 | expect(combo).toHaveAttr('aria-expanded', expectedIsOpen.toString()); 36 | } 37 | 38 | const dropdownOverlay = document.querySelector('.cdk-overlay-container'); 39 | const itemList = dropdownOverlay?.querySelector('.ui-suggest-dropdown-item-list-container'); 40 | expect(!!itemList).toBe(expectedIsOpen); 41 | } 42 | 43 | private _assertDisableState(expected: 'enabled' | 'disabled'): void { 44 | const expectedIsDisabled = expected === 'disabled'; 45 | 46 | expect(this._suggest.disabled).toBe(expectedIsDisabled); 47 | 48 | const combo = this._root.query(By.css('[role=combobox]')).nativeElement; 49 | expect(combo).toHaveAttr('aria-disabled', expectedIsDisabled.toString()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/ui-suggest.animations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | animate, 3 | AnimationTriggerMetadata, 4 | group, 5 | state, 6 | style, 7 | transition, 8 | trigger, 9 | } from '@angular/animations'; 10 | 11 | /** 12 | * Open / Close animation definitions for UiSuggest. 13 | * 14 | * These animations should mainly be kept in sync with the mat-menu animations. 15 | * 16 | * Reference: https://github.com/angular/components/blob/master/src/material/menu/menu-animations.ts 17 | */ 18 | export const UI_SUGGEST_ANIMATIONS: { 19 | readonly transformMenuList: AnimationTriggerMetadata; 20 | } = { 21 | transformMenuList: trigger('displayState', [ 22 | state( 23 | 'void', 24 | style({ 25 | opacity: 0, 26 | transform: 'scale(0.8)', 27 | }), 28 | ), 29 | transition( 30 | 'void => open', 31 | group([ 32 | animate('100ms linear', style({ 33 | opacity: 1, 34 | })), 35 | animate('120ms cubic-bezier(0, 0, 0.2, 1)', style({ 36 | transform: 'scale(1)', 37 | })), 38 | ]), 39 | ), 40 | ]), 41 | }; 42 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/ui-suggest.module.ts: -------------------------------------------------------------------------------- 1 | import { ScrollingModule } from '@angular/cdk/scrolling'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NgModule } from '@angular/core'; 4 | import { ReactiveFormsModule } from '@angular/forms'; 5 | import { MatCheckboxModule } from '@angular/material/checkbox'; 6 | import { MatChipsModule } from '@angular/material/chips'; 7 | import { MatRippleModule } from '@angular/material/core'; 8 | import { MatIconModule } from '@angular/material/icon'; 9 | import { MatInputModule } from '@angular/material/input'; 10 | import { MatListModule } from '@angular/material/list'; 11 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 12 | import { MatTooltipModule } from '@angular/material/tooltip'; 13 | import { UiAutoAccessibleLabelModule } from '@uipath/angular/a11y'; 14 | import { UiAutofocusModule } from '@uipath/angular/directives/ui-autofocus'; 15 | import { UiClickOutsideModule } from '@uipath/angular/directives/ui-click-outside'; 16 | import { UiNgLetModule } from '@uipath/angular/directives/ui-ng-let'; 17 | import { UiVirtualScrollRangeLoaderModule } from '@uipath/angular/directives/ui-virtual-scroll-range-loader'; 18 | import { OverlayModule } from '@angular/cdk/overlay'; 19 | 20 | import { UiSuggestComponent } from './ui-suggest.component'; 21 | 22 | @NgModule({ 23 | imports: [ 24 | CommonModule, 25 | MatIconModule, 26 | MatInputModule, 27 | MatListModule, 28 | MatTooltipModule, 29 | MatCheckboxModule, 30 | ScrollingModule, 31 | MatRippleModule, 32 | MatProgressSpinnerModule, 33 | ReactiveFormsModule, 34 | UiNgLetModule, 35 | UiAutofocusModule, 36 | UiClickOutsideModule, 37 | UiVirtualScrollRangeLoaderModule, 38 | MatChipsModule, 39 | UiAutoAccessibleLabelModule, 40 | OverlayModule, 41 | ], 42 | declarations: [ 43 | UiSuggestComponent, 44 | ], 45 | exports: [ 46 | UiSuggestComponent, 47 | ], 48 | }) 49 | export class UiSuggestModule { } 50 | -------------------------------------------------------------------------------- /projects/angular/components/ui-suggest/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './items.utils'; 2 | -------------------------------------------------------------------------------- /projects/angular/components/ui-tree-select/ng-package.json: -------------------------------------------------------------------------------- 1 | { } 2 | -------------------------------------------------------------------------------- /projects/angular/components/ui-tree-select/src/models/tree.models.ts: -------------------------------------------------------------------------------- 1 | interface IBaseNode { 2 | key: string; 3 | name: string; 4 | data?: T; 5 | } 6 | 7 | export interface ITreeNode extends IBaseNode { 8 | children?: ITreeNode[]; 9 | } 10 | 11 | export interface IFlatNodeObject extends IBaseNode { 12 | hasChildren: boolean; 13 | level: number; 14 | } 15 | -------------------------------------------------------------------------------- /projects/angular/components/ui-tree-select/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiTreeSelectComponent } from './ui-tree-select.component'; 2 | export { 3 | IFlatNodeObject, 4 | ITreeNode, 5 | } from './models/tree.models'; 6 | export { UiTreeItemComponent } from './ui-tree-item/ui-tree-item.component'; 7 | export { TREE_ACTION_DEFAULTS } from './utils/tree.utils'; 8 | -------------------------------------------------------------------------------- /projects/angular/components/ui-tree-select/src/ui-tree-item/ui-tree-item.component.html: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /projects/angular/components/ui-tree-select/src/ui-tree-item/ui-tree-item.component.ts: -------------------------------------------------------------------------------- 1 | import { FocusableOption } from '@angular/cdk/a11y'; 2 | import { CommonModule } from '@angular/common'; 3 | import { 4 | ChangeDetectionStrategy, 5 | Component, 6 | ElementRef, 7 | EventEmitter, 8 | forwardRef, 9 | Inject, 10 | Input, 11 | Output, 12 | ViewEncapsulation, 13 | } from '@angular/core'; 14 | import { MatListModule } from '@angular/material/list'; 15 | 16 | import { IFlatNodeObject } from '../models/tree.models'; 17 | 18 | const LIST_ITEM_SELECTOR = '.mat-mdc-list-item'; 19 | 20 | @Component({ 21 | standalone: true, 22 | imports: [ 23 | CommonModule, 24 | MatListModule, 25 | ], 26 | changeDetection: ChangeDetectionStrategy.OnPush, 27 | selector: 'ui-tree-item[node]', 28 | templateUrl: './ui-tree-item.component.html', 29 | encapsulation: ViewEncapsulation.None, 30 | }) 31 | export class UiTreeItemComponent implements FocusableOption { 32 | @Input() 33 | node!: IFlatNodeObject; 34 | 35 | @Input() 36 | isSelected = false; 37 | 38 | @Input() 39 | isExpanded = false; 40 | 41 | @Output() 42 | expanded = new EventEmitter(); 43 | 44 | @Output() 45 | selected = new EventEmitter(); 46 | 47 | constructor( 48 | @Inject(forwardRef(() => ElementRef)) 49 | private readonly _el: ElementRef, 50 | ) { } 51 | 52 | click() { 53 | this.selected.emit(); 54 | } 55 | 56 | dblclick() { 57 | this.selected.emit(); 58 | this.expanded.emit(); 59 | } 60 | 61 | focus() { 62 | this._el.nativeElement.querySelector(LIST_ITEM_SELECTOR).focus(); 63 | } 64 | 65 | getBoundingClientRect() { 66 | return this._el.nativeElement.querySelector(LIST_ITEM_SELECTOR).getBoundingClientRect(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /projects/angular/components/ui-tree-select/src/ui-tree-select.component.html: -------------------------------------------------------------------------------- 1 |
3 | 5 | 6 | 7 | 9 | 14 | 15 | 16 | 17 | 18 | 25 | 33 | 34 | 35 | 36 | 37 | 38 |
39 | -------------------------------------------------------------------------------- /projects/angular/components/ui-tree-select/src/ui-tree-select.component.scss: -------------------------------------------------------------------------------- 1 | .mat-mdc-list.mdc-list.tree-container { 2 | padding: 0; 3 | height: 100%; 4 | .virtual-scroll-container { 5 | height: 100%; 6 | } 7 | } 8 | 9 | .tree-item-container { 10 | height: 100%; 11 | 12 | .mdc-list-item__primary-text { 13 | display: flex; 14 | flex-direction: row; 15 | align-items: center; 16 | } 17 | } 18 | 19 | @mixin perLevelPadding($level, $padding) { 20 | .node-level-#{$level} { 21 | &:not(.node-type-leaf) { 22 | padding-left: calc($padding * $level); 23 | } 24 | 25 | &.node-type-leaf { 26 | padding-left: calc($padding + max($padding, calc($padding * ($level + 1)))); 27 | } 28 | } 29 | } 30 | 31 | .tree-container { 32 | @for $i from 0 through 7 { 33 | @include perLevelPadding($i, var(--ui-tree-select-item-padding)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /projects/angular/components/ui-tree-select/src/utils/tree.utils.ts: -------------------------------------------------------------------------------- 1 | import { FlatTreeControl } from '@angular/cdk/tree'; 2 | import { MatTreeFlattener } from '@angular/material/tree'; 3 | import { 4 | IFlatNodeObject, ITreeNode, 5 | } from '../models/tree.models'; 6 | 7 | export class TreeUtils { 8 | static treeFlattener = new MatTreeFlattener( 9 | this.nodeTransformer, 10 | this.getNodeLevel, 11 | this.getIsNodeExpandable, 12 | this.getNodeChildren, 13 | ); 14 | 15 | static nodeTransformer(node: ITreeNode, level: number) { 16 | return { 17 | key: node.key, 18 | name: node.name, 19 | data: node.data, 20 | level, 21 | hasChildren: !!node.children, 22 | }; 23 | } 24 | 25 | static nodeBackTransformer(node: IFlatNodeObject): ITreeNode { 26 | return { 27 | key: node.key, 28 | name: node.name, 29 | data: node.data, 30 | }; 31 | } 32 | 33 | static getNodeLevel({ level }: IFlatNodeObject) { 34 | return level; 35 | } 36 | 37 | static getIsNodeExpandable({ hasChildren }: IFlatNodeObject) { 38 | return hasChildren; 39 | } 40 | 41 | static getNodeChildren({ children }: ITreeNode) { 42 | return children; 43 | } 44 | 45 | static getNodeByKey(key: string, level: number, treeControl: FlatTreeControl) { 46 | const node = treeControl.dataNodes.find(n => n.key === key); 47 | if (!node || node.level !== level) { 48 | throw new Error(`Node with key ${key} not found on level ${level} in the data array`); 49 | } 50 | return node; 51 | } 52 | 53 | static getParentNode(activeIndex: number, level: number, dataNodes: IFlatNodeObject[]) { 54 | for (let i = activeIndex; i >= 0; i--) { 55 | if (dataNodes[i].level < level && dataNodes[i].hasChildren) { 56 | return dataNodes[i]; 57 | } 58 | } 59 | return null; 60 | } 61 | 62 | } 63 | 64 | type TreeActionOptions = { 65 | emitEvent: boolean; 66 | }; 67 | 68 | export const TREE_ACTION_DEFAULTS: TreeActionOptions = { 69 | emitEvent: true, 70 | }; 71 | -------------------------------------------------------------------------------- /projects/angular/directives/internal/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/internal/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiFormatDirective } from './ui-format'; 2 | export { UiLoaderButtonDirective } from './ui-loader-button'; 3 | -------------------------------------------------------------------------------- /projects/angular/directives/internal/src/ui-format.ts: -------------------------------------------------------------------------------- 1 | import { Subject } from 'rxjs'; 2 | 3 | import { 4 | Directive, 5 | ElementRef, 6 | OnChanges, 7 | OnDestroy, 8 | Renderer2, 9 | } from '@angular/core'; 10 | 11 | @Directive() 12 | export abstract class UiFormatDirective implements OnChanges, OnDestroy { 13 | protected abstract _text?: HTMLElement; 14 | protected _redraw$ = new Subject(); 15 | protected _destroyed$ = new Subject(); 16 | 17 | constructor( 18 | protected _renderer: Renderer2, 19 | protected _elementRef: ElementRef, 20 | ) { } 21 | 22 | ngOnChanges() { 23 | this._redraw$.next(); 24 | } 25 | 26 | ngOnDestroy() { 27 | if (this._text) { 28 | this._renderer.removeChild(this._elementRef.nativeElement, this._text); 29 | } 30 | this._destroyed$.next(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /projects/angular/directives/keyboard-shortcut/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/keyboard-shortcut/src/keyboard-shortcut.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directive, 3 | EventEmitter, 4 | HostListener, 5 | Input, 6 | Output, 7 | } from '@angular/core'; 8 | 9 | /** 10 | * Directive that listens for specified key combination 11 | * then emits an event 12 | * 13 | * @input: an array of key combinations, where a key combination is an array of strings 14 | */ 15 | 16 | @Directive({ 17 | selector: '[uiKeyboardShortcut][shortcutKeys]', 18 | }) 19 | export class KeyboardShortcutDirective { 20 | @Input() 21 | shortcutKeys: string[][] = []; 22 | 23 | @Output() 24 | shortcutPressed = new EventEmitter(); 25 | 26 | private _pressedKeys: Record = {}; 27 | 28 | @HostListener('document:keydown', ['$event']) 29 | searchShortcutKeydownHandler(event: KeyboardEvent) { 30 | if (!this._keyInShortcut(event)) { return; } 31 | 32 | this._pressedKeys[event.key] = true; 33 | if (this.shortcutKeys.find(keyCombination => keyCombination.every(key => this._pressedKeys[key]))) { 34 | this.shortcutPressed.emit(); 35 | this._pressedKeys = {}; 36 | } 37 | } 38 | 39 | @HostListener('document:keyup', ['$event']) 40 | searchShortcutKeyupHandler(event: KeyboardEvent) { 41 | if (!this._keyInShortcut(event)) { return; } 42 | this._pressedKeys[event.key] = false; 43 | } 44 | 45 | private _keyInShortcut({ key }: KeyboardEvent) { 46 | return this.shortcutKeys.find(keyCombination => !!keyCombination.find(k => k === key)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /projects/angular/directives/keyboard-shortcut/src/keyboard-shortcut.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { KeyboardShortcutDirective } from './keyboard-shortcut.directive'; 4 | 5 | @NgModule({ 6 | declarations: [KeyboardShortcutDirective], 7 | exports: [KeyboardShortcutDirective], 8 | }) 9 | export class KeyboardShortcutModule { } 10 | -------------------------------------------------------------------------------- /projects/angular/directives/keyboard-shortcut/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { KeyboardShortcutDirective } from './keyboard-shortcut.directive'; 2 | export { KeyboardShortcutModule } from './keyboard-shortcut.module'; 3 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-autofocus/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/ui-autofocus/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiAutofocusDirective } from './ui-autofocus.directive'; 2 | export { UiAutofocusModule } from './ui-autofocus.module'; 3 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-autofocus/src/ui-autofocus.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { UiAutofocusDirective } from './ui-autofocus.directive'; 4 | 5 | @NgModule({ 6 | declarations: [UiAutofocusDirective], 7 | exports: [UiAutofocusDirective], 8 | }) 9 | export class UiAutofocusModule { } 10 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-click-outside/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/ui-click-outside/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiClickOutsideDirective } from './ui-click-outside.directive'; 2 | export { UiClickOutsideModule } from './ui-click-outside.module'; 3 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-click-outside/src/ui-click-outside.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { UiClickOutsideDirective } from './ui-click-outside.directive'; 4 | 5 | @NgModule({ 6 | declarations: [UiClickOutsideDirective], 7 | exports: [UiClickOutsideDirective], 8 | }) 9 | export class UiClickOutsideModule { } 10 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-clipboard/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/ui-clipboard/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiClipboardDirective } from './ui-clipboard.directive'; 2 | export { UiClipboardModule } from './ui-clipboard.module'; 3 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-clipboard/src/ui-clipboard.directive.ts: -------------------------------------------------------------------------------- 1 | import Clipboard from 'clipboard'; 2 | 3 | import { 4 | Directive, 5 | ElementRef, 6 | EventEmitter, 7 | Input, 8 | OnDestroy, 9 | OnInit, 10 | Output, 11 | } from '@angular/core'; 12 | 13 | /** 14 | * A directive that copies the decorated element content into the user clipboard. 15 | * 16 | * Depends On: [clipboard](https://www.npmjs.com/package/clipboard) 17 | * 18 | * @export 19 | */ 20 | @Directive({ 21 | selector: '[uiClipboard]', 22 | }) 23 | export class UiClipboardDirective implements OnInit, OnDestroy { 24 | /** 25 | * The element reference what will serve as a `copy` trigger. 26 | * 27 | */ 28 | @Input() 29 | uiClipboard?: Element; 30 | 31 | /** 32 | * Event that emits when the content is copied succesfully to the clipboard. 33 | * 34 | */ 35 | @Output() 36 | clipboardSuccess: EventEmitter = new EventEmitter(); 37 | 38 | /** 39 | * Event that emits when the content could not be copied to the clipboard. 40 | * 41 | */ 42 | @Output() 43 | clipboardError: EventEmitter = new EventEmitter(); 44 | 45 | private _clipboard!: Clipboard; 46 | 47 | /** 48 | * @ignore 49 | */ 50 | constructor(private _eltRef: ElementRef) { } 51 | 52 | /** 53 | * @ignore 54 | */ 55 | ngOnInit() { 56 | if (!this.uiClipboard) { 57 | throw new Error('Missing uiClipboard reference'); 58 | } 59 | 60 | this._clipboard = new Clipboard(this._eltRef.nativeElement, { 61 | target: () => this.uiClipboard!, 62 | }); 63 | 64 | this._clipboard.on('success', (e) => { 65 | this.clipboardSuccess.emit(e); 66 | }); 67 | 68 | this._clipboard.on('error', (e) => { 69 | this.clipboardError.emit(e); 70 | }); 71 | } 72 | 73 | /** 74 | * @ignore 75 | */ 76 | ngOnDestroy() { 77 | if (this._clipboard) { 78 | this._clipboard.destroy(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-clipboard/src/ui-clipboard.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { UiClipboardDirective } from './ui-clipboard.directive'; 4 | 5 | @NgModule({ 6 | declarations: [UiClipboardDirective], 7 | exports: [UiClipboardDirective], 8 | }) 9 | export class UiClipboardModule { } 10 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-content-loader/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/ui-content-loader/src/internal/ui-content-spinner.component.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-content-loader/src/internal/ui-content-spinner.component.scss: -------------------------------------------------------------------------------- 1 | ui-content-spinner { 2 | width: 100%; 3 | height: 100%; 4 | display: block; 5 | position: relative; 6 | 7 | .mat-mdc-progress-spinner { 8 | position: absolute; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-content-loader/src/internal/ui-content-spinner.component.ts: -------------------------------------------------------------------------------- 1 | import { BehaviorSubject } from 'rxjs'; 2 | import { map } from 'rxjs/operators'; 3 | 4 | import { 5 | ChangeDetectionStrategy, 6 | Component, 7 | HostBinding, 8 | OnDestroy, 9 | ViewEncapsulation, 10 | } from '@angular/core'; 11 | import { MatProgressSpinner } from '@angular/material/progress-spinner'; 12 | 13 | @Component({ 14 | selector: 'ui-content-spinner', 15 | templateUrl: './ui-content-spinner.component.html', 16 | styleUrls: ['./ui-content-spinner.component.scss'], 17 | changeDetection: ChangeDetectionStrategy.OnPush, 18 | encapsulation: ViewEncapsulation.None, 19 | }) 20 | export class UiContentSpinnerComponent implements OnDestroy { 21 | @HostBinding('style.min-height.px') 22 | get minHeight() { 23 | return this.diameter$.value * 2.5; 24 | } 25 | 26 | mode$ = new BehaviorSubject('indeterminate'); 27 | value$ = new BehaviorSubject(0); 28 | color$ = new BehaviorSubject('primary'); 29 | diameter$ = new BehaviorSubject(100); 30 | style$ = this.diameter$ 31 | .pipe( 32 | map(diameter => { 33 | const displace = `calc(50% - ${diameter / 2}px)`; 34 | return { 35 | top: displace, 36 | left: displace, 37 | }; 38 | }), 39 | ); 40 | 41 | ngOnDestroy() { 42 | this.diameter$.complete(); 43 | this.mode$.complete(); 44 | this.value$.complete(); 45 | this.color$.complete(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-content-loader/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiContentLoaderDirective } from './ui-content-loader.directive'; 2 | export { UiContentLoaderModule } from './ui-content-loader.module'; 3 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-content-loader/src/ui-content-loader.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 4 | 5 | import { UiContentSpinnerComponent } from './internal/ui-content-spinner.component'; 6 | import { UiContentLoaderDirective } from './ui-content-loader.directive'; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | MatProgressSpinnerModule, 12 | ], 13 | declarations: [ 14 | UiContentLoaderDirective, 15 | UiContentSpinnerComponent, 16 | ], 17 | exports: [ 18 | UiContentLoaderDirective, 19 | ], 20 | }) 21 | export class UiContentLoaderModule { } 22 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-dateformat/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/ui-dateformat/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DisplayType, 3 | IDateFormatOptions, 4 | UiDateFormatDirective, 5 | UI_DATEFORMAT_OPTIONS, 6 | } from './ui-dateformat.directive'; 7 | export { UiDateFormatModule } from './ui-dateformat.module'; 8 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-dateformat/src/ui-dateformat.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { UiDateFormatDirective } from './ui-dateformat.directive'; 4 | 5 | @NgModule({ 6 | declarations: [UiDateFormatDirective], 7 | exports: [UiDateFormatDirective], 8 | }) 9 | export class UiDateFormatModule { } 10 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-drag-and-drop-file/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/ui-drag-and-drop-file/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiDragAndDropFileDirective } from './ui-drag-and-drop-file.directive'; 2 | export { UiDragAndDropModule } from './ui-drag-and-drop-file.module'; 3 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-drag-and-drop-file/src/ui-drag-and-drop-file.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { UiDragAndDropFileDirective } from './ui-drag-and-drop-file.directive'; 4 | 5 | @NgModule({ 6 | declarations: [UiDragAndDropFileDirective], 7 | exports: [UiDragAndDropFileDirective], 8 | }) 9 | export class UiDragAndDropModule { } 10 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-file-drop-zone/ng-package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-file-drop-zone/src/file-drop-zone.utils.ts: -------------------------------------------------------------------------------- 1 | import { sort } from '@uipath/angular/utilities'; 2 | 3 | export const sortAndFilter = (files: File[], sortBy: string | undefined, accept: string[]) => { 4 | files = files.filter(f => isAcceptedExtension(f.name, accept)); 5 | return sortBy ? sort(files, sortBy, false) : files; 6 | }; 7 | 8 | const isAcceptedExtension = (fileName: string, accept: string[]) => { 9 | if (!accept.length) { 10 | return true; 11 | } 12 | 13 | const fileExtension = getFileExtension(fileName); 14 | return accept.includes(fileExtension); 15 | }; 16 | 17 | const getFileExtension = (fileName: string) => { 18 | const fileNameParts = fileName.split('.'); 19 | const extension = fileNameParts.length > 1 ? fileNameParts.slice(-1)[0] : ''; 20 | return `.${extension.toLowerCase()}`; 21 | }; 22 | 23 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-file-drop-zone/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { sortAndFilter } from './file-drop-zone.utils'; 2 | export { 3 | FileReaderService, 4 | FileReaderError, 5 | } from './file-reader.service'; 6 | export { UiFileDropZoneDirective } from './ui-file-drop-zone.directive'; 7 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-matformfield-required/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/ui-matformfield-required/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiMatFormFieldRequiredDirective } from './ui-matformfield-required.directive'; 2 | export { UiMatFormFieldRequiredModule } from './ui-matformfield-required.module'; 3 | export { UiMatFormFieldRequiredIntl } from './ui-matformfield-required.directive.intl'; 4 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-matformfield-required/src/ui-matformfield-required.directive.intl.ts: -------------------------------------------------------------------------------- 1 | import { Subject } from 'rxjs'; 2 | 3 | import { 4 | Injectable, 5 | OnDestroy, 6 | } from '@angular/core'; 7 | 8 | @Injectable() 9 | export class UiMatFormFieldRequiredIntl implements OnDestroy { 10 | /** 11 | * Notify if changes have occured that require that the labels be updated. 12 | * 13 | */ 14 | // eslint-disable-next-line rxjs/finnish 15 | changes = new Subject(); 16 | 17 | tooltipMessage = 'This field is required.'; 18 | 19 | ngOnDestroy() { 20 | this.changes.complete(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-matformfield-required/src/ui-matformfield-required.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { UiMatFormFieldRequiredDirective } from './ui-matformfield-required.directive'; 4 | 5 | @NgModule({ 6 | declarations: [UiMatFormFieldRequiredDirective], 7 | exports: [UiMatFormFieldRequiredDirective], 8 | }) 9 | export class UiMatFormFieldRequiredModule { 10 | } 11 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-ng-let/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/ui-ng-let/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiNgLetDirective } from './ui-ng-let.directive'; 2 | export { UiNgLetModule } from './ui-ng-let.module'; 3 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-ng-let/src/ui-ng-let.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directive, 3 | Input, 4 | TemplateRef, 5 | ViewContainerRef, 6 | } from '@angular/core'; 7 | 8 | /** 9 | * @ignore 10 | */ 11 | class NgLetContext { 12 | $implicit: T = null!; 13 | ngLet: T = null!; 14 | } 15 | 16 | /** 17 | * A directive that allows declaration of streams inside the `template`. 18 | * Similar to `*ngIf="source$ | async as source"`. 19 | * `NgLet` works the same way, the difference being that the content is rendered, 20 | * even if the source has not yet been initialized. 21 | * 22 | * @export 23 | */ 24 | @Directive({ 25 | // eslint-disable-next-line @angular-eslint/directive-selector 26 | selector: '[ngLet]', 27 | }) 28 | export class UiNgLetDirective { 29 | private _context = new NgLetContext(); 30 | 31 | /** 32 | * The context bound to the decorated area. 33 | * 34 | */ 35 | @Input() 36 | set ngLet(value: T) { 37 | this._context.$implicit = this._context.ngLet = value; 38 | } 39 | 40 | /** 41 | * @ignore 42 | */ 43 | constructor( 44 | private _vcr: ViewContainerRef, 45 | private _templateRef: TemplateRef>, 46 | ) { 47 | this._vcr.createEmbeddedView( 48 | this._templateRef, 49 | this._context, 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-ng-let/src/ui-ng-let.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { UiNgLetDirective } from './ui-ng-let.directive'; 4 | 5 | @NgModule({ 6 | declarations: [UiNgLetDirective], 7 | exports: [UiNgLetDirective], 8 | }) 9 | export class UiNgLetModule { } 10 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-progress-button/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/ui-progress-button/src/internal/ui-button-progress-bar.component.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-progress-button/src/internal/ui-button-progress-bar.component.scss: -------------------------------------------------------------------------------- 1 | ui-button-progress-bar { 2 | display: block; 3 | width: 0; 4 | padding: 0; 5 | margin: 0; 6 | 7 | .mat-mdc-progress-bar { 8 | position: absolute; 9 | top: 0; 10 | left: 0; 11 | } 12 | } 13 | 14 | .ui-button-progress { 15 | overflow: hidden; 16 | } 17 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-progress-button/src/internal/ui-button-progress-bar.component.ts: -------------------------------------------------------------------------------- 1 | import { BehaviorSubject } from 'rxjs'; 2 | 3 | import { 4 | ChangeDetectionStrategy, 5 | Component, 6 | OnDestroy, 7 | ViewEncapsulation, 8 | } from '@angular/core'; 9 | import { MatProgressBar } from '@angular/material/progress-bar'; 10 | 11 | @Component({ 12 | selector: 'ui-button-progress-bar', 13 | templateUrl: './ui-button-progress-bar.component.html', 14 | styleUrls: ['./ui-button-progress-bar.component.scss'], 15 | changeDetection: ChangeDetectionStrategy.OnPush, 16 | encapsulation: ViewEncapsulation.None, 17 | }) 18 | export class UiButtonProgressBarComponent implements OnDestroy { 19 | loading$ = new BehaviorSubject(false); 20 | 21 | mode$ = new BehaviorSubject('indeterminate'); 22 | value$ = new BehaviorSubject(0); 23 | bufferValue$ = new BehaviorSubject(0); 24 | color$ = new BehaviorSubject('primary'); 25 | 26 | ngOnDestroy() { 27 | this.loading$.complete(); 28 | this.mode$.complete(); 29 | this.value$.complete(); 30 | this.bufferValue$.complete(); 31 | this.color$.complete(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-progress-button/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiProgressButtonDirective } from './ui-progress-button.directive'; 2 | export { UiProgressButtonModule } from './ui-progress-button.module'; 3 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-progress-button/src/ui-progress-button.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { MatProgressBarModule } from '@angular/material/progress-bar'; 4 | 5 | import { UiButtonProgressBarComponent } from './internal/ui-button-progress-bar.component'; 6 | import { UiProgressButtonDirective } from './ui-progress-button.directive'; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | MatProgressBarModule, 12 | ], 13 | declarations: [ 14 | UiButtonProgressBarComponent, 15 | UiProgressButtonDirective, 16 | ], 17 | exports: [ 18 | UiProgressButtonDirective, 19 | ], 20 | }) 21 | export class UiProgressButtonModule { } 22 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-scroll-into-view/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/ui-scroll-into-view/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiScrollIntoViewDirective } from './ui-scroll-into-view.directive'; 2 | export { UiScrollIntoViewModule } from './ui-scroll-into-view.module'; 3 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-scroll-into-view/src/ui-scroll-into-view.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | ViewChild, 4 | } from '@angular/core'; 5 | import { 6 | ComponentFixture, 7 | fakeAsync, 8 | TestBed, 9 | tick, 10 | } from '@angular/core/testing'; 11 | 12 | import { UiScrollIntoViewDirective } from './ui-scroll-into-view.directive'; 13 | 14 | @Component({ 15 | template: `
`, 16 | }) 17 | class TestScrollIntoViewComponent { 18 | @ViewChild(UiScrollIntoViewDirective, { 19 | static: true, 20 | }) 21 | directive!: UiScrollIntoViewDirective; 22 | 23 | editing = false; 24 | } 25 | 26 | describe('Directive: ScrollIntoView', () => { 27 | let component: TestScrollIntoViewComponent; 28 | let fixture: ComponentFixture; 29 | let scrollIfNeededSpy: jasmine.Spy; 30 | 31 | beforeEach(() => { 32 | TestBed.configureTestingModule({ 33 | declarations: [ 34 | TestScrollIntoViewComponent, 35 | UiScrollIntoViewDirective, 36 | ], 37 | }); 38 | fixture = TestBed.createComponent(TestScrollIntoViewComponent); 39 | component = fixture.componentInstance; 40 | fixture.detectChanges(); 41 | scrollIfNeededSpy = spyOn(component.directive, 'scrollIntoViewIfNeeded'); 42 | }); 43 | 44 | afterEach(() => { 45 | fixture.destroy(); 46 | }); 47 | 48 | it('should not call scrollIntoView() when flag is false', () => { 49 | fixture.detectChanges(); 50 | 51 | expect(scrollIfNeededSpy).not.toHaveBeenCalled(); 52 | }); 53 | 54 | it('should call scrollIntoView() when flag is set to true', fakeAsync(() => { 55 | component.editing = true; 56 | fixture.detectChanges(); 57 | tick(); 58 | 59 | expect(scrollIfNeededSpy).toHaveBeenCalled(); 60 | })); 61 | }); 62 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-scroll-into-view/src/ui-scroll-into-view.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { UiScrollIntoViewDirective } from './ui-scroll-into-view.directive'; 4 | 5 | @NgModule({ 6 | declarations: [UiScrollIntoViewDirective], 7 | exports: [UiScrollIntoViewDirective], 8 | }) 9 | export class UiScrollIntoViewModule { } 10 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-secondformat/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/ui-secondformat/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { 2 | ISecondFormatOptions, 3 | UiSecondFormatDirective, 4 | UI_SECONDFORMAT_OPTIONS, 5 | } from './ui-secondformat.directive'; 6 | export { UiSecondFormatModule } from './ui-secondformat.module'; 7 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-secondformat/src/ui-secondformat.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { MatTooltipModule } from '@angular/material/tooltip'; 4 | 5 | import { UiSecondFormatDirective } from './ui-secondformat.directive'; 6 | 7 | @NgModule({ 8 | imports: [CommonModule, MatTooltipModule], 9 | declarations: [UiSecondFormatDirective], 10 | exports: [UiSecondFormatDirective], 11 | }) 12 | export class UiSecondFormatModule { } 13 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-spinner-button/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/ui-spinner-button/src/internal/ui-button-progress-spinner.component.html: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-spinner-button/src/internal/ui-button-progress-spinner.component.scss: -------------------------------------------------------------------------------- 1 | $spinner-diameter: 22px; 2 | 3 | /* https://github.com/angular/material2/blob/master/src/lib/button/_button-base.scss */ 4 | $ui-button-progress-default-transition: 200ms cubic-bezier(0.35, 0, 0.25, 1); 5 | $ui-button-progress-opacity-transition: opacity $ui-button-progress-default-transition; 6 | 7 | ui-button-progress-spinner { 8 | .mat-mdc-progress-spinner { 9 | position: absolute; 10 | top: 0; 11 | left: 0; 12 | 13 | &.ui-mat-progress-spinner-fab[style] { 14 | width: 100% !important; 15 | height: 100% !important; 16 | 17 | svg[style] { 18 | width: 100% !important; 19 | height: 100% !important; 20 | } 21 | } 22 | 23 | &:not(.ui-mat-progress-spinner-fab) { 24 | top: calc(50% - #{$spinner-diameter / 2}); 25 | left: calc(50% - #{$spinner-diameter / 2}); 26 | } 27 | } 28 | } 29 | 30 | .ui-spinner-button { 31 | mat-icon, .mdc-button__label { 32 | transition: $ui-button-progress-opacity-transition; 33 | } 34 | } 35 | 36 | .ui-spinner-button-loading { 37 | mat-icon, .mdc-button__label { 38 | opacity: 0; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-spinner-button/src/internal/ui-button-progress-spinner.component.ts: -------------------------------------------------------------------------------- 1 | import { BehaviorSubject } from 'rxjs'; 2 | 3 | import { 4 | ChangeDetectionStrategy, 5 | Component, 6 | OnDestroy, 7 | ViewEncapsulation, 8 | } from '@angular/core'; 9 | import { MatProgressSpinner } from '@angular/material/progress-spinner'; 10 | 11 | @Component({ 12 | selector: 'ui-button-progress-spinner', 13 | templateUrl: './ui-button-progress-spinner.component.html', 14 | styleUrls: ['./ui-button-progress-spinner.component.scss'], 15 | changeDetection: ChangeDetectionStrategy.OnPush, 16 | encapsulation: ViewEncapsulation.None, 17 | }) 18 | export class UiButtonProgressSpinnerComponent implements OnDestroy { 19 | isRound$ = new BehaviorSubject(false); 20 | loading$ = new BehaviorSubject(false); 21 | 22 | mode$ = new BehaviorSubject('indeterminate'); 23 | value$ = new BehaviorSubject(0); 24 | color$ = new BehaviorSubject('primary'); 25 | 26 | ngOnDestroy() { 27 | this.isRound$.complete(); 28 | this.loading$.complete(); 29 | this.mode$.complete(); 30 | this.value$.complete(); 31 | this.color$.complete(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-spinner-button/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiSpinnerButtonDirective } from './ui-spinner-button.directive'; 2 | export { UiSpinnerButtonModule } from './ui-spinner-button.module'; 3 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-spinner-button/src/ui-spinner-button.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 4 | 5 | import { UiButtonProgressSpinnerComponent } from './internal/ui-button-progress-spinner.component'; 6 | import { UiSpinnerButtonDirective } from './ui-spinner-button.directive'; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | MatProgressSpinnerModule, 12 | ], 13 | declarations: [ 14 | UiButtonProgressSpinnerComponent, 15 | UiSpinnerButtonDirective, 16 | ], 17 | exports: [ 18 | UiSpinnerButtonDirective, 19 | ], 20 | }) 21 | export class UiSpinnerButtonModule { } 22 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-virtual-scroll-range-loader/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/ui-virtual-scroll-range-loader/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { 2 | UiVirtualScrollRangeLoaderDirective, 3 | VirtualScrollItem, 4 | VirtualScrollItemStatus, 5 | } from './ui-virtual-scroll-range-loader.directive'; 6 | export { UiVirtualScrollRangeLoaderModule } from './ui-virtual-scroll-range-loader.module'; 7 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-virtual-scroll-range-loader/src/ui-virtual-scroll-range-loader.module.ts: -------------------------------------------------------------------------------- 1 | import { ScrollingModule } from '@angular/cdk/scrolling'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NgModule } from '@angular/core'; 4 | 5 | import { UiVirtualScrollRangeLoaderDirective } from './ui-virtual-scroll-range-loader.directive'; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | ScrollingModule, 11 | ], 12 | declarations: [UiVirtualScrollRangeLoaderDirective], 13 | exports: [UiVirtualScrollRangeLoaderDirective], 14 | }) 15 | export class UiVirtualScrollRangeLoaderModule { } 16 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-virtual-scroll-viewport-resize/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/directives/ui-virtual-scroll-viewport-resize/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiVirtualScrollViewportResizeDirective } from './ui-virtual-scroll-viewport-resize.directive'; 2 | export { UiVirtualScrollViewportResizeModule } from './ui-virtual-scroll-viewport-resize.module'; 3 | -------------------------------------------------------------------------------- /projects/angular/directives/ui-virtual-scroll-viewport-resize/src/ui-virtual-scroll-viewport-resize.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { UiVirtualScrollViewportResizeDirective } from './ui-virtual-scroll-viewport-resize.directive'; 4 | 5 | @NgModule({ 6 | declarations: [UiVirtualScrollViewportResizeDirective], 7 | exports: [UiVirtualScrollViewportResizeDirective], 8 | }) 9 | export class UiVirtualScrollViewportResizeModule { } 10 | -------------------------------------------------------------------------------- /projects/angular/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', 'jasmine-matchers', 'jasmine-dom-matchers', '@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('karma-junit-reporter'), 14 | require('karma-jasmine-dom-matchers'), 15 | require('karma-jasmine-matchers'), 16 | require('@angular-devkit/build-angular/plugins/karma') 17 | ], 18 | client: { 19 | jasmine: { 20 | random: false, 21 | }, 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | coverageIstanbulReporter: { 25 | dir: require('path').join(__dirname, '../../coverage'), 26 | reports: ['html', 'lcovonly', 'text-summary'], 27 | fixWebpackSourcePaths: true, 28 | skipFilesWithNoCoverage: true, 29 | }, 30 | junitReporter: { 31 | outputDir: require('path').join(__dirname, '../../coverage'), 32 | outputFile: '@uipath/angular.test.xml', 33 | useBrowserName: false, 34 | }, 35 | reporters: ['progress', 'junit', 'kjhtml', 'coverage-istanbul'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false, 42 | }); 43 | }; 44 | -------------------------------------------------------------------------------- /projects/angular/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/angular", 4 | "lib": { 5 | "entryFile": "./public_api.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /projects/angular/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@uipath/angular", 3 | "version": "15.2.5", 4 | "license": "MIT", 5 | "author": { 6 | "name": "UiPath Inc", 7 | "url": "https://uipath.com" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/UiPath/angular-components.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "components", 16 | "material", 17 | "ui", 18 | "ui-kit", 19 | "uipath" 20 | ], 21 | "maintainers": [ 22 | "adrian-macuc", 23 | "anbalase", 24 | "eugen-v", 25 | "sergiubologa", 26 | "toxik", 27 | "vasyop", 28 | "vladjerca" 29 | ], 30 | "peerDependencies": { 31 | "@angular/animations": ">=15.2.0", 32 | "@angular/cdk": ">=15.2.0", 33 | "@angular/common": ">=15.2.0", 34 | "@angular/compiler": ">=15.2.0", 35 | "@angular/core": ">=15.2.0", 36 | "@angular/forms": ">=15.2.0", 37 | "@angular/material": ">=15.2.0", 38 | "@angular/platform-browser": ">=15.2.0", 39 | "@angular/platform-browser-dynamic": ">=15.2.0", 40 | "@angular/router": ">=15.2.0", 41 | "clipboard": "^2.0.8", 42 | "lodash-es": "^4.17.21", 43 | "luxon": "^3.2.1", 44 | "object-hash": "^2.2.0", 45 | "rxjs": "^7.5", 46 | "scroll-into-view-if-needed": "^2.2.28" 47 | }, 48 | "dependencies": { 49 | "tslib": "^2.3.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /projects/angular/pipes/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/pipes/src/file-size/file-size.intl.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { of } from 'rxjs'; 3 | 4 | @Injectable({ providedIn: 'root' }) 5 | export class UiFileSizeIntl { 6 | bytes$ = (size: string) => of(`${size} B`); 7 | kiloBytes$ = (size: string) => of(`${size} KB`); 8 | megaBytes$ = (size: string) => of(`${size} MB`); 9 | gigaBytes$ = (size: string) => of(`${size} GB`); 10 | } 11 | -------------------------------------------------------------------------------- /projects/angular/pipes/src/file-size/file-size.pipe.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Pipe, 3 | PipeTransform, 4 | } from '@angular/core'; 5 | import { Observable } from 'rxjs'; 6 | import { UiFileSizeIntl } from './file-size.intl'; 7 | 8 | @Pipe({ name: 'uiFileSize' }) 9 | export class UiFileSizePipe implements PipeTransform { 10 | FileSizeUnits = [ 11 | { 12 | key: this._intl.bytes$, 13 | divider: 1, 14 | }, 15 | { 16 | key: this._intl.kiloBytes$, 17 | divider: 1024, 18 | }, 19 | { 20 | key: this._intl.megaBytes$, 21 | divider: 1024 * 1024, 22 | }, 23 | { 24 | key: this._intl.gigaBytes$, 25 | divider: 1024 * 1024 * 1024, 26 | }, 27 | ]; 28 | 29 | constructor( 30 | private readonly _intl: UiFileSizeIntl, 31 | ) {} 32 | 33 | transform(value: number | null | undefined): Observable { 34 | if (!value) { 35 | return this.FileSizeUnits[0].key('0'); 36 | } 37 | // default to highest unit if no unit matches the range from 1 to 1024 38 | const correctUnit = this.FileSizeUnits.find( 39 | (unit) => value / unit.divider >= 1 && value / unit.divider < 1024, 40 | ) ?? this.FileSizeUnits[this.FileSizeUnits.length - 1]; 41 | 42 | const computedValue = Math.round(value / correctUnit.divider * 10) / 10; 43 | return correctUnit.key(computedValue.toLocaleString()); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /projects/angular/pipes/src/nl2br/nl2br.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { UiNl2BrPipe } from './nl2br.pipe'; 2 | 3 | describe('Pipe: UiNl2Br', () => { 4 | // This pipe is a pure, stateless function so no need for BeforeEach 5 | const pipe = new UiNl2BrPipe(); 6 | 7 | it('should return null when there is no value', () => { 8 | expect(pipe.transform('')).toBeNull(); 9 | expect(pipe.transform(undefined!)).toBeNull(); 10 | expect(pipe.transform(null!)).toBeNull(); 11 | }); 12 | 13 | it('should transform \r\n to
', () => { 14 | expect(pipe.transform('\r\ntest\r\ntest')).toBe('
test
test'); 15 | }); 16 | 17 | it('should transform \r to
', () => { 18 | expect(pipe.transform('\rtest\rtest')).toBe('
test
test'); 19 | }); 20 | 21 | it('should transform \n to
', () => { 22 | expect(pipe.transform('\ntest\ntest')).toBe('
test
test'); 23 | }); 24 | 25 | it('should work with mixed line endings', () => { 26 | expect(pipe.transform('\r\ntest\rtest\n')).toBe('
test
test
'); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /projects/angular/pipes/src/nl2br/nl2br.pipe.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Pipe, 3 | PipeTransform, 4 | } from '@angular/core'; 5 | 6 | @Pipe({ name: 'nl2br' }) 7 | export class UiNl2BrPipe implements PipeTransform { 8 | transform(value?: string | null) { 9 | return value ? 10 | value 11 | .replace(/\r\n/g, '
') 12 | .replace(/\r/g, '
') 13 | .replace(/\n/g, '
') 14 | : 15 | null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/angular/pipes/src/pipe.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { UiFileSizePipe } from './file-size/file-size.pipe'; 4 | import { UiNl2BrPipe } from './nl2br/nl2br.pipe'; 5 | 6 | const PIPES = [ 7 | UiFileSizePipe, 8 | UiNl2BrPipe, 9 | ]; 10 | 11 | @NgModule({ 12 | declarations: PIPES, 13 | exports: PIPES, 14 | }) 15 | export class UiPipeModule { } 16 | -------------------------------------------------------------------------------- /projects/angular/pipes/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { UiFileSizeIntl } from './file-size/file-size.intl'; 2 | export { UiFileSizePipe } from './file-size/file-size.pipe'; 3 | export { UiNl2BrPipe } from './nl2br/nl2br.pipe'; 4 | 5 | export { UiPipeModule } from './pipe.module'; 6 | -------------------------------------------------------------------------------- /projects/angular/public_api.ts: -------------------------------------------------------------------------------- 1 | export default void 0; 2 | -------------------------------------------------------------------------------- /projects/angular/test.theme.ts: -------------------------------------------------------------------------------- 1 | export const JASMINE_STYLES = ` 2 | html, body { 3 | background-color: #222; 4 | color: #aaa; 5 | } 6 | 7 | .jasmine_html-reporter { 8 | background-color: transparent!important; 9 | } 10 | 11 | .jasmine-stack-trace, 12 | .jasmine-bar.jasmine-menu, 13 | .jasmine-spec-list-menu { 14 | border: none!important; 15 | background-color: #333!important; 16 | color: #efefef!important; 17 | } 18 | 19 | .jasmine-result-message { 20 | color: #efefef!important; 21 | } 22 | 23 | li.jasmine-passed a, 24 | li.jasmine-passed:before { 25 | color: #66bb6a!important; 26 | } 27 | 28 | .jasmine-spec-detail.jasmine-failed, 29 | .jasmine-overall-result.jasmine-failed { 30 | border-top: 2px solid #222!important; 31 | border-bottom: 2px solid #222!important; 32 | background-color: #ca3a11!important; 33 | } 34 | 35 | .jasmine-bar.jasmine-skipped { 36 | background-color: #333!important; 37 | } 38 | 39 | .jasmine-bar.jasmine-incomplete { 40 | background-color: #666!important; 41 | } 42 | 43 | .jamine-passed { 44 | background-color: #250; 45 | } 46 | 47 | .jasmine-failed { 48 | background-color: #900; 49 | } 50 | 51 | .jasmine-symbol-summary li.jasmine-failed { 52 | background-color: transparent!important; 53 | } 54 | 55 | ::-webkit-scrollbar { 56 | width: 8px; 57 | } 58 | 59 | ::-webkit-scrollbar-track { 60 | background: rgba(10, 10, 10, .3); 61 | } 62 | 63 | ::-webkit-scrollbar-thumb { 64 | background: rgba(10, 10, 10, .6); 65 | } 66 | 67 | ::-webkit-scrollbar:hover { 68 | background: rgba(10, 10, 10, .44); 69 | } 70 | `; 71 | -------------------------------------------------------------------------------- /projects/angular/test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/order */ 2 | 3 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 4 | import 'core-js/es/reflect'; 5 | import 'zone.js'; 6 | import 'zone.js/testing'; 7 | import 'jasmine-expect'; 8 | 9 | import * as faker from 'faker'; 10 | import { toHaveNoViolations } from 'jasmine-axe'; 11 | import { setSpecFn } from 'projects/angular/axe-helper'; 12 | 13 | /* eslint-enable import/order */ 14 | import { getTestBed } from '@angular/core/testing'; 15 | import { 16 | BrowserDynamicTestingModule, 17 | platformBrowserDynamicTesting, 18 | } from '@angular/platform-browser-dynamic/testing'; 19 | 20 | import { JASMINE_STYLES } from './test.theme'; 21 | 22 | const SEED = 1337; 23 | 24 | const materialIconsLink = document.createElement('link'); 25 | materialIconsLink.href = 'https://fonts.googleapis.com/icon?family=Material+Icons'; 26 | materialIconsLink.rel = 'stylesheet'; 27 | document.head.appendChild(materialIconsLink); 28 | 29 | const customStyle = document.createElement('style'); 30 | customStyle.innerHTML = JASMINE_STYLES; 31 | document.head.appendChild(customStyle); 32 | 33 | const reseed = () => { 34 | faker.seed(SEED); 35 | // overwrite Math.radom again in each global context 36 | Math.random = () => faker.random.number({ 37 | min: 0, 38 | max: 100000, 39 | }) / 100000; 40 | }; 41 | 42 | beforeAll(() => jasmine.addMatchers(toHaveNoViolations)); 43 | 44 | beforeEach(reseed); 45 | 46 | // eslint-disable-next-line no-underscore-dangle 47 | const __describe = describe; 48 | (window as any).global = window; 49 | global.describe = function desc() { 50 | reseed(); 51 | // eslint-disable-next-line prefer-rest-params 52 | __describe.apply(this, arguments as any); 53 | }; 54 | 55 | // eslint-disable-next-line no-restricted-globals 56 | setSpecFn(it, fit, xit); 57 | 58 | // First, initialize the Angular testing environment. 59 | getTestBed().initTestEnvironment( 60 | BrowserDynamicTestingModule, 61 | platformBrowserDynamicTesting(), 62 | { 63 | teardown: { destroyAfterEach: true }, 64 | }, 65 | ); 66 | -------------------------------------------------------------------------------- /projects/angular/testing/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/testing/src/component-utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './grid-testing-utils'; 2 | export * from './suggest-testing-utils'; 3 | -------------------------------------------------------------------------------- /projects/angular/testing/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export * from './utilities'; 2 | export * from './component-utils'; 3 | -------------------------------------------------------------------------------- /projects/angular/testing/src/utilities/events/drop-event.ts: -------------------------------------------------------------------------------- 1 | import { FakeFileList } from '../fake-file-list'; 2 | 3 | /** 4 | * Defines the structure requried for a file `DropEvent`. 5 | * 6 | * @export 7 | */ 8 | export interface IDropEvent extends Event { 9 | /** 10 | * Defines the `files` required for file-centric event dispatching. 11 | * 12 | */ 13 | dataTransfer: { 14 | files: FakeFileList; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /projects/angular/testing/src/utilities/events/index.ts: -------------------------------------------------------------------------------- 1 | export * from './drop-event'; 2 | -------------------------------------------------------------------------------- /projects/angular/testing/src/utilities/fake-file-list.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A class that implements the `FileList` interface. 3 | * This class facilitates easy creation of `FileList` mocks, for UTs. 4 | * 5 | * @export 6 | */ 7 | export class FakeFileList implements FileList { 8 | [index: number]: File; 9 | 10 | /** 11 | * The total file count. 12 | * 13 | */ 14 | get length() { 15 | return this.files.length; 16 | } 17 | 18 | /** 19 | * Creates a `mock` for `FileList` in order to easily test file centric scenarios with `input`s. 20 | * 21 | * @param [files=[]] A list of files. 22 | * @returns The mocked `FileList` instance. 23 | */ 24 | constructor(public files: File[] = []) { 25 | files.forEach((file, idx) => { 26 | this[idx] = file; 27 | }); 28 | } 29 | 30 | [Symbol.iterator](): IterableIterator { 31 | return this.files[Symbol.iterator](); 32 | } 33 | 34 | /** 35 | * Retrieve an item at the specified index. 36 | * 37 | * @param idx The accesed index. 38 | * @returns The file at the requested `idx`. 39 | */ 40 | item(idx: number): File { 41 | return this[idx]; 42 | } 43 | 44 | /** 45 | * Add files to the collection. 46 | * 47 | * @param files The files that will be added to the collection. 48 | */ 49 | add(...files: File[]): void { 50 | files.forEach(file => { 51 | this[this.files.length] = file; 52 | this.files.push(file); 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /projects/angular/testing/src/utilities/html-testing-utils.ts: -------------------------------------------------------------------------------- 1 | import { EventGenerator } from './event-generator'; 2 | 3 | export class HtmlTestingUtils { 4 | private _rootEl: HTMLElement; 5 | 6 | constructor(element: HTMLElement) { 7 | this._rootEl = element; 8 | } 9 | 10 | getElement = (selector: string, element: HTMLElement = this._rootEl) => 11 | element.querySelector(selector) as T | null; 12 | 13 | isToggleChecked = (selector: string, element: HTMLElement = this._rootEl) => 14 | this.getElement(selector, element)?.classList.contains('mat-mdc-slide-toggle-checked'); 15 | 16 | toggleElement = (additionalSelector: string) => (selector: string, element: HTMLElement = this._rootEl) => 17 | this.getElement(`${selector} ${additionalSelector}`, element)?.dispatchEvent(EventGenerator.click); 18 | 19 | // eslint-disable-next-line @typescript-eslint/member-ordering 20 | toggleCheckbox = this.toggleElement('label'); 21 | 22 | // eslint-disable-next-line @typescript-eslint/member-ordering 23 | toggleSlider = this.toggleElement('button'); 24 | 25 | setInput = (selector: string, value: any, element: HTMLElement = this._rootEl) => { 26 | const input = this.getElement(selector, element)!; 27 | input.value = value; 28 | input.dispatchEvent(EventGenerator.input()); 29 | return input; 30 | }; 31 | 32 | changeInput = (selector: string, value: any, element: HTMLElement = this._rootEl) => { 33 | const input = this.getElement(selector, element)!; 34 | input.value = value; 35 | 36 | const changeEvent = EventGenerator.change(); 37 | const targetElement = changeEvent.target as any as { value: number }; 38 | 39 | targetElement.value = value; 40 | input.dispatchEvent(changeEvent); 41 | input.dispatchEvent(EventGenerator.input()); 42 | return input; 43 | }; 44 | 45 | click = (selector: string, element: HTMLElement = this._rootEl) => 46 | this.getElement(selector, element)!.dispatchEvent(EventGenerator.click); 47 | } 48 | -------------------------------------------------------------------------------- /projects/angular/testing/src/utilities/index.ts: -------------------------------------------------------------------------------- 1 | export * from './event-generator'; 2 | export * from './fake-file-list'; 3 | export * from './key'; 4 | export * from './events'; 5 | export * from './fixture-testing-utils'; 6 | export * from './html-testing-utils'; 7 | -------------------------------------------------------------------------------- /projects/angular/testing/src/utilities/internal/cursor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @ignore 3 | */ 4 | // eslint-disable-next-line max-len 5 | export const CURSOR_IMG = 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAABLFBMVEUAAAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQECAgIKCgoNDQ0ODg4PDw8TExMVFRUeHh4hISEoKCgvLy89PT1QUFBWVlZaWlpiYmJoaGh3d3d5eXl9fX2IiIiXl5eZmZmmpqaoqKiqqqqrq6usrKywsLC1tbW4uLi8vLzBwcHU1NTc3Nzf39/o6Ojp6ens7Ozw8PD39/f4+Pj5+fn6+vr7+/v8/Pz9/f3///8jvPNLAAAAMnRSTlMAAAEGBxETFRodKDE/Q0ldXmZqeX1/hYeLjZaYmZqbnJ2ep6y0ur3JzNbi4+To7O38/mEAm4cAAADUSURBVHgBXdDlUoZAFMbxxcBu7MAQW8EHsbtDVOxgFX3u/x7EAfeFPTPny2/OhzN/YRhG0yQ6hZoU/nbQ34al4xgez9Cv4SgiHsMuo40b8hQTGt7yh+dw9Ms44QWmCziCkDLVK8zWlFHGX4wwV/uPwxl+kk+BW1/AOJGSZISpAn6Td5s7a1vBeI5DuGZy+PGOxS6rrVrhPfdwyRPPVC+lGB64C7t8xUAF+/xVNLbigUfLpsK67vkWUbW0zzf0KMxC9OLled3RsAEbK16zhqJ9piMP8gvd2SzacwFNVgAAAABJRU5ErkJggg==)'; 6 | -------------------------------------------------------------------------------- /projects/angular/testing/src/utilities/internal/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cursor'; 2 | -------------------------------------------------------------------------------- /projects/angular/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "declarationMap": true, 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "types": [], 14 | "lib": [ 15 | "dom", 16 | "es2018" 17 | ] 18 | }, 19 | "angularCompilerOptions": { 20 | "strictTemplates": true, 21 | "skipTemplateCodegen": true, 22 | "strictMetadataEmit": true, 23 | "fullTemplateTypeCheck": true, 24 | "strictInjectionParameters": true, 25 | "enableResourceInlining": true 26 | }, 27 | "exclude": [ 28 | "./test.ts", 29 | "**/test/*.ts", 30 | "**/*.spec.ts" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /projects/angular/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "compilerOptions": { 4 | "declarationMap": false 5 | }, 6 | "angularCompilerOptions": { 7 | "strictTemplates": true, 8 | "compilationMode": "partial" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/angular/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "jasmine_dom_matchers", 8 | "node" 9 | ] 10 | }, 11 | "files": [ 12 | "./test.ts" 13 | ], 14 | "include": [ 15 | "**/test/*.ts", 16 | "**/*.spec.ts", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /projects/angular/utilities/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/angular/utilities/src/array/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sort'; 2 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/array/sort.spec.ts: -------------------------------------------------------------------------------- 1 | import { sort } from './sort'; 2 | 3 | describe('sort', () => { 4 | [ 5 | { 6 | array: [{ key: 3 }, { key: 4 }, { key: 2 }, { key: 1 }], 7 | querySort: 'key', 8 | result: [{ key: 1 }, { key: 2 }, { key: 3 }, { key: 4 }], 9 | }, { 10 | array: [{ key: 103 }, { key: 102 }, { key: 101 }, { key: 104 }], 11 | querySort: '-key', 12 | result: [{ key: 104 }, { key: 103 }, { key: 102 }, { key: 101 }], 13 | }, 14 | ].forEach(({ array, querySort, result }) => it('should sort array by column of type number', () => { 15 | expect(sort(array, querySort)).toEqual(result); 16 | })); 17 | 18 | [ 19 | { 20 | array: [{ key: 'b' }, { key: 'd' }, { key: 'a' }, { key: 'c' }], 21 | querySort: 'key', 22 | result: [{ key: 'a' }, { key: 'b' }, { key: 'c' }, { key: 'd' }], 23 | }, { 24 | array: [{ key: 'c' }, { key: 'a' }, { key: 'd' }, { key: 'b' }], 25 | querySort: '-key', 26 | result: [{ key: 'd' }, { key: 'c' }, { key: 'b' }, { key: 'a' }], 27 | }, 28 | ].forEach(({ array, querySort, result }) => it('should sort array by column of type string', () => { 29 | expect(sort(array, querySort)).toEqual(result); 30 | })); 31 | 32 | it('should return array unchanged if no querySort is provided', () => { 33 | const array = [3, 2, 5, 1]; 34 | expect(sort(array)).toEqual(array); 35 | }); 36 | 37 | it('should return array unchanged if key is not found in the objects provided in array', () => { 38 | const array = [3, 2, 5, 1]; 39 | expect(sort(array, 'notFoundKey')).toEqual(array); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/array/sort.ts: -------------------------------------------------------------------------------- 1 | export function sort(array: T[], querySort?: string, locale = true): T[] { 2 | if (!querySort) { 3 | return array; 4 | } 5 | const sortDirection = querySort.startsWith('-') ? -1 : 1; 6 | const sortColumn: K = (sortDirection === 1 ? querySort : querySort.substring(1)) as K; 7 | return array.sort((a, b) => { 8 | const aValue = a[sortColumn]; 9 | const bValue = b[sortColumn]; 10 | let result: number; 11 | if (typeof aValue === 'number' && typeof bValue === 'number') { 12 | result = aValue - bValue; 13 | } else { 14 | result = locale 15 | ? `${aValue}`.localeCompare(`${bValue}`) 16 | : aValue < bValue ? -1 : 1; 17 | } 18 | return result * sortDirection; 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/browser/extract-cookies.spec.ts: -------------------------------------------------------------------------------- 1 | import { extractCookies } from './extract-cookies'; 2 | 3 | describe('Util(browser): extractCookies', () => { 4 | it('should an empty object if no cookies are set', () => { 5 | const matchMediaSpy = spyOnProperty(document, 'cookie', 'get'); 6 | matchMediaSpy.and.returnValue(''); 7 | 8 | expect(extractCookies()).toEqual({}); 9 | }); 10 | 11 | it('should return an object with ONE property', () => { 12 | const matchMediaSpy = spyOnProperty(document, 'cookie', 'get'); 13 | matchMediaSpy.and.returnValue('myCookie=1234'); 14 | 15 | expect(extractCookies()).toEqual({ myCookie: '1234' }); 16 | }); 17 | 18 | it('should return an object with MULTIPLE properties', () => { 19 | const matchMediaSpy = spyOnProperty(document, 'cookie', 'get'); 20 | matchMediaSpy.and.returnValue('myCookie=1234; myCookie2=XXXYYY; myCookie3=https://whatever.org'); 21 | console.log(extractCookies()); 22 | expect(extractCookies()).toEqual({ 23 | myCookie: '1234', 24 | myCookie2: 'XXXYYY', 25 | myCookie3: 'https://whatever.org', 26 | }); 27 | }); 28 | }); 29 | 30 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/browser/extract-cookies.ts: -------------------------------------------------------------------------------- 1 | import { isBrowserPlatform } from './is-browser-platform'; 2 | 3 | /** 4 | * Reads the current application cookies. 5 | * 6 | * @export 7 | * @returns A map with all the cookie KV pairs. 8 | */ 9 | export function extractCookies(): Record { 10 | if (!isBrowserPlatform()) { return {}; } 11 | 12 | return document.cookie.split('; ') 13 | .reduce((cookieMap, cookie) => { 14 | const [key, value] = cookie.split('='); 15 | 16 | if (!key) { return cookieMap; } 17 | 18 | cookieMap[key] = value; 19 | return cookieMap; 20 | }, {} as Record); 21 | } 22 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/browser/has-support-for-prefers-color-scheme.spec.ts: -------------------------------------------------------------------------------- 1 | import { hasSupportForPrefersColorScheme } from './has-support-for-prefers-color-scheme'; 2 | 3 | describe('Util(browser): hasSupportForPrefersColorScheme', () => { 4 | it('should return true if the media query matches', () => { 5 | const matchMediaSpy = spyOn(window, 'matchMedia'); 6 | matchMediaSpy.and.callFake(() => ({ matches: true } as MediaQueryList)); 7 | 8 | expect(hasSupportForPrefersColorScheme()).toEqual(true); 9 | }); 10 | 11 | it('should return false if the media query does NOT match', () => { 12 | const matchMediaSpy = spyOn(window, 'matchMedia'); 13 | matchMediaSpy.and.callFake(() => ({ matches: false } as MediaQueryList)); 14 | 15 | expect(hasSupportForPrefersColorScheme()).toEqual(false); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/browser/has-support-for-prefers-color-scheme.ts: -------------------------------------------------------------------------------- 1 | import { isBrowserPlatform } from './is-browser-platform'; 2 | 3 | /** 4 | * Determines if the browsers can read the OS theme preference. 5 | * 6 | * @export 7 | * @returns Returns `true` if the app can be themed according to the user OS preference. 8 | */ 9 | export function hasSupportForPrefersColorScheme() { 10 | if (!isBrowserPlatform()) { return false; } 11 | 12 | return !!window.matchMedia && ( 13 | window.matchMedia('(prefers-color-scheme: light)').matches || 14 | window.matchMedia('(prefers-color-scheme: dark)').matches 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/browser/index.ts: -------------------------------------------------------------------------------- 1 | export * from './is-internet-explorer'; 2 | export * from './extract-cookies'; 3 | export * from './is-edge'; 4 | export * from './is-browser-platform'; 5 | export * from './has-support-for-prefers-color-scheme'; 6 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/browser/is-browser-platform.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Determines if the current platform is a browser. 3 | * 4 | */ 5 | export function isBrowserPlatform() { 6 | return typeof window === 'object'; 7 | } 8 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/browser/is-edge.ts: -------------------------------------------------------------------------------- 1 | import { isBrowserPlatform } from './is-browser-platform'; 2 | 3 | /** 4 | * Determines if the current agent is Edge. 5 | * 6 | * @export 7 | * @returns Returns `true` if the current browser is `Edge`. 8 | */ 9 | export function isEdge(): boolean { 10 | if (!isBrowserPlatform()) { return false; } 11 | 12 | const userAgent = window.navigator.userAgent; 13 | 14 | return userAgent.indexOf('Edge') > -1; 15 | } 16 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/browser/is-internet-explorer.ts: -------------------------------------------------------------------------------- 1 | import { isBrowserPlatform } from './is-browser-platform'; 2 | 3 | /** 4 | * Determines if the current agent is Internet Explorer. 5 | * 6 | * @export 7 | * @returns Returns `true` if the current browser is `Internet Explorer`. 8 | */ 9 | export function isInternetExplorer(): boolean { 10 | if (!isBrowserPlatform()) { return false; } 11 | 12 | const userAgent = window.navigator.userAgent; 13 | 14 | // IE 10 or older => return version number 15 | const msie = userAgent.indexOf('MSIE '); 16 | if (msie > -1) { return true; } 17 | 18 | // IE 11 => return version number 19 | const trident = userAgent.indexOf('Trident/'); 20 | if (trident > -1) { return true; } 21 | 22 | return false; 23 | } 24 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/luxon/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-luxon'; 2 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/luxon/use-luxon.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | /** 4 | * Temporary token for using Luxon instead of moment. 5 | * After Luxon becomes the default, this token can be removed. 6 | * 7 | */ 8 | export const USE_LUXON = new InjectionToken('Token for using Luxon instead of moment.'); 9 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export * from './array'; 2 | export * from './browser'; 3 | export * from './luxon'; 4 | export * from './rxjs'; 5 | export * from './string'; 6 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/rxjs/async-of.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Observable, 3 | of, 4 | } from 'rxjs'; 5 | import { delay } from 'rxjs/operators'; 6 | 7 | /** 8 | * Static operator to avoid breaking the stream when using catchError. 9 | * 10 | * @param obj Object that will be asynchronously returned. 11 | * @param [delay=0] The delay applied to the stream until it emits the provided value; 12 | * @returns The delayed stream. 13 | */ 14 | export function asyncOf(obj: T, delayMs = 0): Observable { 15 | return of(obj).pipe(delay(delayMs)); 16 | } 17 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/rxjs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './async-of'; 2 | export * from './concat-join'; 3 | export * from './repeat-stream'; 4 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/rxjs/internal/observed-value-of.ts: -------------------------------------------------------------------------------- 1 | import { ObservableInput } from 'rxjs'; 2 | 3 | export type ObservedValueOf = O extends ObservableInput ? T : never; 4 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/rxjs/repeat-stream.spec.ts: -------------------------------------------------------------------------------- 1 | import { of } from 'rxjs'; 2 | import { 3 | finalize, 4 | take, 5 | } from 'rxjs/operators'; 6 | 7 | import { 8 | fakeAsync, 9 | tick, 10 | } from '@angular/core/testing'; 11 | 12 | import { repeatStream } from './repeat-stream'; 13 | 14 | const REPEAT_COUNT_LIST = Array(10) 15 | .fill(void 0) 16 | .map((_, idx) => (idx + 1) * 13); 17 | 18 | const REPEAT_INTERVAL_LIST = Array(5) 19 | .fill(void 0) 20 | .map((_, idx) => (idx + 1) * 1337); 21 | 22 | describe('Util(rxjs): repeatStream', () => { 23 | let emissionCount: number; 24 | 25 | beforeEach(() => { 26 | emissionCount = 0; 27 | }); 28 | 29 | REPEAT_COUNT_LIST 30 | .forEach(repeat => { 31 | REPEAT_INTERVAL_LIST 32 | .forEach(interval => { 33 | it(`should ${repeat} times at an interval of ${interval}`, fakeAsync((done: DoneFn) => { 34 | const callbacks = { 35 | emission: (count: number) => expect(count).toEqual(emissionCount), 36 | }; 37 | 38 | const emissionSpy = spyOn(callbacks, 'emission'); 39 | emissionSpy.and.callThrough(); 40 | 41 | repeatStream(() => of(++emissionCount), interval) 42 | .pipe( 43 | take(repeat), 44 | finalize(done), 45 | ) 46 | .subscribe(callbacks.emission); 47 | 48 | for (let i = 0; i < repeat; i++) { 49 | tick(interval); 50 | } 51 | 52 | expect(emissionSpy).toHaveBeenCalled(); 53 | expect(emissionSpy).toHaveBeenCalledTimes(repeat); 54 | })); 55 | }); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/rxjs/repeat-stream.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Observable, 3 | timer, 4 | } from 'rxjs'; 5 | import { switchMap } from 'rxjs/operators'; 6 | 7 | type StreamFactory = () => Observable; 8 | 9 | /** 10 | * Repeats the requested stream indefinitely. 11 | * 12 | * @export 13 | * @param stream The stream factory. 14 | * @param [interval=5000] The interval at which to repeat the stream. 15 | * @returns A hot observable that switches to the provided factory at the requested interval. 16 | */ 17 | export function repeatStream(stream: StreamFactory, interval = 5000) { 18 | return timer(0, interval) 19 | .pipe( 20 | switchMap(() => stream()), 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/string/file-extension.ts: -------------------------------------------------------------------------------- 1 | export const getFileExtension = (fileName: string) => { 2 | const fileNameParts = fileName.split('.'); 3 | const extension = fileNameParts.length > 1 ? fileNameParts.slice(-1)[0] : ''; 4 | return extension.toUpperCase(); 5 | }; 6 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/string/identifier.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @ignore 3 | */ 4 | const CHARMAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 5 | 6 | /** 7 | * Generates a random string identifier. 8 | * 9 | * @export 10 | * @param [length=16] The desired identifier length. 11 | * @returns The generated string identifier. 12 | */ 13 | export function identifier(length = 16): string { 14 | return Array(length) 15 | .fill(void 0) 16 | .map(() => CHARMAP.charAt(Math.floor(Math.random() * CHARMAP.length))) 17 | .join(''); 18 | } 19 | -------------------------------------------------------------------------------- /projects/angular/utilities/src/string/index.ts: -------------------------------------------------------------------------------- 1 | export * from './file-extension'; 2 | export * from './identifier'; 3 | -------------------------------------------------------------------------------- /projects/playground/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json", 3 | "ignorePatterns": [ 4 | "!**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "projects/playground/tsconfig.app.json", 14 | "projects/playground/tsconfig.spec.json", 15 | "projects/playground/e2e/tsconfig.json" 16 | ], 17 | "createDefaultProgram": true 18 | }, 19 | "plugins": [ 20 | "sonarjs", 21 | "eslint-plugin-rxjs" 22 | ], 23 | "rules": { 24 | "no-unused-vars": "off" 25 | } 26 | }, 27 | { 28 | "files": [ 29 | "*.html" 30 | ], 31 | "rules": {} 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /projects/playground/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'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/playground'), 20 | subdir: '.', 21 | reporters: [ 22 | { type: 'html' }, 23 | { type: 'text-summary' } 24 | ], 25 | }, 26 | reporters: ['progress', 'kjhtml'], 27 | port: 9876, 28 | colors: true, 29 | logLevel: config.LOG_INFO, 30 | autoWatch: true, 31 | browsers: ['Chrome'], 32 | singleRun: false, 33 | restartOnFileChange: true 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /projects/playground/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { GridPageComponent } from 'projects/playground/src/app/pages/grid/grid.page'; 2 | import { HomePageComponent } from 'projects/playground/src/app/pages/home/home.page'; 3 | import { SnackbarPageComponent } from 'projects/playground/src/app/pages/snackbar/snackbar.page'; 4 | import { SuggestPageComponent } from 'projects/playground/src/app/pages/suggest/suggest.page'; 5 | 6 | import { NgModule } from '@angular/core'; 7 | import { 8 | RouterModule, 9 | Routes, 10 | } from '@angular/router'; 11 | import { FilePickerPageComponent } from 'projects/playground/src/app/pages/file-picker/file-picker.page'; 12 | import { TreeSelectPageComponent } from 'projects/playground/src/app/pages/tree-select/tree-select.page'; 13 | import { ProgressButtonPageComponent } from 'projects/playground/src/app/pages/progress-button/progress-button.page'; 14 | 15 | const routes: Routes = [ 16 | { 17 | path: 'home', 18 | component: HomePageComponent, 19 | }, 20 | { 21 | path: 'grid', 22 | component: GridPageComponent, 23 | }, 24 | { 25 | path: 'snackbar', 26 | component: SnackbarPageComponent, 27 | }, 28 | { 29 | path: 'suggest', 30 | component: SuggestPageComponent, 31 | }, 32 | { 33 | path: 'tree', 34 | component: TreeSelectPageComponent, 35 | }, 36 | { 37 | path: 'file-picker', 38 | component: FilePickerPageComponent, 39 | }, 40 | { 41 | path: 'progress-button', 42 | component: ProgressButtonPageComponent, 43 | }, 44 | { 45 | path: '**', 46 | redirectTo: 'home', 47 | }, 48 | ]; 49 | 50 | @NgModule({ 51 | imports: [RouterModule.forRoot(routes)], 52 | exports: [RouterModule], 53 | }) 54 | export class AppRoutingModule { } 55 | -------------------------------------------------------------------------------- /projects/playground/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | Angular Components Playground 8 | 9 | 10 | 11 | 13 | 14 |
15 |

Component Playground

16 |
17 | 18 | 26 | 27 |
28 | 29 | 30 | 31 | . 32 | 33 |
34 | -------------------------------------------------------------------------------- /projects/playground/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | mat-sidenav-container { 2 | h3 { 3 | text-align: center; 4 | } 5 | 6 | min-height: 100vh; 7 | 8 | mat-sidenav { 9 | min-width: 300px; 10 | 11 | .sidenav-title { 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | } 16 | 17 | .sidenav-links { 18 | display: flex; 19 | flex-direction: column; 20 | } 21 | } 22 | 23 | mat-sidenav-content { 24 | padding: 24px; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /projects/playground/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection 4 | @Component({ 5 | selector: 'ui-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.scss'], 8 | }) 9 | export class AppComponent { 10 | isSidenavOpen = true; 11 | 12 | componentLinks = [ 13 | { 14 | name: 'Home', 15 | link: '/home', 16 | }, 17 | { 18 | name: 'Grid', 19 | link: '/grid', 20 | }, 21 | { 22 | name: 'Snackbar', 23 | link: '/snackbar', 24 | }, 25 | { 26 | name: 'Suggest', 27 | link: '/suggest', 28 | }, 29 | { 30 | name: 'Progress button', 31 | link: '/progress-button', 32 | }, 33 | { 34 | name: 'Tree select', 35 | link: '/tree', 36 | }, 37 | { 38 | name: 'File Picker', 39 | link: '/file-picker', 40 | }, 41 | ]; 42 | } 43 | -------------------------------------------------------------------------------- /projects/playground/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { MatButtonModule } from '@angular/material/button'; 4 | import { MatIconModule } from '@angular/material/icon'; 5 | import { MatSidenavModule } from '@angular/material/sidenav'; 6 | import { MatToolbarModule } from '@angular/material/toolbar'; 7 | import { BrowserModule } from '@angular/platform-browser'; 8 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 9 | import { SnackbarModule } from './pages/snackbar/snackbar.module'; 10 | import { HomeModule } from './pages/home/home.page.module'; 11 | import { GridModule } from './pages/grid/grid.page.module'; 12 | 13 | import { FilePickerModule } from './pages/file-picker/file-picker.module'; 14 | import { AppRoutingModule } from './app-routing.module'; 15 | import { AppComponent } from './app.component'; 16 | 17 | @NgModule({ 18 | declarations: [ 19 | AppComponent, 20 | ], 21 | imports: [ 22 | AppRoutingModule, 23 | CommonModule, 24 | BrowserModule, 25 | BrowserAnimationsModule, 26 | GridModule, 27 | HomeModule, 28 | SnackbarModule, 29 | FilePickerModule, 30 | MatSidenavModule, 31 | MatButtonModule, 32 | MatIconModule, 33 | MatToolbarModule, 34 | ], 35 | bootstrap: [AppComponent], 36 | }) 37 | export class AppModule { } 38 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/file-picker/file-picker.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { 5 | UiFilePickerComponent, UiInputFileDropZoneComponent, 6 | } from '@uipath/angular/components/ui-file-picker'; 7 | import { UiFileDropZoneDirective } from '@uipath/angular/directives/ui-file-drop-zone'; 8 | 9 | import { FilePickerPageComponent } from './file-picker.page'; 10 | 11 | @NgModule({ 12 | declarations: [ 13 | FilePickerPageComponent, 14 | ], 15 | imports: [ 16 | CommonModule, 17 | 18 | UiFileDropZoneDirective, 19 | UiFilePickerComponent, 20 | UiInputFileDropZoneComponent, 21 | ], 22 | }) 23 | export class FilePickerModule { } 24 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/file-picker/file-picker.page.html: -------------------------------------------------------------------------------- 1 | File Picker with max-height, secondary message and accepting only jpg and png files 2 | 5 | 6 | File Picker with max-height and descending sort by name 7 | 9 | 10 | File Picker with hideSummaryAfterFilesSelection = true (will emit files but will not show grid of files) 11 |
{{hiddenSummaryFiles$ | async}}
12 | 14 | 15 | File Picker disabled 16 | 17 | 18 | Input File Drop Zone 19 | 22 | Click to upload or drag and drop 23 | 24 | 25 |
29 |
Drop zone without input
30 | 31 |
32 | 33 |
34 | Dropped files: {{files}} 35 |
36 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/file-picker/file-picker.page.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 10px; 5 | } 6 | 7 | .custom-drop-zone { 8 | display: flex; 9 | flex-direction: column; 10 | max-height: 100px; 11 | border: 1px solid; 12 | position: relative; 13 | overflow: auto; 14 | 15 | div { 16 | display: flex; 17 | justify-content: center; 18 | } 19 | } 20 | 21 | .drop-zone { 22 | border-radius: 3px; 23 | border-width: 2px; 24 | border-style: dashed; 25 | 26 | &.ui-file-drop-zone-highlight { 27 | border-style: solid; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/file-picker/file-picker.page.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ChangeDetectionStrategy, Component, 3 | } from '@angular/core'; 4 | import { BehaviorSubject } from 'rxjs'; 5 | 6 | @Component({ 7 | selector: 'ui-app-file-picker', 8 | templateUrl: './file-picker.page.html', 9 | styleUrls: ['./file-picker.page.scss'], 10 | changeDetection: ChangeDetectionStrategy.OnPush, 11 | }) 12 | export class FilePickerPageComponent { 13 | customDropZoneFiles$ = new BehaviorSubject(''); 14 | hiddenSummaryFiles$ = new BehaviorSubject(''); 15 | 16 | addFiles(files: File[]) { 17 | this.customDropZoneFiles$.next(files.map(file => file.name).join(', ')); 18 | } 19 | 20 | onFilesChanged(files: File[]) { 21 | this.hiddenSummaryFiles$.next(files.map(f => f.name + ` (${f.size})`).join(', ')); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/grid/component/grid.component.scss: -------------------------------------------------------------------------------- 1 | .expanded-row { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | align-items: center; 6 | } 7 | 8 | .card { 9 | background: #ffffff; 10 | border: 1px solid #cfd8dd; 11 | color: #273139; 12 | padding: 16px; 13 | text-align: center; 14 | border-radius: 5px; 15 | } 16 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/grid/grid.intl.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { UiGridIntl } from '@uipath/angular/components/ui-grid'; 3 | 4 | @Injectable() 5 | export class UiGridTable extends UiGridIntl { 6 | noDataMessageAlternative = (searchValue?: string, activeFilters?: number | null) => { 7 | if (searchValue && activeFilters) { 8 | return `No results with current filters applied for: ${searchValue}`; 9 | } 10 | if (searchValue) { 11 | return `No results for: ${searchValue}`; 12 | } 13 | if (activeFilters) { 14 | return `No results to display with current filters applied`; 15 | } 16 | 17 | return 'No results to display'; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/grid/grid.models.ts: -------------------------------------------------------------------------------- 1 | export interface IFooter { 2 | total: number; 3 | pageSize: number; 4 | hidePageSize: boolean; 5 | hideTotalCount?: boolean; 6 | showFirstLastButtons?: boolean; 7 | } 8 | 9 | export interface IHeader { 10 | searchable: boolean; 11 | showFilter: boolean; 12 | main: number; 13 | inline: number; 14 | action: number; 15 | } 16 | 17 | export interface IInputs { 18 | collapseFiltersCount: number; 19 | // deprecated 20 | collapsibleFilters?: boolean; 21 | loading: boolean; 22 | isProjected: boolean; 23 | hasHighDensity: boolean; 24 | disabled: boolean; 25 | selectable: boolean; 26 | singleSelectable: boolean; 27 | toggleColumns: boolean; 28 | multiPageSelect: boolean; 29 | refreshable: boolean; 30 | virtualScroll: boolean; 31 | showPaintTime: boolean; 32 | showHeaderRow: boolean; 33 | customFilter: boolean; 34 | useCardView: boolean; 35 | hideTotalCount: boolean; 36 | isScrollable: boolean; 37 | allowHighlight: boolean; 38 | swapFilterContainers: boolean; 39 | selectablePageIndex: boolean; 40 | } 41 | 42 | export interface IGridSettings { 43 | inputs: IInputs; 44 | header: IHeader; 45 | footer: IFooter; 46 | } 47 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/grid/grid.page.module.ts: -------------------------------------------------------------------------------- 1 | import { GridComponent } from 'projects/playground/src/app/pages/grid/component/grid.component'; 2 | import { UiGridTable } from 'projects/playground/src/app/pages/grid/grid.intl'; 3 | 4 | import { CommonModule } from '@angular/common'; 5 | import { NgModule } from '@angular/core'; 6 | import { 7 | FormsModule, 8 | ReactiveFormsModule, 9 | } from '@angular/forms'; 10 | import { MatButtonModule } from '@angular/material/button'; 11 | import { MatCardModule } from '@angular/material/card'; 12 | import { MatCheckboxModule } from '@angular/material/checkbox'; 13 | import { MatIconModule } from '@angular/material/icon'; 14 | import { MatInputModule } from '@angular/material/input'; 15 | import { MAT_SELECT_CONFIG } from '@angular/material/select'; 16 | import { BrowserModule } from '@angular/platform-browser'; 17 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 18 | import { 19 | UiGridIntl, 20 | UiGridModule, 21 | } from '@uipath/angular/components/ui-grid'; 22 | 23 | import { GridPageComponent } from './grid.page'; 24 | 25 | @NgModule({ 26 | declarations: [ 27 | GridPageComponent, 28 | GridComponent, 29 | ], 30 | imports: [ 31 | CommonModule, 32 | BrowserModule, 33 | BrowserAnimationsModule, 34 | ReactiveFormsModule, 35 | FormsModule, 36 | UiGridModule, 37 | MatCardModule, 38 | MatCheckboxModule, 39 | MatButtonModule, 40 | MatIconModule, 41 | MatInputModule, 42 | ], 43 | providers: [ 44 | { 45 | provide: UiGridIntl, 46 | useClass: UiGridTable, 47 | }, 48 | { 49 | provide: MAT_SELECT_CONFIG, 50 | useValue: { overlayPanelClass: 'expandable-overlay-panel' }, 51 | }, 52 | ], 53 | }) 54 | export class GridModule { } 55 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/grid/grid.page.scss: -------------------------------------------------------------------------------- 1 | .grid-playground { 2 | &-actions { 3 | display: grid; 4 | grid-template-columns: 1fr 1fr; 5 | margin-bottom: 50px; 6 | 7 | &-inputs, 8 | &-header, 9 | &-data { 10 | margin-right: 50px; 11 | display: flex; 12 | flex-direction: column; 13 | } 14 | 15 | .header-search-checkbox { 16 | margin-bottom: 8px; 17 | } 18 | } 19 | } 20 | 21 | app-grid-component { 22 | margin: 0 50px; 23 | } 24 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/home/home.page.html: -------------------------------------------------------------------------------- 1 |

Welcome to Angular Components Playground

2 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/home/home.page.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { BrowserModule } from '@angular/platform-browser'; 4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 5 | import { RouterModule } from '@angular/router'; 6 | 7 | import { HomePageComponent } from './home.page'; 8 | 9 | @NgModule({ 10 | declarations: [ 11 | HomePageComponent, 12 | ], 13 | imports: [ 14 | CommonModule, 15 | BrowserModule, 16 | BrowserAnimationsModule, 17 | RouterModule, 18 | ], 19 | providers: [], 20 | }) 21 | export class HomeModule { } 22 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/home/home.page.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UiPath/angular-components/d5660a9805152dae8c1290f322db07a9d8abad26/projects/playground/src/app/pages/home/home.page.scss -------------------------------------------------------------------------------- /projects/playground/src/app/pages/home/home.page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection 4 | @Component({ 5 | templateUrl: './home.page.html', 6 | styleUrls: ['./home.page.scss'], 7 | }) 8 | export class HomePageComponent { } 9 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/progress-button/progress-button.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { UiProgressButtonModule } from '@uipath/angular/directives/ui-progress-button'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { MatIconModule } from '@angular/material/icon'; 7 | import { ProgressButtonPageComponent } from './progress-button.page'; 8 | 9 | @NgModule({ 10 | declarations: [ 11 | ProgressButtonPageComponent, 12 | ], 13 | imports: [ 14 | CommonModule, 15 | UiProgressButtonModule, 16 | MatButtonModule, 17 | MatIconModule, 18 | ], 19 | }) 20 | export class SuggestModule { } 21 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/progress-button/progress-button.page.html: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 | 11 | 15 | 19 | 23 | 24 |
25 | 26 |
27 | 30 | 34 | 38 | 42 | 43 |
44 | 45 |
46 | 51 | 57 | 63 | 69 | 70 |
71 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/progress-button/progress-button.page.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 10px; 5 | 6 | > button { 7 | width: fit-content; 8 | } 9 | 10 | .group { 11 | display: flex; 12 | gap: 10px; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/progress-button/progress-button.page.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ChangeDetectionStrategy, 3 | Component, 4 | } from '@angular/core'; 5 | 6 | @Component({ 7 | selector: 'ui-app-progress-button', 8 | templateUrl: './progress-button.page.html', 9 | styleUrls: ['./progress-button.page.scss'], 10 | changeDetection: ChangeDetectionStrategy.OnPush, 11 | }) 12 | export class ProgressButtonPageComponent { 13 | isLoading = false; 14 | 15 | toggle() { 16 | this.isLoading = !this.isLoading; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/snackbar/snackbar-content.component.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { 3 | ChangeDetectionStrategy, 4 | Component, 5 | Inject, 6 | NgModule, 7 | } from '@angular/core'; 8 | import { MatButtonModule } from '@angular/material/button'; 9 | import { UI_MAT_SNACK_BAR_PAYLOAD } from 'projects/angular/components/ui-snackbar/src/public_api'; 10 | 11 | export type SnackbarContentPayload = { 12 | buttonLabel: string; 13 | }; 14 | 15 | @Component({ 16 | selector: 'ui-snackbar-content', 17 | template: ` 18 | Snackbar w/ Component 19 | 20 | `, 21 | changeDetection: ChangeDetectionStrategy.OnPush, 22 | }) 23 | export class SnackbarContentComponent { 24 | constructor( 25 | @Inject(UI_MAT_SNACK_BAR_PAYLOAD) 26 | public payload: SnackbarContentPayload, 27 | ) { } 28 | } 29 | 30 | @NgModule({ 31 | imports: [ 32 | CommonModule, 33 | MatButtonModule, 34 | ], 35 | declarations: [SnackbarContentComponent], 36 | exports: [SnackbarContentComponent], 37 | }) 38 | export class SnackbarContentModule { } 39 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/snackbar/snackbar.module.ts: -------------------------------------------------------------------------------- 1 | import { UiSnackBarModule } from 'projects/angular/components/ui-snackbar/src/ui-snackbar.module'; 2 | 3 | import { CommonModule } from '@angular/common'; 4 | import { NgModule } from '@angular/core'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { MatCardModule } from '@angular/material/card'; 7 | import { MatIconModule } from '@angular/material/icon'; 8 | import { MatSnackBarModule } from '@angular/material/snack-bar'; 9 | 10 | import { SnackbarPageComponent } from './snackbar.page'; 11 | 12 | @NgModule({ 13 | imports: [ 14 | CommonModule, 15 | MatSnackBarModule, 16 | UiSnackBarModule, 17 | MatCardModule, 18 | MatButtonModule, 19 | MatIconModule, 20 | ], 21 | declarations: [SnackbarPageComponent], 22 | }) 23 | export class SnackbarModule { } 24 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/snackbar/snackbar.page.html: -------------------------------------------------------------------------------- 1 | 2 | Snackbar w/ Text 3 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | Snackbar w/ TemplateRef 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | Snackbar w/ Component 29 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | Snackbar w/ custom CSS class 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | Snackbar w/ TemplateRef 55 | 56 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/snackbar/snackbar.page.scss: -------------------------------------------------------------------------------- 1 | .snackbar-toggle-button { 2 | margin: 0 0.5em; 3 | text-transform: capitalize; 4 | 5 | &:first-of-type { 6 | margin-left: 0; 7 | } 8 | 9 | &:last-of-type { 10 | margin-right: 0; 11 | } 12 | 13 | mat-icon { 14 | font-size: 18px; 15 | width: 18px; 16 | height: 18px; 17 | line-height: 18px; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/snackbar/snackbar.page.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ICON_MAP, 3 | SnackBarType, 4 | UiSnackBarService, 5 | } from 'projects/angular/components/ui-snackbar/src/ui-snackbar.component'; 6 | import { 7 | SnackbarContentComponent, 8 | SnackbarContentPayload, 9 | } from 'projects/playground/src/app/pages/snackbar/snackbar-content.component'; 10 | 11 | import { 12 | Component, 13 | TemplateRef, 14 | } from '@angular/core'; 15 | 16 | // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection 17 | @Component({ 18 | selector: 'ui-app-snackbar', 19 | templateUrl: './snackbar.page.html', 20 | styleUrls: ['snackbar.page.scss'], 21 | }) 22 | export class SnackbarPageComponent { 23 | readonly IconMap = ICON_MAP; 24 | readonly iconType = [...ICON_MAP.keys()]; 25 | 26 | constructor( 27 | private _snack: UiSnackBarService, 28 | ) { } 29 | 30 | openSnackbar(snackbarType: SnackBarType) { 31 | (this._snack as any)[snackbarType](`This is ${snackbarType}`); 32 | } 33 | 34 | openSnackbarWithTemplateRef( 35 | snackbarType: SnackBarType, 36 | template: TemplateRef, 37 | ) { 38 | (this._snack as any)[snackbarType](template); 39 | } 40 | 41 | openSnackbarWithComponent(snackbarType: SnackBarType) { 42 | const payload: SnackbarContentPayload = { buttonLabel: 'Click me' }; 43 | (this._snack as any)[snackbarType]( 44 | SnackbarContentComponent, 45 | { payload }, 46 | ); 47 | } 48 | 49 | openWithCustomCssClass(snackbarType: SnackBarType) { 50 | const extraCssClasses = ['ui-my-custom-css-class']; 51 | (this._snack as any)[snackbarType](`This is ${snackbarType} with custom CSS class on panel`, { extraCssClasses }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/suggest/suggest.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { 4 | FormsModule, ReactiveFormsModule, 5 | } from '@angular/forms'; 6 | import { MatFormFieldModule } from '@angular/material/form-field'; 7 | import { UiSuggestModule } from '@uipath/angular/components/ui-suggest'; 8 | import { MatRadioModule } from '@angular/material/radio'; 9 | 10 | import { SuggestPageComponent } from './suggest.page'; 11 | 12 | @NgModule({ 13 | declarations: [ 14 | SuggestPageComponent, 15 | ], 16 | imports: [ 17 | CommonModule, 18 | UiSuggestModule, 19 | MatFormFieldModule, 20 | FormsModule, 21 | ReactiveFormsModule, 22 | MatRadioModule, 23 | ], 24 | }) 25 | export class SuggestModule { } 26 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/suggest/suggest.page.scss: -------------------------------------------------------------------------------- 1 | .spacer { 2 | $margin: 100px; 3 | 4 | margin-top: $margin; 5 | margin-bottom: $margin; 6 | } 7 | 8 | .expanded-list { 9 | width: 100%; 10 | height: 200px; 11 | margin-block-end: 100px; 12 | } 13 | 14 | .container-overflow-hidden { 15 | overflow: hidden; 16 | width: 100%; 17 | height: 100px; 18 | background-color: gray; 19 | } 20 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/suggest/suggest.page.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ComponentFixture, 3 | TestBed, 4 | } from '@angular/core/testing'; 5 | 6 | import { SuggestPageComponent } from './suggest.page'; 7 | 8 | describe('SuggestPage', () => { 9 | let component: SuggestPageComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async () => { 13 | await TestBed.configureTestingModule({ 14 | declarations: [SuggestPageComponent], 15 | }) 16 | .compileComponents(); 17 | }); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(SuggestPageComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/tree-select/tree-select.page.html: -------------------------------------------------------------------------------- 1 | 5 |

no data

6 | 7 | 8 |
9 | folder 10 | Header 11 |
12 |
13 | 14 | 19 | 29 | {{ expanded ? 'folder_open' : 'folder'}} 30 | {{ node.name }} 31 | 32 |
33 | 34 | Selected: {{ selectedNode }} 35 | -------------------------------------------------------------------------------- /projects/playground/src/app/pages/tree-select/tree-select.page.scss: -------------------------------------------------------------------------------- 1 | .spacer { 2 | $margin: 100px; 3 | 4 | margin-top: $margin; 5 | margin-bottom: $margin; 6 | } 7 | 8 | .ui-tree-select-container { 9 | border: 2px solid cyan; 10 | height: 500px; 11 | 12 | .header { 13 | display: flex; 14 | align-items: center; 15 | } 16 | } 17 | 18 | .tree-item-container { 19 | &:active, 20 | &:focus, 21 | &:focus-visible, 22 | &:focus-within { 23 | background-color: silver; 24 | } 25 | 26 | &.selected { 27 | background-color: gold; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /projects/playground/src/app/utils/settings-store.ts: -------------------------------------------------------------------------------- 1 | export class SettingsStore { 2 | private _storageKey: string; 3 | 4 | constructor( 5 | storageKey: string, 6 | ) { 7 | this._storageKey = storageKey; 8 | } 9 | 10 | get(): Partial { 11 | try { 12 | const storedObject = localStorage.getItem(this._storageKey) ?? '{}'; 13 | return JSON.parse(storedObject); 14 | } catch { 15 | return {}; 16 | } 17 | } 18 | 19 | store(settings: TStoredObject) { 20 | localStorage.setItem(this._storageKey, JSON.stringify(settings)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /projects/playground/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UiPath/angular-components/d5660a9805152dae8c1290f322db07a9d8abad26/projects/playground/src/assets/.gitkeep -------------------------------------------------------------------------------- /projects/playground/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line 2 | export const environment = { 3 | production: true, 4 | }; 5 | -------------------------------------------------------------------------------- /projects/playground/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 | // eslint-disable-next-line 6 | export const environment = { 7 | production: false, 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/plugins/zone-error'; // Included with Angular CLI. 18 | -------------------------------------------------------------------------------- /projects/playground/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UiPath/angular-components/d5660a9805152dae8c1290f322db07a9d8abad26/projects/playground/src/favicon.ico -------------------------------------------------------------------------------- /projects/playground/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Playground 7 | 8 | 10 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /projects/playground/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /projects/playground/src/styles.scss: -------------------------------------------------------------------------------- 1 | @use "@angular/material" as mat; 2 | @import "./projects/angular/uipath-angular.theme"; 3 | 4 | $theme: mat.define-light-theme( 5 | mat.define-palette(mat.$blue-grey-palette, 500), 6 | mat.define-palette(mat.$amber-palette, 500) 7 | ); 8 | 9 | @include mat.core(); 10 | @include mat.all-component-themes($theme); 11 | @include uipath-angular-theme($theme); 12 | @include mat.snack-bar-theme($theme); 13 | 14 | body { 15 | margin: 0; 16 | padding: 0; 17 | } 18 | 19 | .mat-mdc-form-field { 20 | .mat-mdc-text-field-wrapper, 21 | .mat-mdc-form-field-focus-overlay { 22 | background-color: unset; 23 | padding: 0; 24 | } 25 | } 26 | 27 | input[type="search"]::-webkit-search-decoration, 28 | input[type="search"]::-webkit-search-cancel-button, 29 | input[type="search"]::-webkit-search-results-button, 30 | input[type="search"]::-webkit-search-results-decoration { 31 | -webkit-appearance:none; 32 | } 33 | 34 | .expandable-overlay-panel { 35 | min-width: min-content; 36 | } 37 | -------------------------------------------------------------------------------- /projects/playground/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/testing'; 4 | 5 | import { getTestBed } from '@angular/core/testing'; 6 | import { 7 | BrowserDynamicTestingModule, 8 | platformBrowserDynamicTesting, 9 | } from '@angular/platform-browser-dynamic/testing'; 10 | 11 | declare const require: { 12 | context(path: string, deep?: boolean, filter?: RegExp): { 13 | keys(): string[]; 14 | (id: string): T; 15 | }; 16 | }; 17 | 18 | // First, initialize the Angular testing environment. 19 | getTestBed().initTestEnvironment( 20 | BrowserDynamicTestingModule, 21 | platformBrowserDynamicTesting(), 22 | { 23 | teardown: { destroyAfterEach: false }, 24 | }, 25 | ); 26 | // Then we find all the tests. 27 | const context = require.context('./', true, /\.spec\.ts$/); 28 | // And load the modules. 29 | context.keys().map(context); 30 | -------------------------------------------------------------------------------- /projects/playground/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/app", 5 | "declarationMap": true, 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "types": [], 14 | "lib": [ 15 | "dom", 16 | "es2018" 17 | ] 18 | }, 19 | "angularCompilerOptions": { 20 | "skipTemplateCodegen": true, 21 | "strictMetadataEmit": true, 22 | "fullTemplateTypeCheck": true, 23 | "strictInjectionParameters": true, 24 | "enableResourceInlining": true, 25 | "noUnusedLocals": false, 26 | "noUnusedParameters": false 27 | }, 28 | "exclude": [ 29 | "./test.ts", 30 | "**/test/*.ts", 31 | "**/*.spec.ts" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /projects/playground/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/spec", 6 | "types": [ 7 | "jasmine" 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 | -------------------------------------------------------------------------------- /scss-bundle.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "bundlerOptions": { 3 | "entryFile": "./projects/angular/_uipath-angular.theme.scss", 4 | "outFile": "./dist/angular/_theming.scss", 5 | "includePaths": [ 6 | "./node_modules" 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test.theme.scss: -------------------------------------------------------------------------------- 1 | @import "@angular/material/theming"; 2 | @import "./projects/angular/uipath-angular.theme"; 3 | 4 | $theme: mat-dark-theme(mat-palette($mat-green, 500), mat-palette($mat-amber, 500)); 5 | 6 | @include mat-core(); 7 | @include angular-material-theme($theme); 8 | @include uipath-angular-theme($theme); 9 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2020", 5 | "typeRoots": [ 6 | "node_modules/@types" 7 | ] 8 | } 9 | } 10 | --------------------------------------------------------------------------------