├── .browserslistrc ├── .editorconfig ├── .eslintrc.js ├── .firebaserc ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md ├── PULL_REQUEST_TEMPLATE.md ├── codecov.yml ├── dependabot.yml └── workflows │ ├── blui-ci.yml │ ├── blui-pr-actions.yml │ ├── firebase-hosting-merge.yml │ └── firebase-hosting-pull-request.yml ├── .gitignore ├── .stackblitzrc ├── CODEOWNERS ├── README.md ├── angular.json ├── cypress.json ├── cypress ├── coverage.webpack.js ├── fixtures │ └── example.json ├── integration │ ├── account-menu │ │ ├── in-appbar.spec.js │ │ └── in-drawer.spec.js │ ├── appbar │ │ ├── collapsible-app-bar.spec.js │ │ ├── contextual-action.spec.js │ │ ├── dropdown-toolbar.spec.js │ │ ├── page-wide-search.spec.js │ │ └── search-bar.spec.js │ ├── dynamic-stepper │ │ └── dynamic-stepper.spec.js │ ├── form-validation │ │ ├── fixed-length.spec.js │ │ ├── in-list.spec.js │ │ ├── password.spec.js │ │ ├── phone-number.spec.js │ │ ├── sectioned-form.spec.js │ │ ├── table.spec.js │ │ └── verify-submit.spec.js │ ├── i18n │ │ ├── i18n.spec.js │ │ └── right-to-left.spec.js │ ├── landing-page │ │ └── landing-page.spec.js │ ├── lists │ │ ├── action-list.spec.js │ │ ├── data-list.spec.js │ │ ├── multiselect-list.spec.js │ │ ├── responsive-table.spec.js │ │ ├── sortable-list.spec.js │ │ └── status-list.spec.js │ ├── loading-wait-states │ │ ├── contextual-spinners.spec.js │ │ ├── progress-bars.spec.js │ │ ├── skeleton-loader.spec.js │ │ └── spinner-overlay.spec.js │ ├── overlays │ │ ├── basic-bottom-sheet.spec.js │ │ └── complex-bottom-sheet.spec.js │ └── view-port-test │ │ └── toolbar-view-ports.spec.js ├── plugins │ └── index.js └── support │ ├── commands.js │ └── index.js ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.json ├── firebase.json ├── karma.conf.js ├── package.json ├── src ├── app │ ├── app-routing.module.ts │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── pages │ │ ├── account-menu │ │ │ ├── in-a-drawer │ │ │ │ ├── in-a-drawer.module.ts │ │ │ │ ├── menu-in-a-drawer.component.html │ │ │ │ ├── menu-in-a-drawer.component.scss │ │ │ │ ├── menu-in-a-drawer.component.spec.ts │ │ │ │ └── menu-in-a-drawer.component.ts │ │ │ └── in-an-app-bar │ │ │ │ ├── chip │ │ │ │ ├── chip.component.html │ │ │ │ ├── chip.component.scss │ │ │ │ ├── chip.component.spec.ts │ │ │ │ └── chip.component.ts │ │ │ │ ├── in-an-app-bar.component.html │ │ │ │ ├── in-an-app-bar.component.scss │ │ │ │ ├── in-an-app-bar.component.spec.ts │ │ │ │ ├── in-an-app-bar.component.ts │ │ │ │ └── in-an-app-bar.module.ts │ │ ├── app-bar │ │ │ ├── collapsible-app-bar │ │ │ │ ├── collapsible-app-bar.component.html │ │ │ │ ├── collapsible-app-bar.component.scss │ │ │ │ ├── collapsible-app-bar.component.spec.ts │ │ │ │ ├── collapsible-app-bar.component.ts │ │ │ │ ├── collapsible-app-bar.module.ts │ │ │ │ └── data.ts │ │ │ ├── contextual │ │ │ │ ├── contextual.component.html │ │ │ │ ├── contextual.component.scss │ │ │ │ ├── contextual.component.spec.ts │ │ │ │ ├── contextual.component.ts │ │ │ │ └── contextual.module.ts │ │ │ ├── dropdown-toolbar │ │ │ │ ├── dropdown-toolbar-bottomsheet.component.ts │ │ │ │ ├── dropdown-toolbar.component.html │ │ │ │ ├── dropdown-toolbar.component.scss │ │ │ │ ├── dropdown-toolbar.component.spec.ts │ │ │ │ ├── dropdown-toolbar.component.ts │ │ │ │ ├── dropdown-toolbar.module.ts │ │ │ │ ├── dropdown-toolbar.service.ts │ │ │ │ └── menuItems.ts │ │ │ ├── page-wide-search │ │ │ │ ├── page-wide-search.component.html │ │ │ │ ├── page-wide-search.component.scss │ │ │ │ ├── page-wide-search.component.spec.ts │ │ │ │ ├── page-wide-search.component.ts │ │ │ │ └── page-wide-search.module.ts │ │ │ └── search-bar │ │ │ │ ├── search-bar.component.html │ │ │ │ ├── search-bar.component.scss │ │ │ │ ├── search-bar.component.spec.ts │ │ │ │ ├── search-bar.component.ts │ │ │ │ ├── search-bar.module.ts │ │ │ │ └── shared │ │ │ │ ├── bold-search.pipe.ts │ │ │ │ └── filter.pipe.ts │ │ ├── dynamic-stepper │ │ │ ├── dynamic-stepper.component.html │ │ │ ├── dynamic-stepper.component.scss │ │ │ ├── dynamic-stepper.component.spec.ts │ │ │ ├── dynamic-stepper.component.ts │ │ │ └── dynamic-stepper.module.ts │ │ ├── forms-and-validation │ │ │ ├── fix-length-passcode │ │ │ │ ├── fix-length-passcode.component.html │ │ │ │ ├── fix-length-passcode.component.scss │ │ │ │ ├── fix-length-passcode.component.spec.ts │ │ │ │ ├── fix-length-passcode.component.ts │ │ │ │ └── fix-length-passcode.module.ts │ │ │ ├── in-a-list │ │ │ │ ├── in-a-list.component.html │ │ │ │ ├── in-a-list.component.scss │ │ │ │ ├── in-a-list.component.spec.ts │ │ │ │ ├── in-a-list.component.ts │ │ │ │ └── in-a-list.module.ts │ │ │ ├── in-a-table │ │ │ │ ├── in-a-table.component.html │ │ │ │ ├── in-a-table.component.scss │ │ │ │ ├── in-a-table.component.spec.ts │ │ │ │ ├── in-a-table.component.ts │ │ │ │ └── in-a-table.module.ts │ │ │ ├── password │ │ │ │ ├── password-strength-checker │ │ │ │ │ ├── blui-password-strength-checker.component.ts │ │ │ │ │ ├── password-strength-checker.component.scss │ │ │ │ │ ├── password-strength-checker.component.ts │ │ │ │ │ └── password-strength-checker.spec.ts │ │ │ │ ├── password.component.html │ │ │ │ ├── password.component.scss │ │ │ │ ├── password.component.spec.ts │ │ │ │ ├── password.component.ts │ │ │ │ └── password.module.ts │ │ │ ├── phone-number-format │ │ │ │ ├── phone-number-format.component.html │ │ │ │ ├── phone-number-format.component.scss │ │ │ │ ├── phone-number-format.component.spec.ts │ │ │ │ ├── phone-number-format.component.ts │ │ │ │ ├── phone-number-format.module.ts │ │ │ │ └── shared │ │ │ │ │ ├── format-phone-number.pipe.spec.ts │ │ │ │ │ └── format-phone-number.pipe.ts │ │ │ ├── sectioned-form │ │ │ │ ├── sectioned-form.component.html │ │ │ │ ├── sectioned-form.component.scss │ │ │ │ ├── sectioned-form.component.spec.ts │ │ │ │ ├── sectioned-form.component.ts │ │ │ │ └── sectioned-form.module.ts │ │ │ └── verify-on-submit │ │ │ │ ├── slide-panel │ │ │ │ ├── slide-panel.component.html │ │ │ │ ├── slide-panel.component.scss │ │ │ │ └── slide-panel.component.ts │ │ │ │ ├── verify-on-submit.component.html │ │ │ │ ├── verify-on-submit.component.scss │ │ │ │ ├── verify-on-submit.component.spec.ts │ │ │ │ ├── verify-on-submit.component.ts │ │ │ │ └── verify-on-submit.module.ts │ │ ├── i18n │ │ │ ├── i18n.component.html │ │ │ ├── i18n.component.scss │ │ │ ├── i18n.component.spec.ts │ │ │ ├── i18n.component.ts │ │ │ ├── i18n.module.ts │ │ │ ├── services │ │ │ │ ├── bidirectional.service.spec.ts │ │ │ │ ├── bidirectional.service.ts │ │ │ │ ├── fruit.service.ts │ │ │ │ └── language-loader.service.ts │ │ │ ├── snack-bar.component.ts │ │ │ └── translations │ │ │ │ ├── arabic.ts │ │ │ │ ├── chinese.ts │ │ │ │ ├── english.ts │ │ │ │ ├── french.ts │ │ │ │ ├── german.ts │ │ │ │ ├── portuguese.ts │ │ │ │ ├── sample-translation.ts │ │ │ │ └── spanish.ts │ │ ├── landing-page │ │ │ ├── landing-page.component.html │ │ │ ├── landing-page.component.scss │ │ │ ├── landing-page.component.spec.ts │ │ │ ├── landing-page.component.ts │ │ │ └── landing-page.module.ts │ │ ├── list │ │ │ ├── action-list │ │ │ │ ├── action-list.component.html │ │ │ │ ├── action-list.component.scss │ │ │ │ ├── action-list.component.spec.ts │ │ │ │ ├── action-list.component.ts │ │ │ │ └── action-list.module.ts │ │ │ ├── data-list │ │ │ │ ├── data-list.component.html │ │ │ │ ├── data-list.component.scss │ │ │ │ ├── data-list.component.spec.ts │ │ │ │ ├── data-list.component.ts │ │ │ │ └── data-list.module.ts │ │ │ ├── in-panel-header │ │ │ │ ├── in-panel-header.component.html │ │ │ │ ├── in-panel-header.component.scss │ │ │ │ ├── in-panel-header.component.spec.ts │ │ │ │ ├── in-panel-header.component.ts │ │ │ │ ├── in-panel-header.module.ts │ │ │ │ └── shared │ │ │ │ │ ├── filter.pipe.spec.ts │ │ │ │ │ └── filter.pipe.ts │ │ │ ├── inline-actions │ │ │ │ ├── inline-actions.component.html │ │ │ │ ├── inline-actions.component.scss │ │ │ │ ├── inline-actions.component.spec.ts │ │ │ │ ├── inline-actions.component.ts │ │ │ │ └── inline-actions.module.ts │ │ │ ├── inline-button-panel │ │ │ │ ├── dialog │ │ │ │ │ ├── prompt-dialog.html │ │ │ │ │ ├── prompt-dialog.module.ts │ │ │ │ │ └── prompt-dialog.ts │ │ │ │ ├── inline-button-panel.component.html │ │ │ │ ├── inline-button-panel.component.scss │ │ │ │ ├── inline-button-panel.component.spec.ts │ │ │ │ ├── inline-button-panel.component.ts │ │ │ │ ├── inline-button-panel.module.ts │ │ │ │ └── services │ │ │ │ │ └── dialog-service.ts │ │ │ ├── inline-local-actions │ │ │ │ ├── device-edit-mobile │ │ │ │ │ ├── device-edit-mobile.component.html │ │ │ │ │ ├── device-edit-mobile.component.scss │ │ │ │ │ ├── device-edit-mobile.component.spec.ts │ │ │ │ │ └── device-edit-mobile.component.ts │ │ │ │ ├── inline-local-actions.component.html │ │ │ │ ├── inline-local-actions.component.scss │ │ │ │ ├── inline-local-actions.component.spec.ts │ │ │ │ ├── inline-local-actions.component.ts │ │ │ │ ├── inline-local-actions.module.ts │ │ │ │ ├── language-list │ │ │ │ │ ├── language-list.component.html │ │ │ │ │ ├── language-list.component.scss │ │ │ │ │ ├── language-list.component.spec.ts │ │ │ │ │ └── language-list.component.ts │ │ │ │ ├── local-actions-dialog │ │ │ │ │ ├── local-actions-dialog.component.html │ │ │ │ │ ├── local-actions-dialog.component.scss │ │ │ │ │ ├── local-actions-dialog.component.spec.ts │ │ │ │ │ └── local-actions-dialog.component.ts │ │ │ │ ├── scorecard │ │ │ │ │ ├── scorecard.component.html │ │ │ │ │ ├── scorecard.component.scss │ │ │ │ │ ├── scorecard.component.spec.ts │ │ │ │ │ └── scorecard.component.ts │ │ │ │ └── slide-panel │ │ │ │ │ ├── slide-panel.component.html │ │ │ │ │ ├── slide-panel.component.scss │ │ │ │ │ └── slide-panel.component.ts │ │ │ ├── multiselect-list │ │ │ │ ├── multiselect-list.component.html │ │ │ │ ├── multiselect-list.component.scss │ │ │ │ ├── multiselect-list.component.spec.ts │ │ │ │ ├── multiselect-list.component.ts │ │ │ │ └── multiselect-list.module.ts │ │ │ ├── responsive-table │ │ │ │ ├── responsive-table.component.html │ │ │ │ ├── responsive-table.component.scss │ │ │ │ ├── responsive-table.component.spec.ts │ │ │ │ ├── responsive-table.component.ts │ │ │ │ └── responsive-table.module.ts │ │ │ ├── sortable-list │ │ │ │ ├── sortable-list.component.html │ │ │ │ ├── sortable-list.component.scss │ │ │ │ ├── sortable-list.component.spec.ts │ │ │ │ ├── sortable-list.component.ts │ │ │ │ └── sortable-list.module.ts │ │ │ ├── status-list │ │ │ │ ├── status-list.component.html │ │ │ │ ├── status-list.component.scss │ │ │ │ ├── status-list.component.spec.ts │ │ │ │ ├── status-list.component.ts │ │ │ │ └── status-list.module.ts │ │ │ └── tree │ │ │ │ ├── tree-item.component.ts │ │ │ │ ├── tree.component.html │ │ │ │ ├── tree.component.scss │ │ │ │ ├── tree.component.spec.ts │ │ │ │ ├── tree.component.ts │ │ │ │ └── tree.module.ts │ │ ├── loading-waiting-states │ │ │ ├── contextual-spinners │ │ │ │ ├── contextual-spinners.component.html │ │ │ │ ├── contextual-spinners.component.scss │ │ │ │ ├── contextual-spinners.component.spec.ts │ │ │ │ ├── contextual-spinners.component.ts │ │ │ │ └── contextual-spinners.module.ts │ │ │ ├── progress-bar-indeterminate │ │ │ │ ├── progress-bar-indeterminate.component.html │ │ │ │ ├── progress-bar-indeterminate.component.scss │ │ │ │ ├── progress-bar-indeterminate.component.spec.ts │ │ │ │ ├── progress-bar-indeterminate.component.ts │ │ │ │ └── progress-bar-indeterminate.module.ts │ │ │ ├── progress-bars │ │ │ │ ├── progress-bars.component.html │ │ │ │ ├── progress-bars.component.scss │ │ │ │ ├── progress-bars.component.spec.ts │ │ │ │ ├── progress-bars.component.ts │ │ │ │ └── progress-bars.module.ts │ │ │ ├── skeleton-loader │ │ │ │ ├── components │ │ │ │ │ ├── common-style.scss │ │ │ │ │ ├── placeholder-hero │ │ │ │ │ │ ├── placeholder-hero.component.html │ │ │ │ │ │ ├── placeholder-hero.component.scss │ │ │ │ │ │ ├── placeholder-hero.component.spec.ts │ │ │ │ │ │ └── placeholder-hero.component.ts │ │ │ │ │ ├── placeholder-list │ │ │ │ │ │ ├── placeholder-list.component.html │ │ │ │ │ │ ├── placeholder-list.component.scss │ │ │ │ │ │ ├── placeholder-list.component.spec.ts │ │ │ │ │ │ └── placeholder-list.component.ts │ │ │ │ │ └── placeholder-score-card │ │ │ │ │ │ ├── placeholder-score-card.component.html │ │ │ │ │ │ ├── placeholder-score-card.component.scss │ │ │ │ │ │ ├── placeholder-score-card.component.spec.ts │ │ │ │ │ │ └── placeholder-score-card.component.ts │ │ │ │ ├── skeleton-loader.component.html │ │ │ │ ├── skeleton-loader.component.scss │ │ │ │ ├── skeleton-loader.component.spec.ts │ │ │ │ ├── skeleton-loader.component.ts │ │ │ │ └── skeleton-loader.module.ts │ │ │ └── spinner-overlays │ │ │ │ ├── eula-details.ts │ │ │ │ ├── spinner-overlays.component.html │ │ │ │ ├── spinner-overlays.component.scss │ │ │ │ ├── spinner-overlays.component.spec.ts │ │ │ │ ├── spinner-overlays.component.ts │ │ │ │ └── spinner-overlays.module.ts │ │ └── overlays │ │ │ ├── basic-bottom-sheet │ │ │ ├── basic-bottom-sheet.component.html │ │ │ ├── basic-bottom-sheet.component.scss │ │ │ ├── basic-bottom-sheet.component.spec.ts │ │ │ ├── basic-bottom-sheet.component.ts │ │ │ ├── basic-bottom-sheet.module.ts │ │ │ ├── bottom-sheet │ │ │ │ ├── bottom-sheet.html │ │ │ │ ├── bottom-sheet.scss │ │ │ │ └── bottom-sheet.ts │ │ │ └── data.service.ts │ │ │ └── complex-bottom-sheet │ │ │ ├── bottom-sheet │ │ │ ├── bottom-sheet.html │ │ │ ├── bottom-sheet.scss │ │ │ └── bottom-sheet.ts │ │ │ ├── complex-bottom-sheet.component.html │ │ │ ├── complex-bottom-sheet.component.scss │ │ │ ├── complex-bottom-sheet.component.spec.ts │ │ │ ├── complex-bottom-sheet.component.ts │ │ │ ├── complex-bottom-sheet.module.ts │ │ │ ├── data.service.ts │ │ │ └── filter.ts │ └── services │ │ ├── state.service.spec.ts │ │ ├── state.service.ts │ │ ├── viewport.service.spec.ts │ │ └── viewport.service.ts ├── assets │ ├── .gitkeep │ ├── avatar_40.png │ ├── collapsible_app_bar_demo.jpg │ └── cubes_tile.png ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts ├── styles.scss └── test.ts ├── tsconfig.app.json ├── tsconfig.es5.json ├── tsconfig.json ├── tsconfig.spec.json └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # You can see what browsers were selected by your queries by running: 5 | # npx browserslist 6 | > 0.5% 7 | last 2 versions 8 | Firefox ESR 9 | not dead 10 | IE 11 -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: ['@brightlayer-ui/eslint-config/ts'], 4 | parserOptions: { 5 | project: './tsconfig.json', 6 | }, 7 | env: { 8 | browser: true, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "blui-angular-design-patterns" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report a bug with a Brightlayer UI resource 4 | title: '' 5 | labels: 'bug, needs-review, brightlayer-ui' 6 | assignees: '' 7 | 8 | --- 9 | 10 | #### Describe the bug / expected behavior 11 | 12 | #### What are the steps to reproduce? 13 | 1. Go to... 14 | 2. Click on... 15 | 16 | #### Screenshots / Screen recording 17 | 18 | #### Code snippet / Link to minimum reproduction example 19 | 20 | ``` 21 | CODE HERE 22 | ``` 23 | 24 | #### Your environment information 25 | 26 | 27 | #### Suggested fix 28 | 29 | 30 | #### Anything else to add? 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for this resource 4 | title: '' 5 | labels: 'enhancement, needs-review, brightlayer-ui' 6 | assignees: '' 7 | 8 | --- 9 | 10 | #### Describe the desired feature/functionality 11 | 12 | #### Additional Context (where / how would this be used) 13 | 14 | #### Is this request related to a current issue? 15 | 16 | #### Suggested implementation details 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | Fixes # . 3 | 4 | 5 | #### Changes proposed in this Pull Request: 6 | - 7 | - 8 | - 9 | 10 | 11 | #### Screenshots / Screen Recording (if applicable) 12 | - 13 | 14 | 15 | 16 | #### To Test: 17 | - 18 | 19 | 20 | #### Any specific feedback you are looking for? 21 | - 22 | 23 | 24 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | enabled: false 5 | patch: 6 | default: 7 | enabled: false -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'npm' 4 | directory: '/' 5 | schedule: 6 | interval: 'monthly' 7 | day: 'monday' 8 | open-pull-requests-limit: 1 9 | target-branch: 'dev' 10 | labels: 11 | - 'external-dependency' 12 | -------------------------------------------------------------------------------- /.github/workflows/blui-pr-actions.yml: -------------------------------------------------------------------------------- 1 | name: blui-pr-actions 2 | on: 3 | pull_request_target: 4 | types: 5 | - opened 6 | 7 | permissions: 8 | pull-requests: write 9 | contents: read 10 | 11 | jobs: 12 | pr-labels: 13 | uses: etn-ccis/blui-automation/.github/workflows/blui-labels.yml@dev 14 | secrets: inherit 15 | 16 | pr-comment: 17 | uses: etn-ccis/blui-automation/.github/workflows/blui-comment.yml@dev 18 | secrets: inherit -------------------------------------------------------------------------------- /.github/workflows/firebase-hosting-merge.yml: -------------------------------------------------------------------------------- 1 | # This file was auto-generated by the Firebase CLI 2 | # https://github.com/firebase/firebase-tools 3 | 4 | name: Deploy to Firebase Hosting on merge 5 | 'on': 6 | push: 7 | branches: 8 | - master 9 | jobs: 10 | build_and_deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - run: yarn && yarn build 15 | - uses: FirebaseExtended/action-hosting-deploy@v0 16 | with: 17 | repoToken: '${{ secrets.GITHUB_TOKEN }}' 18 | firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_BLUI_ANGULAR_DESIGN_PATTERNS }}' 19 | channelId: live 20 | projectId: blui-angular-design-patterns 21 | -------------------------------------------------------------------------------- /.github/workflows/firebase-hosting-pull-request.yml: -------------------------------------------------------------------------------- 1 | # This file was auto-generated by the Firebase CLI 2 | # https://github.com/firebase/firebase-tools 3 | 4 | name: Deploy to Firebase Hosting on PR 5 | 'on': pull_request 6 | jobs: 7 | build_and_preview: 8 | if: '${{ github.event.pull_request.head.repo.full_name == github.repository }}' 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - run: yarn && yarn build 13 | - uses: FirebaseExtended/action-hosting-deploy@v0 14 | with: 15 | repoToken: '${{ secrets.GITHUB_TOKEN }}' 16 | firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_BLUI_ANGULAR_DESIGN_PATTERNS }}' 17 | expires: 3d 18 | projectId: blui-angular-design-patterns 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | .angular 10 | 11 | # dependencies 12 | /node_modules 13 | 14 | # profiling files 15 | chrome-profiler-events*.json 16 | speed-measure-plugin*.json 17 | 18 | # IDEs and editors 19 | /.idea 20 | .project 21 | .classpath 22 | .c9/ 23 | *.launch 24 | .settings/ 25 | *.sublime-workspace 26 | 27 | # IDE - VSCode 28 | .vscode/* 29 | !.vscode/settings.json 30 | !.vscode/tasks.json 31 | !.vscode/launch.json 32 | !.vscode/extensions.json 33 | .history/* 34 | 35 | # misc 36 | /.sass-cache 37 | /connect.lock 38 | /coverage 39 | /cypress-coverage 40 | .nyc_output 41 | /libpeerconnection.log 42 | npm-debug.log 43 | yarn-error.log 44 | testem.log 45 | /typings 46 | 47 | # System Files 48 | .DS_Store 49 | Thumbs.db 50 | -------------------------------------------------------------------------------- /.stackblitzrc: -------------------------------------------------------------------------------- 1 | { 2 | "installDependencies": true, 3 | "startCommand": "turbo start", 4 | "env": { 5 | "ENABLE_CJS_IMPORTS": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @surajeaton @JeffGreiner-eaton @ektaghag-eaton 2 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "video": false 3 | } 4 | -------------------------------------------------------------------------------- /cypress/coverage.webpack.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | module: { 3 | rules: [ 4 | { 5 | test: /\.(js|ts)$/, 6 | loader: 'istanbul-instrumenter-loader', 7 | options: { esModules: true }, 8 | enforce: 'post', 9 | include: require('path').join(__dirname, '..', 'src'), 10 | exclude: [ 11 | /\.(e2e|spec)\.ts$/, 12 | /node_modules/, 13 | /(ngfactory|ngstyle)\.js/ 14 | ] 15 | } 16 | ] 17 | } 18 | }; -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } -------------------------------------------------------------------------------- /cypress/integration/account-menu/in-appbar.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Account menu in appbar', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/account-menu/in-an-app-bar') 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'In an App Bar'); 11 | }); 12 | 13 | it('should display menu from generic avatar', () => { 14 | cy.get('[data-cy=generic-avatar-menu]').click() 15 | cy.contains('Log In') 16 | }); 17 | 18 | it('should dismiss menu from generic avatar', () => { 19 | cy.get('[data-cy=generic-avatar-menu]').click() 20 | cy.get('body').click().should('not.contain', 'Log In') 21 | }); 22 | }); -------------------------------------------------------------------------------- /cypress/integration/account-menu/in-drawer.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Account menu in drawer', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/account-menu/in-a-drawer') 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'In a Drawer'); 11 | }); 12 | 13 | it('should display drawer when toggled', () => { 14 | cy.get('[data-cy=toggle-drawer]').click({ force: true }) 15 | cy.get('[data-cy=drawer-header]').should('be.visible') 16 | }); 17 | 18 | it('should dismiss drawer on close', () => { 19 | cy.get('[data-cy=toggle-drawer]').click() 20 | cy.get('[data-cy=drawer-header]').should('be.visible') 21 | cy.get('[data-cy=close-drawer]').click() 22 | cy.get('[data-cy=drawer-header] > .mat-toolbar').should('not.be.visible') 23 | }); 24 | }); -------------------------------------------------------------------------------- /cypress/integration/appbar/collapsible-app-bar.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Collapsible app bar', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/app-bar/collapsible'); 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Timeline'); 11 | }); 12 | 13 | it('should display collapsed appbar on scroll', () => { 14 | cy.get('.mat-toolbar-row').scrollIntoView({ duration: 1000, offset:{ top: 200, left: 0 }}) 15 | cy.get('[data-cy=blui-toolbar]').scrollIntoView().should('be.visible') 16 | .invoke('prop', 'offsetHeight').should('equal', 64) 17 | }); 18 | 19 | it('should display expanded appbar on scroll', () => { 20 | cy.get('.mat-toolbar-row').scrollIntoView({ duration: 1000, offset:{ bottom: 200, left: 0 }}) 21 | cy.get('[data-cy=blui-toolbar]').scrollIntoView().should('be.visible') 22 | .invoke('prop', 'offsetHeight').should('equal', 200) 23 | }); 24 | }); -------------------------------------------------------------------------------- /cypress/integration/appbar/dropdown-toolbar.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Dropdown app bar', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/app-bar/dropdown-toolbar') 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('.blui-dropdown-toolbar-title').should('contain', 'Title'); 11 | }); 12 | 13 | it('should display correct subtitle on selected', () => { 14 | cy.contains('subtitle').click() 15 | cy.contains('All Locations').click() 16 | cy.get('.blui-dropdown-toolbar-subtitle').should('contain', 'All Locations') 17 | }); 18 | }); -------------------------------------------------------------------------------- /cypress/integration/appbar/page-wide-search.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Page wide search', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/app-bar/page-wide-search') 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Page Search'); 11 | }); 12 | 13 | it('should filter data when searching', () => { 14 | cy.get('#mat-input-0').click().type('grape') 15 | cy.get('[data-cy=list-view]').should('contain', 'Grape').and('have.length', (1)) 16 | cy.get('#mat-input-0').clear() 17 | cy.get('#mat-input-0').click().type('water') 18 | cy.get('[data-cy=list-view]').should('contain', 'Watermelon').and('have.length', (1)) 19 | 20 | }); 21 | 22 | it('should return no results when data does not exist', () => { 23 | cy.get('#mat-input-0').click().type('123') 24 | cy.get('body').should('contain', 'No Results.') 25 | 26 | }); 27 | }); -------------------------------------------------------------------------------- /cypress/integration/appbar/search-bar.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Search bar', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/app-bar/global-search') 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Search'); 11 | }); 12 | 13 | it('should filter data when searching', () => { 14 | cy.get('[data-cy=search-btn]').click() 15 | cy.get('[data-cy=searchfield]').type('grape') 16 | cy.get('[data-cy=list-view] > .mat-list-item > .mat-list-item-content').should('contain', 'Grape').and('have.length', (1)) 17 | cy.get('[data-cy=search-close-btn]').click() 18 | cy.get('[data-cy=searchfield]').type('water') 19 | cy.get('[data-cy=list-view]').should('contain', 'Watermelon').and('have.length', (1)) 20 | 21 | }); 22 | 23 | it('should return no results when data does not exist', () => { 24 | cy.get('[data-cy=search-btn]').click() 25 | cy.get('[data-cy=searchfield]').type('123') 26 | cy.get('body').should('contain', 'No Results') 27 | 28 | }); 29 | }); -------------------------------------------------------------------------------- /cypress/integration/form-validation/in-list.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Form validation in a list', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/forms-and-validation/in-a-list'); 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'In a List'); 11 | }); 12 | 13 | it('should display ip address left aligned', () => { 14 | cy.get('[data-cy=ip-address]').invoke('css', 'text-align').should('equal', 'left') 15 | }); 16 | 17 | it('should display unselected toggle', () => { 18 | cy.get('.mat-slide-toggle-bar') 19 | .find('input') 20 | .filter('#mat-slide-toggle-1-input') 21 | .should('have.attr', 'aria-checked', 'false') 22 | }); 23 | 24 | it('should display selected toggle', () => { 25 | cy.get('.mat-slide-toggle-bar').click() 26 | cy.get('.mat-slide-toggle-bar') 27 | .find('input') 28 | .filter('#mat-slide-toggle-1-input') 29 | .should('have.attr', 'aria-checked', 'true') 30 | }); 31 | }); -------------------------------------------------------------------------------- /cypress/integration/form-validation/table.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Form validation in a table', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/forms-and-validation/in-a-table'); 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'In A Table'); 11 | }); 12 | 13 | it('should have Min placeholder', () => { 14 | cy.get('#mat-input-0').invoke('attr', 'placeholder').should('contain', 'Min') 15 | }); 16 | 17 | it('should have Max placeholder', () => { 18 | cy.get('#mat-input-1').invoke('attr', 'placeholder').should('contain', 'Max') 19 | }); 20 | 21 | it('should display Min Max align right', () => { 22 | cy.get('#mat-input-1').invoke('css', 'text-align').should('equal', 'right') 23 | }); 24 | }); -------------------------------------------------------------------------------- /cypress/integration/i18n/right-to-left.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('R2L', () => { 4 | beforeEach(() => { 5 | cy.visit('http://localhost:4200/internationalization'); 6 | }); 7 | 8 | it('should display default page title', () => { 9 | cy.get('.app-bar').should('contain', 'Internationalization'); 10 | }); 11 | 12 | it('should display page title RTL', () => { 13 | cy.get('[data-cy=change-language]').click() 14 | cy.contains('Arabic').click() 15 | cy.get('.app-bar').should('contain', 'تدويل') 16 | .invoke('prop', 'offsetLeft').should('be.lessThan', 20) 17 | }); 18 | 19 | it('should display R2L menu RTL', () => { 20 | cy.get('[data-cy=change-language]').click() 21 | cy.contains('Arabic').click() 22 | cy.get('[data-cy=R2L-menu]') 23 | .invoke('prop', 'offsetLeft').should('be.greaterThan', 5) 24 | }); 25 | 26 | it('should display language selector RTL', () => { 27 | cy.get('[data-cy=change-language]').click() 28 | cy.contains('Arabic').click() 29 | cy.get('[data-cy=change-language]') 30 | .invoke('prop', 'offsetLeft').should('eq', 0) 31 | }); 32 | 33 | // it('should display item list RTL', () => { 34 | // cy.get('[data-cy=change-language]').click() 35 | // cy.contains('Arabic').click() 36 | // // cy.get('[data-cy=list-items] > :nth-child(2) > .mat-list-item > .mat-list-item-content').first() 37 | // cy.get('[data-cy=list-items]').first() 38 | // .invoke('prop', 'offsetLeft').should('be.greaterThan', 10) 39 | // }); 40 | }); -------------------------------------------------------------------------------- /cypress/integration/lists/action-list.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Action list', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/lists/action-list'); 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Action List'); 11 | }); 12 | 13 | it('should add list items when add is clicked', () => { 14 | cy.get('[data-cy=list-content]').children().should('have.length', '10'); 15 | cy.get('[data-cy=toolbar-add]').click() 16 | cy.get('[data-cy=list-content]').children().should('have.length', '11'); 17 | cy.get('[data-cy=toolbar-add]').click() 18 | cy.get('[data-cy=list-content]').children().should('have.length', '12'); 19 | }); 20 | 21 | it('should remove list items when delete is clicked', () => { 22 | cy.get('[data-cy=list-content]').children().should('have.length', '10'); 23 | cy.get('[data-cy=action-menu]').first().click() 24 | cy.get('[data-cy=remove]').click() 25 | cy.get('[data-cy=list-content]').children().should('have.length', '9'); 26 | cy.get('[data-cy=action-menu]').first().click() 27 | cy.get('[data-cy=remove]').click() 28 | cy.get('[data-cy=list-content]').children().should('have.length', '8'); 29 | }); 30 | 31 | it('should remove all items and display empty state', () => { 32 | cy.get('[data-cy=toolbar-delete]').click() 33 | cy.get('.blui-empty-state').should('contain', 'No Items Found') 34 | cy.get('[data-cy=blui-empty-state-add]').click() 35 | cy.get('[data-cy=list-content]').children().should('have.length', '1'); 36 | }); 37 | }); -------------------------------------------------------------------------------- /cypress/integration/lists/data-list.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Data list', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/lists/data-list'); 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Data List'); 11 | }); 12 | 13 | it('should display list', () => { 14 | cy.contains('George Washington') 15 | cy.contains('John Adams') 16 | cy.contains('Thomas Jefferson') 17 | cy.contains('James Madison') 18 | cy.contains('James Monroe') 19 | 20 | }); 21 | }); -------------------------------------------------------------------------------- /cypress/integration/lists/multiselect-list.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Multi-select list', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/lists/multi-select-list'); 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Multiselect List'); 11 | }); 12 | }); -------------------------------------------------------------------------------- /cypress/integration/lists/responsive-table.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Responsive table', () => { 5 | 6 | it('should display title and table header on desktop', () => { 7 | cy.viewport(1024, 635) 8 | cy.visit('localhost:4200/lists/responsive-table') 9 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Responsive Table') 10 | cy.get('.mat-header-row').should('contain', 'Name').and('contain', 'Details') 11 | cy.get('[data-cy=blui-toolbar]').should('not.contain.value', 'data-cy=toolbar-menu') 12 | 13 | }); 14 | 15 | it('should display title and table header on tablet', () => { 16 | cy.viewport(768, 1024) 17 | cy.visit('localhost:4200/lists/responsive-table') 18 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Responsive Table') 19 | cy.get('.mat-header-row').should('contain', 'Name').and('contain', 'Details') 20 | cy.get('[data-cy=toolbar-menu]').should('be.visible') 21 | 22 | }); 23 | 24 | it('should display title and no table header on phone', () => { 25 | cy.viewport(375, 812) 26 | cy.visit('localhost:4200/lists/responsive-table') 27 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Responsive Table') 28 | cy.get('.mat-header-row').should('not.be.visible') 29 | cy.get('[data-cy=toolbar-menu]').should('be.visible') 30 | 31 | 32 | }); 33 | }); -------------------------------------------------------------------------------- /cypress/integration/lists/sortable-list.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Sortable list', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/lists/sortable-list'); 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Sortable List'); 11 | }); 12 | it('should enable sortable list items on edit', () => { 13 | cy.get('[data-cy=action-btn] > .mat-focus-indicator').click() 14 | 15 | cy.get('[data-cy=sortable-row-0] > .mat-list-item > .mat-list-item-content > .mat-list-icon').should('be.visible') 16 | 17 | }); 18 | 19 | it('should drag item in list to location', () => { 20 | cy.get('[data-cy=action-btn] > .mat-focus-indicator').click() 21 | cy.get('[data-cy=sortable-row-0] > .mat-list-item > .mat-list-item-content > .mat-list-icon') 22 | .trigger('mousedown', { button: 0 }, { force: true }) 23 | .trigger('mousemove', { force: true, x: 100, y: 100 }) 24 | cy.get('[data-cy=sortable-row-1] > .mat-list-item > .mat-list-item-content').click() 25 | cy.get('[data-cy=sortable-row-1]').should('contain', 'Item 01') 26 | cy.get('[data-cy=action-btn] > .mat-focus-indicator').click() 27 | 28 | }); 29 | }); -------------------------------------------------------------------------------- /cypress/integration/lists/status-list.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | // todo add data-test=id to info list items 4 | describe('status list', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/lists/status-list'); 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Status List'); 11 | }); 12 | }); -------------------------------------------------------------------------------- /cypress/integration/loading-wait-states/contextual-spinners.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Loading contextual spinner', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/loading-states/contextual-spinners'); 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Contextual Spinner') 11 | }); 12 | 13 | it('Should trigger spinner loading overlay on login button ', () => { 14 | cy.get('[data-cy=login-btn]').click() 15 | cy.get('[data-cy=login-btn-spinner] > .mat-button-wrapper > .spinner-container > .mat-spinner > svg > .ng-star-inserted').should('be.visible') 16 | }); 17 | it('Should trigger spinner loading overlay on start fab button ', () => { 18 | cy.get('[data-cy=start-btn]').click() 19 | cy.get('[data-cy=start-btn-spinner] > .mat-button-wrapper > .spinner-container > .mat-spinner > svg > .ng-star-inserted').should('be.visible') 20 | }); 21 | }); -------------------------------------------------------------------------------- /cypress/integration/loading-wait-states/progress-bars.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Progress bar loading', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/loading-states/progress-bars'); 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Progress Bars') 11 | }); 12 | 13 | it('Should trigger progress bar on load', () => { 14 | cy.get('[data-cy=upload-btn] > .mat-focus-indicator').click() 15 | cy.get('[data-cy=upload-status] > .blui-info-list-item > .mat-list-item > .mat-list-item-content').should('be.visible',{ force: true }) 16 | }); 17 | }); -------------------------------------------------------------------------------- /cypress/integration/loading-wait-states/skeleton-loader.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Skeleton loading state', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/loading-states/skeletons-loader'); 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Skeletons') 11 | }); 12 | 13 | it('Should trigger score card skeleton loading on refreash', () => { 14 | cy.reload() 15 | cy.get('.score-card-section > .mat-card > .ph-item').should('be.visible') 16 | }); 17 | }); -------------------------------------------------------------------------------- /cypress/integration/loading-wait-states/spinner-overlay.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Loading states', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/loading-states/spinner-overlays'); 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Spinner Overlays') 11 | }); 12 | 13 | it('Should trigger spinner loading overlay on reload ', () => { 14 | cy.get('[data-cy=reload]').click() 15 | cy.get('[data-cy=loading-overlay-spinner] > .mat-spinner > svg > .ng-star-inserted').should('be.visible') 16 | }); 17 | }); -------------------------------------------------------------------------------- /cypress/integration/overlays/basic-bottom-sheet.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | describe('Basic bottom sheet', () => { 5 | beforeEach(() => { 6 | cy.visit('http://localhost:4200/overlays/basic-bottom-sheet'); 7 | }); 8 | 9 | it('should display page title', () => { 10 | cy.get('[data-cy=blui-toolbar]').should('contain', 'Basic Bottom Sheet'); 11 | }); 12 | 13 | it('should display bottom sheet and action items', () => { 14 | cy.get('[data-cy=toolbar-action-menu]').click() 15 | cy.get('[data-cy=ack]').should('contain', 'Acknowledge All') 16 | cy.get('[data-cy=export]').should('contain', 'Export') 17 | cy.get('[data-cy=cancel]').should('contain', 'Cancel') 18 | }); 19 | }); -------------------------------------------------------------------------------- /cypress/integration/view-port-test/toolbar-view-ports.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | const sizes = ['iphone-6', 'ipad-2']; 4 | const urls = ['http://localhost:4200/app-bar/collapsible', 'http://localhost:4200/app-bar/global-search', 5 | 'http://localhost:4200/loading-states/spinner-overlays','http://localhost:4200/loading-states/contextual-spinners','http://localhost:4200/loading-states/skeletons-loader', 6 | 'http://localhost:4200/loading-states/progress-bars','http://localhost:4200/forms-and-validation/password', 'http://localhost:4200/forms-and-validation/phone-number-format', 7 | 'http://localhost:4200/forms-and-validation/sectioned-form', 'http://localhost:4200/forms-and-validation/in-a-table', 'http://localhost:4200/forms-and-validation/in-a-list', 'http://localhost:4200/internationalization', 8 | 'http://localhost:4200/lists/action-list', 'http://localhost:4200/lists/data-list', 'http://localhost:4200/lists/multi-select-list', 9 | 'http://localhost:4200/lists/sortable-list', 'http://localhost:4200/lists/status-list', 'http://localhost:4200/lists/responsive-table', 10 | 'http://localhost:4200/overlays/basic-bottom-sheet', 'http://localhost:4200/overlays/complex-bottom-sheet', 11 | 'http://localhost:4200/dynamic-stepper']; 12 | 13 | describe('Hidden toolbar menu displays on small devices', () => { 14 | urls.forEach(url => { 15 | describe(`url: ${url}`, () => { 16 | sizes.forEach(size => { 17 | it(`should display menu icon ${url}`, () => { 18 | cy.visit(url); 19 | cy.viewport(size); 20 | cy.get('[data-cy=toolbar-menu]') 21 | .should('be.visible') 22 | }); 23 | }); 24 | }); 25 | }); 26 | }); -------------------------------------------------------------------------------- /cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | /** 16 | * @type {Cypress.PluginConfig} 17 | */ 18 | // cypress/plugins/index.js 19 | module.exports = (on, config) => { 20 | require('@cypress/code-coverage/task')(on, config); 21 | return config; 22 | }; -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | //require('@4tw/cypress-drag-drop') 27 | import '@4tw/cypress-drag-drop' 28 | -------------------------------------------------------------------------------- /cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | 22 | // cypress/support/index.js 23 | import '@cypress/code-coverage/support' 24 | -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: ['./src/**/*.e2e-spec.ts'], 13 | capabilities: { 14 | browserName: 'chrome', 15 | }, 16 | directConnect: true, 17 | baseUrl: 'http://localhost:4200/', 18 | framework: 'jasmine', 19 | jasmineNodeOpts: { 20 | showColors: true, 21 | defaultTimeoutInterval: 30000, 22 | print: function () {}, 23 | }, 24 | onPrepare() { 25 | require('ts-node').register({ 26 | project: require('path').join(__dirname, './tsconfig.json'), 27 | }); 28 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('angular-design-patterns app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain( 20 | jasmine.objectContaining({ 21 | level: logging.Level.SEVERE, 22 | } as logging.Entry) 23 | ); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo(): Promise { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText(): Promise { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": ["jasmine", "jasminewd2", "node"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "dist/angular-design-patterns", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ], 9 | "rewrites": [ 10 | { 11 | "source": "**", 12 | "destination": "/index.html" 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /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-coverage'), 11 | require('karma-chrome-launcher'), 12 | require('karma-jasmine-html-reporter'), 13 | require('karma-coverage-istanbul-reporter'), 14 | require('@angular-devkit/build-angular/plugins/karma'), 15 | ], 16 | client: { 17 | clearContext: false, // leave Jasmine Spec Runner output visible in browser 18 | }, 19 | coverageIstanbulReporter: { 20 | dir: require('path').join(__dirname, './coverage/angular-design-patterns'), 21 | reports: ['html', 'clover', 'text-summary'], 22 | fixWebpackSourcePaths: true, 23 | }, 24 | reporters: ['progress', 'kjhtml', 'coverage-istanbul'], 25 | port: 9876, 26 | colors: true, 27 | logLevel: config.LOG_INFO, 28 | autoWatch: true, 29 | browsers: ['Chrome'], 30 | singleRun: false, 31 | restartOnFileChange: true, 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 15 | 21 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |
37 |
38 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, waitForAsync } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | import { AppModule } from './app.module'; 5 | 6 | describe('AppComponent', () => { 7 | beforeEach(waitForAsync(() => { 8 | void TestBed.configureTestingModule({ 9 | imports: [AppModule, RouterTestingModule], 10 | }).compileComponents(); 11 | })); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.componentInstance; 16 | void expect(app).toBeTruthy(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/pages/account-menu/in-a-drawer/in-a-drawer.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { 3 | DrawerLayoutModule, 4 | DrawerModule, 5 | EmptyStateModule, 6 | InfoListItemModule, 7 | SpacerModule, 8 | } from '@brightlayer-ui/angular-components'; 9 | import { MatToolbarModule } from '@angular/material/toolbar'; 10 | import { CommonModule } from '@angular/common'; 11 | import { MatIconModule } from '@angular/material/icon'; 12 | import { MatButtonModule } from '@angular/material/button'; 13 | import { MatFormFieldModule } from '@angular/material/form-field'; 14 | import { MatInputModule } from '@angular/material/input'; 15 | import { FormsModule } from '@angular/forms'; 16 | import { MenuInADrawerComponent } from './menu-in-a-drawer.component'; 17 | import { MatSidenavModule } from '@angular/material/sidenav'; 18 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 19 | 20 | @NgModule({ 21 | imports: [ 22 | CommonModule, 23 | BrowserAnimationsModule, 24 | InfoListItemModule, 25 | MatToolbarModule, 26 | SpacerModule, 27 | MatIconModule, 28 | MatInputModule, 29 | MatFormFieldModule, 30 | FormsModule, 31 | EmptyStateModule, 32 | DrawerLayoutModule, 33 | DrawerModule, 34 | MatSidenavModule, 35 | MatButtonModule, 36 | ], 37 | declarations: [MenuInADrawerComponent], 38 | exports: [MenuInADrawerComponent], 39 | }) 40 | export class MenuInDrawerModule {} 41 | -------------------------------------------------------------------------------- /src/app/pages/account-menu/in-a-drawer/menu-in-a-drawer.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette'; 2 | 3 | .menu-in-drawer-example { 4 | .button-container { 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | height: calc(100% - 64px); 9 | 10 | mat-icon { 11 | margin-right: 8px; 12 | font-size: 16px; 13 | height: 16px; 14 | width: 16px; 15 | } 16 | } 17 | } 18 | 19 | .drawer-container { 20 | height: 100vh; 21 | width: 100%; 22 | .mat-sidenav { 23 | max-width: 90%; 24 | transition: width 200ms cubic-bezier(0.4, 0, 0.2, 1); 25 | width: 300px; 26 | } 27 | } 28 | 29 | .avatar { 30 | height: 48px; 31 | width: 48px; 32 | border-radius: 50%; 33 | margin-top: 24px; 34 | margin-bottom: 24px; 35 | } 36 | 37 | ::ng-deep .account-menu-drawer-header { 38 | .blui-drawer-header-content { 39 | height: 150px; 40 | } 41 | .blui-drawer-header-background { 42 | background: linear-gradient(to right, rgba(0, 123, 193, 0.7) 22.4%, rgba(0, 123, 193, 0) 100%), 43 | url('../../../../assets/cubes_tile.png'); 44 | background-size: contain; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/app/pages/account-menu/in-a-drawer/menu-in-a-drawer.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { MenuInADrawerComponent } from './menu-in-a-drawer.component'; 3 | import { MenuInDrawerModule } from './in-a-drawer.module'; 4 | 5 | describe('MenuInADrawerComponent', () => { 6 | let component: MenuInADrawerComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | void TestBed.configureTestingModule({ 11 | imports: [MenuInDrawerModule], 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(MenuInADrawerComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | void expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/account-menu/in-an-app-bar/chip/chip.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 | {{ label }} 7 |
8 | 9 |
10 |
11 |
12 | -------------------------------------------------------------------------------- /src/app/pages/account-menu/in-an-app-bar/chip/chip.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette.scss'; 2 | 3 | .blui-chip-list { 4 | ::ng-deep .mat-chip.mat-standard-chip { 5 | background-color: map-get($blui-white, 50) !important; 6 | border: 1px solid #424e541f; 7 | cursor: pointer; 8 | } 9 | 10 | ::ng-deep .mat-chip.mat-standard-chip.highlight, 11 | ::ng-deep .mat-chip.mat-standard-chip:hover { 12 | background-color: map-get($blui-white, 500) !important; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/app/pages/account-menu/in-an-app-bar/chip/chip.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ChipComponent } from './chip.component'; 4 | 5 | describe('ChipComponent', () => { 6 | let component: ChipComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ChipComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ChipComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/account-menu/in-an-app-bar/chip/chip.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, SimpleChanges, Output } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'blui-chip', 5 | templateUrl: './chip.component.html', 6 | styleUrls: ['./chip.component.scss'], 7 | }) 8 | export class ChipComponent { 9 | @Input() icon: string; 10 | @Input() label: string; 11 | @Input() selected = false; 12 | @Output() 13 | openUserMenu = new EventEmitter(); 14 | open = false; 15 | constructor() {} 16 | 17 | ngOnChanges(simpleChanges: SimpleChanges): void { 18 | this.selected = simpleChanges.selected.currentValue; 19 | } 20 | 21 | toggleChip(): void { 22 | this.openUserMenu.emit(!this.open); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/app/pages/account-menu/in-an-app-bar/in-an-app-bar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { InAnAppBarComponent } from './in-an-app-bar.component'; 4 | 5 | describe('InAnAppBarComponent', () => { 6 | let component: InAnAppBarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [InAnAppBarComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(InAnAppBarComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/account-menu/in-an-app-bar/in-an-app-bar.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { MatButtonModule } from '@angular/material/button'; 4 | import { MatChipsModule } from '@angular/material/chips'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatListModule } from '@angular/material/list'; 7 | import { MatMenuModule } from '@angular/material/menu'; 8 | import { MatToolbarModule } from '@angular/material/toolbar'; 9 | 10 | import { SpacerModule, UserMenuModule, InfoListItemModule } from '@brightlayer-ui/angular-components'; 11 | 12 | import { InAnAppBarComponent } from './in-an-app-bar.component'; 13 | import { ChipComponent } from './chip/chip.component'; 14 | 15 | @NgModule({ 16 | declarations: [InAnAppBarComponent, ChipComponent], 17 | imports: [ 18 | CommonModule, 19 | MatButtonModule, 20 | MatChipsModule, 21 | MatIconModule, 22 | MatListModule, 23 | MatMenuModule, 24 | MatToolbarModule, 25 | InfoListItemModule, 26 | SpacerModule, 27 | UserMenuModule, 28 | ], 29 | }) 30 | export class InAnAppBarModule {} 31 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/collapsible-app-bar/collapsible-app-bar.component.scss: -------------------------------------------------------------------------------- 1 | .collapsible-appbar-actions { 2 | display: flex; 3 | margin: 0 -12px; 4 | button { 5 | margin: 0 8px; 6 | } 7 | } 8 | .blui-three-liner-subtitle { 9 | font-weight: 400; 10 | } 11 | 12 | .blui-app-bar-background { 13 | background: linear-gradient(to bottom, rgba(0, 123, 193, 1) 22.4%, rgba(0, 123, 193, 0.2) 100%), 14 | url('../../../../assets/collapsible_app_bar_demo.jpg'); 15 | } 16 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/collapsible-app-bar/collapsible-app-bar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { CollapsibleAppBarComponent } from './collapsible-app-bar.component'; 3 | import { CollapsibleAppBarModule } from './collapsible-app-bar.module'; 4 | import { InAListComponent } from '../../forms-and-validation/in-a-list/in-a-list.component'; 5 | 6 | describe('CollapsibleAppBarComponent', () => { 7 | let component: CollapsibleAppBarComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async () => { 11 | await TestBed.configureTestingModule({ 12 | declarations: [InAListComponent], 13 | imports: [CollapsibleAppBarModule], 14 | }).compileComponents(); 15 | }); 16 | 17 | beforeEach(() => { 18 | fixture = TestBed.createComponent(CollapsibleAppBarComponent); 19 | component = fixture.componentInstance; 20 | component.scrollElement = undefined; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/collapsible-app-bar/collapsible-app-bar.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectorRef, Component, OnInit, ViewEncapsulation } from '@angular/core'; 2 | import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout'; 3 | import { StateService } from '../../../services/state.service'; 4 | 5 | @Component({ 6 | selector: 'app-collapsible-app-bar', 7 | templateUrl: './collapsible-app-bar.component.html', 8 | styleUrls: ['./collapsible-app-bar.component.scss'], 9 | encapsulation: ViewEncapsulation.None, 10 | }) 11 | export class CollapsibleAppBarComponent implements OnInit { 12 | isCollapsed: boolean; 13 | isSmall: boolean; 14 | scrollElement = { name: 'mat-drawer-content', index: 0 }; 15 | 16 | constructor( 17 | private readonly _ref: ChangeDetectorRef, 18 | private readonly _drawerService: StateService, 19 | private readonly _breakpointObserver: BreakpointObserver 20 | ) {} 21 | 22 | ngOnInit(): void { 23 | this._breakpointObserver 24 | .observe([Breakpoints.Small, Breakpoints.Handset]) 25 | .subscribe((state: BreakpointState) => { 26 | if (state.matches) { 27 | this.isSmall = true; 28 | } else { 29 | this.isSmall = false; 30 | } 31 | }); 32 | } 33 | 34 | toggleMenu(): void { 35 | const drawerOpen = this._drawerService.getDrawerOpen(); 36 | this._drawerService.setDrawerOpen(!drawerOpen); 37 | } 38 | 39 | setCollapsed(isCollapsed: boolean): void { 40 | this.isCollapsed = isCollapsed; 41 | this._ref.detectChanges(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/collapsible-app-bar/collapsible-app-bar.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { AppBarModule, SpacerModule, ThreeLinerModule } from '@brightlayer-ui/angular-components'; 3 | import { CommonModule } from '@angular/common'; 4 | import { MatIconModule } from '@angular/material/icon'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { CollapsibleAppBarComponent } from './collapsible-app-bar.component'; 7 | import { MatBadgeModule } from '@angular/material/badge'; 8 | 9 | @NgModule({ 10 | imports: [ 11 | CommonModule, 12 | AppBarModule, 13 | ThreeLinerModule, 14 | SpacerModule, 15 | MatIconModule, 16 | MatButtonModule, 17 | MatBadgeModule, 18 | ], 19 | declarations: [CollapsibleAppBarComponent], 20 | exports: [CollapsibleAppBarComponent], 21 | }) 22 | export class CollapsibleAppBarModule {} 23 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/contextual/contextual.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { ContextualAppBarComponent } from './contextual.component'; 3 | import { ContextualAppbarModule } from './contextual.module'; 4 | 5 | describe('ContextualAppBarComponent', () => { 6 | let component: ContextualAppBarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | void TestBed.configureTestingModule({ 11 | imports: [ContextualAppbarModule], 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ContextualAppBarComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | void expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/contextual/contextual.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { MatToolbarModule } from '@angular/material/toolbar'; 3 | import { CommonModule } from '@angular/common'; 4 | import { MatIconModule } from '@angular/material/icon'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { MatTableModule } from '@angular/material/table'; 7 | import { MatCheckboxModule } from '@angular/material/checkbox'; 8 | import { ContextualAppBarComponent } from './contextual.component'; 9 | import { EmptyStateModule, SpacerModule } from '@brightlayer-ui/angular-components'; 10 | 11 | @NgModule({ 12 | imports: [ 13 | CommonModule, 14 | EmptyStateModule, 15 | MatButtonModule, 16 | MatCheckboxModule, 17 | MatIconModule, 18 | MatTableModule, 19 | MatToolbarModule, 20 | SpacerModule, 21 | ], 22 | declarations: [ContextualAppBarComponent], 23 | }) 24 | export class ContextualAppbarModule {} 25 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/dropdown-toolbar/dropdown-toolbar-bottomsheet.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MatBottomSheetRef } from '@angular/material/bottom-sheet'; 3 | import { DropdownToolbarStateService } from './dropdown-toolbar.service'; 4 | import { menuItems } from './menuItems'; 5 | 6 | @Component({ 7 | selector: 'app-dropdown-toolbar-bottomsheet', 8 | template: ` 9 | 10 | {{ item }} 18 | 19 | `, 20 | styleUrls: ['./dropdown-toolbar.component.scss'], 21 | }) 22 | export class DropdownToolbarBottomSheetExample { 23 | items = menuItems; 24 | constructor( 25 | private readonly _bottomSheetRef: MatBottomSheetRef, 26 | private readonly _stateService: DropdownToolbarStateService 27 | ) {} 28 | 29 | setSelected(item: string): void { 30 | this._stateService.setSelected(item); 31 | this._bottomSheetRef.dismiss(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/dropdown-toolbar/dropdown-toolbar.component.html: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/dropdown-toolbar/dropdown-toolbar.component.scss: -------------------------------------------------------------------------------- 1 | ::ng-deep .app-dropdown-toolbar-bottomsheet-overlay { 2 | padding: 0; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/dropdown-toolbar/dropdown-toolbar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { DropdownToolbarComponent } from './dropdown-toolbar.component'; 3 | import { DropdownToolbarModule } from './dropdown-toolbar.module'; 4 | 5 | describe('DropdownToolbarComponent', () => { 6 | let component: DropdownToolbarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | void TestBed.configureTestingModule({ 11 | imports: [DropdownToolbarModule], 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(DropdownToolbarComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | void expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/dropdown-toolbar/dropdown-toolbar.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { DropdownToolbarModule as BluiDropdownToolbarModule } from '@brightlayer-ui/angular-components'; 3 | import { CommonModule } from '@angular/common'; 4 | import { MatIconModule } from '@angular/material/icon'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { DropdownToolbarComponent } from './dropdown-toolbar.component'; 7 | import { FlexLayoutModule } from '@angular/flex-layout'; 8 | import { MatMenuModule } from '@angular/material/menu'; 9 | import { MatListModule } from '@angular/material/list'; 10 | import { DropdownToolbarBottomSheetExample } from './dropdown-toolbar-bottomsheet.component'; 11 | import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; 12 | 13 | @NgModule({ 14 | imports: [ 15 | CommonModule, 16 | BluiDropdownToolbarModule, 17 | MatIconModule, 18 | MatButtonModule, 19 | FlexLayoutModule, 20 | MatMenuModule, 21 | MatListModule, 22 | MatBottomSheetModule, 23 | ], 24 | declarations: [DropdownToolbarComponent, DropdownToolbarBottomSheetExample], 25 | exports: [DropdownToolbarComponent, DropdownToolbarBottomSheetExample], 26 | entryComponents: [DropdownToolbarBottomSheetExample], 27 | }) 28 | export class DropdownToolbarModule {} 29 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/dropdown-toolbar/dropdown-toolbar.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root', 5 | }) 6 | export class DropdownToolbarStateService { 7 | selected: string; 8 | 9 | getSelected(): string { 10 | return this.selected; 11 | } 12 | 13 | setSelected(selected: string): void { 14 | this.selected = selected; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/dropdown-toolbar/menuItems.ts: -------------------------------------------------------------------------------- 1 | export const menuItems: string[] = ['All Locations', 'Gary Steel Works', 'US Steel']; 2 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/page-wide-search/page-wide-search.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { PageWideSearchComponent } from './page-wide-search.component'; 3 | import { PageWideSearchModule } from './page-wide-search.module'; 4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 5 | 6 | describe('PageWidthSearchComponent', () => { 7 | let component: PageWideSearchComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(waitForAsync(() => { 11 | void TestBed.configureTestingModule({ 12 | imports: [PageWideSearchModule, BrowserAnimationsModule], 13 | }).compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PageWideSearchComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | void expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/page-wide-search/page-wide-search.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { InfoListItemModule, SpacerModule } from '@brightlayer-ui/angular-components'; 3 | import { MatToolbarModule } from '@angular/material/toolbar'; 4 | import { CommonModule } from '@angular/common'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { MatFormFieldModule } from '@angular/material/form-field'; 8 | import { MatInputModule } from '@angular/material/input'; 9 | import { FormsModule } from '@angular/forms'; 10 | import { PageWideSearchComponent } from './page-wide-search.component'; 11 | import { SearchBarModule } from '../search-bar/search-bar.module'; 12 | import { MatBadgeModule } from '@angular/material/badge'; 13 | 14 | @NgModule({ 15 | imports: [ 16 | CommonModule, 17 | FormsModule, 18 | InfoListItemModule, 19 | MatBadgeModule, 20 | MatButtonModule, 21 | MatFormFieldModule, 22 | MatIconModule, 23 | MatInputModule, 24 | MatToolbarModule, 25 | SearchBarModule, 26 | SpacerModule, 27 | ], 28 | declarations: [PageWideSearchComponent], 29 | }) 30 | export class PageWideSearchModule {} 31 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/search-bar/search-bar.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette.scss'; 2 | 3 | .toolbar-container { 4 | position: sticky; 5 | z-index: 1000; 6 | top: 0; 7 | overflow-x: hidden; 8 | } 9 | .main-header { 10 | transition: opacity 250ms ease-in-out; 11 | 12 | &.hidden { 13 | opacity: 0; 14 | } 15 | } 16 | 17 | .search-header { 18 | position: absolute; 19 | top: 0; 20 | right: 0; 21 | width: 0%; 22 | background: map-get($blui-white, 50); 23 | transition: width 250ms ease-in-out; 24 | 25 | &.active { 26 | width: 100%; 27 | } 28 | 29 | .search-control { 30 | font-family: 'Open Sans'; 31 | flex: 1; 32 | font-size: 1rem; 33 | border: 0; 34 | outline: none; 35 | padding-left: 12px; 36 | margin-left: 12px; 37 | 38 | &::-ms-clear { 39 | height: 0; 40 | width: 0; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/search-bar/search-bar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { SearchBarComponent } from './search-bar.component'; 3 | import { SearchBarModule } from './search-bar.module'; 4 | 5 | describe('SearchBarComponent', () => { 6 | let component: SearchBarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | void TestBed.configureTestingModule({ 11 | imports: [SearchBarModule], 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(SearchBarComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | void expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/search-bar/search-bar.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { EmptyStateModule, InfoListItemModule, SpacerModule } from '@brightlayer-ui/angular-components'; 3 | import { MatToolbarModule } from '@angular/material/toolbar'; 4 | import { CommonModule } from '@angular/common'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { MatFormFieldModule } from '@angular/material/form-field'; 8 | import { MatInputModule } from '@angular/material/input'; 9 | import { FormsModule } from '@angular/forms'; 10 | import { SearchBarComponent } from './search-bar.component'; 11 | import { FilterPipe } from './shared/filter.pipe'; 12 | import { BoldSearchPipe } from './shared/bold-search.pipe'; 13 | 14 | @NgModule({ 15 | imports: [ 16 | CommonModule, 17 | InfoListItemModule, 18 | MatToolbarModule, 19 | SpacerModule, 20 | MatIconModule, 21 | MatButtonModule, 22 | MatInputModule, 23 | MatFormFieldModule, 24 | FormsModule, 25 | EmptyStateModule, 26 | ], 27 | declarations: [SearchBarComponent, FilterPipe, BoldSearchPipe], 28 | exports: [SearchBarComponent, FilterPipe, BoldSearchPipe], 29 | }) 30 | export class SearchBarModule {} 31 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/search-bar/shared/bold-search.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | @Pipe({ 3 | name: 'boldSearch', 4 | }) 5 | export class BoldSearchPipe implements PipeTransform { 6 | transform(item: string, searchText: string): string { 7 | if (searchText) { 8 | const re = new RegExp(searchText, 'gi'); 9 | return item.replace(re, '$&'); 10 | } 11 | return item; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/pages/app-bar/search-bar/shared/filter.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | @Pipe({ 3 | name: 'filter', 4 | }) 5 | export class FilterPipe implements PipeTransform { 6 | transform(items: any[], searchText: string): any[] { 7 | if (!items) { 8 | return []; 9 | } 10 | if (!searchText) { 11 | return items; 12 | } 13 | return items.filter((it) => it.toLowerCase().includes(searchText.toLowerCase())); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/app/pages/dynamic-stepper/dynamic-stepper.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette.scss'; 2 | 3 | .radio-group { 4 | display: flex; 5 | flex-direction: column; 6 | } 7 | 8 | .delete-icon { 9 | vertical-align: middle; 10 | color: map-get($blui-black, 500); 11 | font-size: 24px; 12 | line-height: 24px; 13 | } 14 | 15 | .add-button { 16 | width: 100%; 17 | height: 100%; 18 | padding: 16px 60px; 19 | background: transparent; 20 | text-align: left; 21 | 22 | &[disabled] { 23 | background: transparent; 24 | } 25 | } 26 | 27 | ::ng-deep .mat-step-label, 28 | .add-button { 29 | font-weight: 400; 30 | font-size: 1rem; 31 | } 32 | 33 | ::ng-deep .mat-step:last-child .mat-step-label { 34 | position: absolute; 35 | left: 0; 36 | top: 0; 37 | bottom: 0; 38 | right: 0; 39 | } 40 | 41 | .stepper-with-data, 42 | .stepper-with-empty-state { 43 | background-color: map-get($blui-white, 50); 44 | min-height: calc(100vh - 64px); 45 | } 46 | 47 | @media only screen and (max-width: 600px) { 48 | .stepper-with-data, 49 | .stepper-with-empty-state { 50 | min-height: calc(100vh - 56px); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/app/pages/dynamic-stepper/dynamic-stepper.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { InfoListItemModule, SpacerModule, EmptyStateModule } from '@brightlayer-ui/angular-components'; 3 | import { MatToolbarModule } from '@angular/material/toolbar'; 4 | import { CommonModule } from '@angular/common'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { DynamicStepperComponent } from './dynamic-stepper.component'; 8 | import { MatStepperModule } from '@angular/material/stepper'; 9 | import { MatRadioModule } from '@angular/material/radio'; 10 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 11 | import { MatTooltipModule } from '@angular/material/tooltip'; 12 | 13 | @NgModule({ 14 | imports: [ 15 | CommonModule, 16 | InfoListItemModule, 17 | MatToolbarModule, 18 | SpacerModule, 19 | MatIconModule, 20 | MatButtonModule, 21 | MatStepperModule, 22 | MatRadioModule, 23 | BrowserAnimationsModule, 24 | EmptyStateModule, 25 | MatTooltipModule, 26 | ], 27 | declarations: [DynamicStepperComponent], 28 | }) 29 | export class DynamicStepperModule {} 30 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/fix-length-passcode/fix-length-passcode.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { MatFormFieldModule } from '@angular/material/form-field'; 5 | import { MatToolbarModule } from '@angular/material/toolbar'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { MatIconModule } from '@angular/material/icon'; 8 | import { MatInputModule } from '@angular/material/input'; 9 | import { MatDividerModule } from '@angular/material/divider'; 10 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 11 | import { BrowserModule } from '@angular/platform-browser'; 12 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 13 | 14 | import { FixLengthPasscodeComponent } from './fix-length-passcode.component'; 15 | @NgModule({ 16 | declarations: [FixLengthPasscodeComponent], 17 | imports: [ 18 | BrowserModule, 19 | FormsModule, 20 | ReactiveFormsModule, 21 | BrowserAnimationsModule, 22 | CommonModule, 23 | MatToolbarModule, 24 | MatIconModule, 25 | MatButtonModule, 26 | MatInputModule, 27 | MatFormFieldModule, 28 | MatDividerModule, 29 | MatProgressSpinnerModule, 30 | ], 31 | }) 32 | export class FixLengthPasscodeModule {} 33 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/in-a-list/in-a-list.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette.scss'; 2 | 3 | section.list-validation { 4 | padding-top: 24px; 5 | max-width: 686px; 6 | margin: 0 auto; 7 | 8 | .list-card { 9 | padding: 0; 10 | mat-list { 11 | padding: 0; 12 | } 13 | } 14 | } 15 | .ip-address { 16 | border: 0; 17 | background-color: map-get($blui-white, 200); 18 | height: 40px; 19 | outline: 0; 20 | font-size: 16px; 21 | padding: 8px 16px; 22 | border-bottom: 1px solid map-get($blui-gray, 100); 23 | } 24 | 25 | .list-validation { 26 | ::ng-deep .mat-form-field-flex { 27 | padding-top: 0; 28 | margin-top: 20px; 29 | height: 40px; 30 | } 31 | 32 | ::ng-deep .mat-form-field-appearance-fill .mat-form-field-infix { 33 | padding: 0; 34 | } 35 | 36 | ::ng-deep 37 | .blui-blue 38 | .mat-form-field.mat-primary:not(.mat-form-field-disabled).mat-form-field-appearance-fill 39 | .mat-form-field-flex { 40 | padding-top: 0; 41 | } 42 | } 43 | 44 | @media only screen and (max-width: 600px) { 45 | section.list-validation { 46 | padding-top: 0; 47 | .list-card { 48 | border-radius: 0; 49 | } 50 | ::ng-deep .mat-form-field-wrapper { 51 | width: 138px; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/in-a-list/in-a-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { InAListComponent } from './in-a-list.component'; 4 | import { InAListModule } from './in-a-list.module'; 5 | 6 | describe('InAListComponent', () => { 7 | let component: InAListComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async () => { 11 | await TestBed.configureTestingModule({ 12 | declarations: [InAListComponent], 13 | imports: [InAListModule], 14 | }).compileComponents(); 15 | }); 16 | 17 | beforeEach(() => { 18 | fixture = TestBed.createComponent(InAListComponent); 19 | component = fixture.componentInstance; 20 | fixture.detectChanges(); 21 | }); 22 | 23 | it('should create', () => { 24 | expect(component).toBeTruthy(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/in-a-list/in-a-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout'; 3 | import { FormGroup, FormBuilder } from '@angular/forms'; 4 | import { StateService } from '../../../services/state.service'; 5 | 6 | @Component({ 7 | selector: 'app-in-a-list', 8 | templateUrl: './in-a-list.component.html', 9 | styleUrls: ['./in-a-list.component.scss'], 10 | }) 11 | export class InAListComponent implements OnInit { 12 | isSmall: boolean; 13 | listForm: FormGroup; 14 | constructor( 15 | private readonly _drawerService: StateService, 16 | private readonly _breakpointObserver: BreakpointObserver, 17 | private readonly _formBuilder: FormBuilder 18 | ) { 19 | this.initForm(); 20 | } 21 | 22 | ngOnInit(): void { 23 | this._breakpointObserver 24 | .observe([Breakpoints.Small, Breakpoints.Handset]) 25 | .subscribe((state: BreakpointState) => { 26 | if (state.matches) { 27 | this.isSmall = true; 28 | } else { 29 | this.isSmall = false; 30 | } 31 | }); 32 | } 33 | 34 | initForm(): void { 35 | this.listForm = this._formBuilder.group({ 36 | ipAddress: ['10.0.0.1'], 37 | }); 38 | } 39 | 40 | toggleMenu(): void { 41 | const drawerOpen = this._drawerService.getDrawerOpen(); 42 | this._drawerService.setDrawerOpen(!drawerOpen); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/in-a-list/in-a-list.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { MatFormFieldModule } from '@angular/material/form-field'; 5 | import { MatToolbarModule } from '@angular/material/toolbar'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { MatIconModule } from '@angular/material/icon'; 8 | import { MatInputModule } from '@angular/material/input'; 9 | import { MatCardModule } from '@angular/material/card'; 10 | import { MatSlideToggleModule } from '@angular/material/slide-toggle'; 11 | import { BrowserModule } from '@angular/platform-browser'; 12 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 13 | import { InfoListItemModule } from '@brightlayer-ui/angular-components'; 14 | 15 | import { InAListComponent } from './in-a-list.component'; 16 | 17 | @NgModule({ 18 | declarations: [InAListComponent], 19 | imports: [ 20 | BrowserModule, 21 | FormsModule, 22 | ReactiveFormsModule, 23 | BrowserAnimationsModule, 24 | CommonModule, 25 | MatToolbarModule, 26 | MatButtonModule, 27 | MatIconModule, 28 | MatInputModule, 29 | MatFormFieldModule, 30 | MatCardModule, 31 | MatSlideToggleModule, 32 | InfoListItemModule, 33 | ], 34 | }) 35 | export class InAListModule {} 36 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/in-a-table/in-a-table.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { InATableComponent } from './in-a-table.component'; 4 | import { InATableModule } from './in-a-table.module'; 5 | 6 | describe('InATableComponent', () => { 7 | let component: InATableComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async () => { 11 | await TestBed.configureTestingModule({ 12 | imports: [InATableModule], 13 | declarations: [InATableComponent], 14 | }).compileComponents(); 15 | }); 16 | 17 | beforeEach(() => { 18 | fixture = TestBed.createComponent(InATableComponent); 19 | component = fixture.componentInstance; 20 | fixture.detectChanges(); 21 | }); 22 | 23 | it('should create', () => { 24 | expect(component).toBeTruthy(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/in-a-table/in-a-table.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { MatFormFieldModule } from '@angular/material/form-field'; 5 | import { MatToolbarModule } from '@angular/material/toolbar'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { MatIconModule } from '@angular/material/icon'; 8 | import { MatInputModule } from '@angular/material/input'; 9 | import { BrowserModule } from '@angular/platform-browser'; 10 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 11 | import { MatTableModule } from '@angular/material/table'; 12 | import { InfoListItemModule } from '@brightlayer-ui/angular-components'; 13 | 14 | import { InATableComponent } from './in-a-table.component'; 15 | @NgModule({ 16 | declarations: [InATableComponent], 17 | imports: [ 18 | BrowserModule, 19 | FormsModule, 20 | ReactiveFormsModule, 21 | BrowserAnimationsModule, 22 | CommonModule, 23 | MatToolbarModule, 24 | MatIconModule, 25 | MatButtonModule, 26 | MatInputModule, 27 | MatFormFieldModule, 28 | MatTableModule, 29 | InfoListItemModule, 30 | ], 31 | }) 32 | export class InATableModule {} 33 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/password/password-strength-checker/password-strength-checker.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette'; 2 | 3 | .blui-auth-password-check.mat-list-item ::ng-deep .mat-list-item-content { 4 | padding: 0; 5 | } 6 | 7 | .blui-auth-password-check { 8 | height: auto; 9 | 10 | .validation-message { 11 | padding-left: 8px; 12 | font-weight: 400; 13 | line-height: 1.75; 14 | } 15 | .check-icon { 16 | height: 18px; 17 | width: 18px; 18 | font-size: 18px; 19 | } 20 | } 21 | 22 | mat-icon, 23 | .validation-message.success { 24 | color: map-get($blui-gray, 200); 25 | } 26 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/password/password-strength-checker/password-strength-checker.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-password-strength-check', 5 | template: ` 6 | 7 | {{ icon }} 8 | 9 | {{ validationMessage }} 10 | 11 | 12 | `, 13 | styleUrls: ['password-strength-checker.component.scss'], 14 | }) 15 | export class PasswordStrengthCheckComponent { 16 | @Input() icon = 'done'; 17 | @Input() validationMessage: string; 18 | @Input() success = false; 19 | } 20 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/password/password-strength-checker/password-strength-checker.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { PasswordModule } from '../password.module'; 3 | 4 | import { PasswordStrengthCheckComponent } from './password-strength-checker.component'; 5 | 6 | describe('PasswordComponent', () => { 7 | let component: PasswordStrengthCheckComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(waitForAsync(() => { 11 | void TestBed.configureTestingModule({ 12 | imports: [PasswordModule], 13 | }).compileComponents(); 14 | })); 15 | 16 | beforeEach(async () => { 17 | await TestBed.configureTestingModule({ 18 | declarations: [PasswordStrengthCheckComponent], 19 | }).compileComponents(); 20 | }); 21 | 22 | beforeEach(() => { 23 | fixture = TestBed.createComponent(PasswordStrengthCheckComponent); 24 | component = fixture.componentInstance; 25 | fixture.detectChanges(); 26 | }); 27 | 28 | it('should create', () => { 29 | expect(component).toBeTruthy(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/password/password.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { PasswordModule } from './password.module'; 3 | 4 | import { PasswordComponent } from './password.component'; 5 | 6 | describe('PasswordComponent', () => { 7 | let component: PasswordComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(waitForAsync(() => { 11 | void TestBed.configureTestingModule({ 12 | imports: [PasswordModule], 13 | }).compileComponents(); 14 | })); 15 | 16 | beforeEach(async () => { 17 | await TestBed.configureTestingModule({ 18 | declarations: [PasswordComponent], 19 | }).compileComponents(); 20 | }); 21 | 22 | beforeEach(() => { 23 | fixture = TestBed.createComponent(PasswordComponent); 24 | component = fixture.componentInstance; 25 | fixture.detectChanges(); 26 | }); 27 | 28 | it('should create', () => { 29 | expect(component).toBeTruthy(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/phone-number-format/phone-number-format.component.scss: -------------------------------------------------------------------------------- 1 | section.form-validation-section { 2 | padding-top: 40px; 3 | max-width: 504px; 4 | margin: 0 auto; 5 | .phone-number-form { 6 | display: flex; 7 | .country-code-select { 8 | max-width: 135px; 9 | } 10 | .phone-number-input { 11 | margin-left: 16px; 12 | flex: 1; 13 | } 14 | } 15 | } 16 | 17 | ::ng-deep .mat-form-field.mat-focused.mat-primary .mat-select-arrow { 18 | border-top: 0; 19 | border-bottom: 5px solid; 20 | } 21 | 22 | ::ng-deep .cdk-overlay-container .cdk-overlay-pane .selectPanel { 23 | min-width: 135px !important; 24 | height: 152px; 25 | margin: 32px 4px; 26 | } 27 | 28 | @media only screen and (max-width: 600px) { 29 | section.form-validation-section { 30 | max-width: 100%; 31 | padding: 32px 16px 16px; 32 | .phone-number-form { 33 | .phone-number-input { 34 | width: 192px; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/phone-number-format/phone-number-format.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PhoneNumberFormatComponent } from './phone-number-format.component'; 4 | import { PhoneNumberFormatModule } from './phone-number-format.module'; 5 | 6 | describe('PhoneNumberFormatComponent', () => { 7 | let component: PhoneNumberFormatComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async () => { 11 | await TestBed.configureTestingModule({ 12 | imports: [PhoneNumberFormatModule], 13 | declarations: [PhoneNumberFormatComponent], 14 | }).compileComponents(); 15 | }); 16 | 17 | beforeEach(() => { 18 | fixture = TestBed.createComponent(PhoneNumberFormatComponent); 19 | component = fixture.componentInstance; 20 | fixture.detectChanges(); 21 | }); 22 | 23 | it('should create', () => { 24 | expect(component).toBeTruthy(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/phone-number-format/phone-number-format.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { MatToolbarModule } from '@angular/material/toolbar'; 4 | import { MatIconModule } from '@angular/material/icon'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { MatInputModule } from '@angular/material/input'; 7 | import { MatFormFieldModule } from '@angular/material/form-field'; 8 | import { BrowserModule } from '@angular/platform-browser'; 9 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 10 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 11 | import { PhoneNumberFormatComponent } from './phone-number-format.component'; 12 | import { MatSelectModule } from '@angular/material/select'; 13 | 14 | import { FormatPhoneNumberPipe } from './shared/format-phone-number.pipe'; 15 | @NgModule({ 16 | declarations: [PhoneNumberFormatComponent, FormatPhoneNumberPipe], 17 | imports: [ 18 | BrowserModule, 19 | FormsModule, 20 | ReactiveFormsModule, 21 | BrowserAnimationsModule, 22 | CommonModule, 23 | MatToolbarModule, 24 | MatIconModule, 25 | MatButtonModule, 26 | MatInputModule, 27 | MatFormFieldModule, 28 | MatSelectModule, 29 | ], 30 | }) 31 | export class PhoneNumberFormatModule {} 32 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/phone-number-format/shared/format-phone-number.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { FormatPhoneNumberPipe } from './format-phone-number.pipe'; 2 | 3 | describe('FormatPhoneNumberPipe', () => { 4 | const pipe = new FormatPhoneNumberPipe(); 5 | it('create an instance', () => { 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | 9 | it('format phone number as per US country', () => { 10 | expect(pipe.transform('1234567890', 'US')).toBe('123 456 7890'); 11 | }); 12 | 13 | it('format phone number as per CA country while typing', () => { 14 | expect(pipe.transform('123456', 'CA')).toBe('123 456'); 15 | }); 16 | 17 | it('format phone number as per EG country', () => { 18 | expect(pipe.transform('12345678', 'EG')).toBe('1 2345678'); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/sectioned-form/sectioned-form.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SectionedFormComponent } from './sectioned-form.component'; 4 | import { SectionedFormModule } from './sectioned-form.module'; 5 | 6 | describe('SectionedFormComponent', () => { 7 | let component: SectionedFormComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async () => { 11 | await TestBed.configureTestingModule({ 12 | imports: [SectionedFormModule], 13 | declarations: [SectionedFormComponent], 14 | }).compileComponents(); 15 | }); 16 | 17 | beforeEach(() => { 18 | fixture = TestBed.createComponent(SectionedFormComponent); 19 | component = fixture.componentInstance; 20 | fixture.detectChanges(); 21 | }); 22 | 23 | it('should create', () => { 24 | expect(component).toBeTruthy(); 25 | }); 26 | 27 | it('form invalid when empty', () => { 28 | expect(component.factoryDetailsForm.valid).toBeFalsy(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/sectioned-form/sectioned-form.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { MatFormFieldModule } from '@angular/material/form-field'; 5 | import { MatToolbarModule } from '@angular/material/toolbar'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { MatIconModule } from '@angular/material/icon'; 8 | import { MatInputModule } from '@angular/material/input'; 9 | import { MatDividerModule } from '@angular/material/divider'; 10 | import { MatSelectModule } from '@angular/material/select'; 11 | import { MatTooltipModule } from '@angular/material/tooltip'; 12 | import { MatCheckboxModule } from '@angular/material/checkbox'; 13 | import { BrowserModule } from '@angular/platform-browser'; 14 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 15 | 16 | import { SectionedFormComponent } from './sectioned-form.component'; 17 | 18 | @NgModule({ 19 | declarations: [SectionedFormComponent], 20 | imports: [ 21 | BrowserModule, 22 | FormsModule, 23 | ReactiveFormsModule, 24 | BrowserAnimationsModule, 25 | CommonModule, 26 | MatToolbarModule, 27 | MatIconModule, 28 | MatButtonModule, 29 | MatInputModule, 30 | MatFormFieldModule, 31 | MatDividerModule, 32 | MatSelectModule, 33 | MatTooltipModule, 34 | MatCheckboxModule, 35 | ], 36 | }) 37 | export class SectionedFormModule {} 38 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/verify-on-submit/slide-panel/slide-panel.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/verify-on-submit/slide-panel/slide-panel.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | overflow: hidden; 4 | } 5 | 6 | .panes { 7 | height: 100%; 8 | width: 200%; 9 | 10 | display: flex; 11 | div { 12 | flex: 1; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/verify-on-submit/slide-panel/slide-panel.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; 2 | import { animate, state, style, transition, trigger } from '@angular/animations'; 3 | 4 | type PaneDirection = 'left' | 'right'; 5 | 6 | @Component({ 7 | selector: 'blui-slide-panel', 8 | styleUrls: ['./slide-panel.component.scss'], 9 | templateUrl: './slide-panel.component.html', 10 | changeDetection: ChangeDetectionStrategy.OnPush, 11 | animations: [ 12 | trigger('slide', [ 13 | state('left', style({ transform: 'translateX(0)' })), 14 | state('right', style({ transform: 'translateX(-50%)' })), 15 | transition('* => *', animate(250)), 16 | ]), 17 | ], 18 | }) 19 | export class SlidePanelComponent { 20 | @Input() activePane: PaneDirection = 'left'; 21 | } 22 | -------------------------------------------------------------------------------- /src/app/pages/forms-and-validation/verify-on-submit/verify-on-submit.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { MatFormFieldModule } from '@angular/material/form-field'; 5 | import { MatToolbarModule } from '@angular/material/toolbar'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { MatIconModule } from '@angular/material/icon'; 8 | import { MatInputModule } from '@angular/material/input'; 9 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 10 | import { BrowserModule } from '@angular/platform-browser'; 11 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 12 | 13 | import { VerifyOnSubmitComponent } from './verify-on-submit.component'; 14 | import { SlidePanelComponent } from './slide-panel/slide-panel.component'; 15 | import { EmptyStateModule } from '@brightlayer-ui/angular-components'; 16 | 17 | @NgModule({ 18 | declarations: [VerifyOnSubmitComponent, SlidePanelComponent], 19 | imports: [ 20 | BrowserModule, 21 | FormsModule, 22 | ReactiveFormsModule, 23 | BrowserAnimationsModule, 24 | CommonModule, 25 | MatToolbarModule, 26 | MatIconModule, 27 | MatButtonModule, 28 | MatInputModule, 29 | MatFormFieldModule, 30 | MatProgressSpinnerModule, 31 | EmptyStateModule, 32 | ], 33 | }) 34 | export class VerifyOnSubmitModule {} 35 | -------------------------------------------------------------------------------- /src/app/pages/i18n/i18n.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { I18nComponent } from './i18n.component'; 3 | import { I18nModule } from './i18n.module'; 4 | 5 | describe('I18nComponent', () => { 6 | let fixture: ComponentFixture; 7 | let component: I18nComponent; 8 | 9 | beforeEach(waitForAsync(() => { 10 | void TestBed.configureTestingModule({ 11 | imports: [I18nModule], 12 | }).compileComponents(); 13 | 14 | fixture = TestBed.createComponent(I18nComponent); 15 | component = fixture.debugElement.componentInstance; 16 | })); 17 | 18 | it('should create the app', () => { 19 | fixture.detectChanges(); 20 | void expect(component).toBeTruthy(); 21 | }); 22 | 23 | it(`should load english by default`, () => { 24 | fixture.detectChanges(); 25 | const i18n = fixture.nativeElement.querySelector('#i18n').innerHTML; 26 | component.translate.use('SE'); 27 | void expect(i18n).toEqual('Internationalization'); 28 | }); 29 | 30 | it(`should load spanish on language change`, () => { 31 | fixture.detectChanges(); 32 | component.translate.use('ES'); 33 | fixture.detectChanges(); 34 | const i18n = fixture.nativeElement.querySelector('#i18n').innerHTML; 35 | void expect(i18n).toEqual('Internacionalización'); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /src/app/pages/i18n/services/bidirectional.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { BidirectionalService } from './bidirectional.service'; 4 | 5 | describe('BidirectionService', () => { 6 | let service: BidirectionalService; 7 | 8 | beforeEach(() => { 9 | void TestBed.configureTestingModule({}); 10 | service = TestBed.inject(BidirectionalService); 11 | }); 12 | 13 | it('should be created', () => { 14 | void expect(service).toBeTruthy(); 15 | }); 16 | 17 | it('should return true if the language is Arabic', () => { 18 | void expect(service.isRTL('AR')).toBe(true); 19 | void expect(service.isRTL('EN')).toBe(false); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/app/pages/i18n/services/bidirectional.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { SampleTranslation } from '../translations/sample-translation'; 3 | 4 | export type LanguageCode = keyof SampleTranslation['LANGUAGES']; 5 | 6 | @Injectable({ 7 | providedIn: 'root', 8 | }) 9 | export class BidirectionalService { 10 | private currentLanguage: LanguageCode; 11 | 12 | changeDirectionality(lang: LanguageCode): void { 13 | this.setCurrentLanguage(lang); 14 | const container = document.querySelector('.app-content'); 15 | // App Container 16 | if (container) { 17 | container.setAttribute('dir', this.isRTL(lang) ? 'rtl' : 'ltr'); 18 | } 19 | 20 | // Snackbar 21 | const overlay = document.querySelector('.cdk-global-overlay-wrapper'); 22 | if (overlay) { 23 | overlay.setAttribute('dir', this.isRTL(lang) ? 'rtl' : 'ltr'); 24 | } 25 | } 26 | 27 | isRTL(lang: LanguageCode): boolean { 28 | return String(lang).toUpperCase() === 'AR'; 29 | } 30 | 31 | getCurrentLanguage(): LanguageCode { 32 | return this.currentLanguage; 33 | } 34 | 35 | setCurrentLanguage(lang: LanguageCode): void { 36 | this.currentLanguage = lang; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/pages/i18n/services/fruit.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | import { english } from '../translations/english'; 4 | 5 | export type Fruit = { 6 | name: string; 7 | price: number; 8 | }; 9 | 10 | @Injectable({ 11 | providedIn: 'root', 12 | }) 13 | export class FruitService { 14 | selectedFruits = new Set(); 15 | fruitSelectionObs = new Subject(); 16 | fruits: Fruit[] = []; 17 | 18 | constructor() { 19 | this._randomlyPriceItems(); 20 | } 21 | 22 | private _randomlyPriceItems(): void { 23 | for (const fruit of Object.keys(english.FRUITS)) { 24 | this.fruits.push({ name: fruit, price: Math.round((Math.random() + Number.EPSILON) * 1000) / 100 }); 25 | } 26 | } 27 | 28 | // Return false to stop event propagation 29 | toggleFruit(fruit: string): boolean { 30 | if (this.selectedFruits.has(fruit)) { 31 | this.selectedFruits.delete(fruit); 32 | } else { 33 | this.selectedFruits.add(fruit); 34 | } 35 | this.emitChange(); 36 | return false; 37 | } 38 | 39 | cancelItems(): void { 40 | this.selectedFruits.clear(); 41 | this.emitChange(); 42 | } 43 | 44 | emitChange(): void { 45 | this.fruitSelectionObs.next(this.selectedFruits.size); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app/pages/i18n/services/language-loader.service.ts: -------------------------------------------------------------------------------- 1 | import { TranslateLoader } from '@ngx-translate/core'; 2 | import { Observable, of } from 'rxjs'; 3 | import { SampleTranslation } from '../translations/sample-translation'; 4 | import { english } from '../translations/english'; 5 | import { spanish } from '../translations/spanish'; 6 | import { german } from '../translations/german'; 7 | import { portuguese } from '../translations/portuguese'; 8 | import { chinese } from '../translations/chinese'; 9 | import { arabic } from '../translations/arabic'; 10 | import { french } from '../translations/french'; 11 | import { Injectable } from '@angular/core'; 12 | import { LanguageCode } from './bidirectional.service'; 13 | 14 | @Injectable({ 15 | providedIn: 'root', 16 | }) 17 | export class LanguageLoaderService implements TranslateLoader { 18 | getTranslation(lang: LanguageCode): Observable { 19 | switch (lang) { 20 | case 'EN': { 21 | return of(english); 22 | } 23 | case 'ES': { 24 | return of(spanish); 25 | } 26 | case 'DE': { 27 | return of(german); 28 | } 29 | case 'PT': { 30 | return of(portuguese); 31 | } 32 | case 'ZH': { 33 | return of(chinese); 34 | } 35 | case 'AR': { 36 | return of(arabic); 37 | } 38 | case 'FR': { 39 | return of(french); 40 | } 41 | default: { 42 | return of(english); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/app/pages/i18n/translations/arabic.ts: -------------------------------------------------------------------------------- 1 | import { SampleTranslation } from './sample-translation'; 2 | 3 | export const arabic: SampleTranslation = { 4 | CURRENCY_CODE: 'EGP', 5 | FRUITS: { 6 | APPLE: 'تفاحة', 7 | BANANA: 'موز', 8 | GRAPE: 'البرتقالي', 9 | ORANGE: 'جوافة', 10 | GUAVA: ' جوزة الهند', 11 | COCONUT: 'عنب', 12 | LEMON: 'ليمون', 13 | PINEAPPLE: 'أناناس', 14 | WATERMELON: 'البطيخ', 15 | }, 16 | LANGUAGES: { 17 | EN: 'الإنجليزية', 18 | ES: 'الأسبانية', 19 | DE: 'ألمانية', 20 | AR: 'عربى', 21 | FR: 'فرنسي', 22 | PT: 'البرتغالية', 23 | ZH: 'صينى', 24 | }, 25 | ADD_TO_CART: 'أضف إلى العربة', 26 | MORE_INFO: 'مزيد من المعلومات', 27 | ITEMS: 'عناصر', 28 | I18N: 'تدويل', 29 | I18N_MENU_TOOLTIP: 'عرض التنقل الجانب', 30 | BLUE: 'أزرق', 31 | MENU_ITEMS: { 32 | HOME: 'الرئيسية', 33 | PROJECTS: 'مشاريع', 34 | WARNINGS: 'إنذار', 35 | SETTINGS: 'إعدادات', 36 | HELP: 'مساعدة', 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /src/app/pages/i18n/translations/chinese.ts: -------------------------------------------------------------------------------- 1 | import { SampleTranslation } from './sample-translation'; 2 | 3 | export const chinese: SampleTranslation = { 4 | CURRENCY_CODE: 'CNY', 5 | FRUITS: { 6 | APPLE: '苹果', 7 | BANANA: '香蕉', 8 | GRAPE: '葡萄', 9 | ORANGE: '橙子', 10 | GUAVA: '番石榴', 11 | COCONUT: '椰子', 12 | LEMON: '柠檬', 13 | PINEAPPLE: '菠萝', 14 | WATERMELON: '西瓜', 15 | }, 16 | LANGUAGES: { 17 | EN: '英语', 18 | ES: '西班牙语', 19 | DE: '德语', 20 | AR: '阿拉伯语', 21 | FR: '法语', 22 | PT: '葡萄牙语', 23 | ZH: '中文', 24 | }, 25 | ADD_TO_CART: '添加到购物车', 26 | MORE_INFO: '更多信息', 27 | ITEMS: '件物品', 28 | I18N: '国际化', 29 | I18N_MENU_TOOLTIP: '查看I18N侧面导航', 30 | BLUE: 'Blue', 31 | MENU_ITEMS: { 32 | HOME: '主页', 33 | PROJECTS: '计划', 34 | WARNINGS: '警告', 35 | SETTINGS: '设定', 36 | HELP: '帮助', 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /src/app/pages/i18n/translations/english.ts: -------------------------------------------------------------------------------- 1 | import { SampleTranslation } from './sample-translation'; 2 | 3 | export const english: SampleTranslation = { 4 | CURRENCY_CODE: 'USD', 5 | FRUITS: { 6 | APPLE: 'Apple', 7 | BANANA: 'Banana', 8 | GRAPE: 'Grape', 9 | ORANGE: 'Orange', 10 | GUAVA: 'Guava', 11 | COCONUT: 'Coconut', 12 | LEMON: 'Lemon', 13 | PINEAPPLE: 'Pineapple', 14 | WATERMELON: 'Watermelon', 15 | }, 16 | LANGUAGES: { 17 | EN: 'English', 18 | ES: 'Spanish', 19 | DE: 'German', 20 | AR: 'Arabic', 21 | FR: 'French', 22 | PT: 'Portuguese', 23 | ZH: 'Chinese', 24 | }, 25 | ADD_TO_CART: 'Add to Cart', 26 | MORE_INFO: 'More information', 27 | ITEMS: 'Items', 28 | I18N: 'Internationalization', 29 | I18N_MENU_TOOLTIP: 'View I18N Side Nav', 30 | BLUE: 'Blue', 31 | MENU_ITEMS: { 32 | HOME: 'Home', 33 | PROJECTS: 'Projects', 34 | WARNINGS: 'Warnings', 35 | SETTINGS: 'Settings', 36 | HELP: 'Help', 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /src/app/pages/i18n/translations/french.ts: -------------------------------------------------------------------------------- 1 | import { SampleTranslation } from './sample-translation'; 2 | 3 | export const french: SampleTranslation = { 4 | CURRENCY_CODE: 'EUR', 5 | 6 | FRUITS: { 7 | APPLE: 'Pomme', 8 | BANANA: 'Banane', 9 | GRAPE: 'Grain de raisin', 10 | ORANGE: 'Orange', 11 | GUAVA: 'Goyave', 12 | COCONUT: 'Noix de coco', 13 | LEMON: 'Citron', 14 | PINEAPPLE: 'Ananas', 15 | WATERMELON: 'Pastèque', 16 | }, 17 | LANGUAGES: { 18 | EN: 'Anglaise', 19 | ES: 'Espagnol', 20 | DE: 'Allemande', 21 | AR: 'Arabe', 22 | FR: 'Française', 23 | PT: 'Portugais', 24 | ZH: 'Chinoise', 25 | }, 26 | ADD_TO_CART: 'Ajouter au chariot', 27 | MORE_INFO: "Plus d'information", 28 | ITEMS: 'Articles', 29 | I18N: 'Internationalisation', 30 | I18N_MENU_TOOLTIP: 'Afficher la navigation latérale', 31 | BLUE: 'Bleue', 32 | MENU_ITEMS: { 33 | HOME: "Page d'accueil", 34 | PROJECTS: 'Projets', 35 | WARNINGS: 'Avertissements', 36 | SETTINGS: 'Réglages', 37 | HELP: 'Aidez-moi', 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /src/app/pages/i18n/translations/german.ts: -------------------------------------------------------------------------------- 1 | import { SampleTranslation } from './sample-translation'; 2 | 3 | export const german: SampleTranslation = { 4 | CURRENCY_CODE: 'EUR', 5 | FRUITS: { 6 | APPLE: 'Apfel', 7 | BANANA: 'Banane', 8 | GRAPE: 'Traube', 9 | ORANGE: 'Orange', 10 | GUAVA: 'Guave', 11 | COCONUT: 'Kokosnuss', 12 | LEMON: 'Zitrone', 13 | PINEAPPLE: 'Ananas', 14 | WATERMELON: 'Wassermelone', 15 | }, 16 | LANGUAGES: { 17 | EN: 'Englisch', 18 | ES: 'Spanisch', 19 | DE: 'Deutsche', 20 | AR: 'Arabisch', 21 | FR: 'Französisch', 22 | PT: 'Portugiesisch', 23 | ZH: 'Chinesisch', 24 | }, 25 | ADD_TO_CART: 'In den Einkaufswagen', 26 | MORE_INFO: 'Mehr Informationen', 27 | ITEMS: 'Artikel', 28 | I18N: 'Internationalisierung', 29 | I18N_MENU_TOOLTIP: 'Seitennavigation anzeigen', 30 | BLUE: 'Blau', 31 | MENU_ITEMS: { 32 | HOME: 'Startseite', 33 | PROJECTS: 'Projekte', 34 | WARNINGS: 'Warnungen', 35 | SETTINGS: 'Einstellungen', 36 | HELP: 'Hilfe', 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /src/app/pages/i18n/translations/portuguese.ts: -------------------------------------------------------------------------------- 1 | import { SampleTranslation } from './sample-translation'; 2 | 3 | export const portuguese: SampleTranslation = { 4 | CURRENCY_CODE: 'EUR', 5 | FRUITS: { 6 | APPLE: 'Maçã', 7 | BANANA: 'Banana', 8 | GRAPE: 'Uva', 9 | ORANGE: 'laranja', 10 | GUAVA: 'Goiaba', 11 | COCONUT: 'Coco', 12 | LEMON: 'Limão', 13 | PINEAPPLE: 'Abacaxi', 14 | WATERMELON: 'Melancia', 15 | }, 16 | LANGUAGES: { 17 | EN: 'Inglês', 18 | ES: 'Espanhola', 19 | DE: 'Alemã', 20 | AR: 'Arábica', 21 | FR: 'Francesa', 22 | PT: 'Portuguesa', 23 | ZH: 'Chinesa', 24 | }, 25 | ADD_TO_CART: 'Adicionar ao carrinho', 26 | MORE_INFO: 'Mais Informações', 27 | ITEMS: 'Itens', 28 | I18N: 'Internacionalização', 29 | I18N_MENU_TOOLTIP: 'Visualizar Navegação Lateral', 30 | BLUE: 'Azul', 31 | MENU_ITEMS: { 32 | HOME: 'Casa', 33 | PROJECTS: 'Projetos', 34 | WARNINGS: 'Advertências', 35 | SETTINGS: 'Definições', 36 | HELP: 'Socorro', 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /src/app/pages/i18n/translations/sample-translation.ts: -------------------------------------------------------------------------------- 1 | export type SampleTranslation = { 2 | CURRENCY_CODE: string; 3 | FRUITS: { 4 | APPLE: string; 5 | BANANA: string; 6 | GRAPE: string; 7 | ORANGE: string; 8 | GUAVA: string; 9 | COCONUT: string; 10 | LEMON: string; 11 | PINEAPPLE: string; 12 | WATERMELON: string; 13 | }; 14 | LANGUAGES: { 15 | EN: string; 16 | ES: string; 17 | AR: string; 18 | DE: string; 19 | FR: string; 20 | PT: string; 21 | ZH: string; 22 | }; 23 | MENU_ITEMS: { 24 | HOME: string; 25 | PROJECTS: string; 26 | WARNINGS: string; 27 | SETTINGS: string; 28 | HELP: string; 29 | }; 30 | ADD_TO_CART: string; 31 | MORE_INFO: string; 32 | ITEMS: string; 33 | I18N: string; 34 | I18N_MENU_TOOLTIP: string; 35 | BLUE: string; 36 | }; 37 | -------------------------------------------------------------------------------- /src/app/pages/i18n/translations/spanish.ts: -------------------------------------------------------------------------------- 1 | import { SampleTranslation } from './sample-translation'; 2 | 3 | export const spanish: SampleTranslation = { 4 | CURRENCY_CODE: 'EUR', 5 | FRUITS: { 6 | APPLE: 'Manzana', 7 | BANANA: 'Plátano', 8 | GRAPE: 'Uva', 9 | ORANGE: 'Naranja', 10 | GUAVA: 'Guayaba', 11 | COCONUT: 'Coco', 12 | LEMON: 'Limón', 13 | PINEAPPLE: 'Piña', 14 | WATERMELON: 'Sandía', 15 | }, 16 | LANGUAGES: { 17 | EN: 'Inglés', 18 | ES: 'Español', 19 | DE: 'Alemán', 20 | AR: 'Arábica', 21 | FR: 'Francesa', 22 | PT: 'Portuguesa', 23 | ZH: 'China', 24 | }, 25 | ADD_TO_CART: ' Añadir a la cesta', 26 | MORE_INFO: 'Más información', 27 | ITEMS: 'artículos', 28 | I18N: 'Internacionalización', 29 | I18N_MENU_TOOLTIP: 'Ver navegación lateral', 30 | BLUE: 'Blue', 31 | MENU_ITEMS: { 32 | HOME: 'Inicio', 33 | PROJECTS: 'Proyectos', 34 | WARNINGS: 'Advertencias', 35 | SETTINGS: 'Ajustes', 36 | HELP: 'Ayuda', 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /src/app/pages/landing-page/landing-page.component.scss: -------------------------------------------------------------------------------- 1 | .landing-page-link { 2 | width: 100%; 3 | text-align: left; 4 | text-transform: none; 5 | } 6 | -------------------------------------------------------------------------------- /src/app/pages/landing-page/landing-page.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { LandingPageComponent } from './landing-page.component'; 3 | import { LandingPageModule } from './landing-page.module'; 4 | 5 | describe('LandingPageComponent', () => { 6 | let component: LandingPageComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | void TestBed.configureTestingModule({ 11 | imports: [LandingPageModule], 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(LandingPageComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | void expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/landing-page/landing-page.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { MatToolbarModule } from '@angular/material/toolbar'; 3 | import { CommonModule } from '@angular/common'; 4 | import { MatIconModule } from '@angular/material/icon'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { LandingPageComponent } from './landing-page.component'; 7 | import { FlexLayoutModule } from '@angular/flex-layout'; 8 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | CommonModule, 13 | BrowserAnimationsModule, 14 | MatToolbarModule, 15 | MatIconModule, 16 | MatButtonModule, 17 | FlexLayoutModule, 18 | ], 19 | declarations: [LandingPageComponent], 20 | }) 21 | export class LandingPageModule {} 22 | -------------------------------------------------------------------------------- /src/app/pages/list/action-list/action-list.component.scss: -------------------------------------------------------------------------------- 1 | mat-list { 2 | padding-top: 0px; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/pages/list/action-list/action-list.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { MatToolbarModule } from '@angular/material/toolbar'; 3 | import { CommonModule } from '@angular/common'; 4 | import { ActionListComponent } from './action-list.component'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { MatIconModule } from '@angular/material/icon'; 7 | import { MatMenuModule } from '@angular/material/menu'; 8 | import { InfoListItemModule, EmptyStateModule, SpacerModule } from '@brightlayer-ui/angular-components'; 9 | import { MatTooltipModule } from '@angular/material/tooltip'; 10 | 11 | @NgModule({ 12 | imports: [ 13 | CommonModule, 14 | MatToolbarModule, 15 | MatButtonModule, 16 | MatIconModule, 17 | MatMenuModule, 18 | InfoListItemModule, 19 | EmptyStateModule, 20 | SpacerModule, 21 | MatTooltipModule, 22 | ], 23 | declarations: [ActionListComponent], 24 | }) 25 | export class ActionListModule {} 26 | -------------------------------------------------------------------------------- /src/app/pages/list/data-list/data-list.component.html: -------------------------------------------------------------------------------- 1 | 2 | 5 |
Data List
6 | 7 |
8 | 9 | 10 | {{ item.name }} 11 | {{ item.year }} 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/app/pages/list/data-list/data-list.component.scss: -------------------------------------------------------------------------------- 1 | mat-nav-list { 2 | padding-top: 0; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/pages/list/data-list/data-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { DataListComponent } from './data-list.component'; 3 | import { DataListModule } from './data-list.module'; 4 | 5 | describe('DataListComponent', () => { 6 | let component: DataListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | void TestBed.configureTestingModule({ 11 | imports: [DataListModule], 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(DataListComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | void expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/list/data-list/data-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout'; 3 | import { StateService } from '../../../services/state.service'; 4 | 5 | @Component({ 6 | selector: 'app-data-list', 7 | templateUrl: './data-list.component.html', 8 | styleUrls: ['./data-list.component.scss'], 9 | }) 10 | export class DataListComponent implements OnInit { 11 | list = [ 12 | { 13 | name: 'George Washington', 14 | year: 1789, 15 | }, 16 | { 17 | name: 'John Adams', 18 | year: 1796, 19 | }, 20 | { 21 | name: 'Thomas Jefferson', 22 | year: 1800, 23 | }, 24 | { 25 | name: 'James Madison', 26 | year: 1808, 27 | }, 28 | { 29 | name: 'James Monroe', 30 | year: 1812, 31 | }, 32 | ]; 33 | isSmall: boolean; 34 | 35 | constructor( 36 | private readonly _drawerService: StateService, 37 | private readonly _breakpointObserver: BreakpointObserver 38 | ) {} 39 | 40 | ngOnInit(): void { 41 | this._breakpointObserver 42 | .observe([Breakpoints.Small, Breakpoints.Handset]) 43 | .subscribe((state: BreakpointState) => { 44 | if (state.matches) { 45 | this.isSmall = true; 46 | } else { 47 | this.isSmall = false; 48 | } 49 | }); 50 | } 51 | 52 | toggleMenu(): void { 53 | const drawerOpen = this._drawerService.getDrawerOpen(); 54 | this._drawerService.setDrawerOpen(!drawerOpen); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/app/pages/list/data-list/data-list.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { DataListComponent } from './data-list.component'; 3 | import { InfoListItemModule, SpacerModule } from '@brightlayer-ui/angular-components'; 4 | import { MatToolbarModule } from '@angular/material/toolbar'; 5 | import { CommonModule } from '@angular/common'; 6 | import { MatIconModule } from '@angular/material/icon'; 7 | import { MatButtonModule } from '@angular/material/button'; 8 | 9 | @NgModule({ 10 | imports: [CommonModule, InfoListItemModule, MatToolbarModule, SpacerModule, MatIconModule, MatButtonModule], 11 | declarations: [DataListComponent], 12 | }) 13 | export class DataListModule {} 14 | -------------------------------------------------------------------------------- /src/app/pages/list/in-panel-header/in-panel-header.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette.scss'; 2 | .panel-header-example { 3 | padding: 24px; 4 | margin: 0 auto; 5 | max-width: 816px; 6 | 7 | .filter-card { 8 | padding: 0; 9 | 10 | .filter-card-header { 11 | padding: 16px 0; 12 | border-bottom: 1px solid rgba(0, 0, 0, 0.12); 13 | 14 | .mat-card-title { 15 | margin: 0; 16 | font-size: 14px; 17 | color: map-get($blui-blue, 500); 18 | } 19 | 20 | .filter-selector { 21 | width: 120px; 22 | padding: 0 16px; 23 | margin-right: 4px; 24 | } 25 | } 26 | } 27 | 28 | .list-section { 29 | padding: 0; 30 | } 31 | 32 | .no-items { 33 | text-align: center; 34 | padding: 16px; 35 | } 36 | } 37 | 38 | ::ng-deep .cdk-overlay-container .cdk-overlay-pane .selectPanel { 39 | min-width: 154px !important; 40 | height: auto; 41 | margin: 32px -12px; 42 | right: 0; 43 | position: absolute; 44 | } 45 | 46 | .list-section { 47 | ::ng-deep .blui-chevron { 48 | color: map-get($blui-gray, 500); 49 | } 50 | } 51 | @media only screen and (max-width: 960px) { 52 | .panel-header-example { 53 | padding: 0; 54 | max-width: 100%; 55 | 56 | .filter-card { 57 | border-radius: 0; 58 | box-shadow: none; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/app/pages/list/in-panel-header/in-panel-header.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { InPanelHeaderComponent } from './in-panel-header.component'; 3 | import { InPanelHeaderModule } from './in-panel-header.module'; 4 | 5 | describe('InPanelHeaderComponent', () => { 6 | let component: InPanelHeaderComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | void TestBed.configureTestingModule({ 11 | imports: [InPanelHeaderModule], 12 | declarations: [InPanelHeaderComponent], 13 | }).compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(InPanelHeaderComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | void expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/pages/list/in-panel-header/in-panel-header.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { MatToolbarModule } from '@angular/material/toolbar'; 3 | import { CommonModule } from '@angular/common'; 4 | import { FilterPipe } from './shared/filter.pipe'; 5 | import { InPanelHeaderComponent } from './in-panel-header.component'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { MatCardModule } from '@angular/material/card'; 8 | import { MatIconModule } from '@angular/material/icon'; 9 | import { MatListModule } from '@angular/material/list'; 10 | import { MatMenuModule } from '@angular/material/menu'; 11 | import { InfoListItemModule, SpacerModule } from '@brightlayer-ui/angular-components'; 12 | import { MatSelectModule } from '@angular/material/select'; 13 | @NgModule({ 14 | imports: [ 15 | CommonModule, 16 | InfoListItemModule, 17 | MatButtonModule, 18 | MatCardModule, 19 | MatIconModule, 20 | MatListModule, 21 | MatMenuModule, 22 | MatSelectModule, 23 | MatToolbarModule, 24 | SpacerModule, 25 | ], 26 | declarations: [InPanelHeaderComponent, FilterPipe], 27 | }) 28 | export class InPanelHeaderModule {} 29 | -------------------------------------------------------------------------------- /src/app/pages/list/in-panel-header/shared/filter.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { FilterPipe } from './filter.pipe'; 2 | 3 | describe('FilterPipe', () => { 4 | const pipe = new FilterPipe(); 5 | const items = [ 6 | { 7 | title: 'Item 01', 8 | subtitle: 'Registered 7 days ago', 9 | value: 8, 10 | }, 11 | { 12 | title: 'Item 02', 13 | subtitle: 'Registered 15 days ago', 14 | value: 15, 15 | }, 16 | { 17 | title: 'Item 03', 18 | subtitle: 'Registered 30 days ago', 19 | value: 30, 20 | }, 21 | ]; 22 | it('create an instance', () => { 23 | expect(pipe).toBeTruthy(); 24 | }); 25 | 26 | it('filtered data which is registered 8 days ago', () => { 27 | expect(pipe.transform(items, 7).length).toBe(0); 28 | }); 29 | 30 | it('filtered data which is registered 15 days ago', () => { 31 | expect(pipe.transform(items, 15).length).toBe(2); 32 | }); 33 | 34 | it('filtered data which is registered 30 days ago', () => { 35 | expect(pipe.transform(items, 30).length).toBe(3); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /src/app/pages/list/in-panel-header/shared/filter.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | @Pipe({ 3 | name: 'filter', 4 | }) 5 | export class FilterPipe implements PipeTransform { 6 | transform(items: any[], day: number): any[] { 7 | if (!items) { 8 | return []; 9 | } 10 | if (!day) { 11 | return items; 12 | } 13 | return items.filter((item) => item.value <= day); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-actions/inline-actions.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette.scss'; 2 | .inline-actions-example { 3 | padding: 24px; 4 | margin: 0 auto; 5 | max-width: 816px; 6 | 7 | .inline-actions-card { 8 | padding: 0; 9 | .mat-card-title { 10 | margin: 0; 11 | } 12 | 13 | .list-section { 14 | padding: 0; 15 | } 16 | } 17 | .no-items { 18 | padding: 16px; 19 | text-align: center; 20 | .reset-data { 21 | color: map-get($blui-blue, 500); 22 | text-decoration: underline; 23 | cursor: pointer; 24 | } 25 | } 26 | .note { 27 | padding-top: 120px; 28 | text-align: center; 29 | } 30 | 31 | .inline-actions-buttons { 32 | display: flex; 33 | button:hover { 34 | color: map-get($blui-blue, 500); 35 | background-color: rgba(0, 123, 193, 0.05); 36 | } 37 | } 38 | 39 | .blui-info-list-item:hover { 40 | background-color: map-get($blui-gray, 50); 41 | } 42 | } 43 | 44 | @media only screen and (max-width: 960px) { 45 | .inline-actions-example { 46 | padding: 0; 47 | max-width: 100%; 48 | 49 | .inline-actions-card { 50 | border-radius: 0; 51 | box-shadow: none; 52 | } 53 | 54 | .note { 55 | display: none; 56 | } 57 | 58 | .blui-info-list-item:hover { 59 | background-color: transparent; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-actions/inline-actions.component.spec.ts: -------------------------------------------------------------------------------- 1 | // import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | // import { InlineActionsComponent } from './inline-actions.component'; 4 | 5 | // describe('InlineActionsComponent', () => { 6 | // let component: InlineActionsComponent; 7 | // let fixture: ComponentFixture; 8 | 9 | // beforeEach(async () => { 10 | // await TestBed.configureTestingModule({ 11 | // declarations: [InlineActionsComponent], 12 | // }).compileComponents(); 13 | // }); 14 | 15 | // beforeEach(() => { 16 | // fixture = TestBed.createComponent(InlineActionsComponent); 17 | // component = fixture.componentInstance; 18 | // fixture.detectChanges(); 19 | // }); 20 | 21 | // it('should create', () => { 22 | // expect(component).toBeTruthy(); 23 | // }); 24 | // }); 25 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-actions/inline-actions.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { MatButtonModule } from '@angular/material/button'; 3 | import { MatToolbarModule } from '@angular/material/toolbar'; 4 | import { CommonModule } from '@angular/common'; 5 | import { InlineActionsComponent } from './inline-actions.component'; 6 | import { MatCardModule } from '@angular/material/card'; 7 | import { MatIconModule } from '@angular/material/icon'; 8 | import { MatListModule } from '@angular/material/list'; 9 | import { MatMenuModule } from '@angular/material/menu'; 10 | import { InfoListItemModule, ListItemTagModule } from '@brightlayer-ui/angular-components'; 11 | import { MatTooltipModule } from '@angular/material/tooltip'; 12 | @NgModule({ 13 | imports: [ 14 | CommonModule, 15 | InfoListItemModule, 16 | ListItemTagModule, 17 | MatButtonModule, 18 | MatCardModule, 19 | MatIconModule, 20 | MatListModule, 21 | MatMenuModule, 22 | MatToolbarModule, 23 | MatTooltipModule, 24 | ], 25 | declarations: [InlineActionsComponent], 26 | }) 27 | export class InlineActionsModule {} 28 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-button-panel/dialog/prompt-dialog.html: -------------------------------------------------------------------------------- 1 |
2 |

3 |

Delete all items?

4 |

5 |
6 |

This cannot be undone.

7 |
8 |
9 | 10 | 11 |
12 |
13 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-button-panel/dialog/prompt-dialog.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { PromptDialog } from './prompt-dialog'; 4 | import { MatDialogModule } from '@angular/material/dialog'; 5 | @NgModule({ 6 | declarations: [PromptDialog], 7 | imports: [CommonModule, MatDialogModule], 8 | }) 9 | export class PromptDialogModule {} 10 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-button-panel/dialog/prompt-dialog.ts: -------------------------------------------------------------------------------- 1 | import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout'; 2 | import { Component, Inject } from '@angular/core'; 3 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 4 | import { DialogService } from '../services/dialog-service'; 5 | @Component({ 6 | selector: 'prompt-dialog', 7 | templateUrl: 'prompt-dialog.html', 8 | }) 9 | export class PromptDialog { 10 | constructor( 11 | public dialogRef: MatDialogRef, 12 | private readonly _dialogService: DialogService, 13 | @Inject(MAT_DIALOG_DATA) public data: any, 14 | private readonly _breakpointObserver: BreakpointObserver 15 | ) {} 16 | 17 | isSmall: boolean; 18 | 19 | ngOnInit(): void { 20 | this._breakpointObserver 21 | .observe([Breakpoints.Small, Breakpoints.Handset]) 22 | .subscribe((state: BreakpointState) => { 23 | if (state.matches) { 24 | this.isSmall = true; 25 | } else { 26 | this.isSmall = false; 27 | } 28 | }); 29 | 30 | const leftPosition = window.innerWidth / 2 - 140 + 146; 31 | const topPosition = window.innerHeight / 2 - 64; 32 | if (!this.isSmall) this.dialogRef.updatePosition({ top: `${topPosition}px`, left: `${leftPosition}px` }); 33 | } 34 | 35 | onCancelClick(): void { 36 | this.dialogRef.close(); 37 | } 38 | 39 | deleteAll(): void { 40 | this._dialogService.deleteData.next(true); 41 | this.dialogRef.close(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-button-panel/inline-button-panel.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette.scss'; 2 | 3 | .inline-button-example { 4 | padding: 24px; 5 | margin: 0 auto; 6 | max-width: 800px; 7 | 8 | .action-button-container { 9 | display: flex; 10 | margin-bottom: 24px; 11 | 12 | .action-button { 13 | min-width: 64px; 14 | padding-left: 8px; 15 | .action-button-icon { 16 | font-size: 20px; 17 | } 18 | } 19 | 20 | .action-button-delete { 21 | min-width: 64px; 22 | padding-left: 8px; 23 | .action-button-icon { 24 | font-size: 20px; 25 | } 26 | } 27 | } 28 | 29 | .list-card { 30 | padding: 0; 31 | } 32 | .list-section { 33 | padding: 0; 34 | } 35 | } 36 | 37 | .list-section { 38 | ::ng-deep .blui-chevron { 39 | color: map-get($blui-gray, 500); 40 | } 41 | } 42 | @media only screen and (max-width: 960px) { 43 | .inline-button-example { 44 | padding: 0; 45 | max-width: 100%; 46 | 47 | .list-card { 48 | border-radius: 0; 49 | box-shadow: none; 50 | } 51 | } 52 | } 53 | 54 | .empty-state-container { 55 | align-items: center; 56 | height: calc(100vh - 64px); 57 | } 58 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-button-panel/inline-button-panel.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { InlineButtonPanelComponent } from './inline-button-panel.component'; 4 | import { InlineButtonPanelModule } from './inline-button-panel.module'; 5 | import { MatDialogModule } from '@angular/material/dialog'; 6 | 7 | describe('InlineButtonPanelComponent', () => { 8 | let component: InlineButtonPanelComponent; 9 | let fixture: ComponentFixture; 10 | 11 | beforeEach(async () => { 12 | await TestBed.configureTestingModule({ 13 | declarations: [InlineButtonPanelComponent], 14 | imports: [InlineButtonPanelModule, MatDialogModule], 15 | }).compileComponents(); 16 | }); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(InlineButtonPanelComponent); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-button-panel/inline-button-panel.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { MatToolbarModule } from '@angular/material/toolbar'; 3 | import { CommonModule } from '@angular/common'; 4 | import { MatButtonModule } from '@angular/material/button'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatMenuModule } from '@angular/material/menu'; 7 | import { InfoListItemModule, EmptyStateModule, SpacerModule } from '@brightlayer-ui/angular-components'; 8 | import { MatTooltipModule } from '@angular/material/tooltip'; 9 | import { InlineButtonPanelComponent } from './inline-button-panel.component'; 10 | import { MatCardModule } from '@angular/material/card'; 11 | import { PromptDialog } from './dialog/prompt-dialog'; 12 | 13 | @NgModule({ 14 | declarations: [InlineButtonPanelComponent, PromptDialog], 15 | imports: [ 16 | CommonModule, 17 | MatToolbarModule, 18 | MatButtonModule, 19 | MatIconModule, 20 | MatMenuModule, 21 | InfoListItemModule, 22 | EmptyStateModule, 23 | SpacerModule, 24 | MatTooltipModule, 25 | MatCardModule, 26 | ], 27 | entryComponents: [PromptDialog], 28 | }) 29 | export class InlineButtonPanelModule {} 30 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-button-panel/services/dialog-service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | 4 | @Injectable({ 5 | providedIn: 'root', 6 | }) 7 | export class DialogService { 8 | deleteData = new Subject(); 9 | constructor() {} 10 | } 11 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/device-edit-mobile/device-edit-mobile.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | Type 7 | 8 | 9 | 10 | 11 | 12 | 20 | 21 | 22 |
23 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/device-edit-mobile/device-edit-mobile.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette.scss'; 2 | 3 | .device-edit { 4 | width: 100%; 5 | height: 100%; 6 | display: flex; 7 | align-items: center; 8 | justify-content: center; 9 | height: calc(100vh - 64px); 10 | .device-edit-card { 11 | width: 100%; 12 | height: 100%; 13 | display: flex; 14 | position: relative; 15 | max-width: 100%; 16 | max-height: calc(100vh - 64px); 17 | flex-direction: column; 18 | padding: 0; 19 | 20 | .device-edit-card-content { 21 | flex: 1 1 0px; 22 | display: flex; 23 | padding: 16px; 24 | margin-bottom: 0; 25 | overflow: auto; 26 | flex-direction: column; 27 | } 28 | 29 | .bottom-divider { 30 | position: relative; 31 | } 32 | 33 | .device-edit-card-actions { 34 | padding: 0; 35 | margin: 16px; 36 | 37 | .done-button { 38 | width: 100%; 39 | } 40 | } 41 | } 42 | } 43 | 44 | @media only screen and (max-width: 600px) { 45 | .device-edit { 46 | height: calc(100vh - 56px); 47 | .device-edit-card { 48 | max-width: 100%; 49 | max-height: 100%; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/device-edit-mobile/device-edit-mobile.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DeviceEditMobileComponent } from './device-edit-mobile.component'; 4 | 5 | describe('DeviceEditMobileComponent', () => { 6 | let component: DeviceEditMobileComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [DeviceEditMobileComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(DeviceEditMobileComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/device-edit-mobile/device-edit-mobile.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, EventEmitter } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-device-edit-mobile', 5 | templateUrl: './device-edit-mobile.component.html', 6 | styleUrls: ['./device-edit-mobile.component.scss'], 7 | }) 8 | export class DeviceEditMobileComponent { 9 | @Input() mobileDeviceName = ''; 10 | @Output() newMobileDeviceName = new EventEmitter(); 11 | 12 | addNewMobileDeviceName(value: string): void { 13 | this.newMobileDeviceName.emit(value); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/inline-local-actions.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { InlineLocalActionsComponent } from './inline-local-actions.component'; 3 | import { LocalActionsDialogComponent } from './local-actions-dialog/local-actions-dialog.component'; 4 | import { InlineLocalActionsModule } from './inline-local-actions.module'; 5 | 6 | describe('InlineLocalActionsComponent', () => { 7 | let component: InlineLocalActionsComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async () => { 11 | await TestBed.configureTestingModule({ 12 | declarations: [InlineLocalActionsComponent, LocalActionsDialogComponent], 13 | imports: [InlineLocalActionsModule], 14 | }).compileComponents(); 15 | }); 16 | 17 | beforeEach(() => { 18 | fixture = TestBed.createComponent(InlineLocalActionsComponent); 19 | component = fixture.componentInstance; 20 | fixture.detectChanges(); 21 | }); 22 | 23 | it('should create', () => { 24 | expect(component).toBeTruthy(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/language-list/language-list.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 9 | {{ item.id === selectedLanguage ? 'check' : undefined }} 10 |
11 | {{ item.language }} 12 |
13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/language-list/language-list.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette.scss'; 2 | 3 | .language-list { 4 | background-color: map-get($blui-white, 50); 5 | .language-setting-section { 6 | padding-top: 0; 7 | ::ng-deep .blui-info-list-item { 8 | cursor: pointer; 9 | &:hover { 10 | background-color: rgba(0, 0, 0, 0.04); 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/language-list/language-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LanguageListComponent } from './language-list.component'; 4 | 5 | describe('LanguageListComponent', () => { 6 | let component: LanguageListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [LanguageListComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(LanguageListComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/language-list/language-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, EventEmitter } from '@angular/core'; 2 | 3 | type LanguageItem = { 4 | id: string; 5 | language: string; 6 | }; 7 | @Component({ 8 | selector: 'app-language-list', 9 | templateUrl: './language-list.component.html', 10 | styleUrls: ['./language-list.component.scss'], 11 | }) 12 | export class LanguageListComponent { 13 | @Input() selectedLanguage = ''; 14 | @Output() newSelectedLanguage = new EventEmitter(); 15 | 16 | languageList: LanguageItem[] = [ 17 | { id: 'Deutsch (Germany)', language: 'Deutsch' }, 18 | { id: 'English (United States)', language: 'English (U.S.)' }, 19 | { id: 'Español (Spain)', language: 'Español (ES)' }, 20 | { id: 'Français (France)', language: 'Français (FR)' }, 21 | ]; 22 | 23 | updateLanguage(language: string): void { 24 | this.newSelectedLanguage.emit(language); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/local-actions-dialog/local-actions-dialog.component.html: -------------------------------------------------------------------------------- 1 |

Device

2 |
3 | 4 | Type 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/local-actions-dialog/local-actions-dialog.component.scss: -------------------------------------------------------------------------------- 1 | .mat-dialog-title.local-action-dialog-title { 2 | margin-bottom: 56px; 3 | } 4 | 5 | .mat-dialog-content.local-action-dialog-content { 6 | height: calc(100% - 152px); 7 | border-bottom: 1px solid rgba(0, 0, 0, 0.12); 8 | 9 | mat-form-field { 10 | width: 100%; 11 | } 12 | } 13 | 14 | .mat-dialog-actions.local-action-dialog-actions { 15 | padding-top: 24px; 16 | button { 17 | width: 100%; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/local-actions-dialog/local-actions-dialog.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { LocalActionsDialogComponent } from './local-actions-dialog.component'; 3 | import { MatDialogModule, MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; 4 | 5 | describe('LocalActionsDialogComponent', () => { 6 | let component: LocalActionsDialogComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [LocalActionsDialogComponent], 12 | imports: [MatDialogModule], 13 | providers: [ 14 | { provide: MAT_DIALOG_DATA, useValue: {} }, 15 | { provide: MatDialogRef, useValue: {} }, 16 | ], 17 | }).compileComponents(); 18 | }); 19 | 20 | beforeEach(() => { 21 | fixture = TestBed.createComponent(LocalActionsDialogComponent); 22 | component = fixture.componentInstance; 23 | fixture.detectChanges(); 24 | }); 25 | 26 | it('should create', () => { 27 | expect(component).toBeTruthy(); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/local-actions-dialog/local-actions-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject } from '@angular/core'; 2 | 3 | import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; 4 | type DialogData = { 5 | deviceName: string; 6 | }; 7 | 8 | @Component({ 9 | selector: 'app-local-actions-dialog', 10 | templateUrl: './local-actions-dialog.component.html', 11 | styleUrls: ['./local-actions-dialog.component.scss'], 12 | }) 13 | export class LocalActionsDialogComponent { 14 | constructor( 15 | public dialogRef: MatDialogRef, 16 | @Inject(MAT_DIALOG_DATA) public data: DialogData 17 | ) {} 18 | 19 | closeDialog(): void { 20 | this.dialogRef.close(this.data.deviceName); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/scorecard/scorecard.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 19 |
20 |
21 |
22 |
23 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/scorecard/scorecard.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette.scss'; 2 | .score-card-container { 3 | max-width: 350px; 4 | margin: 16px; 5 | } 6 | ::ng-deep .blui-blue .action-details-score-card.blui-score-card .blui-score-card-header { 7 | background-color: map-get($blui-white, 50); 8 | height: 48px; 9 | color: map-get($blui-blue, 500); 10 | border-bottom: 1px solid rgba(0, 0, 0, 0.12); 11 | .blui-score-card-title { 12 | font-size: 14px; 13 | } 14 | } 15 | 16 | ::ng-deep .blui-blue .action-details-score-card.blui-score-card { 17 | .mat-list-text { 18 | width: 100%; 19 | } 20 | 21 | .blui-info-list-item-spacer { 22 | flex: 0 0 0; 23 | } 24 | 25 | .blui-info-list-item-right-content { 26 | display: none; 27 | } 28 | 29 | .blui-score-card-action-row-wrapper .mat-list-item-content { 30 | margin: 8px 0; 31 | } 32 | 33 | .blui-score-card-body { 34 | display: block; 35 | justify-content: center; 36 | align-items: center; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/scorecard/scorecard.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ScorecardComponent } from './scorecard.component'; 4 | 5 | describe('ScorecardComponent', () => { 6 | let component: ScorecardComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ScorecardComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ScorecardComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/scorecard/scorecard.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import * as Colors from '@brightlayer-ui/colors'; 3 | @Component({ 4 | selector: 'app-scorecard', 5 | templateUrl: './scorecard.component.html', 6 | styleUrls: ['./scorecard.component.scss'], 7 | }) 8 | export class ScorecardComponent { 9 | colors = Colors; 10 | } 11 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/slide-panel/slide-panel.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/slide-panel/slide-panel.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | overflow: hidden; 4 | } 5 | 6 | .panes { 7 | height: 100%; 8 | width: 200%; 9 | 10 | display: flex; 11 | div { 12 | flex: 1; 13 | } 14 | &.right-pane { 15 | height: calc(100vh - 64px); 16 | } 17 | } 18 | @media only screen and (max-width: 960px) { 19 | .panes { 20 | height: 100%; 21 | width: 200%; 22 | 23 | display: flex; 24 | div { 25 | flex: 1; 26 | } 27 | &.right-pane { 28 | height: calc(100vh - 56px); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/app/pages/list/inline-local-actions/slide-panel/slide-panel.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; 2 | import { animate, state, style, transition, trigger } from '@angular/animations'; 3 | 4 | type PaneDirection = 'left' | 'right'; 5 | 6 | @Component({ 7 | selector: 'blui-slide-panel-inline-actions', 8 | styleUrls: ['./slide-panel.component.scss'], 9 | templateUrl: './slide-panel.component.html', 10 | changeDetection: ChangeDetectionStrategy.OnPush, 11 | animations: [ 12 | trigger('slide', [ 13 | state('left', style({ transform: 'translateX(0)' })), 14 | state('right', style({ transform: 'translateX(-50%)' })), 15 | transition('* => *', animate(250)), 16 | ]), 17 | ], 18 | }) 19 | export class SlidePanelComponent { 20 | @Input() activePane: PaneDirection = 'left'; 21 | } 22 | -------------------------------------------------------------------------------- /src/app/pages/list/multiselect-list/multiselect-list.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette.scss'; 2 | 3 | .multiselect-list-container { 4 | padding: 24px; 5 | margin: 0 auto; 6 | max-width: 816px; 7 | .action-button-container { 8 | text-align: right; 9 | .action-button mat-icon { 10 | margin-right: 4px; 11 | margin-bottom: 2px; 12 | font-size: 16px; 13 | height: 16px; 14 | width: 16px; 15 | } 16 | } 17 | .multiselect-list-card { 18 | margin: 24px 0 0; 19 | padding: 0; 20 | .mat-list-base { 21 | padding-top: 0; 22 | } 23 | } 24 | .multi-select-checkbox { 25 | display: flex; 26 | align-items: center; 27 | margin-left: 4px; 28 | } 29 | 30 | .list-title { 31 | color: map-get($blui-blue, 500); 32 | } 33 | 34 | .no-results { 35 | font-weight: 400; 36 | color: map-get($blui-gray, 500); 37 | } 38 | 39 | .reset-data { 40 | text-decoration: underline; 41 | color: map-get($blui-blue, 500); 42 | cursor: pointer; 43 | } 44 | 45 | ::ng-deep .selected .mat-list-item.blui-info-list-item-content .mat-list-item-content { 46 | background-color: rgba(0, 123, 193, 0.05) !important; 47 | } 48 | } 49 | 50 | @media only screen and (max-width: 960px) { 51 | .multiselect-list-container { 52 | padding: 0; 53 | max-width: 100%; 54 | .multiselect-list-card { 55 | margin: 0; 56 | border-radius: 0; 57 | box-shadow: none !important; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/app/pages/list/multiselect-list/multiselect-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { MultiselectListComponent } from './multiselect-list.component'; 3 | import { MultiselectListModule } from './multiselect-list.module'; 4 | 5 | describe('MultiSelectListComponent', () => { 6 | let component: MultiselectListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(() => { 10 | void TestBed.configureTestingModule({ 11 | imports: [MultiselectListModule], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(MultiselectListComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | void expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/list/multiselect-list/multiselect-list.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { MatToolbarModule } from '@angular/material/toolbar'; 3 | import { CommonModule } from '@angular/common'; 4 | import { MatButtonModule } from '@angular/material/button'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatListModule } from '@angular/material/list'; 7 | import { MatCardModule } from '@angular/material/card'; 8 | import { InfoListItemModule } from '@brightlayer-ui/angular-components'; 9 | import { MultiselectListComponent } from './multiselect-list.component'; 10 | import { MatCheckboxModule } from '@angular/material/checkbox'; 11 | 12 | @NgModule({ 13 | imports: [ 14 | CommonModule, 15 | MatToolbarModule, 16 | MatButtonModule, 17 | MatCardModule, 18 | MatIconModule, 19 | MatCheckboxModule, 20 | MatListModule, 21 | InfoListItemModule, 22 | MatCheckboxModule, 23 | ], 24 | declarations: [MultiselectListComponent], 25 | }) 26 | export class MultiselectListModule {} 27 | -------------------------------------------------------------------------------- /src/app/pages/list/responsive-table/responsive-table.component.html: -------------------------------------------------------------------------------- 1 | 2 | 5 |
Responsive Table
6 | 7 |
8 |

Resize your browser to view responsiveness

9 | 10 | 11 | 12 | {{ i.name }} 13 | 14 | 15 | {{ i.details }} 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
Name{{ item.name }}Details{{ item.details }}
32 | -------------------------------------------------------------------------------- /src/app/pages/list/responsive-table/responsive-table.component.scss: -------------------------------------------------------------------------------- 1 | mat-list { 2 | padding-top: 0px; 3 | } 4 | 5 | table { 6 | width: 100%; 7 | overflow: auto; 8 | } 9 | -------------------------------------------------------------------------------- /src/app/pages/list/responsive-table/responsive-table.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { ResponsiveTableComponent } from './responsive-table.component'; 3 | import { ResponsiveTableModule } from './responsive-table.module'; 4 | 5 | describe('ResponsiveTableComponent', () => { 6 | let component: ResponsiveTableComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | void TestBed.configureTestingModule({ 11 | imports: [ResponsiveTableModule], 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ResponsiveTableComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | void expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/list/responsive-table/responsive-table.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { InfoListItemModule, SpacerModule } from '@brightlayer-ui/angular-components'; 3 | import { MatToolbarModule } from '@angular/material/toolbar'; 4 | import { CommonModule } from '@angular/common'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { ResponsiveTableComponent } from './responsive-table.component'; 8 | import { MatTableModule } from '@angular/material/table'; 9 | import { FlexLayoutModule } from '@angular/flex-layout'; 10 | 11 | @NgModule({ 12 | imports: [ 13 | CommonModule, 14 | InfoListItemModule, 15 | MatToolbarModule, 16 | SpacerModule, 17 | MatIconModule, 18 | MatButtonModule, 19 | MatTableModule, 20 | FlexLayoutModule, 21 | ], 22 | declarations: [ResponsiveTableComponent], 23 | }) 24 | export class ResponsiveTableModule {} 25 | -------------------------------------------------------------------------------- /src/app/pages/list/sortable-list/sortable-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { SortableListComponent } from './sortable-list.component'; 3 | import { SortableListModule } from './sortable-list.module'; 4 | 5 | describe('SortableListComponent', () => { 6 | let component: SortableListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | void TestBed.configureTestingModule({ 11 | imports: [SortableListModule], 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(SortableListComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | void expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/list/sortable-list/sortable-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; 3 | import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout'; 4 | import { StateService } from '../../../services/state.service'; 5 | @Component({ 6 | selector: 'app-sortable-list', 7 | templateUrl: './sortable-list.component.html', 8 | styleUrls: ['./sortable-list.component.scss'], 9 | }) 10 | export class SortableListComponent implements OnInit { 11 | data = ['Item 01', 'Item 02', 'Item 03']; 12 | draggable = false; 13 | isSmall: boolean; 14 | 15 | constructor( 16 | private readonly _drawerService: StateService, 17 | private readonly _breakpointObserver: BreakpointObserver 18 | ) {} 19 | 20 | ngOnInit(): void { 21 | this._breakpointObserver 22 | .observe([Breakpoints.Small, Breakpoints.Handset]) 23 | .subscribe((state: BreakpointState) => { 24 | if (state.matches) { 25 | this.isSmall = true; 26 | } else { 27 | this.isSmall = false; 28 | } 29 | }); 30 | } 31 | 32 | onDrop(event: CdkDragDrop): void { 33 | moveItemInArray(this.data, event.previousIndex, event.currentIndex); 34 | } 35 | 36 | edit(): void { 37 | this.draggable = !this.draggable; 38 | } 39 | 40 | toggleMenu(): void { 41 | const drawerOpen = this._drawerService.getDrawerOpen(); 42 | this._drawerService.setDrawerOpen(!drawerOpen); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/app/pages/list/sortable-list/sortable-list.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { MatToolbarModule } from '@angular/material/toolbar'; 3 | import { CommonModule } from '@angular/common'; 4 | import { SortableListComponent } from './sortable-list.component'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { MatExpansionModule } from '@angular/material/expansion'; 7 | import { MatIconModule } from '@angular/material/icon'; 8 | import { MatListModule } from '@angular/material/list'; 9 | import { MatSelectModule } from '@angular/material/select'; 10 | import { DragDropModule } from '@angular/cdk/drag-drop'; 11 | import { MatButtonToggleModule } from '@angular/material/button-toggle'; 12 | import { MatCardModule } from '@angular/material/card'; 13 | import { SpacerModule, InfoListItemModule, ChannelValueModule } from '@brightlayer-ui/angular-components'; 14 | 15 | @NgModule({ 16 | imports: [ 17 | CommonModule, 18 | MatToolbarModule, 19 | DragDropModule, 20 | MatButtonModule, 21 | MatButtonToggleModule, 22 | MatCardModule, 23 | MatExpansionModule, 24 | MatIconModule, 25 | MatListModule, 26 | MatSelectModule, 27 | SpacerModule, 28 | InfoListItemModule, 29 | ChannelValueModule, 30 | ], 31 | declarations: [SortableListComponent], 32 | }) 33 | export class SortableListModule {} 34 | -------------------------------------------------------------------------------- /src/app/pages/list/status-list/status-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { StatusListComponent } from './status-list.component'; 3 | import { StatusListModule } from './status-list.module'; 4 | 5 | describe('StatusListComponent', () => { 6 | let component: StatusListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [StatusListComponent], 12 | imports: [StatusListModule], 13 | }).compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(StatusListComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | void expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/pages/list/status-list/status-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import * as Colors from '@brightlayer-ui/colors'; 3 | import { StateService } from '../../../services/state.service'; 4 | import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout'; 5 | @Component({ 6 | selector: 'app-status-list', 7 | templateUrl: './status-list.component.html', 8 | styleUrls: ['./status-list.component.scss'], 9 | }) 10 | export class StatusListComponent { 11 | colors = Colors; 12 | isSmall: boolean; 13 | 14 | constructor( 15 | private readonly _drawerService: StateService, 16 | private readonly _breakpointObserver: BreakpointObserver 17 | ) {} 18 | 19 | ngOnInit(): void { 20 | this._breakpointObserver 21 | .observe([Breakpoints.Small, Breakpoints.Handset]) 22 | .subscribe((state: BreakpointState) => { 23 | if (state.matches) { 24 | this.isSmall = true; 25 | } else { 26 | this.isSmall = false; 27 | } 28 | }); 29 | } 30 | 31 | toggleMenu(): void { 32 | const drawerOpen = this._drawerService.getDrawerOpen(); 33 | this._drawerService.setDrawerOpen(!drawerOpen); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/app/pages/list/status-list/status-list.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 3 | import { CommonModule } from '@angular/common'; 4 | import { StatusListComponent } from './status-list.component'; 5 | import { MatBadgeModule } from '@angular/material/badge'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { MatExpansionModule } from '@angular/material/expansion'; 8 | import { MatIconModule } from '@angular/material/icon'; 9 | import { MatListModule } from '@angular/material/list'; 10 | import { MatToolbarModule } from '@angular/material/toolbar'; 11 | import { InfoListItemModule, ListItemTagModule } from '@brightlayer-ui/angular-components'; 12 | @NgModule({ 13 | imports: [ 14 | BrowserAnimationsModule, 15 | CommonModule, 16 | InfoListItemModule, 17 | ListItemTagModule, 18 | MatBadgeModule, 19 | MatButtonModule, 20 | MatExpansionModule, 21 | MatListModule, 22 | MatIconModule, 23 | MatToolbarModule, 24 | ], 25 | declarations: [StatusListComponent], 26 | }) 27 | export class StatusListModule {} 28 | -------------------------------------------------------------------------------- /src/app/pages/list/tree/tree-item.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { TreeItem } from './tree.component'; 3 | 4 | @Component({ 5 | selector: 'app-tree-item', 6 | template: ` 7 | 8 | 9 | 10 | 11 | folder 12 | folder_opened 13 | {{ item.title }} 14 | 15 | `, 16 | }) 17 | export class TreeItemComponent { 18 | @Input() item: TreeItem; 19 | } 20 | -------------------------------------------------------------------------------- /src/app/pages/list/tree/tree.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { TreeListComponent } from './tree.component'; 3 | import { TreeListModule } from './tree.module'; 4 | 5 | describe('TreeListComponent', () => { 6 | let component: TreeListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | void TestBed.configureTestingModule({ 11 | imports: [TreeListModule], 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(TreeListComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | void expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/list/tree/tree.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { TreeListComponent } from './tree.component'; 3 | import { InfoListItemModule, SpacerModule } from '@brightlayer-ui/angular-components'; 4 | import { MatToolbarModule } from '@angular/material/toolbar'; 5 | import { CommonModule } from '@angular/common'; 6 | import { MatIconModule } from '@angular/material/icon'; 7 | import { MatButtonModule } from '@angular/material/button'; 8 | import { MatExpansionModule } from '@angular/material/expansion'; 9 | import { MatRadioModule } from '@angular/material/radio'; 10 | import { TreeItemComponent } from './tree-item.component'; 11 | import { FormsModule } from '@angular/forms'; 12 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 13 | import { MatDividerModule } from '@angular/material/divider'; 14 | 15 | @NgModule({ 16 | imports: [ 17 | BrowserAnimationsModule, 18 | CommonModule, 19 | FormsModule, 20 | InfoListItemModule, 21 | MatButtonModule, 22 | MatDividerModule, 23 | MatExpansionModule, 24 | MatIconModule, 25 | MatToolbarModule, 26 | MatRadioModule, 27 | SpacerModule, 28 | ], 29 | declarations: [TreeListComponent, TreeItemComponent], 30 | }) 31 | export class TreeListModule {} 32 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/contextual-spinners/contextual-spinners.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ContextualSpinnersComponent } from './contextual-spinners.component'; 4 | 5 | describe('ContextualSpinnersComponent', () => { 6 | let component: ContextualSpinnersComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ContextualSpinnersComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ContextualSpinnersComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/contextual-spinners/contextual-spinners.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ContextualSpinnersComponent } from './contextual-spinners.component'; 4 | import { MatToolbarModule } from '@angular/material/toolbar'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { MatIconModule } from '@angular/material/icon'; 7 | import { MatSlideToggleModule } from '@angular/material/slide-toggle'; 8 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 9 | import { SpacerModule } from '@brightlayer-ui/angular-components'; 10 | 11 | @NgModule({ 12 | declarations: [ContextualSpinnersComponent], 13 | imports: [ 14 | CommonModule, 15 | MatToolbarModule, 16 | MatButtonModule, 17 | MatIconModule, 18 | MatSlideToggleModule, 19 | MatProgressSpinnerModule, 20 | SpacerModule, 21 | ], 22 | }) 23 | export class ContextualSpinnersModule {} 24 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/progress-bar-indeterminate/progress-bar-indeterminate.component.html: -------------------------------------------------------------------------------- 1 | 2 | 5 |
Progress Bars (Indeterminate)
6 | 7 | 8 | 12 |
13 |
14 | 15 | bluetooth_searching 16 | 17 |
18 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/progress-bar-indeterminate/progress-bar-indeterminate.component.scss: -------------------------------------------------------------------------------- 1 | .mat-toolbar .mat-progress-bar { 2 | position: absolute; 3 | margin: 0 -16px; 4 | bottom: 0; 5 | } 6 | section.mat-typography { 7 | height: calc(100vh - 64px); 8 | display: flex; 9 | align-items: center; 10 | justify-content: center; 11 | } 12 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/progress-bar-indeterminate/progress-bar-indeterminate.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ProgressBarIndeterminateComponent } from './progress-bar-indeterminate.component'; 4 | 5 | describe('ProgressBarIndeterminateComponent', () => { 6 | let component: ProgressBarIndeterminateComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ProgressBarIndeterminateComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ProgressBarIndeterminateComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/progress-bar-indeterminate/progress-bar-indeterminate.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout'; 3 | import { StateService } from '../../../services/state.service'; 4 | 5 | @Component({ 6 | selector: 'app-progress-bar-indeterminate', 7 | templateUrl: './progress-bar-indeterminate.component.html', 8 | styleUrls: ['./progress-bar-indeterminate.component.scss'], 9 | }) 10 | export class ProgressBarIndeterminateComponent implements OnInit { 11 | isSmall: boolean; 12 | checked = false; 13 | 14 | constructor( 15 | private readonly _drawerService: StateService, 16 | private readonly _breakpointObserver: BreakpointObserver 17 | ) {} 18 | 19 | ngOnInit(): void { 20 | this._breakpointObserver 21 | .observe([Breakpoints.Small, Breakpoints.Handset]) 22 | .subscribe((state: BreakpointState) => { 23 | if (state.matches) { 24 | this.isSmall = true; 25 | } else { 26 | this.isSmall = false; 27 | } 28 | }); 29 | } 30 | 31 | toggleMenu(): void { 32 | const drawerOpen = this._drawerService.getDrawerOpen(); 33 | this._drawerService.setDrawerOpen(!drawerOpen); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/progress-bar-indeterminate/progress-bar-indeterminate.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ProgressBarIndeterminateComponent } from './progress-bar-indeterminate.component'; 4 | import { MatToolbarModule } from '@angular/material/toolbar'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { MatIconModule } from '@angular/material/icon'; 7 | import { MatProgressBarModule } from '@angular/material/progress-bar'; 8 | import { MatSlideToggleModule } from '@angular/material/slide-toggle'; 9 | import { SpacerModule, EmptyStateModule } from '@brightlayer-ui/angular-components'; 10 | 11 | @NgModule({ 12 | declarations: [ProgressBarIndeterminateComponent], 13 | imports: [ 14 | CommonModule, 15 | MatToolbarModule, 16 | MatButtonModule, 17 | MatIconModule, 18 | MatProgressBarModule, 19 | SpacerModule, 20 | MatSlideToggleModule, 21 | EmptyStateModule, 22 | ], 23 | }) 24 | export class ProgressBarIndeterminateModule {} 25 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/progress-bars/progress-bars.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ProgressBarsComponent } from './progress-bars.component'; 4 | 5 | describe('ProgressBarsComponent', () => { 6 | let component: ProgressBarsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ProgressBarsComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ProgressBarsComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/progress-bars/progress-bars.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ProgressBarsComponent } from './progress-bars.component'; 4 | import { MatToolbarModule } from '@angular/material/toolbar'; 5 | import { MatCardModule } from '@angular/material/card'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { MatIconModule } from '@angular/material/icon'; 8 | import { MatProgressBarModule } from '@angular/material/progress-bar'; 9 | import { MatSlideToggleModule } from '@angular/material/slide-toggle'; 10 | import { MatRadioModule } from '@angular/material/radio'; 11 | import { MatDividerModule } from '@angular/material/divider'; 12 | import { SpacerModule, InfoListItemModule } from '@brightlayer-ui/angular-components'; 13 | 14 | @NgModule({ 15 | declarations: [ProgressBarsComponent], 16 | imports: [ 17 | CommonModule, 18 | MatToolbarModule, 19 | MatButtonModule, 20 | MatIconModule, 21 | MatProgressBarModule, 22 | SpacerModule, 23 | MatSlideToggleModule, 24 | MatCardModule, 25 | MatRadioModule, 26 | MatDividerModule, 27 | InfoListItemModule, 28 | ], 29 | }) 30 | export class ProgressBarsModule {} 31 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/skeleton-loader/components/common-style.scss: -------------------------------------------------------------------------------- 1 | .ph-item { 2 | padding: 16px 0; 3 | margin: 0; 4 | justify-content: space-around; 5 | border: none; 6 | } 7 | 8 | .ph-row div:not(.empty), 9 | .ph-avatar { 10 | background-color: map-get($blui-black, 50); 11 | } 12 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/skeleton-loader/components/placeholder-hero/placeholder-hero.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/skeleton-loader/components/placeholder-hero/placeholder-hero.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette.scss'; 2 | @import '../common-style.scss'; 3 | 4 | .hero-banner-section { 5 | .mat-card { 6 | width: 350px; 7 | height: 120px; 8 | padding: 0; 9 | .ph-item { 10 | padding-bottom: 0; 11 | .ph-col-4 { 12 | justify-content: center; 13 | max-width: 100px; 14 | .ph-avatar { 15 | min-width: 0; 16 | height: 36px; 17 | width: 36px; 18 | margin: 0 auto 8px; 19 | } 20 | .ph-row { 21 | &:first-child { 22 | margin-bottom: 0; 23 | } 24 | 25 | [class^='ph-col-'], 26 | [class*=' ph-col-'] { 27 | border-radius: 2px; 28 | margin-bottom: 0; 29 | } 30 | } 31 | } 32 | } 33 | } 34 | } 35 | @media (max-width: 520px) { 36 | .hero-banner-section { 37 | .mat-card { 38 | width: 100%; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/skeleton-loader/components/placeholder-hero/placeholder-hero.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PlaceholderHeroComponent } from './placeholder-hero.component'; 4 | 5 | describe('PlaceholderHeroComponent', () => { 6 | let component: PlaceholderHeroComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [PlaceholderHeroComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(PlaceholderHeroComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/skeleton-loader/components/placeholder-hero/placeholder-hero.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-placeholder-hero', 5 | templateUrl: './placeholder-hero.component.html', 6 | styleUrls: ['./placeholder-hero.component.scss'], 7 | }) 8 | export class PlaceholderHeroComponent implements OnInit { 9 | constructor() {} 10 | 11 | ngOnInit(): void {} 12 | } 13 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/skeleton-loader/components/placeholder-list/placeholder-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PlaceholderListComponent } from './placeholder-list.component'; 4 | 5 | describe('PlaceholderListComponent', () => { 6 | let component: PlaceholderListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [PlaceholderListComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(PlaceholderListComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/skeleton-loader/components/placeholder-list/placeholder-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout'; 3 | @Component({ 4 | selector: 'app-placeholder-list', 5 | templateUrl: './placeholder-list.component.html', 6 | styleUrls: ['./placeholder-list.component.scss'], 7 | }) 8 | export class PlaceholderListComponent implements OnInit { 9 | isSmall: boolean; 10 | constructor(private readonly _breakpointObserver: BreakpointObserver) {} 11 | 12 | ngOnInit(): void { 13 | this._breakpointObserver 14 | .observe([Breakpoints.Small, Breakpoints.Handset]) 15 | .subscribe((state: BreakpointState) => { 16 | if (state.matches) { 17 | this.isSmall = true; 18 | } else { 19 | this.isSmall = false; 20 | } 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/skeleton-loader/components/placeholder-score-card/placeholder-score-card.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PlaceholderScoreCardComponent } from './placeholder-score-card.component'; 4 | 5 | describe('PlaceholderScoreCardComponent', () => { 6 | let component: PlaceholderScoreCardComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [PlaceholderScoreCardComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(PlaceholderScoreCardComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/skeleton-loader/components/placeholder-score-card/placeholder-score-card.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-placeholder-score-card', 5 | templateUrl: './placeholder-score-card.component.html', 6 | styleUrls: ['./placeholder-score-card.component.scss'], 7 | }) 8 | export class PlaceholderScoreCardComponent implements OnInit { 9 | constructor() {} 10 | 11 | ngOnInit(): void {} 12 | } 13 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/skeleton-loader/skeleton-loader.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette.scss'; 2 | 3 | .mat-typography { 4 | padding: 24px 16px; 5 | 6 | .example-title { 7 | color: map-get($blui-blue, 500); 8 | margin: 40px 0 24px; 9 | } 10 | 11 | .mat-card { 12 | padding: 0; 13 | } 14 | } 15 | 16 | .list-items-example-section { 17 | .loaded-section { 18 | .list-item-example-two { 19 | margin-top: 24px; 20 | } 21 | } 22 | } 23 | .score-card-example-section { 24 | .loaded-section { 25 | width: 400px; 26 | padding: 0; 27 | .mat-list-base { 28 | padding: 8px 0px !important; 29 | } 30 | .sb-score-card-content mat-list-item { 31 | height: 2.25rem !important; 32 | } 33 | } 34 | } 35 | 36 | .hero-banner-example-section { 37 | .loaded-section .mat-card { 38 | width: 350px; 39 | } 40 | } 41 | 42 | .time-section { 43 | font-size: 12px; 44 | width: 60px; 45 | flex-direction: column; 46 | .ampm { 47 | font-weight: 600; 48 | margin-left: 2px; 49 | } 50 | } 51 | 52 | .title-section { 53 | .title-desc { 54 | font-weight: normal; 55 | margin-left: 4px; 56 | } 57 | .subtitle { 58 | font-size: 12px; 59 | font-weight: normal; 60 | margin-top: 2px; 61 | } 62 | } 63 | 64 | @media (max-width: 520px) { 65 | .score-card-example-section { 66 | .loaded-section { 67 | width: 100%; 68 | } 69 | } 70 | 71 | .hero-banner-example-section { 72 | .loaded-section .mat-card { 73 | width: 100%; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/skeleton-loader/skeleton-loader.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SkeletonLoaderComponent } from './skeleton-loader.component'; 4 | 5 | describe('SkeletonLoaderComponent', () => { 6 | let component: SkeletonLoaderComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [SkeletonLoaderComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(SkeletonLoaderComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/spinner-overlays/spinner-overlays.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SpinnerOverlaysComponent } from './spinner-overlays.component'; 4 | 5 | describe('SpinnerOverlaysComponent', () => { 6 | let component: SpinnerOverlaysComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [SpinnerOverlaysComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(SpinnerOverlaysComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/loading-waiting-states/spinner-overlays/spinner-overlays.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { SpinnerOverlaysComponent } from './spinner-overlays.component'; 4 | import { MatToolbarModule } from '@angular/material/toolbar'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 8 | import { MatSlideToggleModule } from '@angular/material/slide-toggle'; 9 | import { MatCardModule } from '@angular/material/card'; 10 | import { MatCheckboxModule } from '@angular/material/checkbox'; 11 | import { MatDividerModule } from '@angular/material/divider'; 12 | import { SpacerModule } from '@brightlayer-ui/angular-components'; 13 | import { FormsModule } from '@angular/forms'; 14 | 15 | @NgModule({ 16 | declarations: [SpinnerOverlaysComponent], 17 | imports: [ 18 | CommonModule, 19 | MatToolbarModule, 20 | MatIconModule, 21 | MatButtonModule, 22 | MatProgressSpinnerModule, 23 | MatSlideToggleModule, 24 | MatCardModule, 25 | MatCheckboxModule, 26 | MatDividerModule, 27 | SpacerModule, 28 | FormsModule, 29 | ], 30 | }) 31 | export class SpinnerOverlaysModule {} 32 | -------------------------------------------------------------------------------- /src/app/pages/overlays/basic-bottom-sheet/basic-bottom-sheet.component.html: -------------------------------------------------------------------------------- 1 | 2 | 5 |
Basic Bottom Sheet
6 | 7 | 10 |
11 | 12 | 18 | notifications 19 | notifications_active 21 | 22 | 23 | ACTIVE: {{ i.details }} 24 | 25 | 26 | {{ i.date | date: 'MM/dd/yyyy' }} 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/app/pages/overlays/basic-bottom-sheet/basic-bottom-sheet.component.scss: -------------------------------------------------------------------------------- 1 | mat-list { 2 | padding-top: 0; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/pages/overlays/basic-bottom-sheet/basic-bottom-sheet.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { BasicBottomSheetComponent } from './basic-bottom-sheet.component'; 3 | import { BasicBottomSheetModule } from './basic-bottom-sheet.module'; 4 | 5 | describe('BasicBottomSheetComponent', () => { 6 | let component: BasicBottomSheetComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | void TestBed.configureTestingModule({ 11 | imports: [BasicBottomSheetModule], 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(BasicBottomSheetComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | void expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/pages/overlays/basic-bottom-sheet/basic-bottom-sheet.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { InfoListItemModule, SpacerModule } from '@brightlayer-ui/angular-components'; 3 | import { MatToolbarModule } from '@angular/material/toolbar'; 4 | import { CommonModule } from '@angular/common'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { BasicBottomSheetComponent } from './basic-bottom-sheet.component'; 8 | import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; 9 | import { MatListModule } from '@angular/material/list'; 10 | import { DataService } from './data.service'; 11 | import { BottomSheet } from './bottom-sheet/bottom-sheet'; 12 | 13 | @NgModule({ 14 | imports: [ 15 | CommonModule, 16 | InfoListItemModule, 17 | MatToolbarModule, 18 | MatIconModule, 19 | MatButtonModule, 20 | MatBottomSheetModule, 21 | MatListModule, 22 | SpacerModule, 23 | ], 24 | declarations: [BasicBottomSheetComponent, BottomSheet], 25 | providers: [DataService], 26 | entryComponents: [BottomSheet], 27 | }) 28 | export class BasicBottomSheetModule {} 29 | -------------------------------------------------------------------------------- /src/app/pages/overlays/basic-bottom-sheet/bottom-sheet/bottom-sheet.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | done 4 | Acknowledge All 5 | 6 | 7 | 8 | get_app 9 | Export 10 | 11 | 12 | 13 | clear 14 | Cancel 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/app/pages/overlays/basic-bottom-sheet/bottom-sheet/bottom-sheet.scss: -------------------------------------------------------------------------------- 1 | .mat-bottom-sheet-container { 2 | padding: 0; 3 | } 4 | 5 | .mat-nav-list { 6 | padding-top: 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/pages/overlays/basic-bottom-sheet/bottom-sheet/bottom-sheet.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation, NgModule } from '@angular/core'; 2 | import { MatBottomSheetRef } from '@angular/material/bottom-sheet'; 3 | import { MatListModule } from '@angular/material/list'; 4 | import { MatIconModule } from '@angular/material/icon'; 5 | import { CommonModule } from '@angular/common'; 6 | import { InfoListItemModule } from '@brightlayer-ui/angular-components'; 7 | 8 | @Component({ 9 | selector: 'bottom-sheet', 10 | templateUrl: './bottom-sheet.html', 11 | styleUrls: ['./bottom-sheet.scss'], 12 | encapsulation: ViewEncapsulation.None, 13 | }) 14 | export class BottomSheet { 15 | constructor(private readonly _bottomSheetRef: MatBottomSheetRef) {} 16 | 17 | // this is the placeholder for actual functionality 18 | openLink(event: MouseEvent): void { 19 | this._bottomSheetRef.dismiss(); 20 | event.preventDefault(); 21 | } 22 | } 23 | 24 | @NgModule({ 25 | imports: [CommonModule, MatIconModule, MatListModule, InfoListItemModule], 26 | declarations: [], 27 | providers: [], 28 | }) 29 | export class BasicBottomSheetModule {} 30 | -------------------------------------------------------------------------------- /src/app/pages/overlays/basic-bottom-sheet/data.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root', 5 | }) 6 | export class DataService { 7 | data: any[] = []; 8 | public NOW = Date.now(); 9 | public LOCATIONS = [ 10 | 'Dos Valley Field', 11 | 'Jameson Field', 12 | 'Parker Field West', 13 | 'Parker Field East', 14 | 'North Park Garden', 15 | ]; 16 | public DEVICES = ['MX Power Pro', 'PXL DG1', 'Pentair Aurora']; 17 | public DETAILS = ['Over Voltage Fault', 'Over Current Fault', 'Under Voltage Fault', 'Under Current Fault']; 18 | 19 | getRandomData(): any { 20 | return { 21 | date: Math.round(this.NOW - Math.random() * 1000000000), 22 | active: Math.random() < 0.3, 23 | location: this.LOCATIONS[Math.floor(Math.random() * this.LOCATIONS.length)], 24 | device: this.DEVICES[Math.floor(Math.random() * this.DEVICES.length)], 25 | details: this.DETAILS[Math.floor(Math.random() * this.DETAILS.length)], 26 | }; 27 | } 28 | 29 | constructor() { 30 | for (let i = 1; i <= 10; i++) { 31 | this.data.push(this.getRandomData()); 32 | } 33 | this.data.sort((a, b) => b.date - a.date); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/app/pages/overlays/complex-bottom-sheet/bottom-sheet/bottom-sheet.scss: -------------------------------------------------------------------------------- 1 | @import '~@brightlayer-ui/colors/palette.scss'; 2 | 3 | .mat-h3 { 4 | margin-bottom: 0; 5 | } 6 | 7 | .active { 8 | .blui-hero-label, 9 | &.blui-hero .blui-hero-primary-wrapper { 10 | color: map-get($blui-blue, 500); 11 | } 12 | } 13 | 14 | .bottomPanel { 15 | min-width: 600px; 16 | width: 100%; 17 | max-width: 600px; 18 | padding: 0; 19 | max-height: unset; 20 | .footer-list { 21 | padding: 8px 16px; 22 | } 23 | .footer-close { 24 | position: sticky; 25 | bottom: 0; 26 | background-color: white; 27 | } 28 | } 29 | 30 | @media (max-width: 600px) { 31 | .bottomPanel { 32 | min-width: 100vw; 33 | .mat-nav-list mat-list-item .mat-line { 34 | text-overflow: inherit; 35 | white-space: normal; 36 | } 37 | } 38 | .on-wide-screen { 39 | display: none; 40 | } 41 | } 42 | 43 | @media (min-width: 601px) { 44 | .footer-close.mat-elevation-z2 { 45 | box-shadow: unset; 46 | } 47 | } 48 | 49 | .bottomPanel { 50 | blui-hero { 51 | flex: unset !important; 52 | cursor: pointer; 53 | min-width: 100px; 54 | } 55 | blui-hero-banner { 56 | height: 100px; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/app/pages/overlays/complex-bottom-sheet/complex-bottom-sheet.component.scss: -------------------------------------------------------------------------------- 1 | mat-list { 2 | padding-top: 0; 3 | } 4 | 5 | ::ng-deep .blui-empty-state { 6 | width: 100%; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/pages/overlays/complex-bottom-sheet/complex-bottom-sheet.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { EmptyStateModule, HeroModule, InfoListItemModule, SpacerModule } from '@brightlayer-ui/angular-components'; 3 | import { MatToolbarModule } from '@angular/material/toolbar'; 4 | import { CommonModule } from '@angular/common'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { ComplexBottomSheetComponent } from './complex-bottom-sheet.component'; 8 | import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; 9 | import { MatListModule } from '@angular/material/list'; 10 | import { DataService } from './data.service'; 11 | import { BottomSheet } from './bottom-sheet/bottom-sheet'; 12 | import { FlexLayoutModule } from '@angular/flex-layout'; 13 | 14 | @NgModule({ 15 | imports: [ 16 | CommonModule, 17 | InfoListItemModule, 18 | MatToolbarModule, 19 | MatIconModule, 20 | MatButtonModule, 21 | MatBottomSheetModule, 22 | MatListModule, 23 | SpacerModule, 24 | FlexLayoutModule, 25 | EmptyStateModule, 26 | HeroModule, 27 | ], 28 | declarations: [ComplexBottomSheetComponent, BottomSheet], 29 | providers: [DataService], 30 | entryComponents: [BottomSheet], 31 | }) 32 | export class ComplexBottomSheetModule {} 33 | -------------------------------------------------------------------------------- /src/app/pages/overlays/complex-bottom-sheet/filter.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export enum FiltersList { 3 | ACTIVE_ALARMS = 'activeAlarms', 4 | ALARMS = 'alarms', 5 | TIME = 'time', 6 | SETTINGS = 'settings', 7 | EVENT_TYPE = 'eventType', 8 | SESSION = 'session', 9 | } 10 | -------------------------------------------------------------------------------- /src/app/services/state.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { StateService } from './state.service'; 4 | 5 | describe('StateService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: StateService = TestBed.get(StateService); 10 | void expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/services/state.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root', 5 | }) 6 | export class StateService { 7 | private drawerOpen = false; 8 | 9 | setDrawerOpen(drawerOpen: boolean): void { 10 | this.drawerOpen = drawerOpen; 11 | } 12 | 13 | getDrawerOpen(): boolean { 14 | return this.drawerOpen; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/services/viewport.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ViewportService } from './viewport.service'; 4 | 5 | describe('ViewportService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: ViewportService = TestBed.get(ViewportService); 10 | void expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/services/viewport.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { BreakpointObserver } from '@angular/cdk/layout'; 3 | 4 | // Use this service to get viewport size. 5 | @Injectable({ 6 | providedIn: 'root', 7 | }) 8 | export class ViewportService { 9 | breakpointSubscription: any; 10 | mobileViewport: boolean; 11 | 12 | constructor(private readonly _breakpointObserver: BreakpointObserver) { 13 | this.breakpointSubscription = this._breakpointObserver 14 | .observe(['(max-width: 960px)', '(max-width: 600px)']) 15 | .subscribe((result) => { 16 | const small = Object.keys(result.breakpoints)[0]; 17 | this.mobileViewport = result.breakpoints[small]; 18 | }); 19 | } 20 | 21 | isSmall(): boolean { 22 | return this.mobileViewport; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etn-ccis/blui-angular-design-patterns/0ad3c432aa94d8eff7795c2ad8d9d794f66e1788/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/avatar_40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etn-ccis/blui-angular-design-patterns/0ad3c432aa94d8eff7795c2ad8d9d794f66e1788/src/assets/avatar_40.png -------------------------------------------------------------------------------- /src/assets/collapsible_app_bar_demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etn-ccis/blui-angular-design-patterns/0ad3c432aa94d8eff7795c2ad8d9d794f66e1788/src/assets/collapsible_app_bar_demo.jpg -------------------------------------------------------------------------------- /src/assets/cubes_tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etn-ccis/blui-angular-design-patterns/0ad3c432aa94d8eff7795c2ad8d9d794f66e1788/src/assets/cubes_tile.png -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etn-ccis/blui-angular-design-patterns/0ad3c432aa94d8eff7795c2ad8d9d794f66e1788/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | angular-design-patterns 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /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 | // Needed for codesandbox to work 8 | import 'core-js/es/reflect'; 9 | import 'core-js/stable/reflect'; 10 | import 'core-js/features/reflect'; 11 | 12 | if (environment.production) { 13 | enableProdMode(); 14 | } 15 | 16 | platformBrowserDynamic() 17 | .bootstrapModule(AppModule) 18 | .catch((err) => console.error(err)); 19 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; 6 | 7 | declare const require: { 8 | context( 9 | path: string, 10 | deep?: boolean, 11 | filter?: RegExp 12 | ): { 13 | keys(): string[]; 14 | (id: string): T; 15 | }; 16 | }; 17 | 18 | // First, initialize the Angular testing environment. 19 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); 20 | // Then we find all the tests. 21 | const context = require.context('./', true, /\.spec\.ts$/); 22 | // And load the modules. 23 | context.keys().map(context); 24 | -------------------------------------------------------------------------------- /tsconfig.app.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/app", 6 | "types": ["node"] 7 | }, 8 | "files": ["./src/main.ts", "./src/polyfills.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.es5.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "target": "es5" 5 | }, 6 | "files": ["./src/main.ts", "./src/polyfills.ts"] 7 | 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "sourceMap": true, 8 | "declaration": false, 9 | "downlevelIteration": true, 10 | "experimentalDecorators": true, 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "module": "es2020", 15 | "lib": ["es2018", "dom"] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /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": ["jasmine", "node"] 7 | }, 8 | "files": ["src/test.ts", "src/polyfills.ts"], 9 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] 10 | } 11 | --------------------------------------------------------------------------------