├── .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 |
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 | [](https://github.com/lekhmanrus/ngx-multiple-dates/actions/workflows/build.yml)
22 | [](https://github.com/lekhmanrus/ngx-multiple-dates/actions/workflows/publish.yml)
23 | [](https://codecov.io/gh/lekhmanrus/ngx-multiple-dates)
24 | [](https://www.npmjs.com/package/ngx-multiple-dates)
25 | [](https://www.npmjs.com/package/ngx-multiple-dates)
26 |
27 |
28 | 
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 |
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 |
6 |
--------------------------------------------------------------------------------
/projects/ngx-multiple-dates-app/src/assets/icons/ngx-multiple-dates.svg:
--------------------------------------------------------------------------------
1 |
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 |
--------------------------------------------------------------------------------