├── .commitlintrc.json ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug.yml │ ├── config.yml │ ├── docs.yml │ └── feature.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── build.yml │ └── publish.yml ├── .gitignore ├── .husky ├── pre-commit └── prepare-commit-msg ├── .npmrc ├── .postcssrc ├── .versionrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── angular.json ├── assets └── animation.gif ├── package-lock.json ├── package.json ├── projects ├── ngx-multiple-dates-app │ ├── .browserslistrc │ ├── .eslintrc.json │ ├── e2e │ │ ├── protractor.conf.js │ │ ├── src │ │ │ ├── app.e2e-spec.ts │ │ │ └── app.po.ts │ │ └── tsconfig.json │ ├── karma.conf.js │ ├── src │ │ ├── app │ │ │ ├── app.constants.ts │ │ │ ├── app.module.ts │ │ │ └── components │ │ │ │ ├── root │ │ │ │ ├── root.component.html │ │ │ │ ├── root.component.scss │ │ │ │ ├── root.component.spec.ts │ │ │ │ └── root.component.ts │ │ │ │ └── theme-picker │ │ │ │ ├── theme-picker.component.html │ │ │ │ ├── theme-picker.component.scss │ │ │ │ ├── theme-picker.component.spec.ts │ │ │ │ └── theme-picker.component.ts │ │ ├── assets │ │ │ └── icons │ │ │ │ ├── github.svg │ │ │ │ └── ngx-multiple-dates.svg │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ └── styles │ │ │ └── styles.scss │ ├── tsconfig.app.json │ └── tsconfig.spec.json └── ngx-multiple-dates │ ├── .browserslistrc │ ├── .eslintrc.json │ ├── README.md │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ ├── lib │ │ ├── _index.scss │ │ ├── _theming.scss │ │ ├── components │ │ │ └── multiple-dates │ │ │ │ ├── _multiple-dates-legacy-index.scss │ │ │ │ ├── _multiple-dates-theme.import.scss │ │ │ │ ├── _multiple-dates-theme.scss │ │ │ │ ├── multiple-dates.component.html │ │ │ │ ├── multiple-dates.component.scss │ │ │ │ ├── multiple-dates.component.spec.ts │ │ │ │ └── multiple-dates.component.ts │ │ ├── models │ │ │ ├── date-class.model.ts │ │ │ └── date-remove-event.model.ts │ │ └── ngx-multiple-dates.module.ts │ ├── prebuilt-themes │ │ ├── azure-blue.scss │ │ ├── cyan-orange.scss │ │ ├── magenta-violet.scss │ │ └── rose-red.scss │ └── public-api.ts │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ └── tsconfig.spec.json └── tsconfig.json /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ "@commitlint/config-conventional" ], 3 | "parserPreset": "conventional-changelog-angular", 4 | "formatter": "@commitlint/format" 5 | } 6 | -------------------------------------------------------------------------------- /.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 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "overrides": [ 4 | { 5 | "files": [ 6 | "*.ts" 7 | ], 8 | "settings": { 9 | "import/parsers": { 10 | "@typescript-eslint/parser": [ ".ts" ] 11 | }, 12 | "import/resolver": { 13 | "typescript": { 14 | "alwaysTryTypes": true, 15 | "project": "." 16 | } 17 | } 18 | }, 19 | "parserOptions": { 20 | "project": [ 21 | "tsconfig.json", 22 | "projects/ngx-multiple-dates/tsconfig.lib.json", 23 | "projects/ngx-multiple-dates-app/tsconfig.app.json", 24 | "projects/ngx-multiple-dates-app/e2e/tsconfig.json" 25 | ], 26 | "createDefaultProgram": true 27 | }, 28 | "plugins": [ 29 | "@angular-eslint", 30 | "@typescript-eslint", 31 | "import", 32 | "jsdoc", 33 | "prefer-arrow", 34 | "rxjs", 35 | "unicorn" 36 | ], 37 | "extends": [ 38 | "plugin:@angular-eslint/template/process-inline-templates", 39 | "plugin:import/recommended", 40 | "plugin:import/typescript", 41 | "plugin:jsdoc/recommended", 42 | "plugin:rxjs/recommended", 43 | "plugin:import/errors" 44 | ], 45 | "rules": { 46 | "@angular-eslint/component-class-suffix": [ 47 | "error", 48 | { 49 | "suffixes": [ "Component" ] 50 | } 51 | ], 52 | "@angular-eslint/no-forward-ref": "error", 53 | "@typescript-eslint/array-type": [ 54 | "error", 55 | { 56 | "default": "array-simple" 57 | } 58 | ], 59 | "@typescript-eslint/await-thenable": "error", 60 | "@typescript-eslint/consistent-type-definitions": "error", 61 | "@typescript-eslint/dot-notation": "off", 62 | "@typescript-eslint/explicit-member-accessibility": [ 63 | "off", 64 | { 65 | "accessibility": "explicit" 66 | } 67 | ], 68 | "@typescript-eslint/member-ordering": [ 69 | 0, 70 | [ 71 | // Index signature 72 | "signature", 73 | 74 | // Fields 75 | "public-static-field", 76 | "public-decorated-field", 77 | "public-instance-field", 78 | "public-abstract-field", 79 | "protected-static-field", 80 | "protected-decorated-field", 81 | "protected-instance-field", 82 | "protected-abstract-field", 83 | "private-static-field", 84 | "private-decorated-field", 85 | "private-instance-field", 86 | "private-abstract-field", 87 | 88 | // Constructors 89 | "public-constructor", 90 | "protected-constructor", 91 | "private-constructor", 92 | 93 | // Methods 94 | "public-static-method", 95 | "public-decorated-method", 96 | "public-instance-method", 97 | "public-abstract-method", 98 | "protected-static-method", 99 | "protected-decorated-method", 100 | "protected-instance-method", 101 | "protected-abstract-method", 102 | "private-static-method", 103 | "private-decorated-method", 104 | "private-instance-method", 105 | "private-abstract-method" 106 | ] 107 | ], 108 | "@typescript-eslint/naming-convention": [ 109 | "error", 110 | { 111 | "selector": "variable", 112 | "types": [ "boolean" ], 113 | "format": [ "PascalCase" ], 114 | "prefix": [ "is", "should", "has", "can", "did", "will" ] 115 | }, 116 | { 117 | "selector": "enumMember", 118 | "format": [ "PascalCase" ] 119 | } 120 | ], 121 | "jsdoc/newline-after-description": 0, 122 | "jsdoc/require-param-type": 0, 123 | "jsdoc/require-property-type": 0, 124 | "jsdoc/require-returns-type": 0, 125 | "jsdoc/require-returns": [ 126 | "error", 127 | { "checkGetters": false } 128 | ], 129 | "prefer-arrow/prefer-arrow-functions": [ 130 | "warn", 131 | { 132 | "disallowPrototype": true, 133 | "singleReturnOnly": false, 134 | "classPropertiesAllowed": false 135 | } 136 | ], 137 | "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error", 138 | "@typescript-eslint/no-unnecessary-type-assertion": "error", 139 | "@typescript-eslint/no-var-requires": "error", 140 | "@typescript-eslint/promise-function-async": "error", 141 | // "@typescript-eslint/quotes": [ 142 | // "error", 143 | // "single", 144 | // { 145 | // "avoidEscape": true, 146 | // "allowTemplateLiterals": true 147 | // } 148 | // ], 149 | "brace-style": [ 150 | "error", 151 | "1tbs" 152 | ], 153 | "comma-dangle": "error", 154 | "import/order": "off", 155 | "max-classes-per-file": [ 156 | "error", 157 | 1 158 | ], 159 | "max-len": [ 160 | "error", 161 | { 162 | "code": 100 163 | } 164 | ], 165 | "no-redeclare": "error", 166 | "no-underscore-dangle": "off", 167 | "quote-props": [ "error", "consistent-as-needed" ], 168 | "no-duplicate-imports": "error", 169 | "no-irregular-whitespace": "error", 170 | "no-multiple-empty-lines": [ 171 | "error", 172 | { 173 | "max": 2 174 | } 175 | ], 176 | "prefer-template": "error", 177 | "rxjs/no-internal": "error", 178 | "unicorn/filename-case": "error", 179 | "import/no-deprecated": "off", 180 | "import/no-unresolved": "warn" 181 | } 182 | }, 183 | { 184 | "files": [ 185 | "*.html" 186 | ], 187 | "extends": [ 188 | "plugin:@angular-eslint/template/recommended" 189 | ], 190 | "rules": { } 191 | }, 192 | { 193 | "files": [ 194 | "**/**/components/modals/**/**/*.ts" 195 | ], 196 | "rules": { 197 | "@angular-eslint/component-class-suffix": [ 198 | "error", 199 | { 200 | "suffixes": [ "ModalComponent" ] 201 | } 202 | ] 203 | } 204 | }, 205 | { 206 | "files": [ 207 | "**/**/components/pages/**/**/*.ts" 208 | ], 209 | "rules": { 210 | "@angular-eslint/component-class-suffix": [ 211 | "error", 212 | { 213 | "suffixes": [ "PageComponent" ] 214 | } 215 | ] 216 | } 217 | } 218 | ] 219 | } 220 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report a bug in Angular Multiple Dates 3 | labels: ["bug"] 4 | --- 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Description 10 | description: A clear and concise description of the problem. 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: reproduction 15 | attributes: 16 | label: Reproduction 17 | value: | 18 | Steps to reproduce: 19 | 1. 20 | 2. 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: expected-behavior 25 | attributes: 26 | label: Expected Behavior 27 | description: What behavior were you expecting to see? 28 | validations: 29 | required: true 30 | - type: textarea 31 | id: actual-behavior 32 | attributes: 33 | label: Actual Behavior 34 | description: What behavior did you actually see? 35 | validations: 36 | required: true 37 | - type: textarea 38 | id: environment 39 | attributes: 40 | label: Environment 41 | description: You can use `ng version` command. 42 | value: | 43 | - ngx-multiple-dates: 44 | - Angular: 45 | - Angular CDK / Material: 46 | - Browser(s): 47 | - Operating System: 48 | validations: 49 | required: true 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/docs.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | description: Suggest an improvement to our documentation 3 | labels: ["documentation"] 4 | body: 5 | - type: textarea 6 | id: description 7 | attributes: 8 | label: Documentation Feedback 9 | description: | 10 | Provide a brief summary of what you would like to see changed in our documentation. 11 | Feel free to provide any suggestions of content or examples you’d like us to include. 12 | validations: 13 | required: true 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yml: -------------------------------------------------------------------------------- 1 | name: Feature 2 | description: Propose a new feature for Angular Multiple Dates 3 | labels: ["enhancement"] 4 | body: 5 | - type: textarea 6 | id: description 7 | attributes: 8 | label: Feature Description 9 | description: Provide a brief summary of the feature you would like to see. 10 | validations: 11 | required: true 12 | - type: textarea 13 | id: use-case 14 | attributes: 15 | label: Use Case 16 | description: Describe the use case(s) that the proposed feature would enable. 17 | validations: 18 | required: false 19 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## PR Checklist 2 | Please check if your PR fulfills the following requirements: 3 | 4 | - [ ] Tests for the changes have been added (for bug fixes / features) 5 | - [ ] Docs have been added / updated (for bug fixes / features) 6 | 7 | 8 | ## PR Type 9 | What kind of change does this PR introduce? 10 | 11 | 12 | 13 | - [ ] Bugfix 14 | - [ ] Feature 15 | - [ ] Refactoring 16 | - [ ] Build related changes 17 | - [ ] Documentation 18 | - [ ] Other: 19 | 20 | 21 | ## What is the current behavior? 22 | 23 | 24 | Issue Number: N/A 25 | 26 | 27 | ## What is the new behavior? 28 | 29 | 30 | ## Does this PR introduce a breaking change? 31 | 32 | - [ ] Yes 33 | - [ ] No 34 | 35 | 36 | 37 | 38 | 39 | ## Other information 40 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-node@v4 15 | with: 16 | node-version: 20 17 | cache: 'npm' 18 | - run: npm ci 19 | - run: npm run build 20 | - uses: actions/upload-artifact@v4 21 | with: 22 | name: ngx-multiple-dates 23 | path: dist/ngx-multiple-dates 24 | 25 | build-app: 26 | needs: build 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: actions/setup-node@v4 31 | with: 32 | node-version: 20 33 | cache: 'npm' 34 | - uses: actions/download-artifact@v4 35 | with: 36 | name: ngx-multiple-dates 37 | path: dist/ngx-multiple-dates 38 | - run: npm ci 39 | - run: npm run build:app 40 | 41 | test: 42 | needs: build 43 | runs-on: ubuntu-latest 44 | steps: 45 | - uses: actions/checkout@v4 46 | - uses: actions/setup-node@v4 47 | with: 48 | node-version: 20 49 | cache: 'npm' 50 | - uses: actions/download-artifact@v4 51 | with: 52 | name: ngx-multiple-dates 53 | path: dist/ngx-multiple-dates 54 | - run: npm ci 55 | - run: npm run test:ci 56 | - uses: actions/upload-artifact@v4 57 | with: 58 | name: coverage 59 | path: coverage/ngx-multiple-dates/cobertura-coverage.xml 60 | - uses: actions/upload-artifact@v4 61 | with: 62 | name: coverage-app 63 | path: coverage/ngx-multiple-dates-app/cobertura-coverage.xml 64 | - name: Upload to Codecov 65 | uses: codecov/codecov-action@v4 66 | with: 67 | token: ${{ secrets.CODECOV_TOKEN }} 68 | files: ./coverage/ngx-multiple-dates/cobertura-coverage.xml,./coverage/ngx-multiple-dates-app/cobertura-coverage.xml 69 | flags: unittests 70 | name: codecov-umbrella 71 | fail_ci_if_error: true 72 | env: 73 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 74 | 75 | lint: 76 | needs: build 77 | runs-on: ubuntu-latest 78 | steps: 79 | - uses: actions/checkout@v4 80 | - uses: actions/setup-node@v4 81 | with: 82 | node-version: 20 83 | cache: 'npm' 84 | - uses: actions/download-artifact@v4 85 | with: 86 | name: ngx-multiple-dates 87 | path: dist/ngx-multiple-dates 88 | - run: npm ci 89 | - run: npm run lint 90 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [ published ] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version: 20 15 | cache: 'npm' 16 | - run: npm ci 17 | - run: npm run build 18 | - uses: actions/upload-artifact@v4 19 | with: 20 | name: ngx-multiple-dates 21 | path: dist/ngx-multiple-dates 22 | 23 | build-app: 24 | needs: build 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v4 28 | - uses: actions/setup-node@v4 29 | with: 30 | node-version: 20 31 | cache: 'npm' 32 | - uses: actions/download-artifact@v4 33 | with: 34 | name: ngx-multiple-dates 35 | path: dist/ngx-multiple-dates 36 | - run: npm ci 37 | - run: npm run build:app 38 | - uses: actions/upload-artifact@v4 39 | with: 40 | name: ngx-multiple-dates-app 41 | path: dist/ngx-multiple-dates-app 42 | 43 | test: 44 | needs: build 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@v4 48 | - uses: actions/setup-node@v4 49 | with: 50 | node-version: 20 51 | cache: 'npm' 52 | - uses: actions/download-artifact@v4 53 | with: 54 | name: ngx-multiple-dates 55 | path: dist/ngx-multiple-dates 56 | - run: npm ci 57 | - run: npm run test:ci 58 | 59 | lint: 60 | needs: build 61 | runs-on: ubuntu-latest 62 | steps: 63 | - uses: actions/checkout@v4 64 | - uses: actions/setup-node@v4 65 | with: 66 | node-version: 20 67 | cache: 'npm' 68 | - uses: actions/download-artifact@v4 69 | with: 70 | name: ngx-multiple-dates 71 | path: dist/ngx-multiple-dates 72 | - run: npm ci 73 | - run: npm run lint 74 | 75 | deploy: 76 | needs: [build, build-app, test, lint] 77 | runs-on: ubuntu-latest 78 | steps: 79 | - uses: actions/checkout@v4 80 | - uses: actions/download-artifact@v4 81 | with: 82 | name: ngx-multiple-dates-app 83 | path: dist/ngx-multiple-dates-app 84 | - name: get-npm-version 85 | id: package-version 86 | uses: martinbeentjes/npm-get-version-action@v1.3.1 87 | - name: Deploy 🚀 88 | uses: JamesIves/github-pages-deploy-action@v4 89 | with: 90 | branch: gh-pages 91 | folder: dist/ngx-multiple-dates-app 92 | commit-message: ${{ format('chore{0} deploy v{1} from @ {2}@{3} 🚀', ':', steps.package-version.outputs.current-version, github.repository, github.sha) }} 93 | 94 | publish: 95 | needs: [build, build-app, test, lint] 96 | runs-on: ubuntu-latest 97 | steps: 98 | - uses: actions/checkout@v4 99 | - uses: actions/setup-node@v4 100 | with: 101 | node-version: 20 102 | registry-url: https://registry.npmjs.org/ 103 | cache: 'npm' 104 | - uses: actions/download-artifact@v4 105 | with: 106 | name: ngx-multiple-dates 107 | path: dist/ngx-multiple-dates 108 | - name: get-npm-version 109 | id: package-version 110 | uses: martinbeentjes/npm-get-version-action@v1.3.1 111 | - name: Publish to NPM 112 | working-directory: ./dist/ngx-multiple-dates 113 | run: npm publish && npm dist-tag add ngx-multiple-dates@${{ steps.package-version.outputs.current-version }} stable 114 | env: 115 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 116 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | speed-measure-plugin*.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.angular/cache 36 | /.angular/ 37 | /.sass-cache 38 | /connect.lock 39 | /coverage 40 | /libpeerconnection.log 41 | npm-debug.log 42 | yarn-error.log 43 | testem.log 44 | /typings 45 | 46 | # System Files 47 | .DS_Store 48 | Thumbs.db 49 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run lint 5 | -------------------------------------------------------------------------------- /.husky/prepare-commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run commitlint -- -e $1 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | legacy-peer-deps=true 2 | -------------------------------------------------------------------------------- /.postcssrc: -------------------------------------------------------------------------------- 1 | { 2 | "map": false, 3 | "plugins": { 4 | "autoprefixer": { 5 | "cascade": false 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.versionrc: -------------------------------------------------------------------------------- 1 | { 2 | "bumpFiles": [ 3 | { 4 | "filename": "./package.json", 5 | "type": "json" 6 | }, 7 | { 8 | "filename": "./package-lock.json", 9 | "type": "json" 10 | }, 11 | { 12 | "filename": "./projects/ngx-multiple-dates/package.json", 13 | "type": "json" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [18.1.0](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v18.0.0...v18.1.0) (2025-02-14) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * use addValidators instead of setValidators ([#46](https://github.com/lekhmanrus/ngx-multiple-dates/issues/46)) ([78202e1](https://github.com/lekhmanrus/ngx-multiple-dates/commit/78202e15a79dd0ce1fd7b6c80526f8900da4ac44)), closes [#45](https://github.com/lekhmanrus/ngx-multiple-dates/issues/45) 11 | * use Date Adapter to display formatted date ([#44](https://github.com/lekhmanrus/ngx-multiple-dates/issues/44)) ([b410441](https://github.com/lekhmanrus/ngx-multiple-dates/commit/b4104413fbd5d66e29ae5870873f0c775579f29a)), closes [#43](https://github.com/lekhmanrus/ngx-multiple-dates/issues/43) 12 | 13 | ## [18.0.0](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v17.0.1...v18.0.0) (2024-08-07) 14 | 15 | 16 | ### Features 17 | 18 | * support angular 18 ([323e013](https://github.com/lekhmanrus/ngx-multiple-dates/commit/323e013b4d0e364d19dad9af77057a63485d1f46)), closes [#35](https://github.com/lekhmanrus/ngx-multiple-dates/issues/35) 19 | 20 | ### [17.0.1](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v17.0.0...v17.0.1) (2023-11-21) 21 | 22 | 23 | ### Bug Fixes 24 | 25 | * update gh actions ([df80a8c](https://github.com/lekhmanrus/ngx-multiple-dates/commit/df80a8c632d7b93fc9bc77f522e4f24a2d7e9fa6)) 26 | 27 | ## [17.0.0](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v16.0.0...v17.0.0) (2023-11-21) 28 | 29 | 30 | ### Features 31 | 32 | * add filter example ([4f894c8](https://github.com/lekhmanrus/ngx-multiple-dates/commit/4f894c8d487d12c53a34972372b292400c2df1f0)) 33 | * support angular 17 ([cba8b81](https://github.com/lekhmanrus/ngx-multiple-dates/commit/cba8b81d72c9aab7ff6cd0736c37d5afac9a67f1)) 34 | 35 | ## [16.0.0](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v15.0.1...v16.0.0) (2023-06-09) 36 | 37 | 38 | ### Features 39 | 40 | * support Angular Components 16 ([5a56896](https://github.com/lekhmanrus/ngx-multiple-dates/commit/5a56896918558b57fe48062b4cda57acbfd3ae96)) 41 | 42 | ### [15.0.1](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v15.0.0...v15.0.1) (2023-06-08) 43 | 44 | 45 | ### Bug Fixes 46 | 47 | * update peer dependencies ([cbdb6a5](https://github.com/lekhmanrus/ngx-multiple-dates/commit/cbdb6a536016dc82cec970744e6677114a2359ed)) 48 | 49 | ## [15.0.0](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v14.3.1...v15.0.0) (2023-06-07) 50 | 51 | 52 | ### Features 53 | 54 | * support Angular Material 15 ([1426140](https://github.com/lekhmanrus/ngx-multiple-dates/commit/1426140f2232bc5704931ea499d4bc091cc2a6c7)), closes [#21](https://github.com/lekhmanrus/ngx-multiple-dates/issues/21) 55 | 56 | ### [14.3.1](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v14.3.0...v14.3.1) (2022-09-20) 57 | 58 | ## [14.3.0](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v14.2.0...v14.3.0) (2022-09-19) 59 | 60 | 61 | ### Features 62 | 63 | * emit date remove event ([47f523c](https://github.com/lekhmanrus/ngx-multiple-dates/commit/47f523c8c1734ace5cf17bd01cd7e43ded17cca8)), closes [#11](https://github.com/lekhmanrus/ngx-multiple-dates/issues/11) 64 | * support Angular 15 ([7719c1f](https://github.com/lekhmanrus/ngx-multiple-dates/commit/7719c1f925fcf57f3eaeb7adb1deeeccc7e07ed1)) 65 | 66 | 67 | ### Bug Fixes 68 | 69 | * readme ([3681543](https://github.com/lekhmanrus/ngx-multiple-dates/commit/3681543cbeda42bbce11b54d77c329fa57176028)) 70 | 71 | ## [14.2.0](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v14.1.1...v14.2.0) (2022-08-19) 72 | 73 | 74 | ### Features 75 | 76 | * support `mat-calendar` ([b1d1402](https://github.com/lekhmanrus/ngx-multiple-dates/commit/b1d1402b0185dbc8c8dfad629b7485455c3983bd)), closes [#10](https://github.com/lekhmanrus/ngx-multiple-dates/issues/10) 77 | 78 | ### [14.1.1](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v14.1.0...v14.1.1) (2022-06-27) 79 | 80 | ## [14.1.0](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v14.0.0...v14.1.0) (2022-04-27) 81 | 82 | 83 | ### Features 84 | 85 | * add chip date format ([ca617c6](https://github.com/lekhmanrus/ngx-multiple-dates/commit/ca617c6a178bc76f0e5a69149cdd8a98876fad0f)), closes [#7](https://github.com/lekhmanrus/ngx-multiple-dates/issues/7) 86 | 87 | ## [14.0.0](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v13.1.0...v14.0.0) (2022-04-08) 88 | 89 | 90 | ### Features 91 | 92 | * support Angular Components 14 ([f658d4c](https://github.com/lekhmanrus/ngx-multiple-dates/commit/f658d4cdf70c0aa3aaa5ab3a1aa440e175c07b02)) 93 | 94 | ## [13.1.0](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v13.0.0...v13.1.0) (2022-01-25) 95 | 96 | 97 | ### Features 98 | 99 | * support all date adapters ([04404fd](https://github.com/lekhmanrus/ngx-multiple-dates/commit/04404fd6a0438573ac844966f83a49a16074a58e)), closes [#2](https://github.com/lekhmanrus/ngx-multiple-dates/issues/2) 100 | 101 | ## [13.0.0](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v12.0.0...v13.0.0) (2021-12-09) 102 | 103 | 104 | ### Features 105 | 106 | * support Angular Components 13 ([41d7fe8](https://github.com/lekhmanrus/ngx-multiple-dates/commit/41d7fe8e73e3317583acce6b7f51fed089440f57)) 107 | 108 | ## [12.0.0](https://github.com/lekhmanrus/ngx-multiple-dates/compare/v1.1.0...v12.0.0) (2021-12-08) 109 | 110 | 111 | ### Features 112 | 113 | * support Angular Components 12 ([b6826df](https://github.com/lekhmanrus/ngx-multiple-dates/commit/b6826df9225e4649c78abc3aa288a9a1b5507c55)) 114 | 115 | ## 1.1.0 (2021-03-05) 116 | 117 | 118 | ### Features 119 | 120 | * custom classes for dates/chips ([4033577](https://github.com/lekhmanrus/ngx-multiple-dates/commit/4033577fa2705c3ac6c6375577ce35c62ae887f8)), closes [#1](https://github.com/lekhmanrus/ngx-multiple-dates/issues/1) 121 | 122 | 123 | ### Bug Fixes 124 | 125 | * github link ([9f24a72](https://github.com/lekhmanrus/ngx-multiple-dates/commit/9f24a72212571c5f5d8ba8691d89693b69305672)) 126 | * model bugs ([a44cefd](https://github.com/lekhmanrus/ngx-multiple-dates/commit/a44cefd59a9781d36313dcd76d725db802e4d0e7)) 127 | * reactive form validation ([07c5ac3](https://github.com/lekhmanrus/ngx-multiple-dates/commit/07c5ac3ab181aea2364150db30335319d7175578)) 128 | * touched on remove ([9c2e68f](https://github.com/lekhmanrus/ngx-multiple-dates/commit/9c2e68f9e74becdd097a6ea4734d247fce2e9f18)) 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ruslan Lekhman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Angular Multiple Dates

2 | 3 |

4 | Angular Multiple Dates Logo 6 |
7 | 8 | Multiple dates picker based on Angular Material. 9 |
10 | Compatible with Angular / CDK / Material **>= 9.x.x**. See Versioning. 11 |
12 |
13 |

14 | 15 |

16 | Demo 17 |
18 |

19 | 20 | 21 | [![Build](https://github.com/lekhmanrus/ngx-multiple-dates/actions/workflows/build.yml/badge.svg)](https://github.com/lekhmanrus/ngx-multiple-dates/actions/workflows/build.yml) 22 | [![Publish](https://github.com/lekhmanrus/ngx-multiple-dates/actions/workflows/publish.yml/badge.svg)](https://github.com/lekhmanrus/ngx-multiple-dates/actions/workflows/publish.yml) 23 | [![codecov](https://codecov.io/gh/lekhmanrus/ngx-multiple-dates/branch/master/graph/badge.svg?token=N9T5Q1CXLU)](https://codecov.io/gh/lekhmanrus/ngx-multiple-dates) 24 | [![npm version](https://img.shields.io/npm/v/ngx-multiple-dates.svg)](https://www.npmjs.com/package/ngx-multiple-dates) 25 | [![npm](https://img.shields.io/npm/dm/ngx-multiple-dates.svg)](https://www.npmjs.com/package/ngx-multiple-dates) 26 | 27 | 28 | ![Example](https://raw.githubusercontent.com/lekhmanrus/ngx-multiple-dates/master/assets/animation.gif) 29 | 30 | 31 |
32 | 33 | 34 | 35 | ## Installation 36 | 37 | 1. Install dependency: 38 | 39 | ```sh 40 | npm install --save ngx-multiple-dates 41 | ``` 42 | 43 | 2. Include `NgxMultipleDatesModule ` to your module: 44 | 45 | ```ts 46 | import { NgModule } from '@angular/core'; 47 | import { BrowserModule } from '@angular/platform-browser'; 48 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 49 | 50 | // Any of the supported date adapter should be imported. For more details - see 51 | // https://material.angular.io/components/datepicker/overview#choosing-a-date-implementation-and-date-format-settings 52 | import { MatNativeDateModule } from '@angular/material/core'; 53 | // import { MatDateFnsModule } from '@angular/material-date-fns-adapter'; 54 | // import { MatLuxonDateModule } from '@angular/material-luxon-adapter'; 55 | // import { MatMomentDateModule } from '@angular/material-moment-adapter'; 56 | 57 | import { MatDatepickerModule } from '@angular/material/datepicker'; 58 | import { MatIconModule } from '@angular/material/icon'; 59 | import { NgxMultipleDatesModule } from 'ngx-multiple-dates'; // module import 60 | 61 | @NgModule({ 62 | imports: [ 63 | BrowserModule, 64 | BrowserAnimationsModule, 65 | MatNativeDateModule, // any of the supported date adapter should be imported 66 | MatDatepickerModule, 67 | MatIconModule, 68 | NgxMultipleDatesModule // import to Angular 69 | // ... 70 | ], 71 | // ... 72 | }) 73 | export class AppModule { } 74 | ``` 75 | 76 | 3. Styles: 77 | 78 | * Add one of the prebuilt themes to `angular.json` or your styles file: 79 | ```css 80 | @import 'ngx-multiple-dates/prebuilt-themes/azure-blue.css'; 81 | ``` 82 | 83 | * Or you can use custom SCSS theme 84 | * Angular **< 12.x.x**: 85 | ```scss 86 | @import '~@angular/material/theming'; 87 | @import '~ngx-multiple-dates/theming'; // import library theme 88 | 89 | @include mat-core(); 90 | // Palette 91 | $primary: mat-palette($mat-indigo); 92 | $accent: mat-palette($mat-pink); 93 | 94 | $theme: mat-light-theme($primary, $accent); // theme 95 | @include angular-material-theme($theme); // apply Angular Material styles 96 | @include ngx-multiple-dates-theme($theme); // apply Angular Multiple Dates styles 97 | 98 | // ... 99 | ``` 100 | * Angular **>= 12.x.x** && **< 18.x.x**: 101 | ```scss 102 | @use '@angular/material' as mat; 103 | @import '~ngx-multiple-dates/theming'; // import library theme 104 | 105 | @include mat.core; 106 | // Palette 107 | $primary: mat.define-palette(mat.$indigo-palette); 108 | $accent: mat.define-palette(mat.$pink-palette); 109 | 110 | $theme: mat.define-light-theme($primary, $accent); // theme 111 | @include mat.all-component-themes($theme); // apply Angular Material styles 112 | @include ngx-multiple-dates-theme($theme); // apply Angular Multiple Dates styles 113 | 114 | // ... 115 | ``` 116 | * Starting Angular Multiple Dates **= 14.x.x** SASS `@use` rule supported: 117 | ```scss 118 | @use '@angular/material' as mat; 119 | @use 'ngx-multiple-dates' as ngxMultipleDates; // use library theme 120 | 121 | @include mat.core; 122 | // Palette 123 | $primary: mat.define-palette(mat.$indigo-palette); 124 | $accent: mat.define-palette(mat.$pink-palette); 125 | 126 | $theme: mat.define-light-theme($primary, $accent); // theme 127 | @include mat.all-component-themes($theme); // apply Angular Material styles 128 | @include ngxMultipleDates.multiple-dates-theme($theme); // apply Angular Multiple Dates styles 129 | 130 | // ... 131 | ``` 132 | * Angular Multiple Dates **>= 15.x.x** && **< 18.x.x**: 133 | ```scss 134 | @use '@angular/material' as mat; 135 | @use 'ngx-multiple-dates' as ngxMultipleDates; // use library theme 136 | 137 | @include mat.core; 138 | // Palette 139 | $primary: mat.define-palette(mat.$indigo-palette); 140 | $accent: mat.define-palette(mat.$pink-palette); 141 | 142 | $theme: mat.define-light-theme(( 143 | color: ( 144 | primary: $primary, 145 | accent: $accent 146 | ) 147 | )); // theme 148 | @include mat.all-component-themes($theme); // apply Angular Material styles 149 | @include ngxMultipleDates.multiple-dates-theme($theme); // apply Angular Multiple Dates styles 150 | 151 | // ... 152 | ``` 153 | * Angular Multiple Dates **>= 18.x.x**: 154 | ```scss 155 | @use '@angular/material' as mat; 156 | @use 'ngx-multiple-dates' as ngxMultipleDates; // use library theme 157 | 158 | @include mat.core; 159 | // Theme 160 | $my-theme: mat.define-theme( 161 | ( 162 | color: ( 163 | theme-type: light, 164 | primary: mat.$azure-palette, 165 | tertiary: mat.$blue-palette 166 | ), 167 | density: ( 168 | scale: 0 169 | ) 170 | ) 171 | ); 172 | @include mat.all-component-themes($theme); // apply Angular Material styles 173 | @include ngxMultipleDates.multiple-dates-theme($theme); // apply Angular Multiple Dates styles 174 | 175 | // ... 176 | ``` 177 | 178 | 179 | 180 | ### Available pre-built themes: 181 | 182 | * `azure-blue.css` 183 | * `cyan-orange.css` 184 | * `magenta-violet.css` 185 | * `rose-red.css` 186 | 187 | 188 | 189 | ## Versioning 190 | 191 | Library tested for Angular / CDK / Material versions **>= 9.x.x**. 192 | 193 | Use Angular Multiple Dates `1.x.x` for Angular Components `<= 11.x.x` 194 | 195 | Later versions are consistant with major Angular Components version. E.g.: 196 | 197 | Use `v13.x.x` with Angular Components `13.x.x`. 198 | 199 | Use `v12.x.x` with Angular Components `12.x.x`. 200 | 201 | 202 | 203 | ## Dependencies 204 | 205 | * Angular 206 | * Angular CDK 207 | * Angular Material 208 | * RxJs 209 | 210 | 211 | 212 | ## Examples 213 | 214 | ### Date Picker 215 | 216 | ```html 217 | 218 | 220 | 221 | 222 | 223 | 224 | ``` 225 | 226 | ### Calendar (inline) 227 | 228 | ```html 229 | 230 | 232 | 233 | 234 | 235 | ``` 236 | 237 | ### More 238 | 239 | For more examples please visit the [Demo](https://lekhmanrus.github.io/ngx-multiple-dates/) page. 240 | Its source code is located [here](https://github.com/lekhmanrus/ngx-multiple-dates/tree/master/projects/ngx-multiple-dates-app/src/app/components/root). 241 | 242 | 243 | 244 | ## API reference 245 | 246 | ### MultipleDatesComponent 247 | 248 | Selector: `ngx-multiple-dates` 249 | 250 | Exported as: `ngxMultipleDates` 251 | 252 | **Properties** 253 | 254 | | Name | Description | 255 | |---------------------|--------------------------------------------------------------------------| 256 | | **Input** | | 257 | | `@Input()`
`value: D \| null` | The value of the `ngx-multiple-dates` control. | 258 | | `@Input()`
`matDatepicker: MatDatepicker \| MatCalendar` | The datepicker (or calendar - for inline view) that this `ngx-multiple-dates` element is associated with. | 259 | | `@Input()`
`color: ThemePalette` | Theme color palette for the component. This API is supported in M2 themes only, it has no effect in M3 themes.
For information on applying color variants in M3, see [Using component color variants](https://material.angular.io/guide/theming#using-component-color-variants). | 260 | | `@Input()`
`placeholder: string` | Placeholder to be shown if no value has been selected. | 261 | | `@Input()`
`required: boolean` | Whether the component is required. | 262 | | `@Input()`
`disabled: boolean` | Whether the component is disabled. | 263 | | `@Input()`
`matDatepickerFilter: (date: D) => boolean` | Function that can be used to filter out dates within the datepicker. | 264 | | `@Input()`
`max: D \| null` | The maximum valid date. | 265 | | `@Input()`
`min: D \| null` | The minimum valid date. | 266 | | `@Input()`
`classes: Array>` | Custom date classes. | 267 | | `@Input()`
`id: string` | Unique id of the element. | 268 | | `@Input()`
`errorStateMatcher: ErrorStateMatcher` | An object used to control when error messages are shown. | 269 | | `@Input()`
`format: string` | The date/time components to include, using predefined options or a custom format string.
See [DatePipe Usage notes](https://angular.io/api/common/DatePipe#usage-notes) for more information. | 270 | | **Output** | | 271 | | `@Output()`
`dateChange: EventEmitter>` | Emits when a change event is fired on this `ngx-multiple-dates` element. | 272 | | `@Output()`
`remove: EventEmitter>` | Emits on a date removal. | 273 | | **Properties** | | 274 | | `resetModel: Date` | Model used to reset datepicker selected value, so unselect just selected date will be possible. | 275 | | `closeOnSelected: boolean` | Whether datepicker should be closed on date selected, or opened to select more dates. | 276 | | `empty: boolean` | Whether the select has a value. | 277 | | `shouldLabelFloat: boolean` | Whether the `MatFormField` label should try to float. | 278 | | `focused: boolean` | Whether `ngx-multiple-dates` element has focus. | 279 | | `errorState: boolean` | Whether the control is in an error state. | 280 | | `stateChanges: Observable` | Stream that emits whenever the state of the control changes such that the parent `MatFormField` needs to run change detection. | 281 | | `ngControl: NgControl` | Form control to manage component. | 282 | | `controlType: 'ngx-multiple-dates'` | A name for this control that can be used by mat-form-field. | 283 | 284 | 285 | **Methods** 286 | 287 | * `focus`
Focuses the `ngx-multiple-dates` element. 288 | 289 | * `blur`
Used to leave focus from the `ngx-multiple-dates` element. 290 | 291 | * `setDescribedByIds`
Sets the list of element IDs that currently describe this control. 292 | 293 | | **Parameters** | | 294 | |---------------------|-----------------------------------------------------------------------------| 295 | | `ids: string[]` | Ids to set. | 296 | 297 | * `onContainerClick`
Handles a click on the control's container. 298 | 299 | * `validate`
Performs synchronous validation for the control. 300 | 301 | | **Parameters** | | 302 | |----------------------------|----------------------------------------------------------------------| 303 | | `control: AbstractControl` | The control to validate against. | 304 | | **Returns** | 305 | | `ValidationErrors \| null` | A map of validation errors if validation fails, otherwise `null`. | 306 | 307 | * `dateClass`
Function used to add CSS classes to selected dates. 308 | 309 | | **Parameters** | | 310 | |-----------------------------|---------------------------------------------------------------------| 311 | | `date: D` | Date to check if classes should be applied. | 312 | | **Returns** | | 313 | | `MatCalendarCellCssClasses` | CSS classes to apply. | 314 | 315 | * `dateChanged`
Fires when a change event is fired on the datepicker ``. 316 | 317 | | **Parameters** | | 318 | |-------------------------------------|-------------------------------------------------------------| 319 | | `event: MatDatepickerInputEvent` | Change event. | 320 | 321 | * `remove`
Removes selected chip from the list. 322 | 323 | | **Parameters** | | 324 | |-----------------------------|---------------------------------------------------------------------| 325 | | `date: D` | Value to remove. | 326 | 327 | 328 | 329 | ## Build 330 | 331 | Run `npm run build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 332 | 333 | 334 | 335 | ## Development server 336 | 337 | Run `npm start` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 338 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ngx-multiple-dates": { 7 | "projectType": "library", 8 | "root": "projects/ngx-multiple-dates", 9 | "sourceRoot": "projects/ngx-multiple-dates/src", 10 | "prefix": "ngx", 11 | "architect": { 12 | "build": { 13 | "builder": "@angular-devkit/build-angular:ng-packagr", 14 | "options": { 15 | "project": "projects/ngx-multiple-dates/ng-package.json" 16 | }, 17 | "configurations": { 18 | "production": { 19 | "tsConfig": "projects/ngx-multiple-dates/tsconfig.lib.prod.json" 20 | }, 21 | "development": { 22 | "tsConfig": "projects/ngx-multiple-dates/tsconfig.lib.json" 23 | } 24 | }, 25 | "defaultConfiguration": "production" 26 | }, 27 | "test": { 28 | "builder": "@angular-devkit/build-angular:karma", 29 | "options": { 30 | "tsConfig": "projects/ngx-multiple-dates/tsconfig.spec.json", 31 | "karmaConfig": "projects/ngx-multiple-dates/karma.conf.js", 32 | "codeCoverage": true, 33 | "polyfills": [ 34 | "zone.js", 35 | "zone.js/testing" 36 | ] 37 | } 38 | }, 39 | "lint": { 40 | "builder": "@angular-eslint/builder:lint", 41 | "options": { 42 | "lintFilePatterns": [ 43 | "projects/ngx-multiple-dates/src/**/*.ts", 44 | "projects/ngx-multiple-dates/src/**/*.html" 45 | ] 46 | } 47 | } 48 | } 49 | }, 50 | "ngx-multiple-dates-app": { 51 | "projectType": "application", 52 | "schematics": { 53 | "@schematics/angular:component": { 54 | "style": "scss" 55 | }, 56 | "@schematics/angular:application": { 57 | "strict": true 58 | } 59 | }, 60 | "root": "projects/ngx-multiple-dates-app", 61 | "sourceRoot": "projects/ngx-multiple-dates-app/src", 62 | "prefix": "app", 63 | "architect": { 64 | "build": { 65 | "builder": "@angular-devkit/build-angular:browser", 66 | "options": { 67 | "outputPath": "dist/ngx-multiple-dates-app", 68 | "index": "projects/ngx-multiple-dates-app/src/index.html", 69 | "main": "projects/ngx-multiple-dates-app/src/main.ts", 70 | "tsConfig": "projects/ngx-multiple-dates-app/tsconfig.app.json", 71 | "polyfills": [ 72 | "zone.js" 73 | ], 74 | "inlineStyleLanguage": "scss", 75 | "assets": [ 76 | "projects/ngx-multiple-dates-app/src/favicon.ico", 77 | "projects/ngx-multiple-dates-app/src/assets" 78 | ], 79 | "stylePreprocessorOptions": { 80 | "includePaths": [ 81 | "dist" 82 | ] 83 | }, 84 | "styles": [ 85 | "projects/ngx-multiple-dates-app/src/styles/styles.scss" 86 | ], 87 | "scripts": [ ] 88 | }, 89 | "configurations": { 90 | "production": { 91 | "budgets": [ 92 | { 93 | "type": "initial", 94 | "maximumWarning": "2mb", 95 | "maximumError": "5mb" 96 | }, 97 | { 98 | "type": "anyComponentStyle", 99 | "maximumWarning": "6kb", 100 | "maximumError": "10kb" 101 | } 102 | ], 103 | "fileReplacements": [ 104 | { 105 | "replace": "projects/ngx-multiple-dates-app/src/environments/environment.ts", 106 | "with": "projects/ngx-multiple-dates-app/src/environments/environment.prod.ts" 107 | } 108 | ], 109 | "outputHashing": "all", 110 | "optimization": true, 111 | "sourceMap": false, 112 | "namedChunks": false, 113 | "extractLicenses": true, 114 | "vendorChunk": false, 115 | "buildOptimizer": true 116 | }, 117 | "development": { 118 | "buildOptimizer": false, 119 | "optimization": false, 120 | "vendorChunk": true, 121 | "extractLicenses": false, 122 | "sourceMap": true, 123 | "namedChunks": true 124 | } 125 | }, 126 | "defaultConfiguration": "production" 127 | }, 128 | "serve": { 129 | "builder": "@angular-devkit/build-angular:dev-server", 130 | "configurations": { 131 | "production": { 132 | "buildTarget": "ngx-multiple-dates-app:build:production" 133 | }, 134 | "development": { 135 | "buildTarget": "ngx-multiple-dates-app:build:development" 136 | } 137 | }, 138 | "defaultConfiguration": "development" 139 | }, 140 | "extract-i18n": { 141 | "builder": "@angular-devkit/build-angular:extract-i18n", 142 | "options": { 143 | "buildTarget": "ngx-multiple-dates-app:build" 144 | } 145 | }, 146 | "test": { 147 | "builder": "@angular-devkit/build-angular:karma", 148 | "options": { 149 | "polyfills": [ 150 | "zone.js", 151 | "zone.js/testing" 152 | ], 153 | "tsConfig": "projects/ngx-multiple-dates-app/tsconfig.spec.json", 154 | "karmaConfig": "projects/ngx-multiple-dates-app/karma.conf.js", 155 | "codeCoverage": true, 156 | "inlineStyleLanguage": "scss", 157 | "assets": [ 158 | "projects/ngx-multiple-dates-app/src/favicon.ico", 159 | "projects/ngx-multiple-dates-app/src/assets" 160 | ], 161 | "stylePreprocessorOptions": { 162 | "includePaths": [ 163 | "dist" 164 | ] 165 | }, 166 | "styles": [ 167 | "projects/ngx-multiple-dates-app/src/styles/styles.scss" 168 | ], 169 | "scripts": [ ] 170 | } 171 | }, 172 | "lint": { 173 | "builder": "@angular-eslint/builder:lint", 174 | "options": { 175 | "lintFilePatterns": [ 176 | "projects/ngx-multiple-dates-app/src/**/*.ts", 177 | "projects/ngx-multiple-dates-app/src/**/*.html" 178 | ] 179 | } 180 | } 181 | } 182 | }, 183 | "ngx-multiple-dates-app-e2e": { 184 | "root": "projects/ngx-multiple-dates-app/e2e/", 185 | "projectType": "application", 186 | "prefix": "app", 187 | "architect": { 188 | "e2e": { 189 | "builder": "@angular-devkit/build-angular:protractor", 190 | "options": { 191 | "protractorConfig": "projects/ngx-multiple-dates-app/e2e/protractor.conf.js", 192 | "devServerTarget": "ngx-multiple-dates-app:serve" 193 | }, 194 | "configurations": { 195 | "production": { 196 | "devServerTarget": "ngx-multiple-dates-app:serve:production" 197 | } 198 | } 199 | }, 200 | "lint": { 201 | "builder": "@angular-eslint/builder:lint", 202 | "options": { 203 | "lintFilePatterns": [ 204 | "projects/ngx-multiple-dates-app/e2e/**/*.ts", 205 | "projects/ngx-multiple-dates-app/e2e/**/*.html" 206 | ] 207 | } 208 | } 209 | } 210 | } 211 | }, 212 | "cli": { 213 | "analytics": false 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /assets/animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lekhmanrus/ngx-multiple-dates/7392f7835694f4513bfda2cb2f1310f5fd8b30a2/assets/animation.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-multiple-dates", 3 | "description": "Angular Multiple Dates", 4 | "version": "18.1.0", 5 | "homepage": "https://lekhmanrus.github.io/ngx-multiple-dates/", 6 | "bugs": { 7 | "url": "https://github.com/lekhmanrus/ngx-multiple-dates/issues", 8 | "email": "lekhman112@gmail.com" 9 | }, 10 | "author": "Ruslan Lekhman (https://github.com/lekhmanrus)", 11 | "contributors": [ 12 | "Ruslan Lekhman (https://github.com/lekhmanrus)" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/lekhmanrus/ngx-multiple-dates" 17 | }, 18 | "license": "MIT", 19 | "readme": "https://github.com/lekhmanrus/ngx-multiple-dates/blob/master/README.md", 20 | "scripts": { 21 | "prepare": "is-ci || husky install", 22 | "ng": "ng", 23 | "start": "ng serve", 24 | "build": "ng build ngx-multiple-dates", 25 | "postbuild": "run-p copy:docs copy:scss css", 26 | "build:app": "ng build ngx-multiple-dates-app --base-href=/ngx-multiple-dates/", 27 | "postbuild:app": "cpx \"{LICENSE,CHANGELOG.md,README.md}\" \"dist/ngx-multiple-dates-app\"", 28 | "copy:docs": "cpx \"*.md\" \"dist/ngx-multiple-dates\"", 29 | "copy:scss": "cpx \"projects/ngx-multiple-dates/src/lib/**/*.scss\" \"dist/ngx-multiple-dates\"", 30 | "css": "run-s css:compile css:prefix css:minify", 31 | "css:compile": "run-p css:compile:azure-blue css:compile:cyan-orange css:compile:magenta-violet css:compile:rose-red", 32 | "css:compile:azure-blue": "sass -I node_modules ./projects/ngx-multiple-dates/src/prebuilt-themes/azure-blue.scss:./dist/ngx-multiple-dates/prebuilt-themes/azure-blue.css", 33 | "css:compile:cyan-orange": "sass -I node_modules ./projects/ngx-multiple-dates/src/prebuilt-themes/cyan-orange.scss:./dist/ngx-multiple-dates/prebuilt-themes/cyan-orange.css", 34 | "css:compile:magenta-violet": "sass -I node_modules ./projects/ngx-multiple-dates/src/prebuilt-themes/magenta-violet.scss:./dist/ngx-multiple-dates/prebuilt-themes/magenta-violet.css", 35 | "css:compile:rose-red": "sass -I node_modules ./projects/ngx-multiple-dates/src/prebuilt-themes/rose-red.scss:./dist/ngx-multiple-dates/prebuilt-themes/rose-red.css", 36 | "css:minify": "run-p css:minify:azure-blue css:minify:cyan-orange css:minify:magenta-violet css:minify:rose-red", 37 | "css:minify:azure-blue": "cleancss -f breakWith=lf ./dist/ngx-multiple-dates/prebuilt-themes/azure-blue.css -o ./dist/ngx-multiple-dates/prebuilt-themes/azure-blue.css", 38 | "css:minify:cyan-orange": "cleancss -f breakWith=lf ./dist/ngx-multiple-dates/prebuilt-themes/cyan-orange.css -o ./dist/ngx-multiple-dates/prebuilt-themes/cyan-orange.css", 39 | "css:minify:magenta-violet": "cleancss -f breakWith=lf ./dist/ngx-multiple-dates/prebuilt-themes/magenta-violet.css -o ./dist/ngx-multiple-dates/prebuilt-themes/magenta-violet.css", 40 | "css:minify:rose-red": "cleancss -f breakWith=lf ./dist/ngx-multiple-dates/prebuilt-themes/rose-red.css -o ./dist/ngx-multiple-dates/prebuilt-themes/rose-red.css", 41 | "css:prefix": "postcss --replace \"./dist/ngx-multiple-dates/prebuilt-themes/*.css\"", 42 | "test": "ng test", 43 | "test:ci": "ng test --watch=false", 44 | "lint": "ng lint", 45 | "e2e": "ng e2e", 46 | "release": "standard-version", 47 | "commitlint": "commitlint" 48 | }, 49 | "private": false, 50 | "keywords": [ 51 | "angular", 52 | "material", 53 | "date", 54 | "multiple", 55 | "datepicker" 56 | ], 57 | "dependencies": { 58 | "@angular/animations": "^18.2.13", 59 | "@angular/cdk": "^18.2.14", 60 | "@angular/common": "^18.2.13", 61 | "@angular/compiler": "^18.2.13", 62 | "@angular/core": "^18.2.13", 63 | "@angular/forms": "^18.2.13", 64 | "@angular/material": "^18.2.14", 65 | "@angular/platform-browser": "^18.2.13", 66 | "@angular/platform-browser-dynamic": "^18.2.13", 67 | "@angular/router": "^18.2.13", 68 | "@ng-matero/extensions": "^18.4.1", 69 | "rxjs": "^7.8.1", 70 | "tslib": "^2.8.1", 71 | "zone.js": "~0.15.0" 72 | }, 73 | "devDependencies": { 74 | "@angular-devkit/build-angular": "^18.2.14", 75 | "@angular-eslint/builder": "^18.4.4-alpha.0", 76 | "@angular-eslint/eslint-plugin": "^18.4.4-alpha.0", 77 | "@angular-eslint/eslint-plugin-template": "^18.4.4-alpha.0", 78 | "@angular-eslint/schematics": "^18.4.4-alpha.0", 79 | "@angular-eslint/template-parser": "^18.4.4-alpha.0", 80 | "@angular/cli": "^18.2.14", 81 | "@angular/compiler-cli": "^18.2.13", 82 | "@angular/language-service": "^18.2.13", 83 | "@angular/material-date-fns-adapter": "^18.2.14", 84 | "@angular/material-luxon-adapter": "^18.2.14", 85 | "@angular/material-moment-adapter": "^18.2.14", 86 | "@commitlint/cli": ">=19.7.1", 87 | "@commitlint/config-conventional": ">=19.7.1", 88 | "@commitlint/format": ">=19.5.0", 89 | "@types/jasmine": ">=5.1.6", 90 | "@types/jasminewd2": ">=2.0.13", 91 | "@types/karma-coverage": ">=2.0.3", 92 | "@types/karma-jasmine-html-reporter": ">=1.7.3", 93 | "@types/node": ">=22.13.4", 94 | "@typescript-eslint/eslint-plugin": "^8.24.1-alpha.3", 95 | "@typescript-eslint/parser": "^8.24.1-alpha.3", 96 | "autoprefixer": "^10.4.20", 97 | "clean-css-cli": "5.6.3", 98 | "codelyzer": "^6.0.2", 99 | "conventional-changelog-angular": ">=8.0.0", 100 | "cpx": "^1.5.0", 101 | "date-fns": "^3.6.0", 102 | "eslint": "^8.57.1", 103 | "eslint-import-resolver-typescript": ">=3.8.0", 104 | "eslint-plugin-import": ">=2.31.0", 105 | "eslint-plugin-jsdoc": ">=50.6.3", 106 | "eslint-plugin-prefer-arrow": ">=1.2.3", 107 | "eslint-plugin-rxjs": ">=5.0.3", 108 | "eslint-plugin-unicorn": ">=55.0.0", 109 | "husky": "^9.1.7", 110 | "is-ci": ">=4.1.0", 111 | "jasmine-core": ">=5.6.0", 112 | "jasmine-spec-reporter": ">=7.0.0", 113 | "karma": ">=6.4.4", 114 | "karma-chrome-launcher": ">=3.2.0", 115 | "karma-coverage": ">=2.2.1", 116 | "karma-jasmine": ">=5.1.0", 117 | "karma-jasmine-html-reporter": ">=2.1.0", 118 | "karma-mocha-reporter": ">=2.2.5", 119 | "luxon": "^3.5.0", 120 | "moment": "^2.30.1", 121 | "ng-packagr": "^18.2.1", 122 | "npm-run-all": "^4.1.5", 123 | "postcss-cli": "^11.0.0", 124 | "postcss-load-config": "^6.0.1", 125 | "protractor": "^7.0.0", 126 | "sass": "^1.85.0", 127 | "standard-version": ">=9.5.0", 128 | "ts-node": "^11.0.0-beta.1", 129 | "typescript": "~5.5.4" 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/.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 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": false, 3 | "overrides": [ 4 | { 5 | "files": [ 6 | "*.ts" 7 | ], 8 | "rules": { 9 | "@angular-eslint/component-selector": [ 10 | "error", 11 | { 12 | "type": "element", 13 | "prefix": "app", 14 | "style": "kebab-case" 15 | } 16 | ], 17 | "@angular-eslint/directive-selector": [ 18 | "error", 19 | { 20 | "type": "attribute", 21 | "prefix": "app", 22 | "style": "camelCase" 23 | } 24 | ] 25 | } 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/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: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() { } 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/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('ngx-multiple-dates-app 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(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | async navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | async getTitleText() { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../out-tsc/e2e", 5 | "module": "commonjs", 6 | "types": [ 7 | "jasmine", 8 | "jasminewd2", 9 | "node" 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: [ 'jasmine', '@angular-devkit/build-angular' ], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-mocha-reporter'), 13 | require('karma-coverage'), 14 | require('@angular-devkit/build-angular/plugins/karma') 15 | ], 16 | client: { 17 | clearContext: false // leave Jasmine Spec Runner output visible in browser 18 | }, 19 | reporters: [ 'progress', 'coverage', 'kjhtml', 'mocha' ], 20 | mochaReporter: { 21 | output: 'autowatch' 22 | }, 23 | coverageReporter: { 24 | dir: require('path').join(__dirname, '../../coverage/ngx-multiple-dates-app'), 25 | subdir: '.', 26 | reporters: [ 27 | { type: 'html' }, 28 | { type: 'lcovonly' }, 29 | { type: 'text-summary' }, 30 | { type: 'cobertura' } 31 | ] 32 | }, 33 | port: 9876, 34 | colors: true, 35 | logLevel: config.LOG_INFO, 36 | autoWatch: true, 37 | browsers: [ 'ChromeHeadlessNoSandbox' ], 38 | customLaunchers: { 39 | ChromeHeadlessNoSandbox: { 40 | base: 'ChromeHeadless', 41 | flags: [ '--no-sandbox' ] 42 | } 43 | }, 44 | singleRun: false, 45 | restartOnFileChange: true 46 | }); 47 | }; 48 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/app/app.constants.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_THEME = 'azure-blue-theme'; 2 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule, DomSanitizer } from '@angular/platform-browser'; 3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 4 | import { CommonModule } from '@angular/common'; 5 | import { HttpClientModule } from '@angular/common/http'; 6 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 7 | 8 | import { /*MAT_DATE_LOCALE,*/ MatNativeDateModule, MatRippleModule } from '@angular/material/core'; 9 | // import { MatDateFnsModule } from '@angular/material-date-fns-adapter'; 10 | // import { MatLuxonDateModule } from '@angular/material-luxon-adapter'; 11 | // import { MatMomentDateModule } from '@angular/material-moment-adapter'; 12 | import { MatButtonModule } from '@angular/material/button'; 13 | import { MatCardModule } from '@angular/material/card'; 14 | import { MatDatepickerModule } from '@angular/material/datepicker'; 15 | import { MatIconModule, MatIconRegistry } from '@angular/material/icon'; 16 | import { MatInputModule } from '@angular/material/input'; 17 | import { MatToolbarModule } from '@angular/material/toolbar'; 18 | import { MtxPopoverModule } from '@ng-matero/extensions/popover'; 19 | import { NgxMultipleDatesModule } from 'ngx-multiple-dates'; 20 | // import { enUS } from 'date-fns/locale'; 21 | 22 | import { RootComponent } from './components/root/root.component'; 23 | import { ThemePickerComponent } from './components/theme-picker/theme-picker.component'; 24 | 25 | @NgModule({ 26 | declarations: [ 27 | RootComponent, 28 | ThemePickerComponent 29 | ], 30 | imports: [ 31 | BrowserModule, 32 | BrowserAnimationsModule, 33 | CommonModule, 34 | HttpClientModule, 35 | FormsModule, 36 | ReactiveFormsModule, 37 | 38 | MatNativeDateModule, 39 | // MatDateFnsModule, 40 | // MatLuxonDateModule, 41 | // MatMomentDateModule, 42 | MatRippleModule, 43 | MatButtonModule, 44 | MatCardModule, 45 | MatDatepickerModule, 46 | MatIconModule, 47 | MatInputModule, 48 | MatToolbarModule, 49 | MtxPopoverModule, 50 | NgxMultipleDatesModule 51 | ], 52 | providers: [ 53 | // { provide: MAT_DATE_LOCALE, useValue: enUS } 54 | ], 55 | bootstrap: [ RootComponent ] 56 | }) 57 | export class AppModule { 58 | constructor(matIconRegistry: MatIconRegistry, domSanitizer: DomSanitizer) { 59 | matIconRegistry.addSvgIcon( 60 | 'github', 61 | domSanitizer.bypassSecurityTrustResourceUrl('./assets/icons/github.svg') 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/app/components/root/root.component.html: -------------------------------------------------------------------------------- 1 | 2 | Angular Multiple Dates Demo 3 |
4 | 8 | 9 | 10 | 11 |
12 | 14 | 15 | 16 |
17 |
18 | 19 | 20 | Simple 21 | 22 | 23 | 24 | Simple 25 | 27 | 28 | 29 | 30 |
{{ forValuesOutput.value | json }}
31 |
32 |
33 | 34 | 35 | 36 | ngModel 37 | 38 | 39 | 40 | Model 41 | 43 | 44 | 45 | 46 |
{{ model | json }}
47 |
48 |
49 | 50 | 51 | 52 | Datepicker Toggle Button 53 | 54 | 55 | 56 | Model 57 | 59 | 60 | 61 | 62 | 63 |
{{ modelWithToggleButton | json }}
64 |
65 |
66 | 67 | 68 | 69 | Predefined model 70 | 71 | 72 | 73 | Predefined 74 | 77 | 78 | 79 | 80 | 81 |
{{ modelPredefined | json }}
82 |
83 |
84 | 85 | 86 | 87 | Min/Max Values 88 | 89 | 90 | 91 | Min/Max 92 | 95 | 96 | 97 | 98 | 99 |
Min: {{ min | date }}
100 |
Max: {{ max | date }}
101 |
{{ modelMinMax | json }}
102 |
103 |
104 | 105 | 106 | 107 | Minlength/Maxlength 108 | 109 | 110 | 111 | Minlength/Maxlength 112 | 115 | 116 | 117 | 118 | 119 |
Minlength: 2
120 |
Maxlength: 3
121 |
{{ modelMinlengthMaxlength | json }}
122 |
123 |
124 | 125 | 126 | 127 | Required 128 | 129 | 130 | 131 | Required 132 | 134 | 135 | 136 | 137 | 138 |
{{ modelRequired | json }}
139 |
140 |
141 | 142 | 143 | 144 | Disabled 145 | 146 | 147 | 148 | Disabled 149 | 151 | 152 | 153 | 154 | 155 |
{{ disabledForValuesOutput.value | json }}
156 |
157 |
158 | 159 | 160 | 161 | Color 162 | 163 | 164 | 165 | Color 166 | 168 | 169 | 170 | 171 | 172 |
{{ modelColor | json }}
173 |
174 |
175 | 176 | 177 | 178 | Classes 179 | 180 | 181 | 182 | Classes 183 | 185 | 186 | 187 | 188 | 189 |
Classes:
190 |
{{ classes | json }}
191 |
SCSS:
192 |
193 | ::ng-deep {
194 |   /* Datepicker classes. */
195 |   .mat-calendar-body-cell {
196 |     &.my-red {
197 |       .mat-calendar-body-cell-content,
198 |       &:hover .mat-calendar-body-cell-content {
199 |         border: 1px solid #f44336 !important;
200 |       }
201 |     }
202 | 
203 |     &.my-green {
204 |       .mat-calendar-body-cell-content,
205 |       &:hover .mat-calendar-body-cell-content {
206 |         border: 1px solid #4caf50 !important;
207 |       }
208 |     }
209 | 
210 |     &.my-blue {
211 |       .mat-calendar-body-cell-content,
212 |       &:hover .mat-calendar-body-cell-content {
213 |         border: 1px solid #2196f3 !important;
214 |       }
215 |     }
216 | 
217 |     /* Datepicker selected classes. */
218 |     &.selected:not(.mat-calendar-body-disabled) {
219 |       &.my-red {
220 |         .mat-calendar-body-cell-content:not(.disabled),
221 |         &:hover .mat-calendar-body-cell-content:not(.disabled),
222 |         &:hover .mat-calendar-body-cell-content:not(.disabled):hover {
223 |           background-color: #f44336 !important;
224 |         }
225 |       }
226 | 
227 |       &.my-green {
228 |         .mat-calendar-body-cell-content:not(.disabled),
229 |         &:hover .mat-calendar-body-cell-content:not(.disabled),
230 |         &:hover .mat-calendar-body-cell-content:not(.disabled):hover {
231 |           background-color: #4caf50 !important;
232 |         }
233 |       }
234 | 
235 |       &.my-blue {
236 |         .mat-calendar-body-cell-content:not(.disabled),
237 |         &:hover .mat-calendar-body-cell-content:not(.disabled),
238 |         &:hover .mat-calendar-body-cell-content:not(.disabled):hover {
239 |           background-color: #2196f3 !important;
240 |         }
241 |       }
242 |     }
243 |   }
244 | 
245 |   /* Chip classes. */
246 |   .mat-chip,
247 |   .mat-mdc-chip {
248 |     &.my-red {
249 |       background-color: #f44336 !important;
250 |       color: #fff !important;
251 |     }
252 | 
253 |     &.my-green {
254 |       background-color: #4caf50 !important;
255 |       color: #fff !important;
256 |     }
257 | 
258 |     &.my-blue {
259 |       background-color: #2196f3 !important;
260 |       color: #fff !important;
261 |     }
262 |   }
263 | }
264 |         
265 |
{{ modelClasses | json }}
266 |
267 |
268 | 269 | 270 | 271 | Reactive 272 | 273 | 274 | 275 | Reactive 276 | 278 | 279 | 280 | 281 | 282 |
{{ reactiveControl.value | json }}
283 |
284 |
285 | 286 | 287 | 288 | Formatting 289 | 290 | 291 | 292 | Format 293 | 295 | 296 | 297 | 298 | 299 |
{{ modelFormat | json }}
300 |
301 |
302 | 303 | 304 | 305 | Calendar 306 | 307 | 308 | 309 | Calendar 310 | 312 | 313 | 314 | 315 |
{{ modelCalendar | json }}
316 |
317 |
318 | 319 | 320 | 321 | Datepicker with filter validation 322 | 323 | 324 | 325 | Filter Validation 326 | 329 | 330 | 331 | 332 | 333 |
{{ modelFilterValidation | json }}
334 |
335 |
336 |
337 | 338 |
339 | 340 | 341 | Reactive Form 342 | 343 | 344 | 345 | Reactive 346 | 350 | 351 | 352 | 353 | 354 |
Min: {{ min | date }}
355 |
Max: {{ max | date }}
356 |
Minlength: 2
357 |
Maxlength: 3
358 |
Required
359 |
{{ reactiveFormForValuesOutput.value | json }}
360 |
361 |
362 |
363 |
364 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/app/components/root/root.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | 4 | mat-toolbar { 5 | position: fixed; 6 | top: 0; 7 | z-index: 3; 8 | } 9 | 10 | mat-card { 11 | margin-bottom: 15px; 12 | 13 | mat-calendar { 14 | width: 325px; 15 | } 16 | 17 | pre { 18 | max-height: 161px; 19 | overflow-y: auto; 20 | } 21 | } 22 | 23 | .container { 24 | margin-top: 64px; 25 | padding: 15px; 26 | } 27 | 28 | .full-width { 29 | width: 100%; 30 | } 31 | } 32 | 33 | ::ng-deep { 34 | .mtx-popover-panel { 35 | background-color: var(--mat-app-background-color, var(--mat-app-background, transparent)) !important; 36 | color: var(--mat-app-text-color, var(--mat-app-on-background, inherit)) !important; 37 | } 38 | 39 | /* Datepicker classes. */ 40 | .mat-calendar-body-cell { 41 | &.my-red { 42 | .mat-calendar-body-cell-content, 43 | &:hover .mat-calendar-body-cell-content { 44 | border: 1px solid #f44336 !important; 45 | } 46 | } 47 | 48 | &.my-green { 49 | .mat-calendar-body-cell-content, 50 | &:hover .mat-calendar-body-cell-content { 51 | border: 1px solid #4caf50 !important; 52 | } 53 | } 54 | 55 | &.my-blue { 56 | .mat-calendar-body-cell-content, 57 | &:hover .mat-calendar-body-cell-content { 58 | border: 1px solid #2196f3 !important; 59 | } 60 | } 61 | 62 | /* Datepicker selected classes. */ 63 | &.selected:not(.mat-calendar-body-disabled) { 64 | &.my-red { 65 | .mat-calendar-body-cell-content:not(.disabled), 66 | &:hover .mat-calendar-body-cell-content:not(.disabled), 67 | &:hover .mat-calendar-body-cell-content:not(.disabled):hover { 68 | background-color: #f44336 !important; 69 | } 70 | } 71 | 72 | &.my-green { 73 | .mat-calendar-body-cell-content:not(.disabled), 74 | &:hover .mat-calendar-body-cell-content:not(.disabled), 75 | &:hover .mat-calendar-body-cell-content:not(.disabled):hover { 76 | background-color: #4caf50 !important; 77 | } 78 | } 79 | 80 | &.my-blue { 81 | .mat-calendar-body-cell-content:not(.disabled), 82 | &:hover .mat-calendar-body-cell-content:not(.disabled), 83 | &:hover .mat-calendar-body-cell-content:not(.disabled):hover { 84 | background-color: #2196f3 !important; 85 | } 86 | } 87 | } 88 | } 89 | 90 | /* Chip classes. */ 91 | .mat-chip, 92 | .mat-mdc-chip { 93 | &.my-red { 94 | background-color: #f44336 !important; 95 | color: #fff !important; 96 | } 97 | 98 | &.my-green { 99 | background-color: #4caf50 !important; 100 | color: #fff !important; 101 | } 102 | 103 | &.my-blue { 104 | background-color: #2196f3 !important; 105 | color: #fff !important; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/app/components/root/root.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { NO_ERRORS_SCHEMA } from '@angular/core'; 2 | import { TestBed } from '@angular/core/testing'; 3 | import { MatNativeDateModule } from '@angular/material/core'; 4 | import { MtxPopoverModule } from '@ng-matero/extensions/popover'; 5 | import { NgxMultipleDatesModule } from 'ngx-multiple-dates'; 6 | 7 | import { RootComponent } from './root.component'; 8 | 9 | describe('RootComponent', () => { 10 | beforeEach(async () => { 11 | await TestBed.configureTestingModule({ 12 | imports: [ MatNativeDateModule, MtxPopoverModule, NgxMultipleDatesModule ], 13 | declarations: [ 14 | RootComponent 15 | ], 16 | schemas: [ NO_ERRORS_SCHEMA ] 17 | }).compileComponents(); 18 | }); 19 | 20 | it('should create the app', async () => { 21 | const fixture = TestBed.createComponent(RootComponent); 22 | const app = fixture.componentInstance; 23 | await expect(app).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/app/components/root/root.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ChangeDetectionStrategy, HostBinding } from '@angular/core'; 2 | import { UntypedFormGroup, UntypedFormControl } from '@angular/forms'; 3 | import { OverlayContainer } from '@angular/cdk/overlay'; 4 | import { DateClass, DateRemoveEvent } from 'ngx-multiple-dates'; 5 | // import { DateTime } from 'luxon'; 6 | // import * as moment from 'moment'; 7 | 8 | import { DEFAULT_THEME } from '../../app.constants'; 9 | 10 | @Component({ 11 | selector: 'app-root', 12 | templateUrl: './root.component.html', 13 | styleUrls: [ './root.component.scss' ], 14 | changeDetection: ChangeDetectionStrategy.OnPush 15 | }) 16 | export class RootComponent { 17 | public model: Date[]; 18 | public modelWithToggleButton: Date[]; 19 | public modelPredefined: Date[] = [ 20 | new Date('7/15/1966'), 21 | new Date('3/23/1968'), 22 | new Date('7/4/1992'), 23 | new Date('1/25/1994'), 24 | new Date('6/17/1998') 25 | ]; 26 | public modelMinMax: Date[]; 27 | public modelMinlengthMaxlength: Date[]; 28 | public modelRequired: Date[]; 29 | public modelColor: Date[]; 30 | public modelFormat: Date[]; 31 | public modelCalendar: Date[]; 32 | public modelClasses: Date[] = [ new Date('3/7/2021'), new Date('3/11/2021') ]; 33 | public modelFilterValidation: Date[]; 34 | public min = new Date(+(new Date()) - 30 * 24 * 60 * 60 * 1000); 35 | public max = new Date(+(new Date()) + 30 * 24 * 60 * 60 * 1000); 36 | public classes: DateClass[] = [ 37 | { value: new Date('3/5/2021'), className: 'my-red' }, 38 | { value: new Date('3/7/2021'), className: 'my-green' }, 39 | { value: new Date('3/9/2021'), className: 'my-blue' } 40 | ]; 41 | public reactiveControl = new UntypedFormControl(); 42 | public dynamicName = 'reactiveFormControl'; 43 | public reactiveForm = new UntypedFormGroup({ 44 | [this.dynamicName]: new UntypedFormControl(this.modelPredefined) 45 | }); 46 | private _themeClass: string = DEFAULT_THEME; 47 | 48 | @HostBinding('class') 49 | public get themeClass(): string { 50 | return this._themeClass; 51 | } 52 | public set themeClass(value: string) { 53 | if (value) { 54 | this._overlayContainer.getContainerElement().classList.remove(this._themeClass); 55 | this._overlayContainer.getContainerElement().classList.add(value); 56 | this._themeClass = value; 57 | } 58 | } 59 | 60 | constructor(private _overlayContainer: OverlayContainer) { 61 | this._overlayContainer.getContainerElement().classList.add(this.themeClass); 62 | this.reactiveControl.valueChanges.subscribe((values) => console.log('reactiveControl', values)); 63 | this.reactiveForm.valueChanges.subscribe((values) => console.log('reactiveForm', values)); 64 | } 65 | 66 | public myFilter = (d: Date | null): boolean => { 67 | const day = (d || new Date()).getDay(); 68 | // Prevent Saturday and Sunday from being selected. 69 | return day !== 0 && day !== 6; 70 | }; 71 | 72 | public dateRemoved(date: DateRemoveEvent): void { 73 | console.log('removed', date); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/app/components/theme-picker/theme-picker.component.html: -------------------------------------------------------------------------------- 1 |
2 | 9 |
10 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/app/components/theme-picker/theme-picker.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: flex; 3 | flex-wrap: wrap; 4 | max-width: 248px; 5 | } 6 | 7 | .theme-container { 8 | display: flex; 9 | flex-wrap: wrap; 10 | width: 100%; 11 | 12 | .item { 13 | align-items: center; 14 | border: 4px solid #ffffff; 15 | box-sizing: border-box; 16 | color: #ffffff; 17 | cursor: pointer; 18 | display: flex; 19 | flex: 1 1 auto; 20 | height: 80px; 21 | justify-content: center; 22 | max-height: 80px; 23 | max-width: 80px; 24 | min-height: 80px; 25 | min-width: 80px; 26 | width: 80px; 27 | 28 | & > mat-icon { 29 | margin: 0; 30 | } 31 | 32 | &.hover:not(:hover) { 33 | opacity: .5; 34 | } 35 | 36 | &:focus { 37 | opacity: 1 !important; 38 | } 39 | 40 | span { 41 | font-size: 16pt; 42 | } 43 | 44 | &.azure-blue-theme { 45 | background: -webkit-linear-gradient(-45deg, #0074e9 0%, #0074e9 50%, #5a64ff 51%, #5a64ff 100%); 46 | background-color: #0074e9; 47 | } 48 | 49 | &.cyan-orange-theme { 50 | background: -webkit-linear-gradient(-45deg, #008585 0%, #008585 50%, #bc5d00 51%, #bc5d00 100%); 51 | background-color: #008585; 52 | } 53 | 54 | &.magenta-violet-theme { 55 | background: -webkit-linear-gradient(-45deg, #d200d2 0%, #d200d2 50%, #944aff 51%, #944aff 100%); 56 | background-color: #d200d2; 57 | } 58 | 59 | &.rose-red-theme { 60 | background: -webkit-linear-gradient(-45deg, #e80074 0%, #e80074 50%, #ef0000 51%, #ef0000 100%); 61 | background-color: #e80074; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/app/components/theme-picker/theme-picker.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { NO_ERRORS_SCHEMA } from '@angular/core'; 2 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 3 | 4 | import { ThemePickerComponent } from './theme-picker.component'; 5 | 6 | describe('ThemePickerComponent', () => { 7 | let component: ThemePickerComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async () => { 11 | await TestBed.configureTestingModule({ 12 | declarations: [ ThemePickerComponent ], 13 | schemas: [ NO_ERRORS_SCHEMA ] 14 | }) 15 | .compileComponents(); 16 | }); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(ThemePickerComponent); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', async () => { 25 | await expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/app/components/theme-picker/theme-picker.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, Output, ChangeDetectionStrategy } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-theme-picker', 5 | templateUrl: './theme-picker.component.html', 6 | styleUrls: [ './theme-picker.component.scss' ], 7 | changeDetection: ChangeDetectionStrategy.OnPush 8 | }) 9 | export class ThemePickerComponent { 10 | public hovering: string | null = null; 11 | public items: any[] = [ 12 | { className: 'azure-blue-theme' }, 13 | { className: 'cyan-orange-theme' }, 14 | { className: 'magenta-violet-theme' }, 15 | { className: 'rose-red-theme' } 16 | ]; 17 | @Output() public themeChange = new EventEmitter(); 18 | private _theme = ''; 19 | 20 | @Input() 21 | public get theme(): string { 22 | return this._theme; 23 | } 24 | public set theme(value: string) { 25 | this._theme = value; 26 | this.themeChange.emit(this._theme); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/assets/icons/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/assets/icons/ngx-multiple-dates.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | ngx-multiple-dates 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/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/plugins/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lekhmanrus/ngx-multiple-dates/7392f7835694f4513bfda2cb2f1310f5fd8b30a2/projects/ngx-multiple-dates-app/src/favicon.ico -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular Multiple Dates 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/src/styles/styles.scss: -------------------------------------------------------------------------------- 1 | @use '@angular/material' as mat; 2 | @use '@ng-matero/extensions' as mtx; 3 | @use 'ngx-multiple-dates' as ngxMultipleDates; 4 | 5 | html, 6 | body { 7 | font-family: Roboto, 'Helvetica Neue', sans-serif; 8 | height: 100%; 9 | margin: 0; 10 | } 11 | 12 | @include mat.core; 13 | $my-theme: mat.define-theme( 14 | ( 15 | color: ( 16 | theme-type: light, 17 | primary: mat.$azure-palette, 18 | tertiary: mat.$blue-palette 19 | ), 20 | density: ( 21 | scale: 0 22 | ) 23 | ) 24 | ); 25 | 26 | // Emit theme-dependent styles for common features used across multiple components. 27 | :root { 28 | @include mat.all-component-themes($my-theme); 29 | @include mat.typography-hierarchy($my-theme); 30 | @include mtx.all-component-themes($my-theme); 31 | } 32 | 33 | .azure-blue-theme { 34 | $theme: mat.define-theme( 35 | ( 36 | color: ( 37 | theme-type: light, 38 | primary: mat.$azure-palette, 39 | tertiary: mat.$blue-palette 40 | ), 41 | density: ( 42 | scale: 0 43 | ) 44 | ) 45 | ); 46 | @include mat.all-component-colors($theme); 47 | @include mtx.all-component-colors($theme); 48 | @include ngxMultipleDates.multiple-dates-theme($theme); 49 | } 50 | 51 | .cyan-orange-theme { 52 | $theme: mat.define-theme( 53 | ( 54 | color: ( 55 | theme-type: dark, 56 | primary: mat.$cyan-palette, 57 | tertiary: mat.$orange-palette 58 | ), 59 | density: ( 60 | scale: 0 61 | ) 62 | ) 63 | ); 64 | @include mat.all-component-colors($theme); 65 | @include mtx.all-component-colors($theme); 66 | @include ngxMultipleDates.multiple-dates-theme($theme); 67 | } 68 | 69 | .magenta-violet-theme { 70 | $theme: mat.define-theme( 71 | ( 72 | color: ( 73 | theme-type: dark, 74 | primary: mat.$magenta-palette, 75 | tertiary: mat.$violet-palette 76 | ), 77 | density: ( 78 | scale: 0 79 | ) 80 | ) 81 | ); 82 | @include mat.all-component-colors($theme); 83 | @include mtx.all-component-colors($theme); 84 | @include ngxMultipleDates.multiple-dates-theme($theme); 85 | } 86 | 87 | .rose-red-theme { 88 | $theme: mat.define-theme( 89 | ( 90 | color: ( 91 | theme-type: light, 92 | primary: mat.$rose-palette, 93 | tertiary: mat.$red-palette 94 | ), 95 | density: ( 96 | scale: 0 97 | ) 98 | ) 99 | ); 100 | @include mat.all-component-colors($theme); 101 | @include mtx.all-component-colors($theme); 102 | @include ngxMultipleDates.multiple-dates-theme($theme); 103 | } 104 | 105 | .fill { 106 | flex: 1 1 auto; 107 | } 108 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/app", 5 | "types": [ ] 6 | }, 7 | "files": [ 8 | "src/main.ts" 9 | ], 10 | "include": [ 11 | "src/**/*.d.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates-app/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/.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 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": false, 3 | "overrides": [ 4 | { 5 | "files": [ 6 | "*.ts" 7 | ], 8 | "rules": { 9 | "@angular-eslint/component-selector": [ 10 | "error", 11 | { 12 | "type": "element", 13 | "prefix": "ngx", 14 | "style": "kebab-case" 15 | } 16 | ], 17 | "@angular-eslint/directive-selector": [ 18 | "error", 19 | { 20 | "type": "attribute", 21 | "prefix": "ngx", 22 | "style": "camelCase" 23 | } 24 | ] 25 | } 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/README.md: -------------------------------------------------------------------------------- 1 | # NgxMultipleDates 2 | 3 | This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.0.0-rc.9. 4 | 5 | ## Code scaffolding 6 | 7 | Run `ng generate component component-name --project ngx-multiple-dates` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project ngx-multiple-dates`. 8 | > Note: Don't forget to add `--project ngx-multiple-dates` or else it will be added to the default project in your `angular.json` file. 9 | 10 | ## Build 11 | 12 | Run `ng build ngx-multiple-dates` to build the project. The build artifacts will be stored in the `dist/` directory. 13 | 14 | ## Publishing 15 | 16 | After building your library with `ng build ngx-multiple-dates`, go to the dist folder `cd dist/ngx-multiple-dates` and run `npm publish`. 17 | 18 | ## Running unit tests 19 | 20 | Run `ng test ngx-multiple-dates` to execute the unit tests via [Karma](https://karma-runner.github.io). 21 | 22 | ## Further help 23 | 24 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 25 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: [ 'jasmine', '@angular-devkit/build-angular' ], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-mocha-reporter'), 13 | require('karma-coverage'), 14 | require('@angular-devkit/build-angular/plugins/karma') 15 | ], 16 | client: { 17 | clearContext: false // leave Jasmine Spec Runner output visible in browser 18 | }, 19 | reporters: [ 'progress', 'coverage', 'kjhtml', 'mocha' ], 20 | mochaReporter: { 21 | output: 'autowatch' 22 | }, 23 | coverageReporter: { 24 | dir: require('path').join(__dirname, '../../coverage/ngx-multiple-dates'), 25 | subdir: '.', 26 | reporters: [ 27 | { type: 'html' }, 28 | { type: 'lcovonly' }, 29 | { type: 'text-summary' }, 30 | { type: 'cobertura' } 31 | ] 32 | }, 33 | port: 9876, 34 | colors: true, 35 | logLevel: config.LOG_INFO, 36 | autoWatch: true, 37 | browsers: [ 'ChromeHeadlessNoSandbox' ], 38 | customLaunchers: { 39 | ChromeHeadlessNoSandbox: { 40 | base: 'ChromeHeadless', 41 | flags: [ '--no-sandbox' ] 42 | } 43 | }, 44 | singleRun: false, 45 | restartOnFileChange: true 46 | }); 47 | }; 48 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/ngx-multiple-dates", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-multiple-dates", 3 | "description": "Angular Multiple Dates", 4 | "version": "18.1.0", 5 | "homepage": "https://lekhmanrus.github.io/ngx-multiple-dates/", 6 | "bugs": { 7 | "url": "https://github.com/lekhmanrus/ngx-multiple-dates/issues", 8 | "email": "lekhman112@gmail.com" 9 | }, 10 | "author": "Ruslan Lekhman (https://github.com/lekhmanrus)", 11 | "contributors": [ 12 | "Ruslan Lekhman (https://github.com/lekhmanrus)" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/lekhmanrus/ngx-multiple-dates" 17 | }, 18 | "license": "MIT", 19 | "readme": "https://github.com/lekhmanrus/ngx-multiple-dates/blob/master/README.md", 20 | "private": false, 21 | "keywords": [ 22 | "angular", 23 | "material", 24 | "date", 25 | "multiple", 26 | "datepicker" 27 | ], 28 | "dependencies": { 29 | "tslib": "^2.3.1" 30 | }, 31 | "exports": { 32 | ".": { 33 | "sass": "./_index.scss" 34 | }, 35 | "./theming": { 36 | "sass": "./_theming.scss" 37 | }, 38 | "./_theming": { 39 | "sass": "./_theming.scss" 40 | }, 41 | "./prebuilt-themes/azure-blue.css": { 42 | "style": "./prebuilt-themes/azure-blue.css" 43 | }, 44 | "./prebuilt-themes/cyan-orange.css": { 45 | "style": "./prebuilt-themes/cyan-orange.css" 46 | }, 47 | "./prebuilt-themes/magenta-violet.css": { 48 | "style": "./prebuilt-themes/magenta-violet.css" 49 | }, 50 | "./prebuilt-themes/rose-red.css": { 51 | "style": "./prebuilt-themes/rose-red.css" 52 | }, 53 | "./prebuilt-themes/*": { 54 | "style": "./prebuilt-themes/*.css" 55 | } 56 | }, 57 | "peerDependencies": { 58 | "@angular/cdk": "^18.0.0", 59 | "@angular/common": "^18.0.0", 60 | "@angular/core": "^18.0.0", 61 | "@angular/forms": "^18.0.0", 62 | "@angular/material": "^18.0.0", 63 | "rxjs": "^7.8.1" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/lib/_index.scss: -------------------------------------------------------------------------------- 1 | @forward './components/multiple-dates/multiple-dates-theme' as multiple-dates-* show multiple-dates-theme; 2 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/lib/_theming.scss: -------------------------------------------------------------------------------- 1 | @forward './components/multiple-dates/multiple-dates-legacy-index'; 2 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/lib/components/multiple-dates/_multiple-dates-legacy-index.scss: -------------------------------------------------------------------------------- 1 | @forward 'multiple-dates-theme' hide theme; 2 | @forward 'multiple-dates-theme' as ngx-multiple-dates-*; 3 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/lib/components/multiple-dates/_multiple-dates-theme.import.scss: -------------------------------------------------------------------------------- 1 | 2 | @forward 'multiple-dates-theme' hide theme; 3 | @forward 'multiple-dates-theme' as ngx-multiple-dates-component-*; 4 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/lib/components/multiple-dates/_multiple-dates-theme.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:map'; 2 | @use '@angular/material' as mat; 3 | 4 | @mixin theme($theme-or-color-config) { 5 | .mat-calendar-body-cell.selected { 6 | &, 7 | &:hover, 8 | &:not(.mat-calendar-body-disabled), 9 | &:not(.mat-calendar-body-disabled):hover { 10 | .mat-calendar-body-cell-content { 11 | &, 12 | &:hover, 13 | &:not(.disabled), 14 | &:not(.disabled):hover { 15 | background-color: mat.get-theme-color($theme-or-color-config, primary, 50) !important; 16 | color: mat.get-theme-color($theme-or-color-config, primary, 98) !important; 17 | } 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/lib/components/multiple-dates/multiple-dates.component.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | {{ getDateFormat(item) }} 6 | cancel 7 | 8 | 12 | 13 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/lib/components/multiple-dates/multiple-dates.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | outline: none !important; 4 | 5 | span { 6 | opacity: 0; 7 | transition: opacity 200ms; 8 | } 9 | 10 | &.floating span { 11 | opacity: 1; 12 | } 13 | 14 | ::ng-deep mat-chip-list { 15 | outline: none !important; 16 | 17 | .mat-chip-list-wrapper { 18 | min-height: 18px; 19 | } 20 | 21 | .mat-chip-remove { 22 | outline: none !important; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/lib/components/multiple-dates/multiple-dates.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { NO_ERRORS_SCHEMA } from '@angular/core'; 2 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { MatChipsModule } from '@angular/material/chips'; 4 | import { MatNativeDateModule } from '@angular/material/core'; 5 | 6 | import { MultipleDatesComponent } from './multiple-dates.component'; 7 | 8 | describe('MultipleDatesComponent', () => { 9 | let component: MultipleDatesComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async () => { 13 | await TestBed.configureTestingModule({ 14 | imports: [ MatChipsModule, MatNativeDateModule ], 15 | declarations: [ MultipleDatesComponent ], 16 | schemas: [ NO_ERRORS_SCHEMA ] 17 | }) 18 | .compileComponents(); 19 | }); 20 | 21 | beforeEach(() => { 22 | fixture = TestBed.createComponent(MultipleDatesComponent); 23 | component = fixture.componentInstance; 24 | fixture.detectChanges(); 25 | }); 26 | 27 | it('should create', async () => { 28 | await expect(component).toBeTruthy(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/lib/components/multiple-dates/multiple-dates.component.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-classes-per-file */ 2 | import { 3 | Component, 4 | ChangeDetectorRef, 5 | AfterViewInit, 6 | OnDestroy, 7 | DoCheck, 8 | HostBinding, 9 | Input, 10 | Output, 11 | EventEmitter, 12 | Optional, 13 | Self, 14 | ElementRef, 15 | Attribute, 16 | HostListener 17 | } from '@angular/core'; 18 | import { 19 | ControlValueAccessor, 20 | AbstractControl, 21 | NgControl, 22 | NgForm, 23 | FormGroupDirective, 24 | Validator, 25 | ValidationErrors, 26 | ValidatorFn, 27 | Validators 28 | } from '@angular/forms'; 29 | import { FocusMonitor } from '@angular/cdk/a11y'; 30 | import { coerceArray, coerceBooleanProperty } from '@angular/cdk/coercion'; 31 | import { 32 | DateAdapter, 33 | ThemePalette, 34 | CanUpdateErrorState, 35 | HasTabIndex, 36 | CanDisable, 37 | ErrorStateMatcher, 38 | mixinTabIndex, 39 | mixinDisabled, 40 | mixinErrorState 41 | } from '@angular/material/core'; 42 | import { 43 | MatCalendar, 44 | MatDatepicker, 45 | MatDatepickerInputEvent, 46 | MatCalendarCellClassFunction 47 | } from '@angular/material/datepicker'; 48 | import { MatFormFieldControl } from '@angular/material/form-field'; 49 | import { Subject } from 'rxjs'; 50 | import { takeUntil } from 'rxjs/operators'; 51 | 52 | import { DateClass } from '../../models/date-class.model'; 53 | import { DateRemoveEvent } from '../../models/date-remove-event.model'; 54 | 55 | abstract class MultipleDatesBaseMixinBase { 56 | /** 57 | * Stream that emits whenever the state of the control changes such that the parent 58 | * `MatFormField` needs to run change detection. 59 | */ 60 | public readonly stateChanges = new Subject(); 61 | 62 | constructor( 63 | protected $elementRef: ElementRef, 64 | public _defaultErrorStateMatcher: ErrorStateMatcher, 65 | public _parentForm: NgForm, 66 | public _parentFormGroup: FormGroupDirective, 67 | public ngControl: NgControl 68 | ) { } 69 | } 70 | 71 | const _MultipleDatesBaseMixinBase 72 | = mixinTabIndex(mixinDisabled(mixinErrorState(MultipleDatesBaseMixinBase))); 73 | 74 | /** 75 | * Multiple dates component. 76 | * @template D Date type. 77 | */ 78 | @Component({ 79 | selector: 'ngx-multiple-dates', 80 | templateUrl: './multiple-dates.component.html', 81 | styleUrls: [ './multiple-dates.component.scss' ], 82 | providers: [ 83 | { provide: MatFormFieldControl, useExisting: MultipleDatesComponent } 84 | ], 85 | exportAs: 'ngxMultipleDates' 86 | }) 87 | export class MultipleDatesComponent 88 | extends _MultipleDatesBaseMixinBase 89 | implements AfterViewInit, OnDestroy, DoCheck, ControlValueAccessor, MatFormFieldControl, 90 | HasTabIndex, CanDisable, CanUpdateErrorState, Validator { 91 | public static nextId = 0; 92 | /** Unique id of the element. */ 93 | @Input() 94 | @HostBinding() 95 | public id = `ngx-multiple-dates-${MultipleDatesComponent.nextId++}`; 96 | @HostBinding('attr.aria-describedby') public describedBy = ''; 97 | /** Whether the control is in an error state. */ 98 | @HostBinding('attr.aria-invalid') 99 | @HostBinding('class.mat-form-field-invalid') 100 | public errorState = false; 101 | /** An object used to control when error messages are shown. */ 102 | @Input() public errorStateMatcher: ErrorStateMatcher; 103 | @Input() 104 | @HostBinding('attr.tabindex') 105 | public tabIndex: number; 106 | /** 107 | * The date/time components to include, using predefined options or a custom format string. 108 | * @see {@link https://angular.io/api/common/DatePipe#usage-notes DatePipe Usage notes} for more 109 | * information. 110 | */ 111 | @Input() public format?: string; 112 | /** Emits when a change event is fired on this `ngx-multiple-dates` element. */ 113 | @Output() public readonly dateChange = new EventEmitter>(); 114 | /** Emits on a date removal. */ 115 | @Output() public readonly remove = new EventEmitter>(); 116 | /** Whether `ngx-multiple-dates` element has focus. */ 117 | public focused = false; 118 | /** A name for this control that can be used by mat-form-field. */ 119 | public controlType? = 'ngx-multiple-dates'; 120 | /** 121 | * Model used to reset datepicker selected value, so unselect just selected date will be 122 | * possible. 123 | */ 124 | public resetModel: D; 125 | private readonly _destroy = new Subject(); 126 | /** 127 | * The datepicker (or calendar - for inline view) that this `ngx-multiple-dates` element is 128 | * associated with. 129 | */ 130 | private _matDatepicker: MatDatepicker | MatCalendar; 131 | /** Whether datepicker should be closed on date selected, or opened to select more dates. */ 132 | private _closeOnSelected = false; 133 | /** Placeholder to be shown if no value has been selected. */ 134 | private _placeholder: string; 135 | /** Whether the component is required. */ 136 | private _required = false; 137 | /** Whether the component is disabled. */ 138 | private _disabled = false; 139 | /** The value of the `ngx-multiple-dates` control. */ 140 | private _value: D[] | null = [ ]; 141 | /** 142 | * Theme color palette for the component. This API is supported in M2 themes only, it has no 143 | * effect in M3 themes. 144 | * For information on applying color variants in M3, see 145 | * https://material.angular.io/guide/theming#using-component-color-variants. 146 | */ 147 | private _color: ThemePalette | null = null; 148 | /** Function that can be used to filter out dates within the datepicker. */ 149 | private _dateFilter: (date: D | null) => boolean; 150 | /** The minimum valid date. */ 151 | private _min: D | null; 152 | /** The maximum valid date. */ 153 | private _max: D | null; 154 | /** Custom date classes. */ 155 | private _classes: Array> = [ ]; 156 | private _validator: ValidatorFn | null; 157 | private _onChange: (_: any) => void = () => { }; 158 | private _onTouched: () => void = () => { }; 159 | private _onValidatorChange: () => void = () => { }; 160 | private _filterValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { 161 | const value = this._getValidDateOrNull(this._dateAdapter.deserialize(control.value)); 162 | return !this._dateFilter || !value || this._dateFilter(value) 163 | ? null 164 | : { matDatepickerFilter: true }; 165 | }; 166 | private _minValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { 167 | const value = this._getValidDateOrNull(this._dateAdapter.deserialize(control.value)); 168 | return (!this.min || !value || this._dateAdapter.compareDate(this.min, value) <= 0) 169 | ? null 170 | : { matDatepickerMin: { min: this.min, actual: value } }; 171 | }; 172 | private _maxValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { 173 | const value = this._getValidDateOrNull(this._dateAdapter.deserialize(control.value)); 174 | return (!this.max || !value || this._dateAdapter.compareDate(this.max, value) >= 0) 175 | ? null 176 | : { matDatepickerMax: { max: this.max, actual: value } }; 177 | }; 178 | 179 | /** 180 | * The datepicker (or calendar - for inline view) that this `ngx-multiple-dates` element is 181 | * associated with. 182 | */ 183 | @Input() 184 | public get matDatepicker(): MatDatepicker | MatCalendar { 185 | return this._matDatepicker; 186 | } 187 | public set matDatepicker(value: MatDatepicker | MatCalendar) { 188 | if (!value || (!(value instanceof MatDatepicker) && !(value instanceof MatCalendar))) { 189 | throw new TypeError( 190 | `Either "matDatepicker" or "matCalendar" attribute of "ngx-multiple-dates" is required and 191 | should be an instance of Angular Material Datepicker component.` 192 | ); 193 | } 194 | this._matDatepicker = value; 195 | 196 | if (this.matDatepicker instanceof MatDatepicker) { 197 | this.matDatepicker.closedStream 198 | .pipe(takeUntil(this._destroy)) 199 | .subscribe(() => this.blur()); 200 | } else { 201 | this.matDatepicker.selectedChange 202 | .pipe(takeUntil(this._destroy)) 203 | .subscribe((event) => this.dateChanged({ value: event } as MatDatepickerInputEvent)); 204 | } 205 | if (!this.matDatepicker.startAt) { 206 | this._setStartAt(); 207 | } 208 | this._setDisabled(); 209 | this._setDateClass(); 210 | } 211 | 212 | /** Whether datepicker should be closed on date selected, or opened to select more dates. */ 213 | @Input() 214 | public get closeOnSelected(): boolean { 215 | return this._closeOnSelected; 216 | } 217 | public set closeOnSelected(value: boolean) { 218 | this._closeOnSelected = coerceBooleanProperty(value); 219 | } 220 | 221 | /** Placeholder to be shown if no value has been selected. */ 222 | @Input() 223 | @HostBinding('attr.aria-label') 224 | public get placeholder(): string { 225 | return this._placeholder; 226 | } 227 | public set placeholder(value: string) { 228 | this._placeholder = value; 229 | this.stateChanges.next(); 230 | } 231 | 232 | /** Whether the component is required. */ 233 | @Input() 234 | @HostBinding('attr.aria-required') 235 | public get required(): boolean { 236 | return this._required; 237 | } 238 | public set required(value: boolean) { 239 | this._required = coerceBooleanProperty(value); 240 | this.stateChanges.next(); 241 | } 242 | 243 | /** Whether the component is disabled. */ 244 | @Input() 245 | @HostBinding('attr.disabled') 246 | public get disabled(): boolean { 247 | return this._disabled; 248 | } 249 | public set disabled(value: boolean) { 250 | this._disabled = coerceBooleanProperty(value); 251 | this._setDisabled(); 252 | if (this.focused) { 253 | this.focused = false; 254 | this.stateChanges.next(); 255 | } 256 | } 257 | 258 | /** The value of the `ngx-multiple-dates` control. */ 259 | @Input() 260 | public get value(): D[] | null { 261 | if (!this._value) { 262 | this._value = [ ]; 263 | } 264 | return this._value; 265 | } 266 | public set value(value: D[] | null) { 267 | if (value !== this._value) { 268 | this.writeValue(value); 269 | } 270 | } 271 | 272 | /** 273 | * Theme color palette for the component. This API is supported in M2 themes only, it has no 274 | * effect in M3 themes. 275 | * For information on applying color variants in M3, see 276 | * https://material.angular.io/guide/theming#using-component-color-variants. 277 | */ 278 | @Input() 279 | public get color(): ThemePalette | null { 280 | return this._color; 281 | } 282 | public set color(value: ThemePalette | null) { 283 | this._color = value; 284 | } 285 | 286 | /** Function that can be used to filter out dates within the datepicker. */ 287 | @Input() 288 | public get matDatepickerFilter(): (date: D | null) => boolean { 289 | return this._dateFilter; 290 | } 291 | public set matDatepickerFilter(value: (date: D | null) => boolean) { 292 | this._dateFilter = value; 293 | this._onValidatorChange(); 294 | } 295 | 296 | /** The minimum valid date. */ 297 | @Input() 298 | public get min(): D | null { 299 | return this._min; 300 | } 301 | public set min(value: D | null) { 302 | this._min = this._getValidDateOrNull(this._dateAdapter.deserialize(value)); 303 | this._onValidatorChange(); 304 | } 305 | 306 | /** The maximum valid date. */ 307 | @Input() 308 | public get max(): D | null { 309 | return this._max; 310 | } 311 | public set max(value: D | null) { 312 | this._max = this._getValidDateOrNull(this._dateAdapter.deserialize(value)); 313 | this._onValidatorChange(); 314 | } 315 | 316 | /** Custom date classes. */ 317 | @Input() 318 | public get classes(): Array> { 319 | return this._classes; 320 | } 321 | public set classes(value: Array>) { 322 | this._classes = coerceArray(value); 323 | } 324 | 325 | /** Whether the `MatFormField` label should try to float. */ 326 | @HostBinding('class.floating') 327 | public get shouldLabelFloat() { 328 | return !this.empty || (this.focused && !this.disabled); 329 | } 330 | 331 | /** Whether the select has a value. */ 332 | public get empty(): boolean { 333 | return !this.value || !this.value.length; 334 | } 335 | 336 | /** Whether the settled picker is a datepicker. */ 337 | public get isDatepicker(): boolean { 338 | return this.matDatepicker instanceof MatDatepicker; 339 | } 340 | 341 | /** 342 | * Creates an instance of MultipleDatesComponent. 343 | * @param ngControl Form control to manage component. 344 | * @param $elementRef A wrapper around a native element inside of a View. 345 | * @param _changeDetectorRef Base class that provides change detection functionality. 346 | * @param _focusMonitor Monitors mouse and keyboard events to determine the cause of focus events. 347 | * @param _dateAdapter Adapts type `D` to be usable as a date by cdk-based components that work 348 | * with dates. 349 | * @param parentForm Parent form. 350 | * @param parentFormGroup Parent form group. 351 | * @param defaultErrorStateMatcher Provider that defines how form controls behave with regards to 352 | * displaying error messages. 353 | * @param tabIndex Tab index. 354 | */ 355 | constructor( 356 | @Optional() @Self() public ngControl: NgControl, 357 | protected $elementRef: ElementRef, 358 | private _changeDetectorRef: ChangeDetectorRef, 359 | private _focusMonitor: FocusMonitor, 360 | private _dateAdapter: DateAdapter, 361 | @Optional() parentForm: NgForm, 362 | @Optional() parentFormGroup: FormGroupDirective, 363 | defaultErrorStateMatcher: ErrorStateMatcher, 364 | @Attribute('tabindex') tabIndex: string 365 | ) { 366 | super($elementRef, defaultErrorStateMatcher, parentForm, parentFormGroup, ngControl); 367 | this.resetModel = _dateAdapter.createDate(0, 0, 1); 368 | const validators = [ 369 | this._filterValidator, 370 | this._minValidator, 371 | this._maxValidator 372 | ]; 373 | if (this.ngControl != null) { 374 | this.ngControl.valueAccessor = this; 375 | if (this.ngControl.validator) { 376 | validators.push(this.ngControl.validator); 377 | } 378 | } 379 | this._validator = Validators.compose(validators); 380 | _focusMonitor.monitor($elementRef.nativeElement, true) 381 | .subscribe((origin: any) => { 382 | this.focused = !!origin; 383 | this.stateChanges.next(); 384 | }); 385 | this.tabIndex = Number(tabIndex) || 0; 386 | } 387 | 388 | public ngAfterViewInit(): void { 389 | if (this.ngControl && this.ngControl.control) { 390 | this.ngControl.control.addValidators(this.validate.bind(this)); 391 | } 392 | this._setStartAt(); 393 | this._setDateClass(); 394 | } 395 | 396 | public ngOnDestroy(): void { 397 | this._destroy.next(); 398 | this._destroy.complete(); 399 | this.stateChanges.complete(); 400 | this._focusMonitor.stopMonitoring(this.$elementRef.nativeElement); 401 | } 402 | 403 | public ngDoCheck(): void { 404 | if (this.ngControl) { 405 | this.updateErrorState(); 406 | } 407 | } 408 | 409 | /** Focuses the `ngx-multiple-dates` element. */ 410 | @HostListener('focus') 411 | public focus(): void { 412 | if (!this.disabled) { 413 | this.focused = true; 414 | if (this.matDatepicker && this.matDatepicker instanceof MatDatepicker) { 415 | this.matDatepicker.open(); 416 | } 417 | this.stateChanges.next(); 418 | } 419 | } 420 | 421 | /** Used to leave focus from the `ngx-multiple-dates` element. */ 422 | @HostListener('blur') 423 | public blur(): void { 424 | this.focused = false; 425 | if (!this.disabled) { 426 | this._onTouched(); 427 | this._changeDetectorRef.markForCheck(); 428 | this.stateChanges.next(); 429 | } 430 | } 431 | 432 | public writeValue(value: D[] | null): void { 433 | if (Array.isArray(value)) { 434 | this._value = [ ...value ]; 435 | this._sort(); 436 | } else { 437 | this._value = value; 438 | } 439 | this._onChange(value); 440 | this.stateChanges.next(); 441 | } 442 | 443 | public registerOnChange(fn: (_: any) => void): void { 444 | this._onChange = fn; 445 | } 446 | 447 | public registerOnTouched(fn: () => void): void { 448 | this._onTouched = fn; 449 | } 450 | 451 | public registerOnValidatorChange(fn: () => void): void { 452 | this._onValidatorChange = fn; 453 | } 454 | 455 | /** 456 | * Sets the list of element IDs that currently describe this control. 457 | * @param ids Ids to set. 458 | */ 459 | public setDescribedByIds(ids: string[]): void { 460 | this.describedBy = ids.join(' '); 461 | } 462 | 463 | /** Handles a click on the control's container. */ 464 | public onContainerClick(): void { 465 | if (!this.focused) { 466 | this.focus(); 467 | } 468 | } 469 | 470 | /** 471 | * Performs synchronous validation for the control. 472 | * @param control The control to validate against. 473 | * @returns A map of validation errors if validation fails, otherwise null. 474 | */ 475 | public validate(control: AbstractControl): ValidationErrors | null { 476 | return this._validator ? this._validator(control) : null; 477 | } 478 | 479 | /** 480 | * Function used to add CSS classes to selected dates. 481 | * @param date Date to check if classes should be applied. 482 | * @returns CSS classes to apply. 483 | */ 484 | public dateClass = (date: D) => { 485 | let className: string | undefined; 486 | if (this.classes.length) { 487 | className = this.getClassName(date); 488 | } 489 | if (this._find(date) !== -1) { 490 | return [ 'selected', ...(className ? [ className ] : [ ]) ]; 491 | } 492 | if (className) { 493 | return [ className ]; 494 | } 495 | return [ ]; 496 | }; 497 | 498 | /** 499 | * Fires when a change event is fired on the datepicker ``. 500 | * @param event Change event. 501 | */ 502 | public dateChanged(event: MatDatepickerInputEvent): void { 503 | if (event.value) { 504 | const date = event.value; 505 | if (this.value) { 506 | const index = this._find(date); 507 | if (index === -1) { 508 | this.value.push(date); 509 | this._sort(); 510 | } else { 511 | this.value.splice(index, 1); 512 | this.remove.emit({ type: 'datepicker', date }); 513 | } 514 | } 515 | this.resetModel = this._dateAdapter.createDate(0, 0, 1); 516 | this._setStartAt(); 517 | if (this.matDatepicker && this.matDatepicker instanceof MatDatepicker 518 | && !this.closeOnSelected) { 519 | const closeFn = this.matDatepicker.close; 520 | this.matDatepicker.close = () => { }; 521 | this.matDatepicker['_componentRef'].instance._calendar.monthView._createWeekCells(); 522 | setTimeout(() => (this.matDatepicker as MatDatepicker).close = closeFn); 523 | this._changeDetectorRef.detectChanges(); 524 | } else if (this.matDatepicker instanceof MatCalendar) { 525 | (this.matDatepicker.monthView as any)._createWeekCells(); 526 | } 527 | this.writeValue(this.value); 528 | } 529 | this.dateChange.emit(event); 530 | } 531 | 532 | /** 533 | * Removes selected chip from the list. 534 | * @param date Value to remove. 535 | */ 536 | public removeChip(date: D): void { 537 | if (this.value && this.value.length) { 538 | this._onTouched(); 539 | const index = this._find(date); 540 | this.value.splice(index, 1); 541 | this.writeValue(this.value); 542 | if (this.matDatepicker instanceof MatCalendar) { 543 | (this.matDatepicker.monthView as any)._createWeekCells(); 544 | (this.matDatepicker.monthView as any)._changeDetectorRef.detectChanges(); 545 | } 546 | this.remove.emit({ type: 'chip', date }); 547 | this._changeDetectorRef.detectChanges(); 548 | } 549 | } 550 | 551 | public trackByValue(_index: number, item: D): D { 552 | return item; 553 | } 554 | 555 | public getClassName(value: D): string | undefined { 556 | for (const classValue of this.classes) { 557 | if (this._dateAdapter.compareDate(classValue.value, value) === 0) { 558 | return classValue.className; 559 | } 560 | } 561 | return undefined; 562 | } 563 | 564 | private _setStartAt(): void { 565 | if (this.matDatepicker) { 566 | if (this.value && this.value.length) { 567 | this.matDatepicker.startAt = this.value[this.value.length - 1]; 568 | } else { 569 | this.matDatepicker.startAt = this._dateAdapter.today(); 570 | } 571 | } 572 | } 573 | 574 | private _setDisabled(): void { 575 | if (this.matDatepicker && this.matDatepicker instanceof MatDatepicker) { 576 | this.matDatepicker.disabled = this.disabled; 577 | } 578 | } 579 | 580 | private _setDateClass(): void { 581 | if (this.matDatepicker) { 582 | const dateClassFn: MatCalendarCellClassFunction = this.matDatepicker.dateClass; 583 | this.matDatepicker.dateClass = (date: D) => { 584 | const classList = this.dateClass(date); 585 | if (dateClassFn) { 586 | const oldClasses = dateClassFn(date, 'month'); 587 | if (classList.length) { 588 | if (oldClasses instanceof Set) { 589 | for (const className of classList) { 590 | oldClasses.add(className); 591 | } 592 | } else if (oldClasses instanceof Array) { 593 | for (const className of classList) { 594 | oldClasses.push(className); 595 | } 596 | } else if (typeof('t') === 'string') { 597 | return [ oldClasses, ...classList ].join(' '); 598 | } else { 599 | for (const className of classList) { 600 | oldClasses[className] = className; 601 | } 602 | } 603 | return oldClasses; 604 | } 605 | return oldClasses; 606 | } 607 | return classList; 608 | }; 609 | } 610 | } 611 | 612 | private _find(date: D): number { 613 | if (!this.value) { 614 | return -1; 615 | } 616 | return this.value.map((value) => this._dateAdapter.compareDate(value, date)).indexOf(0); 617 | } 618 | 619 | private _sort(): void { 620 | if (this.value) { 621 | this.value.sort((lhs, rhs) => this._dateAdapter.compareDate(lhs, rhs)); 622 | } 623 | } 624 | 625 | private _getValidDateOrNull(obj: any): D | null { 626 | return (this._dateAdapter.isDateInstance(obj) && this._dateAdapter.isValid(obj)) ? obj : null; 627 | } 628 | 629 | getDateFormat(date: any) { 630 | return this._dateAdapter.format(date, this.format || undefined) 631 | } 632 | } 633 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/lib/models/date-class.model.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Date class item. 3 | * @template D Date type. 4 | */ 5 | export class DateClass { 6 | /** Date value. */ 7 | value: D; 8 | /** CSS class name(s). */ 9 | className: string; 10 | } 11 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/lib/models/date-remove-event.model.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * An event used for `ngx-multiple-dates` date removal. 3 | * @template D Date type. 4 | */ 5 | export class DateRemoveEvent { 6 | /** Event type Specifies where the date was removed from (chip, datepicker). */ 7 | type: 'chip' | 'datepicker'; 8 | /** Date removed. */ 9 | date: D; 10 | } 11 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/lib/ngx-multiple-dates.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ReactiveFormsModule } from '@angular/forms'; 4 | 5 | import { MatChipsModule } from '@angular/material/chips'; 6 | import { MatDatepickerModule } from '@angular/material/datepicker'; 7 | import { MatFormFieldModule } from '@angular/material/form-field'; 8 | import { MatIconModule } from '@angular/material/icon'; 9 | import { MatInputModule } from '@angular/material/input'; 10 | 11 | import { MultipleDatesComponent } from './components/multiple-dates/multiple-dates.component'; 12 | 13 | @NgModule({ 14 | declarations: [ MultipleDatesComponent ], 15 | imports: [ 16 | CommonModule, 17 | ReactiveFormsModule, 18 | 19 | MatChipsModule, 20 | MatDatepickerModule, 21 | MatFormFieldModule, 22 | MatIconModule, 23 | MatInputModule 24 | ], 25 | exports: [ MultipleDatesComponent ] 26 | }) 27 | export class NgxMultipleDatesModule { } 28 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/prebuilt-themes/azure-blue.scss: -------------------------------------------------------------------------------- 1 | @use '@angular/material' as mat; 2 | @use '../lib/theming'; 3 | 4 | $theme: mat.define-theme( 5 | ( 6 | color: ( 7 | theme-type: light, 8 | primary: mat.$azure-palette, 9 | tertiary: mat.$blue-palette 10 | ), 11 | density: ( 12 | scale: 0 13 | ) 14 | ) 15 | ); 16 | 17 | @include theming.ngx-multiple-dates-theme($theme); 18 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/prebuilt-themes/cyan-orange.scss: -------------------------------------------------------------------------------- 1 | @use '@angular/material' as mat; 2 | @use '../lib/theming'; 3 | 4 | $theme: mat.define-theme( 5 | ( 6 | color: ( 7 | theme-type: dark, 8 | primary: mat.$cyan-palette, 9 | tertiary: mat.$orange-palette 10 | ), 11 | density: ( 12 | scale: 0 13 | ) 14 | ) 15 | ); 16 | 17 | @include theming.ngx-multiple-dates-theme($theme); 18 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/prebuilt-themes/magenta-violet.scss: -------------------------------------------------------------------------------- 1 | @use '@angular/material' as mat; 2 | @use '../lib/theming'; 3 | 4 | $theme: mat.define-theme( 5 | ( 6 | color: ( 7 | theme-type: dark, 8 | primary: mat.$magenta-palette, 9 | tertiary: mat.$violet-palette 10 | ), 11 | density: ( 12 | scale: 0 13 | ) 14 | ) 15 | ); 16 | 17 | @include theming.ngx-multiple-dates-theme($theme); 18 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/prebuilt-themes/rose-red.scss: -------------------------------------------------------------------------------- 1 | @use '@angular/material' as mat; 2 | @use '../lib/theming'; 3 | 4 | $theme: mat.define-theme( 5 | ( 6 | color: ( 7 | theme-type: light, 8 | primary: mat.$rose-palette, 9 | tertiary: mat.$red-palette 10 | ), 11 | density: ( 12 | scale: 0 13 | ) 14 | ) 15 | ); 16 | 17 | @include theming.ngx-multiple-dates-theme($theme); 18 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of ngx-multiple-dates 3 | */ 4 | 5 | export * from './lib/components/multiple-dates/multiple-dates.component'; 6 | export * from './lib/models/date-class.model'; 7 | export * from './lib/models/date-remove-event.model'; 8 | export * from './lib/ngx-multiple-dates.module'; 9 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "declaration": true, 6 | "declarationMap": true, 7 | "inlineSources": true, 8 | "types": [ ] 9 | }, 10 | "angularCompilerOptions": { 11 | "skipTemplateCodegen": true, 12 | "strictMetadataEmit": true, 13 | "enableResourceInlining": true 14 | }, 15 | "exclude": [ 16 | "**/*.spec.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "compilerOptions": { 4 | "declarationMap": false 5 | }, 6 | "angularCompilerOptions": { 7 | "compilationMode": "partial" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /projects/ngx-multiple-dates/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "include": [ 11 | "**/*.spec.ts", 12 | "**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist", 6 | "forceConsistentCasingInFileNames": true, 7 | "noPropertyAccessFromIndexSignature": true, 8 | "noImplicitReturns": true, 9 | "noFallthroughCasesInSwitch": true, 10 | "skipLibCheck": true, 11 | "sourceMap": true, 12 | "declaration": false, 13 | "downlevelIteration": true, 14 | "module": "esnext", 15 | "emitDecoratorMetadata": true, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "node", 18 | "importHelpers": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "strictNullChecks": true, 22 | "target": "es2022", 23 | "typeRoots": [ 24 | "node_modules/@types" 25 | ], 26 | "lib": [ 27 | "esnext", 28 | "dom" 29 | ], 30 | "paths": { 31 | "ngx-multiple-dates": [ 32 | "dist/ngx-multiple-dates", 33 | "dist/ngx-multiple-dates/ngx-multiple-dates" 34 | ] 35 | } 36 | }, 37 | "angularCompilerOptions": { 38 | "fullTemplateTypeCheck": true, 39 | "strictInjectionParameters": true, 40 | "strictInputAccessModifiers": true 41 | } 42 | } 43 | --------------------------------------------------------------------------------