├── .circleci └── config.yml ├── .github ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── documentation-report.md │ ├── feature-request.md │ └── support-request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── THIRD_PARTY_LICENSES ├── angular.json ├── atc-logo.png ├── doc ├── assets │ ├── css │ │ ├── main.css │ │ └── main.css.map │ ├── images │ │ ├── icons.png │ │ ├── icons@2x.png │ │ ├── widgets.png │ │ └── widgets@2x.png │ └── js │ │ ├── main.js │ │ ├── modernizr.js │ │ └── search.js ├── globals.html ├── index.html ├── interfaces │ ├── _api_.iallowdragfn.html │ ├── _api_.iallowdropfn.html │ ├── _api_.inodeheightfn.html │ ├── _api_.itreemodel.html │ ├── _api_.itreenode.html │ ├── _api_.itreenodedrag.html │ ├── _api_.itreeoptions.html │ └── _api_.itreestate.html └── modules │ └── _api_.html ├── e2e ├── async.testcafe.js ├── basic.testcafe.js ├── checkboxes.testcafe.js ├── drag.testcafe.js ├── dragover-styling.testcafe.js ├── empty.testcafe.js ├── fields.testcafe.js ├── filter.testcafe.js ├── helpers │ └── tree.driver.js ├── template.testcafe.js └── tsconfig.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── projects ├── angular-tree-component │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ │ ├── lib │ │ │ ├── angular-tree-component.css │ │ │ ├── angular-tree-component.module.ts │ │ │ ├── components │ │ │ │ ├── loading.component.ts │ │ │ │ ├── tree-node-checkbox.component.ts │ │ │ │ ├── tree-node-children.component.ts │ │ │ │ ├── tree-node-collection.component.ts │ │ │ │ ├── tree-node-content.component.ts │ │ │ │ ├── tree-node-drop-slot.component.ts │ │ │ │ ├── tree-node-expander.component.ts │ │ │ │ ├── tree-node-wrapper.component.ts │ │ │ │ ├── tree-node.component.ts │ │ │ │ ├── tree-viewport.component.ts │ │ │ │ └── tree.component.ts │ │ │ ├── constants │ │ │ │ ├── events.ts │ │ │ │ └── keys.ts │ │ │ ├── defs │ │ │ │ └── api.ts │ │ │ ├── deprecated.ts │ │ │ ├── directives │ │ │ │ ├── tree-animate-open.directive.ts │ │ │ │ ├── tree-drag.directive.ts │ │ │ │ └── tree-drop.directive.ts │ │ │ ├── mobx-angular │ │ │ │ ├── mobx-proxy.ts │ │ │ │ └── tree-mobx-autorun.directive.ts │ │ │ └── models │ │ │ │ ├── tree-dragged-element.model.ts │ │ │ │ ├── tree-node.model.ts │ │ │ │ ├── tree-options.model.ts │ │ │ │ ├── tree-virtual-scroll.model.ts │ │ │ │ └── tree.model.ts │ │ ├── public-api.ts │ │ └── test.ts │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ └── tslint.json ├── docs-app │ ├── .browserslistrc │ ├── karma.conf.js │ ├── src │ │ ├── app │ │ │ ├── app-routing.module.ts │ │ │ ├── app.component.html │ │ │ ├── app.component.scss │ │ │ ├── app.component.spec.ts │ │ │ ├── app.component.ts │ │ │ ├── app.module.ts │ │ │ ├── custom-elements │ │ │ │ ├── code │ │ │ │ │ ├── code-example.component.ts │ │ │ │ │ ├── code-example.module.ts │ │ │ │ │ ├── code-tabs.component.ts │ │ │ │ │ ├── code-tabs.module.ts │ │ │ │ │ ├── code.component.ts │ │ │ │ │ ├── code.module.ts │ │ │ │ │ └── pretty-printer.service.ts │ │ │ │ ├── custom-elements.module.ts │ │ │ │ ├── element-registry.ts │ │ │ │ ├── elements-loader.ts │ │ │ │ └── lazy-custom-element.component.ts │ │ │ ├── examples │ │ │ │ ├── basic-usage │ │ │ │ │ ├── basic-tree │ │ │ │ │ │ ├── basic-tree.component.html │ │ │ │ │ │ ├── basic-tree.component.scss │ │ │ │ │ │ ├── basic-tree.component.spec.ts │ │ │ │ │ │ └── basic-tree.component.ts │ │ │ │ │ ├── basic-usage.component.html │ │ │ │ │ ├── basic-usage.component.scss │ │ │ │ │ ├── basic-usage.component.spec.ts │ │ │ │ │ └── basic-usage.component.ts │ │ │ │ ├── columns-example │ │ │ │ │ ├── columns-example.component.html │ │ │ │ │ ├── columns-example.component.scss │ │ │ │ │ ├── columns-example.component.spec.ts │ │ │ │ │ ├── columns-example.component.ts │ │ │ │ │ └── columns │ │ │ │ │ │ ├── columns.component.html │ │ │ │ │ │ ├── columns.component.scss │ │ │ │ │ │ ├── columns.component.spec.ts │ │ │ │ │ │ └── columns.component.ts │ │ │ │ ├── crud-example │ │ │ │ │ ├── crud-example.component.html │ │ │ │ │ ├── crud-example.component.scss │ │ │ │ │ ├── crud-example.component.spec.ts │ │ │ │ │ ├── crud-example.component.ts │ │ │ │ │ └── crud │ │ │ │ │ │ ├── crud.component.html │ │ │ │ │ │ ├── crud.component.scss │ │ │ │ │ │ ├── crud.component.spec.ts │ │ │ │ │ │ └── crud.component.ts │ │ │ │ ├── examples.module.ts │ │ │ │ └── load-more-example │ │ │ │ │ ├── load-more-example.component.html │ │ │ │ │ ├── load-more-example.component.scss │ │ │ │ │ ├── load-more-example.component.spec.ts │ │ │ │ │ ├── load-more-example.component.ts │ │ │ │ │ └── load-more │ │ │ │ │ ├── load-more.component.html │ │ │ │ │ ├── load-more.component.scss │ │ │ │ │ ├── load-more.component.spec.ts │ │ │ │ │ └── load-more.component.ts │ │ │ ├── fundamentals │ │ │ │ ├── actions │ │ │ │ │ ├── actions-demo │ │ │ │ │ │ ├── actions-demo.component.html │ │ │ │ │ │ ├── actions-demo.component.scss │ │ │ │ │ │ ├── actions-demo.component.spec.ts │ │ │ │ │ │ └── actions-demo.component.ts │ │ │ │ │ ├── actions.component.html │ │ │ │ │ ├── actions.component.scss │ │ │ │ │ ├── actions.component.spec.ts │ │ │ │ │ └── actions.component.ts │ │ │ │ ├── api │ │ │ │ │ ├── api-demo │ │ │ │ │ │ ├── api-demo.component.html │ │ │ │ │ │ ├── api-demo.component.scss │ │ │ │ │ │ ├── api-demo.component.spec.ts │ │ │ │ │ │ └── api-demo.component.ts │ │ │ │ │ ├── api.component.html │ │ │ │ │ ├── api.component.scss │ │ │ │ │ ├── api.component.spec.ts │ │ │ │ │ └── api.component.ts │ │ │ │ ├── events │ │ │ │ │ ├── events.component.html │ │ │ │ │ ├── events.component.scss │ │ │ │ │ ├── events.component.spec.ts │ │ │ │ │ └── events.component.ts │ │ │ │ ├── focus │ │ │ │ │ ├── focus.component.html │ │ │ │ │ ├── focus.component.scss │ │ │ │ │ ├── focus.component.spec.ts │ │ │ │ │ └── focus.component.ts │ │ │ │ ├── fundamentals.module.ts │ │ │ │ ├── issues │ │ │ │ │ ├── issues.component.html │ │ │ │ │ ├── issues.component.scss │ │ │ │ │ ├── issues.component.spec.ts │ │ │ │ │ └── issues.component.ts │ │ │ │ ├── nodes │ │ │ │ │ ├── nodes.component.html │ │ │ │ │ ├── nodes.component.scss │ │ │ │ │ ├── nodes.component.spec.ts │ │ │ │ │ └── nodes.component.ts │ │ │ │ ├── options │ │ │ │ │ ├── options.component.html │ │ │ │ │ ├── options.component.scss │ │ │ │ │ ├── options.component.spec.ts │ │ │ │ │ └── options.component.ts │ │ │ │ ├── state-binding │ │ │ │ │ ├── state-binding-demo │ │ │ │ │ │ ├── state-binding-demo.component.html │ │ │ │ │ │ ├── state-binding-demo.component.scss │ │ │ │ │ │ ├── state-binding-demo.component.spec.ts │ │ │ │ │ │ └── state-binding-demo.component.ts │ │ │ │ │ ├── state-binding.component.html │ │ │ │ │ ├── state-binding.component.scss │ │ │ │ │ ├── state-binding.component.spec.ts │ │ │ │ │ └── state-binding.component.ts │ │ │ │ ├── styling │ │ │ │ │ ├── styling.component.html │ │ │ │ │ ├── styling.component.scss │ │ │ │ │ ├── styling.component.spec.ts │ │ │ │ │ └── styling.component.ts │ │ │ │ └── templates │ │ │ │ │ ├── templates-demo │ │ │ │ │ ├── templates-demo.component.html │ │ │ │ │ ├── templates-demo.component.scss │ │ │ │ │ ├── templates-demo.component.spec.ts │ │ │ │ │ └── templates-demo.component.ts │ │ │ │ │ ├── templates.component.html │ │ │ │ │ ├── templates.component.scss │ │ │ │ │ ├── templates.component.spec.ts │ │ │ │ │ └── templates.component.ts │ │ │ ├── getting-started │ │ │ │ ├── getting-started.component.html │ │ │ │ ├── getting-started.component.scss │ │ │ │ ├── getting-started.component.spec.ts │ │ │ │ └── getting-started.component.ts │ │ │ ├── guides │ │ │ │ ├── async-guide │ │ │ │ │ ├── async-guide.component.html │ │ │ │ │ ├── async-guide.component.scss │ │ │ │ │ ├── async-guide.component.spec.ts │ │ │ │ │ ├── async-guide.component.ts │ │ │ │ │ └── async │ │ │ │ │ │ ├── async.component.html │ │ │ │ │ │ ├── async.component.scss │ │ │ │ │ │ ├── async.component.spec.ts │ │ │ │ │ │ └── async.component.ts │ │ │ │ ├── auto-scroll-guide │ │ │ │ │ ├── auto-scroll-guide.component.html │ │ │ │ │ ├── auto-scroll-guide.component.scss │ │ │ │ │ ├── auto-scroll-guide.component.spec.ts │ │ │ │ │ ├── auto-scroll-guide.component.ts │ │ │ │ │ └── auto-scroll │ │ │ │ │ │ ├── auto-scroll.component.html │ │ │ │ │ │ ├── auto-scroll.component.scss │ │ │ │ │ │ ├── auto-scroll.component.spec.ts │ │ │ │ │ │ └── auto-scroll.component.ts │ │ │ │ ├── checkboxes-guide │ │ │ │ │ ├── checkboxes-guide.component.html │ │ │ │ │ ├── checkboxes-guide.component.scss │ │ │ │ │ ├── checkboxes-guide.component.spec.ts │ │ │ │ │ ├── checkboxes-guide.component.ts │ │ │ │ │ └── checkboxes │ │ │ │ │ │ ├── checkboxes.component.html │ │ │ │ │ │ ├── checkboxes.component.scss │ │ │ │ │ │ ├── checkboxes.component.spec.ts │ │ │ │ │ │ └── checkboxes.component.ts │ │ │ │ ├── custom-fields-guide │ │ │ │ │ ├── custom-fields-guide.component.html │ │ │ │ │ ├── custom-fields-guide.component.scss │ │ │ │ │ ├── custom-fields-guide.component.spec.ts │ │ │ │ │ └── custom-fields-guide.component.ts │ │ │ │ ├── drag-drop-guide │ │ │ │ │ ├── drag-drop-guide.component.html │ │ │ │ │ ├── drag-drop-guide.component.scss │ │ │ │ │ ├── drag-drop-guide.component.spec.ts │ │ │ │ │ ├── drag-drop-guide.component.ts │ │ │ │ │ └── drag-drop │ │ │ │ │ │ ├── drag-drop.component.html │ │ │ │ │ │ ├── drag-drop.component.scss │ │ │ │ │ │ ├── drag-drop.component.spec.ts │ │ │ │ │ │ └── drag-drop.component.ts │ │ │ │ ├── expanding-guide │ │ │ │ │ ├── expanding-guide.component.html │ │ │ │ │ ├── expanding-guide.component.scss │ │ │ │ │ ├── expanding-guide.component.spec.ts │ │ │ │ │ └── expanding-guide.component.ts │ │ │ │ ├── filter-guide │ │ │ │ │ ├── filter-guide.component.html │ │ │ │ │ ├── filter-guide.component.scss │ │ │ │ │ ├── filter-guide.component.spec.ts │ │ │ │ │ ├── filter-guide.component.ts │ │ │ │ │ └── filter │ │ │ │ │ │ ├── filter.component.html │ │ │ │ │ │ ├── filter.component.scss │ │ │ │ │ │ ├── filter.component.spec.ts │ │ │ │ │ │ └── filter.component.ts │ │ │ │ ├── guides.module.ts │ │ │ │ ├── large-tree-guide │ │ │ │ │ ├── large-tree-guide.component.html │ │ │ │ │ ├── large-tree-guide.component.scss │ │ │ │ │ ├── large-tree-guide.component.spec.ts │ │ │ │ │ ├── large-tree-guide.component.ts │ │ │ │ │ └── large-tree │ │ │ │ │ │ ├── large-tree.component.html │ │ │ │ │ │ ├── large-tree.component.scss │ │ │ │ │ │ ├── large-tree.component.spec.ts │ │ │ │ │ │ └── large-tree.component.ts │ │ │ │ ├── redux-guide │ │ │ │ │ ├── redux-guide.component.html │ │ │ │ │ ├── redux-guide.component.scss │ │ │ │ │ ├── redux-guide.component.spec.ts │ │ │ │ │ └── redux-guide.component.ts │ │ │ │ ├── rtl-guide │ │ │ │ │ ├── rtl-guide.component.html │ │ │ │ │ ├── rtl-guide.component.scss │ │ │ │ │ ├── rtl-guide.component.spec.ts │ │ │ │ │ ├── rtl-guide.component.ts │ │ │ │ │ └── rtl │ │ │ │ │ │ ├── rtl.component.html │ │ │ │ │ │ ├── rtl.component.scss │ │ │ │ │ │ ├── rtl.component.spec.ts │ │ │ │ │ │ └── rtl.component.ts │ │ │ │ └── update-guide │ │ │ │ │ ├── update-guide.component.html │ │ │ │ │ ├── update-guide.component.scss │ │ │ │ │ ├── update-guide.component.spec.ts │ │ │ │ │ └── update-guide.component.ts │ │ │ ├── layout │ │ │ │ ├── footer │ │ │ │ │ ├── footer.component.html │ │ │ │ │ ├── footer.component.scss │ │ │ │ │ ├── footer.component.spec.ts │ │ │ │ │ └── footer.component.ts │ │ │ │ ├── layout.module.ts │ │ │ │ └── nav-item │ │ │ │ │ ├── nav-item.component.html │ │ │ │ │ ├── nav-item.component.scss │ │ │ │ │ ├── nav-item.component.spec.ts │ │ │ │ │ └── nav-item.component.ts │ │ │ ├── navigation │ │ │ │ ├── navigation.model.ts │ │ │ │ ├── navigation.service.spec.ts │ │ │ │ └── navigation.service.ts │ │ │ └── shared │ │ │ │ ├── copier.service.ts │ │ │ │ ├── logger.service.ts │ │ │ │ └── shared.module.ts │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ ├── github-icon.svg │ │ │ ├── js │ │ │ │ └── prettify.js │ │ │ ├── styling │ │ │ │ ├── 0bd50e1-treecss.png │ │ │ │ ├── 28d7625-Screen_Shot_2016-11-29_at_12.11.01_PM.png │ │ │ │ └── f50955b-Screen_Shot_2016-11-29_at_12.11.52_PM.png │ │ │ └── tree-logo.svg │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── styles.scss │ │ ├── styles │ │ │ ├── _alert.scss │ │ │ ├── _code.scss │ │ │ ├── _constants.scss │ │ │ ├── _images.scss │ │ │ ├── _mixins.scss │ │ │ ├── _scrollbar.scss │ │ │ ├── _typography.scss │ │ │ └── angular-tree-component.css │ │ └── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── tslint.json └── example-app │ ├── .browserslistrc │ ├── .gitignore │ ├── angular.json │ ├── karma.conf.js │ ├── src │ ├── angular-tree-component.css │ ├── app │ │ ├── actions │ │ │ ├── actions.component.css │ │ │ ├── actions.component.html │ │ │ ├── actions.component.spec.ts │ │ │ └── actions.component.ts │ │ ├── api │ │ │ ├── api.component.css │ │ │ ├── api.component.html │ │ │ ├── api.component.spec.ts │ │ │ └── api.component.ts │ │ ├── app-routing.module.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── async │ │ │ └── async.component.ts │ │ ├── basictree │ │ │ └── basictree.component.ts │ │ ├── checkboxes │ │ │ └── checkboxes.component.ts │ │ ├── contextmenu │ │ │ ├── contextmenu.component.html │ │ │ ├── contextmenu.component.spec.ts │ │ │ └── contextmenu.component.ts │ │ ├── drag │ │ │ └── drag.component.ts │ │ ├── dragover-styling │ │ │ ├── dragover-styling-full-tree.component.ts │ │ │ └── dragover-styling.component.ts │ │ ├── empty │ │ │ └── empty.component.ts │ │ ├── fields │ │ │ └── fields.component.ts │ │ ├── filter │ │ │ └── filter.component.ts │ │ ├── fulltree │ │ │ └── fulltree.component.ts │ │ ├── rtl │ │ │ └── rtl-tree.component.ts │ │ ├── save-restore │ │ │ └── save-restore.component.ts │ │ ├── scrollcontainer │ │ │ └── scrollcontainer.component.ts │ │ ├── templates │ │ │ └── templates.component.ts │ │ └── virtualscroll │ │ │ ├── virtualscroll.component.css │ │ │ ├── virtualscroll.component.html │ │ │ ├── virtualscroll.component.spec.ts │ │ │ └── virtualscroll.component.ts │ ├── assets │ │ └── .gitkeep │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.build.html │ ├── index.dev.html │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.scss │ └── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.es5.app.json │ ├── tsconfig.spec.json │ └── tslint.json ├── protractor.conf.js ├── rollup.config.js ├── tsconfig.json ├── tslint.json └── typedoc.json /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:8.9-browsers 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | working_directory: ~/repo 18 | 19 | steps: 20 | - checkout 21 | 22 | # Download and cache dependencies 23 | - restore_cache: 24 | keys: 25 | - v1-dependencies-{{ checksum "package.json" }} 26 | # fallback to using the latest cache if no exact match is found 27 | - v1-dependencies- 28 | 29 | - run: npm install 30 | - run: npm run test:setup 31 | 32 | - save_cache: 33 | paths: 34 | - node_modules 35 | key: v1-dependencies-{{ checksum "package.json" }} 36 | 37 | # run tests! 38 | # - run: npm run test 39 | - run: npm run test:ci:local 40 | 41 | - store_test_results: 42 | path: /tmp/test-results 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## I'm submitting a... 2 | 3 | 4 |

 5 | [ ] Regression (a behavior that used to work and stopped working in a new release)
 6 | [ ] Bug report  
 7 | [ ] Feature request
 8 | [ ] Documentation issue or request
 9 | 
10 | 11 | ## What is the current behavior? 12 | 13 | 14 | 15 | ## Expected behavior: 16 | 17 | 18 | 19 | ## Minimal reproduction of the problem with instructions: 20 | 21 | 25 | 26 | ## Version of affected browser(s),operating system(s), npm, node and angular-tree-component: 27 | 28 | ## Other information: 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report a bug or regression in functionality 4 | --- 5 | 6 | 7 | 8 | ## Minimal reproduction of the bug/regression with instructions: 9 | 10 | 11 | 12 | 13 | 14 | ## Expected behavior: 15 | 16 | 17 | 18 | ## Versions of Angular Tree Component, Angular, Node, affected browser(s) and operating system(s): 19 | 20 | ## Other information: 21 | 22 | ## I would be willing to submit a PR to fix this issue 23 | 24 | [ ] Yes (Assistance will be provided if you need help to submit a pull request) 25 | [ ] No 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation Report 3 | about: Report missing or inaccurate documentation 4 | --- 5 | 6 | 7 | 8 | ## Other information: 9 | 10 | ## I would be willing to submit a PR for the docs :heart: 11 | 12 | [ ] Yes (Assistance will be provided if you need help to submit a pull request) 13 | [ ] No 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Submit a Request For Consideration 4 | --- 5 | 6 | 7 | 8 | ## Describe any alternatives/workarounds you're currently using 9 | 10 | ## Other information: 11 | 12 | ## If accepted, I would be willing to submit a PR for this feature 13 | 14 | [ ] Yes (Assistance will be provided if you need help to submit a pull request) 15 | [ ] No 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/support-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Support Request 3 | about: I need general help 4 | --- 5 | 6 | ## Support Requests should not be opened as issues and should be handled in the following ways: 7 | 8 | ### On [StackOverflow](https://stackoverflow.com/questions/tagged/angular-tree-component) using the `angular-tree-component` tag 9 | 10 | ### In our slack support channel at https://angular-tree-component.herokuapp.com/ 11 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## PR Checklist 2 | Please check if your PR fulfills the following requirements: 3 | 4 | - [ ] The commit message follows our guidelines: https://github.com/CirclonGroup/angular-tree-component/blob/master/CONTRIBUTING.md#commit-message-guidelines 5 | - [ ] Tests for the changes have been added (for bug fixes / features) 6 | 7 | ## PR Type 8 | What kind of change does this PR introduce? 9 | 10 | 11 | 12 | ``` 13 | [ ] Bugfix 14 | [ ] Feature 15 | [ ] Code style update (formatting, local variables) 16 | [ ] Refactoring (no functional changes, no api changes) 17 | [ ] Build related changes 18 | [ ] CI related changes 19 | [ ] Documentation content changes 20 | [ ] Other... Please describe: 21 | ``` 22 | 23 | ## What is the current behavior? 24 | 25 | 26 | Closes # 27 | 28 | ## What is the new behavior? 29 | 30 | ## Does this PR introduce a breaking change? 31 | 32 | ``` 33 | [ ] Yes 34 | [ ] No 35 | ``` 36 | 37 | 38 | 39 | ## Other information 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Users Environment Variables 23 | .lock-wscript 24 | 25 | # OS generated files # 26 | .DS_Store 27 | ehthumbs.db 28 | Icon? 29 | Thumbs.db 30 | 31 | # Node Files # 32 | /node_modules 33 | /bower_components 34 | /projects/**/node_modules 35 | npm-debug.log 36 | 37 | # Coverage # 38 | /coverage/ 39 | /xunit/ 40 | 41 | # Typing # 42 | /src/typings/tsd/ 43 | /typings/ 44 | /tsd_typings/ 45 | 46 | # Dist # 47 | /public/__build__/ 48 | /src/*/__build__/ 49 | /__build__/** 50 | /public/dist/ 51 | /src/*/dist/ 52 | .webpack.json 53 | /projects/**/package-lock.json 54 | 55 | # Doc # 56 | 57 | # IDE # 58 | .vscode/* 59 | .idea/ 60 | *.swp 61 | 62 | /dist 63 | 64 | compiled 65 | 66 | testScreenshots 67 | testResults 68 | e2eResults 69 | 70 | e2e/dist 71 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | dist 2 | doc 3 | typings 4 | node_modules 5 | example 6 | *webpack*conf*.js 7 | *karma*.js 8 | *.ts 9 | !*.d.ts 10 | testScreenshots 11 | testResults 12 | e2eResults 13 | e2e -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | 4 | addons: 5 | firefox: latest 6 | apt: 7 | sources: 8 | - google-chrome 9 | packages: 10 | - google-chrome-stable fluxbox 11 | 12 | language: node_js 13 | node_js: 14 | - "8.9.0" 15 | 16 | before_install: 17 | - "export DISPLAY=:99.0" 18 | - "sh -e /etc/init.d/xvfb start" 19 | - sleep 3 20 | - fluxbox >/dev/null 2>&1 & 21 | 22 | before_script: 23 | - npm run test:setup 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Circlon Group 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 | -------------------------------------------------------------------------------- /THIRD_PARTY_LICENSES: -------------------------------------------------------------------------------- 1 | * https://github.com/ayamflow/virtual-scroll/ 2 | // The MIT License (MIT) 3 | 4 | // Copyright (c) 2014 Florian Morel 5 | 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | -------------------------------------------------------------------------------- /atc-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/atc-logo.png -------------------------------------------------------------------------------- /doc/assets/images/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/doc/assets/images/icons.png -------------------------------------------------------------------------------- /doc/assets/images/icons@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/doc/assets/images/icons@2x.png -------------------------------------------------------------------------------- /doc/assets/images/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/doc/assets/images/widgets.png -------------------------------------------------------------------------------- /doc/assets/images/widgets@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/doc/assets/images/widgets@2x.png -------------------------------------------------------------------------------- /e2e/async.testcafe.js: -------------------------------------------------------------------------------- 1 | import { Selector } from 'testcafe'; 2 | import { TreeDriver } from './helpers/tree.driver'; 3 | 4 | fixture `Async` 5 | .page `http://localhost:4200/#/async` 6 | .beforeEach( async t => { 7 | t.ctx.tree = new TreeDriver('tree-root'); 8 | t.ctx.root2 = t.ctx.tree.getNode('root2'); 9 | }); 10 | 11 | test('should show the tree', async t => { 12 | await t.expect(t.ctx.tree.isPresent()).ok(); 13 | }); 14 | 15 | test('should have 3 nodes', async t => { 16 | await t.expect(t.ctx.tree.getNodes().count).eql(3); 17 | }); 18 | 19 | test('should not show loading before expanding', async t => { 20 | await t.expect(t.ctx.root2.getLoading().exists).notOk(); 21 | }); 22 | 23 | // TODO: find out why fails on saucelabs 24 | test.skip('should show loading', async t => { 25 | await t.ctx.root2.clickExpander(t) 26 | .expect(t.ctx.root2.getLoading().exists).ok(); 27 | }); 28 | 29 | test('should show children and then loading disappears', async t => { 30 | await t.ctx.root2.clickExpander(t) 31 | .expect(t.ctx.root2.getNode('child1').isPresent()).ok() 32 | .expect(t.ctx.root2.getLoading().exists).notOk(); 33 | }); 34 | 35 | test('should show not show loading the second time we expand the node', async t => { 36 | await t.ctx.root2.clickExpander(t) 37 | .expect(t.ctx.root2.getNode('child1').isPresent()).ok(); 38 | 39 | await t.ctx.root2.clickExpander(t); 40 | await t.ctx.root2.clickExpander(t) 41 | .expect(t.ctx.root2.getLoading().exists).notOk(); 42 | }); 43 | -------------------------------------------------------------------------------- /e2e/drag.testcafe.js: -------------------------------------------------------------------------------- 1 | const { TreeDriver } = require('./helpers/tree.driver'); 2 | 3 | fixture `Drag and Drop` 4 | .page `http://localhost:4200/#/drag` 5 | .beforeEach( async t => { 6 | t.ctx.tree = new TreeDriver('tree-root'); 7 | t.ctx.root1 = t.ctx.tree.getNode('root1'); 8 | t.ctx.child1 = t.ctx.root1.getNode('child1'); 9 | t.ctx.root2 = t.ctx.tree.getNode('root2'); 10 | t.ctx.child21 = t.ctx.root2.getNode('child2.1'); 11 | }); 12 | 13 | test('should show the tree', async t => { 14 | await t.expect(t.ctx.tree.isPresent()).ok(); 15 | }); 16 | 17 | test('should have expected children', async t => { 18 | await t.expect(t.ctx.root1.getNodes().count).eql(2) 19 | .expect(t.ctx.root2.getNodes().count).eql(2) 20 | .expect(t.ctx.child21.getNodes().count).eql(0); 21 | }); 22 | 23 | test('should allow to drag leaf', async t => { 24 | await t.ctx.child1.dragToNode(t, t.ctx.child21); 25 | await t.ctx.child21.clickExpander(t) 26 | .expect(t.ctx.root1.getNodes().count).eql(1) 27 | .expect(t.ctx.child21.getNodes().count).eql(1); 28 | }); 29 | 30 | // TODO: find out why fails on saucelabs 31 | test.skip('should allow to drag to drop slot', async t => { 32 | await t.ctx.child1.dragToDropSlot(t, t.ctx.child21) 33 | .expect(t.ctx.root1.getNodes().count).eql(1) 34 | .expect(t.ctx.root2.getNodes().count).eql(3); 35 | }); 36 | -------------------------------------------------------------------------------- /e2e/dragover-styling.testcafe.js: -------------------------------------------------------------------------------- 1 | const { TreeDriver } = require('./helpers/tree.driver'); 2 | 3 | fixture `Drag and Drop Styling` 4 | .page `http://localhost:4200/#/dragover-styling` 5 | .beforeEach( async t => { 6 | t.ctx.tree = new TreeDriver('tree-root'); 7 | t.ctx.root1 = t.ctx.tree.getNode('root1'); 8 | t.ctx.child1 = t.ctx.root1.getNode('child1'); 9 | t.ctx.root2 = t.ctx.tree.getNode('root2'); 10 | t.ctx.child21 = t.ctx.root2.getNode('child2.1'); 11 | }); 12 | 13 | test('should show the tree', async t => { 14 | await t.expect(t.ctx.tree.isPresent()).ok(); 15 | }); 16 | 17 | test('should have expected children', async t => { 18 | await t.expect(t.ctx.root1.getNodes().count).eql(2) 19 | .expect(t.ctx.root2.getNodes().count).eql(2) 20 | .expect(t.ctx.child21.getNodes().count).eql(0); 21 | }); 22 | 23 | test('should allow to drag leaf', async t => { 24 | await t.ctx.child1.dragToNode(t, t.ctx.child21); 25 | await t.ctx.child21.clickExpander(t) 26 | .expect(t.ctx.root1.getNodes().count).eql(1) 27 | .expect(t.ctx.child21.getNodes().count).eql(1); 28 | }); 29 | 30 | // TODO: find out why fails on saucelabs 31 | test.skip('should allow to drag to drop slot', async t => { 32 | await t.ctx.child1.dragToDropSlot(t, t.ctx.child21) 33 | .expect(t.ctx.root1.getNodes().count).eql(1) 34 | .expect(t.ctx.root2.getNodes().count).eql(3); 35 | }); 36 | -------------------------------------------------------------------------------- /e2e/empty.testcafe.js: -------------------------------------------------------------------------------- 1 | import { Selector } from 'testcafe'; 2 | import { TreeDriver } from './helpers/tree.driver'; 3 | 4 | const testEmptyTree = (treeId) => { 5 | fixture `Empty` 6 | .page `http://localhost:4200/#/empty` 7 | .beforeEach( async t => { 8 | t.ctx.tree = new TreeDriver(treeId); 9 | }); 10 | 11 | test('should show the tree', async t => { 12 | await t.expect(t.ctx.tree.isPresent()).ok(); 13 | }); 14 | 15 | test('should have 0 nodes', async t => { 16 | await t.expect(t.ctx.tree.getNodes().count).eql(0); 17 | }); 18 | 19 | test('should load nodes into the tree', async t => { 20 | await t.click(Selector('button')); 21 | await t.expect(t.ctx.tree.getNodes().count).eql(1); 22 | }); 23 | }; 24 | 25 | testEmptyTree('#tree1'); 26 | testEmptyTree('#tree2'); 27 | -------------------------------------------------------------------------------- /e2e/fields.testcafe.js: -------------------------------------------------------------------------------- 1 | import { Selector } from 'testcafe'; 2 | import { TreeDriver } from './helpers/tree.driver'; 3 | 4 | fixture `Fields` 5 | .page `http://localhost:4200/#/fields` 6 | .beforeEach( async t => { 7 | t.ctx.tree = new TreeDriver('#tree1'); 8 | }); 9 | 10 | 11 | test('should show the tree', async t => { 12 | await t.expect(t.ctx.tree.isPresent()).ok(); 13 | }); 14 | 15 | test('should have 2 nodes', async t => { 16 | await t.expect(t.ctx.tree.getNodes().count).eql(2); 17 | }); 18 | 19 | test('should display the custom display field', async t => { 20 | const root1 = t.ctx.tree.getNode('root1'); 21 | 22 | await t.expect(root1.isPresent()).ok(); 23 | }); 24 | 25 | test('should use the nodeClass option', async t => { 26 | const root1Title = t.ctx.tree.selector.find('.root1Class').withText('root1'); 27 | 28 | await t.expect(root1Title.exists).ok(); 29 | }); 30 | -------------------------------------------------------------------------------- /e2e/template.testcafe.js: -------------------------------------------------------------------------------- 1 | import { Selector } from 'testcafe'; 2 | import { TreeDriver } from './helpers/tree.driver'; 3 | 4 | ['#tree1', '#tree2', '#tree3'].forEach((treeId) => { 5 | fixture `Templates ${treeId}` 6 | .page `http://localhost:4200/#/templates` 7 | .beforeEach( async t => { 8 | t.ctx.tree = new TreeDriver('#tree1'); 9 | t.ctx.tree = new TreeDriver(treeId); 10 | }); 11 | 12 | test('should show the tree', async t => { 13 | await t.expect(t.ctx.tree.isPresent()).ok(); 14 | }); 15 | 16 | test('should have 2 nodes', async t => { 17 | await t.expect(t.ctx.tree.getNodes().count).eql(2); 18 | }); 19 | 20 | test('should use the template and pass it a node var', async t => { 21 | const root1Title = t.ctx.tree.selector.find('.root1Class').withText('root1'); 22 | 23 | await t.expect(root1Title.exists).ok(); 24 | }); 25 | 26 | test('should use the template and pass it an index var', async t => { 27 | const root1Index = t.ctx.tree.selector.find('.root1ClassIndex').withText('0'); 28 | 29 | await t.expect(root1Index.exists).ok(); 30 | }); 31 | }); 32 | fixture `Templates loading component` 33 | .page `http://localhost:4200/#/templates` 34 | .beforeEach(async t => { 35 | t.ctx.tree = new TreeDriver('#tree1'); 36 | t.ctx.root2 = t.ctx.tree.getNodeByIndex(1); 37 | }); 38 | 39 | test('should show the loading template', async t => { 40 | await t.ctx.root2.clickExpander(t); 41 | const loadingComponent = t.ctx.tree.selector.find('.root2ClassLoading').withText('Loading root2...'); 42 | 43 | await t.expect(loadingComponent.exists).ok(); 44 | }); 45 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "moduleResolution": "node", 5 | "sourceMap": false, 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "outDir": "dist", 9 | "noEmitOnError": true 10 | }, 11 | "include": [ 12 | "**/*.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /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 | const { join } = require('path'); 5 | const { constants } = require('karma'); 6 | 7 | module.exports = () => { 8 | return { 9 | basePath: '', 10 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 11 | plugins: [ 12 | require('karma-jasmine'), 13 | require('karma-chrome-launcher'), 14 | require('karma-jasmine-html-reporter'), 15 | require('karma-coverage-istanbul-reporter'), 16 | require('@angular-devkit/build-angular/plugins/karma'), 17 | require('@angular-devkit/build-angular/plugins/karma'), 18 | ], 19 | client: { 20 | clearContext: false, // leave Jasmine Spec Runner output visible in browser 21 | jasmine: { 22 | random: true 23 | } 24 | }, 25 | coverageIstanbulReporter: { 26 | dir: join(__dirname, 'coverage'), 27 | reports: ['text-summary', 'html', 'lcovonly'], 28 | fixWebpackSourcePaths: true, 29 | thresholds: { 30 | emitWarning: false, // <- this is important to make karma fail 31 | global: { 32 | statements: 0, 33 | lines: 0, 34 | branches: 0, 35 | functions: 0 36 | } 37 | } 38 | }, 39 | angularCli: { 40 | environment: 'dev' 41 | }, 42 | reporters: ['progress', 'kjhtml', 'coverage-istanbul'], 43 | port: 9876, 44 | browserNoActivityTimeout: 1000000, 45 | colors: true, 46 | logLevel: constants.LOG_INFO, 47 | autoWatch: true, 48 | browsers: ['Chrome'], 49 | singleRun: false 50 | }; 51 | }; 52 | -------------------------------------------------------------------------------- /projects/angular-tree-component/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/angular-tree-component'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /projects/angular-tree-component/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/angular-tree-component", 4 | "assets": ["src/lib/angular-tree-component.css"], 5 | "lib": { 6 | "entryFile": "src/public-api.ts", 7 | "umdModuleIds": { 8 | "mobx": "mobx", 9 | "lodash-es": "lodash-es" 10 | } 11 | }, 12 | "whitelistedNonPeerDependencies": ["core-js", "lodash-es", "mobx"] 13 | } 14 | -------------------------------------------------------------------------------- /projects/angular-tree-component/src/lib/components/loading.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, TemplateRef, ViewEncapsulation } from '@angular/core'; 2 | import { TreeNode } from '../models/tree-node.model'; 3 | 4 | @Component({ 5 | encapsulation: ViewEncapsulation.None, 6 | selector: 'tree-loading-component', 7 | template: ` 8 | loading... 9 | 12 | 13 | `, 14 | }) 15 | export class LoadingComponent { 16 | @Input() template: TemplateRef; 17 | @Input() node: TreeNode; 18 | } 19 | -------------------------------------------------------------------------------- /projects/angular-tree-component/src/lib/components/tree-node-checkbox.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ViewEncapsulation } from '@angular/core'; 2 | import { TreeNode } from '../models/tree-node.model'; 3 | 4 | @Component({ 5 | selector: 'tree-node-checkbox', 6 | encapsulation: ViewEncapsulation.None, 7 | styles: [], 8 | template: ` 9 | 10 | 17 | 18 | ` 19 | }) 20 | export class TreeNodeCheckboxComponent { 21 | @Input() node: TreeNode; 22 | } 23 | -------------------------------------------------------------------------------- /projects/angular-tree-component/src/lib/components/tree-node-children.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ViewEncapsulation } from '@angular/core'; 2 | import { TreeNode } from '../models/tree-node.model'; 3 | 4 | @Component({ 5 | selector: 'tree-node-children', 6 | encapsulation: ViewEncapsulation.None, 7 | styles: [], 8 | template: ` 9 | 10 |
20 | 26 | 27 | 34 |
35 |
36 | ` 37 | }) 38 | export class TreeNodeChildrenComponent { 39 | @Input() node: TreeNode; 40 | @Input() templates: any; 41 | } 42 | -------------------------------------------------------------------------------- /projects/angular-tree-component/src/lib/components/tree-node-content.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ViewEncapsulation, TemplateRef } from '@angular/core'; 2 | import { TreeNode } from '../models/tree-node.model'; 3 | 4 | @Component({ 5 | selector: 'tree-node-content', 6 | encapsulation: ViewEncapsulation.None, 7 | template: ` 8 | {{ node.displayField }} 9 | 12 | `, 13 | }) 14 | export class TreeNodeContent { 15 | @Input() node: TreeNode; 16 | @Input() index: number; 17 | @Input() template: TemplateRef; 18 | } 19 | -------------------------------------------------------------------------------- /projects/angular-tree-component/src/lib/components/tree-node-drop-slot.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ViewEncapsulation } from '@angular/core'; 2 | import { TreeNode } from '../models/tree-node.model'; 3 | 4 | @Component({ 5 | selector: 'TreeNodeDropSlot, tree-node-drop-slot', 6 | encapsulation: ViewEncapsulation.None, 7 | styles: [], 8 | template: ` 9 |
14 |
15 | ` 16 | }) 17 | export class TreeNodeDropSlot { 18 | @Input() node: TreeNode; 19 | @Input() dropIndex: number; 20 | 21 | onDrop($event) { 22 | this.node.mouseAction('drop', $event.event, { 23 | from: $event.element, 24 | to: { parent: this.node, index: this.dropIndex } 25 | }); 26 | } 27 | 28 | allowDrop(element, $event) { 29 | return this.node.options.allowDrop(element, { parent: this.node, index: this.dropIndex }, $event); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /projects/angular-tree-component/src/lib/components/tree-node-expander.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ViewEncapsulation } from '@angular/core'; 2 | import { TreeNode } from '../models/tree-node.model'; 3 | 4 | @Component({ 5 | selector: 'tree-node-expander', 6 | encapsulation: ViewEncapsulation.None, 7 | styles: [], 8 | template: ` 9 | 10 | 17 | 18 | 19 | 20 | 21 | 22 | ` 23 | }) 24 | export class TreeNodeExpanderComponent { 25 | @Input() node: TreeNode; 26 | } 27 | -------------------------------------------------------------------------------- /projects/angular-tree-component/src/lib/constants/events.ts: -------------------------------------------------------------------------------- 1 | export const TREE_EVENTS = { 2 | toggleExpanded: 'toggleExpanded', 3 | activate: 'activate', 4 | deactivate: 'deactivate', 5 | nodeActivate: 'nodeActivate', 6 | nodeDeactivate: 'nodeDeactivate', 7 | select: 'select', 8 | deselect: 'deselect', 9 | focus: 'focus', 10 | blur: 'blur', 11 | initialized: 'initialized', 12 | updateData: 'updateData', 13 | moveNode: 'moveNode', 14 | copyNode: 'copyNode', 15 | event: 'event', 16 | loadNodeChildren: 'loadNodeChildren', 17 | changeFilter: 'changeFilter', 18 | stateChange: 'stateChange' 19 | }; 20 | -------------------------------------------------------------------------------- /projects/angular-tree-component/src/lib/constants/keys.ts: -------------------------------------------------------------------------------- 1 | export const KEYS = { 2 | LEFT: 37, 3 | UP: 38, 4 | RIGHT: 39, 5 | DOWN: 40, 6 | ENTER: 13, 7 | SPACE: 32, 8 | CONTEXT_MENU: 32 9 | }; 10 | -------------------------------------------------------------------------------- /projects/angular-tree-component/src/lib/deprecated.ts: -------------------------------------------------------------------------------- 1 | export function deprecated(methodName, alternative) { 2 | console.warn(`${methodName} is deprecated. 3 | please use ${alternative} instead`); 4 | } 5 | -------------------------------------------------------------------------------- /projects/angular-tree-component/src/lib/mobx-angular/mobx-proxy.ts: -------------------------------------------------------------------------------- 1 | import { action as mobxAction } from 'mobx'; 2 | import { computed as mobxComputed } from 'mobx'; 3 | import { observable as mobxObservable } from 'mobx'; 4 | 5 | // Re-export mobx operators to be able to use inside components with AOT: 6 | export function actionInternal(...args) { 7 | return (mobxAction as any)(...args); 8 | } 9 | export const action: typeof mobxAction = Object.assign( 10 | actionInternal, 11 | mobxAction 12 | ) as any; 13 | 14 | function computedInternal(...args) { 15 | return (mobxComputed as any)(...args); 16 | } 17 | export const computed: typeof mobxComputed = Object.assign( 18 | computedInternal, 19 | mobxComputed 20 | ) as any; 21 | 22 | function observableInternal(...args) { 23 | return (mobxObservable as any)(...args); 24 | } 25 | 26 | export const observable: typeof mobxObservable = Object.assign( 27 | observableInternal, 28 | mobxObservable 29 | ) as any; 30 | -------------------------------------------------------------------------------- /projects/angular-tree-component/src/lib/mobx-angular/tree-mobx-autorun.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directive, 3 | ViewContainerRef, 4 | TemplateRef, 5 | OnInit, 6 | OnDestroy, 7 | Input, 8 | EmbeddedViewRef 9 | } from '@angular/core'; 10 | import { autorun } from 'mobx'; 11 | 12 | @Directive({ selector: '[treeMobxAutorun]' }) 13 | export class TreeMobxAutorunDirective implements OnInit, OnDestroy { 14 | protected templateBindings = {}; 15 | protected dispose: any; 16 | protected view: EmbeddedViewRef; 17 | @Input() treeMobxAutorun; 18 | 19 | constructor( 20 | protected templateRef: TemplateRef, 21 | protected viewContainer: ViewContainerRef 22 | ) {} 23 | 24 | ngOnInit() { 25 | this.view = this.viewContainer.createEmbeddedView(this.templateRef); 26 | 27 | if (this.dispose) { 28 | this.dispose(); 29 | } 30 | 31 | if (this.shouldDetach()) { 32 | this.view.detach(); 33 | } 34 | this.autoDetect(this.view); 35 | } 36 | 37 | shouldDetach() { 38 | return this.treeMobxAutorun && this.treeMobxAutorun.detach; 39 | } 40 | 41 | autoDetect(view: EmbeddedViewRef) { 42 | this.dispose = autorun(() => view.detectChanges()); 43 | } 44 | 45 | ngOnDestroy() { 46 | if (this.dispose) { 47 | this.dispose(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /projects/angular-tree-component/src/lib/models/tree-dragged-element.model.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root' 5 | }) 6 | export class TreeDraggedElement { 7 | _draggedElement: any = null; 8 | 9 | set(draggedElement: any) { 10 | this._draggedElement = draggedElement; 11 | } 12 | 13 | get(): any { 14 | return this._draggedElement; 15 | } 16 | 17 | isDragging() { 18 | return !!this.get(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /projects/angular-tree-component/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of angular-tree-component 3 | */ 4 | 5 | export * from './lib/angular-tree-component.module'; 6 | -------------------------------------------------------------------------------- /projects/angular-tree-component/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone'; 4 | import 'zone.js/dist/zone-testing'; 5 | import { getTestBed } from '@angular/core/testing'; 6 | import { 7 | BrowserDynamicTestingModule, 8 | platformBrowserDynamicTesting 9 | } from '@angular/platform-browser-dynamic/testing'; 10 | 11 | declare const require: any; 12 | 13 | // First, initialize the Angular testing environment. 14 | getTestBed().initTestEnvironment( 15 | BrowserDynamicTestingModule, 16 | platformBrowserDynamicTesting() 17 | ); 18 | // Then we find all the tests. 19 | const context = require.context('./', true, /\.spec\.ts$/); 20 | // And load the modules. 21 | context.keys().map(context); 22 | -------------------------------------------------------------------------------- /projects/angular-tree-component/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "declaration": true, 7 | "inlineSources": true, 8 | "types": [], 9 | "lib": [ 10 | "dom", 11 | "es2018" 12 | ] 13 | }, 14 | "angularCompilerOptions": { 15 | "annotateForClosureCompiler": true, 16 | "skipTemplateCodegen": true, 17 | "strictMetadataEmit": true, 18 | "fullTemplateTypeCheck": true, 19 | "strictInjectionParameters": true, 20 | "enableResourceInlining": true 21 | }, 22 | "exclude": [ 23 | "src/test.ts", 24 | "**/*.spec.ts" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /projects/angular-tree-component/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/angular-tree-component/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "camelCase" 8 | ], 9 | "component-selector": [ 10 | true, 11 | "element", 12 | "kebab-case" 13 | ], 14 | "no-console": [ 15 | true, 16 | "debug", 17 | "info", 18 | "time", 19 | "timeEnd", 20 | "trace", 21 | "log" 22 | ] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /projects/docs-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 | not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line. 18 | IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. 19 | -------------------------------------------------------------------------------- /projects/docs-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-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/docs-app'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Angular Tree Component 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 18 |
19 |
20 | 21 |
22 | 23 |
24 |
25 |
26 | 27 |
28 | 29 |
30 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | .example-spacer { 2 | flex: 1 1 auto; 3 | } 4 | 5 | .toolbar { 6 | position: fixed; 7 | top: 0; 8 | right: 0; 9 | left: 0; 10 | z-index: 10; 11 | box-shadow: 0 2px 5px 0 rgba(0,0,0,.3); 12 | } 13 | 14 | .toolbar-link { 15 | display: flex; 16 | align-items: center; 17 | padding: 24px; 18 | margin: 0 -16px; 19 | } 20 | 21 | .main-logo { 22 | padding: 0 8px; 23 | } 24 | 25 | .sidenav-container { 26 | min-height: 100%; 27 | height: auto; 28 | max-width: 100%; 29 | margin: 0; 30 | transform: none; 31 | 32 | .sidenav { 33 | position: fixed; 34 | top: 64px; 35 | bottom: 0; 36 | left: 0; 37 | padding: 0; 38 | min-width: 260px; 39 | box-shadow: 6px 0 6px rgba(0,0,0,0.1); 40 | 41 | .sidenav-menu { 42 | :first-child { 43 | margin-top: 16px; 44 | } 45 | 46 | .menu-item { 47 | display: block; 48 | max-width: 260px; 49 | font-weight: 400; 50 | padding-left: 20px; 51 | } 52 | } 53 | } 54 | 55 | .sidenav-content { 56 | height: 100%; 57 | 58 | &.sidenav-open { 59 | margin-left: 278px; 60 | } 61 | 62 | .main-content { 63 | min-height: 100vh; 64 | padding: 80px 3rem 2rem; 65 | } 66 | } 67 | } 68 | 69 | footer { 70 | padding: 48px; 71 | font-weight: 300; 72 | background-color: #3f51b5; 73 | z-index: 0; 74 | 75 | .footer-content { 76 | display: flex; 77 | justify-content: center; 78 | margin: 0 0 40px; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, waitForAsync } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(waitForAsync(() => { 6 | TestBed.configureTestingModule({ 7 | declarations: [AppComponent] 8 | }).compileComponents(); 9 | })); 10 | 11 | it('should create the app', () => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.componentInstance; 14 | expect(app).toBeTruthy(); 15 | }); 16 | 17 | it(`should have as title 'docs-app'`, () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app.title).toEqual('docs-app'); 21 | }); 22 | 23 | it('should render title', () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | fixture.detectChanges(); 26 | const compiled = fixture.nativeElement; 27 | expect(compiled.querySelector('.content span').textContent).toContain( 28 | 'docs-app app is running!' 29 | ); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppComponent } from './app.component'; 5 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 6 | import { MatToolbarModule } from '@angular/material/toolbar'; 7 | import { MatIconModule } from '@angular/material/icon'; 8 | import { MatSidenavModule } from '@angular/material/sidenav'; 9 | import { MatButtonModule } from '@angular/material/button'; 10 | import { HttpClientModule } from '@angular/common/http'; 11 | import { GettingStartedComponent } from './getting-started/getting-started.component'; 12 | import { AppRoutingModule } from './app-routing.module'; 13 | import { CustomElementsModule } from './custom-elements/custom-elements.module'; 14 | import { CodeExampleModule } from './custom-elements/code/code-example.module'; 15 | import { ExamplesModule } from './examples/examples.module'; 16 | import { TreeModule } from 'angular-tree-component'; 17 | import { FundamentalsModule } from './fundamentals/fundamentals.module'; 18 | import { LayoutModule } from './layout/layout.module'; 19 | import { SharedModule } from './shared/shared.module'; 20 | import { GuidesModule } from './guides/guides.module'; 21 | 22 | @NgModule({ 23 | declarations: [AppComponent, GettingStartedComponent], 24 | imports: [ 25 | SharedModule, 26 | BrowserModule, 27 | BrowserAnimationsModule, 28 | HttpClientModule, 29 | MatToolbarModule, 30 | MatIconModule, 31 | MatSidenavModule, 32 | MatButtonModule, 33 | AppRoutingModule, 34 | CustomElementsModule, 35 | LayoutModule, 36 | CodeExampleModule, 37 | ExamplesModule, 38 | TreeModule, 39 | FundamentalsModule, 40 | GuidesModule, 41 | ], 42 | providers: [], 43 | bootstrap: [AppComponent] 44 | }) 45 | export class AppModule {} 46 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/custom-elements/code/code-example.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, Type } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { CodeExampleComponent } from './code-example.component'; 4 | import { CodeModule } from './code.module'; 5 | import { WithCustomElementComponent } from '../element-registry'; 6 | 7 | @NgModule({ 8 | imports: [ CommonModule, CodeModule ], 9 | declarations: [ CodeExampleComponent ], 10 | exports: [ CodeExampleComponent ], 11 | entryComponents: [ CodeExampleComponent ] 12 | }) 13 | export class CodeExampleModule implements WithCustomElementComponent { 14 | customElementComponent: Type = CodeExampleComponent; 15 | } 16 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/custom-elements/code/code-tabs.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, Type } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { CodeTabsComponent } from './code-tabs.component'; 4 | import { MatCardModule } from '@angular/material/card'; 5 | import { MatTabsModule } from '@angular/material/tabs'; 6 | import { CodeModule } from './code.module'; 7 | import { WithCustomElementComponent } from '../element-registry'; 8 | 9 | @NgModule({ 10 | imports: [ CommonModule, MatCardModule, MatTabsModule, CodeModule ], 11 | declarations: [ CodeTabsComponent ], 12 | exports: [ CodeTabsComponent ], 13 | entryComponents: [ CodeTabsComponent ] 14 | }) 15 | export class CodeTabsModule implements WithCustomElementComponent { 16 | customElementComponent: Type = CodeTabsComponent; 17 | } 18 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/custom-elements/code/code.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { CodeComponent } from './code.component'; 4 | import { MatSnackBarModule } from '@angular/material/snack-bar'; 5 | import { PrettyPrinter } from './pretty-printer.service'; 6 | import { CopierService } from '../../shared/copier.service'; 7 | 8 | @NgModule({ 9 | imports: [ CommonModule, MatSnackBarModule ], 10 | declarations: [ CodeComponent ], 11 | entryComponents: [ CodeComponent ], 12 | exports: [ CodeComponent ], 13 | providers: [ PrettyPrinter, CopierService ] 14 | }) 15 | export class CodeModule { } 16 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/custom-elements/custom-elements.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { ROUTES} from '@angular/router'; 3 | import { ElementsLoader } from './elements-loader'; 4 | import { 5 | ELEMENT_MODULE_LOAD_CALLBACKS, 6 | ELEMENT_MODULE_LOAD_CALLBACKS_AS_ROUTES, 7 | ELEMENT_MODULE_LOAD_CALLBACKS_TOKEN 8 | } from './element-registry'; 9 | import { LazyCustomElementComponent } from './lazy-custom-element.component'; 10 | 11 | @NgModule({ 12 | declarations: [ LazyCustomElementComponent ], 13 | exports: [ LazyCustomElementComponent ], 14 | providers: [ 15 | ElementsLoader, 16 | { provide: ELEMENT_MODULE_LOAD_CALLBACKS_TOKEN, useValue: ELEMENT_MODULE_LOAD_CALLBACKS }, 17 | 18 | // Providing these routes as a signal to the build system that these modules should be 19 | // registered as lazy-loadable. 20 | // TODO(andrewjs): Provide first-class support for providing this. 21 | // { provide: ROUTES, useValue: ELEMENT_MODULE_LOAD_CALLBACKS_AS_ROUTES, multi: true }, 22 | ], 23 | }) 24 | export class CustomElementsModule { } 25 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/custom-elements/element-registry.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken, Type } from '@angular/core'; 2 | import { LoadChildrenCallback } from '@angular/router'; 3 | 4 | // Modules containing custom elements must be set up as lazy-loaded routes (loadChildren) 5 | // TODO(andrewjs): This is a hack, Angular should have first-class support for preparing a module 6 | // that contains custom elements. 7 | export const ELEMENT_MODULE_LOAD_CALLBACKS_AS_ROUTES = [ 8 | { 9 | selector: 'code-example', 10 | loadChildren: () => import('./code/code-example.module').then(m => m.CodeExampleModule) 11 | }, 12 | { 13 | selector: 'code-tabs', 14 | loadChildren: () => import('./code/code-tabs.module').then(m => m.CodeTabsModule) 15 | }, 16 | ]; 17 | 18 | /** 19 | * Interface expected to be implemented by all modules that declare a component that can be used as 20 | * a custom element. 21 | */ 22 | export interface WithCustomElementComponent { 23 | customElementComponent: Type; 24 | } 25 | 26 | /** Injection token to provide the element path modules. */ 27 | export const ELEMENT_MODULE_LOAD_CALLBACKS_TOKEN = new InjectionToken>('aio/elements-map'); 28 | 29 | /** Map of possible custom element selectors to their lazy-loadable module paths. */ 30 | export const ELEMENT_MODULE_LOAD_CALLBACKS = new Map(); 31 | ELEMENT_MODULE_LOAD_CALLBACKS_AS_ROUTES.forEach(route => { 32 | ELEMENT_MODULE_LOAD_CALLBACKS.set(route.selector, route.loadChildren); 33 | }); 34 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/custom-elements/lazy-custom-element.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, Input, OnInit } from '@angular/core'; 2 | import { ElementsLoader } from './elements-loader'; 3 | import { Logger } from '../shared/logger.service'; 4 | 5 | @Component({ 6 | selector: 'aio-lazy-ce', 7 | template: '', 8 | }) 9 | export class LazyCustomElementComponent implements OnInit { 10 | @Input() selector = ''; 11 | 12 | constructor( 13 | private elementRef: ElementRef, 14 | private elementsLoader: ElementsLoader, 15 | private logger: Logger, 16 | ) {} 17 | 18 | ngOnInit() { 19 | if (!this.selector || /[^\w-]/.test(this.selector)) { 20 | this.logger.error(new Error(`Invalid selector for 'aio-lazy-ce': ${this.selector}`)); 21 | return; 22 | } 23 | 24 | this.elementRef.nativeElement.innerHTML = `<${this.selector}>`; 25 | this.elementsLoader.loadCustomElement(this.selector); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/basic-usage/basic-tree/basic-tree.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/basic-usage/basic-tree/basic-tree.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/examples/basic-usage/basic-tree/basic-tree.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/basic-usage/basic-tree/basic-tree.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { BasicTreeComponent } from './basic-tree.component'; 4 | 5 | describe('BasicTreeComponent', () => { 6 | let component: BasicTreeComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ BasicTreeComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(BasicTreeComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/basic-usage/basic-tree/basic-tree.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-basic-tree', 5 | templateUrl: './basic-tree.component.html', 6 | styleUrls: ['./basic-tree.component.scss'] 7 | }) 8 | export class BasicTreeComponent { 9 | 10 | nodes = [ 11 | { 12 | name: 'root1', 13 | children: [ 14 | { name: 'child1' }, 15 | { name: 'child2' } 16 | ] 17 | }, 18 | { 19 | name: 'root2', 20 | children: [ 21 | { name: 'child2.1', children: [] }, 22 | { name: 'child2.2', children: [ 23 | {name: 'grandchild2.2.1'} 24 | ] } 25 | ] 26 | }, 27 | { name: 'root3' }, 28 | { name: 'root4', children: [] }, 29 | { name: 'root5', children: null } 30 | ]; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/basic-usage/basic-usage.component.html: -------------------------------------------------------------------------------- 1 |

Basic usage

2 | 3 |

Working tree

4 |

Source Code

5 |
6 | 7 |
8 | 9 |

How to implement

10 | 11 |

See Nodes Fundamentals

12 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/basic-usage/basic-usage.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/examples/basic-usage/basic-usage.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/basic-usage/basic-usage.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { BasicUsageComponent } from './basic-usage.component'; 4 | 5 | describe('BasicUsageComponent', () => { 6 | let component: BasicUsageComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ BasicUsageComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(BasicUsageComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/basic-usage/basic-usage.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-basic-usage', 5 | templateUrl: './basic-usage.component.html', 6 | styleUrls: ['./basic-usage.component.scss'] 7 | }) 8 | export class BasicUsageComponent implements OnInit { 9 | 10 | nodes = [ 11 | { 12 | name: 'root1', 13 | children: [ 14 | { name: 'child1' }, 15 | { name: 'child2' } 16 | ] 17 | }, 18 | { 19 | name: 'root2', 20 | children: [ 21 | { name: 'child2.1', children: [] }, 22 | { name: 'child2.2', children: [ 23 | {name: 'grandchild2.2.1'} 24 | ] } 25 | ] 26 | }, 27 | { name: 'root3' }, 28 | { name: 'root4', children: [] }, 29 | { name: 'root5', children: null } 30 | ]; 31 | 32 | constructor() { } 33 | 34 | ngOnInit(): void { 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/columns-example/columns-example.component.html: -------------------------------------------------------------------------------- 1 |

Tree with columns

2 | 3 |

Working tree

4 |

Source Code

5 |
6 | 7 |
8 | 9 |

How to implement

10 | 11 |

To create columns inside the tree we need to use the templating options of the tree and style them with css.

12 |

13 | The example component contains two parts. The header and the tree itself. The header is responsible for displaying the column header. 14 | This is needed to give the tree the grid style. If you just want the tree to have columns without any header you don't need to build a header. 15 |

16 | 17 |

#treeNodeWrapperTemplate

18 |

19 | To show the tree in columns it's not needed to use the full templating option. But at least the treeNodeWrapperTemplate is needed. 20 | See also the Template Fundamentals. 21 |

22 | 23 |

Column definition

24 |

25 | The example uses a simple array columns = ['city', 'zipCode'] to handle the columns. 26 | This array includes the property names that should be shown as columns and does not include the name, which is the first column. 27 | The name is handled differently as seen in the example. The columns array is just looped over and shown. 28 | In a more advanced example you could have the columns array as an input and also handle the name dynamically. 29 |

30 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/columns-example/columns-example.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/examples/columns-example/columns-example.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/columns-example/columns-example.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ColumnsExampleComponent } from './columns-example.component'; 4 | 5 | describe('ColumnsExampleComponent', () => { 6 | let component: ColumnsExampleComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ ColumnsExampleComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ColumnsExampleComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/columns-example/columns-example.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-columns-example', 5 | templateUrl: './columns-example.component.html', 6 | styleUrls: ['./columns-example.component.scss'] 7 | }) 8 | export class ColumnsExampleComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/columns-example/columns/columns.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ColumnsComponent } from './columns.component'; 4 | 5 | describe('ColumnsComponent', () => { 6 | let component: ColumnsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ ColumnsComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ColumnsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/columns-example/columns/columns.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ITreeOptions } from 'angular-tree-component'; 3 | 4 | @Component({ 5 | selector: 'app-columns', 6 | templateUrl: './columns.component.html', 7 | styleUrls: ['./columns.component.scss'] 8 | }) 9 | export class ColumnsComponent implements OnInit { 10 | 11 | nodes = [ 12 | { 13 | name: 'Region 1 Headquarter', 14 | city: 'Central City', 15 | zipCode: '00001', 16 | children: [ 17 | { name: 'Region 1 Subdivision 1', city: 'Highway Town', zipCode: '00002' }, 18 | { name: 'Region 1 Subdivision 2', city: 'Main Town', zipCode: '00003' } 19 | ] 20 | }, 21 | { 22 | name: 'Region 2 Headquarter', 23 | city: 'Beach City', 24 | zipCode: '00010', 25 | children: [ 26 | { name: 'Region 2 Subdivision 1', city: 'Palm Town', zipCode: '00011', children: [] }, 27 | { name: 'Region 2 Subdivision 2', city: 'Sunny Town', zipCode: '00012', children: [ 28 | { name: 'Customer Subdivision 2/2', city: 'Sunny Town', zipCode: '00012' } 29 | ] 30 | } 31 | ] 32 | }, 33 | { name: 'Region 3 Headquarter', city: 'River City', zipCode: '00100' }, 34 | ]; 35 | 36 | options: ITreeOptions = { 37 | displayField: 'name', 38 | useVirtualScroll: false, 39 | nodeHeight: 25, 40 | allowDrag: false, 41 | allowDrop: false 42 | }; 43 | 44 | columns = ['city', 'zipCode']; 45 | 46 | constructor() { } 47 | 48 | ngOnInit(): void { 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/crud-example/crud-example.component.html: -------------------------------------------------------------------------------- 1 |

Tree with create, copy and delete options

2 | 3 |

Working tree

4 |

Source Code

5 |
6 | 7 |
8 | 9 |

How to implement

10 | 11 |

12 | This example is based on Issue 813. 13 | It relies on the treeNodeTemplate to customize the display of each node. 14 | The example uses simple buttons to handle basic functions for creating, copying and deleting nodes. 15 |

16 |

17 | The example does not show a edit function because of the endless possibilities on how to implement this. 18 | One option could be to show an input field instead of the span with the name of the node. 19 | This could be triggered via button, double click or how your app enters edit mode. 20 |

21 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/crud-example/crud-example.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/examples/crud-example/crud-example.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/crud-example/crud-example.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CrudExampleComponent } from './crud-example.component'; 4 | 5 | describe('CrudExampleComponent', () => { 6 | let component: CrudExampleComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ CrudExampleComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CrudExampleComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/crud-example/crud-example.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-crud-example', 5 | templateUrl: './crud-example.component.html', 6 | styleUrls: ['./crud-example.component.scss'] 7 | }) 8 | export class CrudExampleComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/crud-example/crud/crud.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
5 |
6 | {{node.data.name}} 7 |
8 |
9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/crud-example/crud/crud.component.scss: -------------------------------------------------------------------------------- 1 | .tree-node-content { 2 | display: flex; 3 | justify-content: space-between; 4 | 5 | button { 6 | margin-left: 5px; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/crud-example/crud/crud.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CrudComponent } from './crud.component'; 4 | 5 | describe('CrudComponent', () => { 6 | let component: CrudComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ CrudComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CrudComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/examples.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { BasicUsageComponent } from './basic-usage/basic-usage.component'; 4 | import { TreeModule } from 'angular-tree-component'; 5 | import { BasicTreeComponent } from './basic-usage/basic-tree/basic-tree.component'; 6 | import { RouterModule } from '@angular/router'; 7 | import { ColumnsExampleComponent } from './columns-example/columns-example.component'; 8 | import { ColumnsComponent } from './columns-example/columns/columns.component'; 9 | import { CrudExampleComponent } from './crud-example/crud-example.component'; 10 | import { CrudComponent } from './crud-example/crud/crud.component'; 11 | import { LoadMoreExampleComponent } from './load-more-example/load-more-example.component'; 12 | import { LoadMoreComponent } from './load-more-example/load-more/load-more.component'; 13 | 14 | @NgModule({ 15 | declarations: [BasicUsageComponent, BasicTreeComponent, ColumnsExampleComponent, ColumnsComponent, CrudExampleComponent, CrudComponent, LoadMoreExampleComponent, LoadMoreComponent], 16 | imports: [ 17 | CommonModule, 18 | TreeModule, 19 | RouterModule, 20 | ] 21 | }) 22 | export class ExamplesModule { } 23 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/load-more-example/load-more-example.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/examples/load-more-example/load-more-example.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/load-more-example/load-more-example.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LoadMoreExampleComponent } from './load-more-example.component'; 4 | 5 | describe('LoadMoreExampleComponent', () => { 6 | let component: LoadMoreExampleComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ LoadMoreExampleComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LoadMoreExampleComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/load-more-example/load-more-example.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-load-more-example', 5 | templateUrl: './load-more-example.component.html', 6 | styleUrls: ['./load-more-example.component.scss'] 7 | }) 8 | export class LoadMoreExampleComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/load-more-example/load-more/load-more.component.html: -------------------------------------------------------------------------------- 1 |
2 | 7 | 8 |
9 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/load-more-example/load-more/load-more.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/examples/load-more-example/load-more/load-more.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/examples/load-more-example/load-more/load-more.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LoadMoreComponent } from './load-more.component'; 4 | 5 | describe('LoadMoreComponent', () => { 6 | let component: LoadMoreComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ LoadMoreComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LoadMoreComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/actions/actions-demo/actions-demo.component.html: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 | Custom Keys:
10 | enter - show alert
11 |
12 | Custom Mouse Actions:
13 | shift+click - select multi
14 | double click - expand / collapse
15 | right-click - show alert 16 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/actions/actions-demo/actions-demo.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/fundamentals/actions/actions-demo/actions-demo.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/actions/actions-demo/actions-demo.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { ActionsDemoComponent } from './actions-demo.component'; 4 | 5 | describe('ActionsDemoComponent', () => { 6 | let component: ActionsDemoComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ActionsDemoComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ActionsDemoComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/actions/actions.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/fundamentals/actions/actions.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/actions/actions.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { ActionsComponent } from './actions.component'; 4 | 5 | describe('ActionsComponent', () => { 6 | let component: ActionsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ActionsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ActionsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/api/api-demo/api-demo.component.html: -------------------------------------------------------------------------------- 1 | 7 | 8 |

API:

9 | 10 | 11 | 12 | 13 | 14 |

15 | 20 | 25 | 30 | 34 | 38 | 42 | 46 | 50 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/api/api-demo/api-demo.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/fundamentals/api/api-demo/api-demo.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/api/api-demo/api-demo.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { ApiDemoComponent } from './api-demo.component'; 4 | 5 | describe('ApiDemoComponent', () => { 6 | let component: ApiDemoComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ApiDemoComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ApiDemoComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/api/api-demo/api-demo.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ITreeOptions } from 'angular-tree-component'; 3 | 4 | @Component({ 5 | selector: 'app-api-demo', 6 | templateUrl: './api-demo.component.html', 7 | styleUrls: ['./api-demo.component.scss'] 8 | }) 9 | export class ApiDemoComponent { 10 | 11 | options: ITreeOptions = { 12 | 13 | }; 14 | nodes = [ 15 | { 16 | name: 'root1', 17 | children: [ 18 | { 19 | name: 'child1' 20 | }, { 21 | name: 'child2' 22 | } 23 | ] 24 | }, 25 | { 26 | name: 'root2', 27 | children: [ 28 | { 29 | name: 'child2.1' 30 | }, { 31 | name: 'child2.2', 32 | children: [ 33 | { 34 | id: 1001, 35 | name: 'subsub' 36 | } 37 | ] 38 | } 39 | ] 40 | } 41 | ]; 42 | 43 | addNode(tree: any) { 44 | this.nodes[0].children.push({ 45 | name: 'a new child' 46 | }); 47 | tree.treeModel.update(); 48 | } 49 | 50 | activateSubSub(tree: any) { 51 | // tree.treeModel.getNodeBy((node) => node.data.name === 'subsub') 52 | tree.treeModel.getNodeById(1001) 53 | .setActiveAndVisible(); 54 | } 55 | 56 | activeNodes(treeModel: any) { 57 | console.log(treeModel.activeNodes); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/api/api.component.html: -------------------------------------------------------------------------------- 1 |

Calling API Methods

2 | 3 |

Demo

4 | 5 |

Source Code

6 | 7 |
8 | 9 |
10 | 11 | {{ html }} 12 | 13 |

Or by accessing the treeNode that is passed through Events or using Action Mapping.

14 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/api/api.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/fundamentals/api/api.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/api/api.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { ApiComponent } from './api.component'; 4 | 5 | describe('ApiComponent', () => { 6 | let component: ApiComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ApiComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ApiComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/api/api.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-api', 5 | templateUrl: './api.component.html', 6 | styleUrls: ['./api.component.scss'] 7 | }) 8 | export class ApiComponent { 9 | html = ` 10 | 11 | 12 | 13 | 14 | 15 | 16 | `; 17 | } 18 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/events/events.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/fundamentals/events/events.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/events/events.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { EventsComponent } from './events.component'; 4 | 5 | describe('EventsComponent', () => { 6 | let component: EventsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ EventsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(EventsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/events/events.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-events', 5 | templateUrl: './events.component.html', 6 | styleUrls: ['./events.component.scss'] 7 | }) 8 | export class EventsComponent { 9 | event = ` 10 | 15 | 16 | 17 | onEvent = ($event) => console.log($event); 18 | `; 19 | 20 | toggleExpanded = ` 21 | { 22 | eventName: string; 23 | node: ITreeNode; 24 | isActive: boolean; 25 | } 26 | `; 27 | 28 | basicEvent = ` 29 | { 30 | eventName: string; 31 | node: ITreeNode; 32 | } 33 | `; 34 | 35 | eventName = ` 36 | { 37 | eventName: string; 38 | } 39 | `; 40 | 41 | move = ` 42 | { 43 | eventName: string; 44 | node: ITreeNode; // The node that was moved 45 | to: { 46 | parent: ITreeNode; // The parent node that contains the moved node 47 | index: number; // Index in the parent where the node was moved 48 | } 49 | } 50 | `; 51 | 52 | copy = ` 53 | { 54 | eventName: string; 55 | node: ITreeNode; // The node that was copied 56 | to: { 57 | parent: ITreeNode; // The parent node that contains the copied node 58 | index: number; // Index in the parent where the node was copied 59 | } 60 | } 61 | `; 62 | 63 | baseEvent = ` 64 | { 65 | eventName: string; 66 | ...rest: corresponding to the original event 67 | }`; 68 | 69 | // TODO: add stateChange 70 | } 71 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/focus/focus.component.html: -------------------------------------------------------------------------------- 1 |

Focused

2 | 3 | {{ focus }} 4 | 5 |

@Input() focused

6 | 7 |

Whether the tree should be focused. Key navigation only works when the tree is focused.

8 |

Default value: false.

9 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/focus/focus.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/fundamentals/focus/focus.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/focus/focus.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { FocusComponent } from './focus.component'; 4 | 5 | describe('FocusComponent', () => { 6 | let component: FocusComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FocusComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FocusComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/focus/focus.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-focus', 5 | templateUrl: './focus.component.html', 6 | styleUrls: ['./focus.component.scss'] 7 | }) 8 | export class FocusComponent { 9 | focus = ``; 10 | } 11 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/fundamentals.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NodesComponent } from './nodes/nodes.component'; 4 | import { CodeExampleModule } from '../custom-elements/code/code-example.module'; 5 | import { OptionsComponent } from './options/options.component'; 6 | import { ActionsComponent } from './actions/actions.component'; 7 | import { TemplatesComponent } from './templates/templates.component'; 8 | import { ActionsDemoComponent } from './actions/actions-demo/actions-demo.component'; 9 | import { TreeModule } from 'angular-tree-component'; 10 | import { TemplatesDemoComponent } from './templates/templates-demo/templates-demo.component'; 11 | import { EventsComponent } from './events/events.component'; 12 | import { StateBindingComponent } from './state-binding/state-binding.component'; 13 | import { StateBindingDemoComponent } from './state-binding/state-binding-demo/state-binding-demo.component'; 14 | import { ApiComponent } from './api/api.component'; 15 | import { RouterModule } from '@angular/router'; 16 | import { ApiDemoComponent } from './api/api-demo/api-demo.component'; 17 | import { StylingComponent } from './styling/styling.component'; 18 | import { FocusComponent } from './focus/focus.component'; 19 | import { IssuesComponent } from './issues/issues.component'; 20 | 21 | 22 | @NgModule({ 23 | declarations: [ 24 | NodesComponent, 25 | OptionsComponent, 26 | ActionsComponent, 27 | TemplatesComponent, 28 | ActionsDemoComponent, 29 | TemplatesDemoComponent, 30 | EventsComponent, 31 | StateBindingComponent, 32 | StateBindingDemoComponent, 33 | ApiComponent, 34 | ApiDemoComponent, 35 | StylingComponent, 36 | FocusComponent, 37 | IssuesComponent 38 | ], 39 | imports: [ 40 | CommonModule, 41 | CodeExampleModule, 42 | TreeModule, 43 | RouterModule 44 | ] 45 | }) 46 | export class FundamentalsModule { } 47 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/issues/issues.component.html: -------------------------------------------------------------------------------- 1 |

Common Issues

2 | 3 |

Tree not rendered

4 |

Case: when tree is hidden (for example inside tab or modal), it is not rendered when it becomes visible.

5 |

Solution: after it becomes visible (preferably using setTimeout) - call tree.sizeChanged(), which recalculates the rendered nodes according to the actual viewport size.

6 | 7 |

Tree state (expanded / selected nodes) gets lost

8 |

Maybe you are not supplying unique IDs to the nodes.

9 |

The tree maintains its state by using IDs, and if you don't supply ones the tree will generate random ones automatically. Which means that if you update the data - the state will be lost.

10 | 11 |

Scroll Into View doesn't work

12 |

See scrollContainer option in Options for more information.

13 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/issues/issues.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/fundamentals/issues/issues.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/issues/issues.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { IssuesComponent } from './issues.component'; 4 | 5 | describe('IssuesComponent', () => { 6 | let component: IssuesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ IssuesComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(IssuesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/issues/issues.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-issues', 5 | templateUrl: './issues.component.html', 6 | styleUrls: ['./issues.component.scss'] 7 | }) 8 | export class IssuesComponent { 9 | // TODO: add drag/drop 10 | } 11 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/nodes/nodes.component.html: -------------------------------------------------------------------------------- 1 |

Nodes

2 | 3 |

Inputs to Tree component:

4 | 5 | <tree-root [nodes]=nodes [options]="options"></tree-root> 6 | 7 |

nodes

8 | 9 |

Array of root nodes of the tree.

10 |

Each node may contain the following fields:

11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
PropertyDescription
id 21 | Unique ID for the node.
22 | If one is not supplied it will be created by the tree library. 23 |
nameWill be displayed by default in the tree.
childrenAn array of the node's children.
34 | Each child is an object with the same structure as the parent node.
hasChildrenFor async data load. Denotes that this node might have children, even when 'children' attr is empty.
isExpandedDetermines whether the node starts as expanded by default. Notice that this field is not bindable, meaning that changing it doesn't affect the tree and vice versa.
47 | 48 |

Example:

49 | 50 | {{ nodes }} 51 | 52 |

Focused

53 | 54 |

Whether the tree should be focused. Key navigation only works when the tree is focused.

55 |

Default value: false.

56 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/nodes/nodes.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/fundamentals/nodes/nodes.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/nodes/nodes.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { NodesComponent } from './nodes.component'; 4 | 5 | describe('NodesComponent', () => { 6 | let component: NodesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NodesComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NodesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/nodes/nodes.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-nodes', 5 | templateUrl: './nodes.component.html', 6 | styleUrls: ['./nodes.component.scss'] 7 | }) 8 | export class NodesComponent implements OnInit { 9 | 10 | nodes = `[ 11 | { 12 | id: 1, 13 | name: 'root1', 14 | children: [ 15 | { id: 2, name: 'child1' }, 16 | { id: 3, name: 'child2' } 17 | ] 18 | } 19 | ]`; 20 | 21 | constructor() { } 22 | 23 | ngOnInit(): void { 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/options/options.component.html: -------------------------------------------------------------------------------- 1 |

Options

2 | 3 | <tree-root [nodes]=nodes [options]="options"></tree-root> 4 | 5 |

options

6 | 7 |

Object of type ITreeOptions

8 | 9 |

Example:

10 | 11 | {{ options }} 12 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/options/options.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/fundamentals/options/options.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/options/options.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { OptionsComponent } from './options.component'; 4 | 5 | describe('OptionsComponent', () => { 6 | let component: OptionsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ OptionsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(OptionsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/options/options.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-options', 5 | templateUrl: './options.component.html', 6 | styleUrls: ['./options.component.scss'] 7 | }) 8 | export class OptionsComponent implements OnInit { 9 | 10 | options = ` 11 | import { TREE_ACTIONS, KEYS, IActionMapping, ITreeOptions } from '@circlon/angular-tree-component'; 12 | 13 | class MyComponent { 14 | ... 15 | options: ITreeOptions = { 16 | displayField: 'nodeName', 17 | isExpandedField: 'expanded', 18 | idField: 'uuid', 19 | hasChildrenField: 'nodes', 20 | actionMapping: { 21 | mouse: { 22 | dblClick: (tree, node, $event) => { 23 | if (node.hasChildren) TREE_ACTIONS.TOGGLE_EXPANDED(tree, node, $event); 24 | } 25 | }, 26 | keys: { 27 | [KEYS.ENTER]: (tree, node, $event) => { 28 | node.expandAll(); 29 | } 30 | } 31 | }, 32 | nodeHeight: 23, 33 | allowDrag: (node) => { 34 | return true; 35 | }, 36 | allowDrop: (node) => { 37 | return true; 38 | }, 39 | allowDragoverStyling: true, 40 | levelPadding: 10, 41 | useVirtualScroll: true, 42 | animateExpand: true, 43 | scrollOnActivate: true, 44 | animateSpeed: 30, 45 | animateAcceleration: 1.2, 46 | scrollContainer: document.documentElement // HTML 47 | } 48 | } 49 | `; 50 | 51 | constructor() { } 52 | 53 | ngOnInit(): void { 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/state-binding/state-binding-demo/state-binding-demo.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/state-binding/state-binding-demo/state-binding-demo.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/fundamentals/state-binding/state-binding-demo/state-binding-demo.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/state-binding/state-binding-demo/state-binding-demo.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { StateBindingDemoComponent } from './state-binding-demo.component'; 4 | 5 | describe('StateBindingDemoComponent', () => { 6 | let component: StateBindingDemoComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ StateBindingDemoComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(StateBindingDemoComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/state-binding/state-binding-demo/state-binding-demo.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ITreeState } from 'angular-tree-component'; 3 | 4 | @Component({ 5 | selector: 'app-state-binding-demo', 6 | templateUrl: './state-binding-demo.component.html', 7 | styleUrls: ['./state-binding-demo.component.scss'] 8 | }) 9 | export class StateBindingDemoComponent { 10 | 11 | get state(): ITreeState { 12 | return localStorage.treeState && JSON.parse(localStorage.treeState); 13 | } 14 | set state(state: ITreeState) { 15 | localStorage.treeState = JSON.stringify(state); 16 | } 17 | 18 | options = { 19 | getChildren: () => new Promise((resolve) => { 20 | setTimeout(() => resolve([ 21 | { id: 5, name: 'child2.1', children: [] }, 22 | { id: 6, name: 'child2.2', children: [ 23 | { id: 7, name: 'grandchild2.2.1' } 24 | ] } 25 | ]), 2000); 26 | }) 27 | }; 28 | 29 | nodes = [ 30 | { 31 | id: 1, 32 | name: 'root1', 33 | children: [ 34 | { id: 2, name: 'child1' }, 35 | { id: 3, name: 'child2' } 36 | ] 37 | }, 38 | { 39 | id: 4, 40 | name: 'root2', 41 | hasChildren: true 42 | } 43 | ]; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/state-binding/state-binding.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/fundamentals/state-binding/state-binding.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/state-binding/state-binding.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { StateBindingComponent } from './state-binding.component'; 4 | 5 | describe('StateBindingComponent', () => { 6 | let component: StateBindingComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ StateBindingComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(StateBindingComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/styling/styling.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/fundamentals/styling/styling.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/styling/styling.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { StylingComponent } from './styling.component'; 4 | 5 | describe('StylingComponent', () => { 6 | let component: StylingComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ StylingComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(StylingComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/templates/templates-demo/templates-demo.component.scss: -------------------------------------------------------------------------------- 1 | .root1Class { 2 | color: blue; 3 | } 4 | 5 | .root2Class { 6 | color: red; 7 | } 8 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/templates/templates-demo/templates-demo.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { TemplatesDemoComponent } from './templates-demo.component'; 4 | 5 | describe('TemplatesDemoComponent', () => { 6 | let component: TemplatesDemoComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ TemplatesDemoComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TemplatesDemoComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/templates/templates-demo/templates-demo.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ITreeOptions } from 'angular-tree-component'; 3 | 4 | @Component({ 5 | selector: 'app-templates-demo', 6 | templateUrl: './templates-demo.component.html', 7 | styleUrls: ['./templates-demo.component.scss'] 8 | }) 9 | export class TemplatesDemoComponent { 10 | 11 | nodes1 = [ 12 | { 13 | title: 'root1', 14 | className: 'root1Class' 15 | }, 16 | { 17 | title: 'root2', 18 | className: 'root2Class', 19 | hasChildren: true 20 | } 21 | ]; 22 | 23 | nodes2 = [ 24 | { 25 | title: 'root1', 26 | className: 'root1Class' 27 | }, 28 | { 29 | title: 'root2', 30 | className: 'root2Class', 31 | children: [ 32 | { title: 'child1', className: 'child1Class' } 33 | ] 34 | } 35 | ]; 36 | 37 | options1: ITreeOptions = { 38 | getChildren: () => new Promise((resolve, reject) => { }) 39 | }; 40 | 41 | options0: ITreeOptions = { 42 | displayField: 'title', 43 | nodeClass: (node) => `${node.data.title}Class` 44 | }; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/templates/templates.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/fundamentals/templates/templates.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/fundamentals/templates/templates.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { TemplatesComponent } from './templates.component'; 4 | 5 | describe('TemplatesComponent', () => { 6 | let component: TemplatesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ TemplatesComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TemplatesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/getting-started/getting-started.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/getting-started/getting-started.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/getting-started/getting-started.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { GettingStartedComponent } from './getting-started.component'; 4 | 5 | describe('GettingStartedComponent', () => { 6 | let component: GettingStartedComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ GettingStartedComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(GettingStartedComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/getting-started/getting-started.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-getting-started', 5 | templateUrl: './getting-started.component.html', 6 | styleUrls: ['./getting-started.component.scss'] 7 | }) 8 | export class GettingStartedComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/async-guide/async-guide.component.html: -------------------------------------------------------------------------------- 1 |

Async Data

2 | 3 |

4 | The tree allows to load children asynchronously using getChildren option, and hasChildren field on the node. 5 |

6 | 7 |

Demo

8 | 9 |

Source Code

10 | 11 |
12 | 13 |
14 | 15 |

'getChildren' option

16 | 17 |

This options receives a function that has a TreeNode parameter, and returns a value or a promise that resolves to the node's children:

18 | 19 | (node:TreeNode) => TreeNode[] | Promise<TreeNode[]> 20 | 21 |

22 | The function will be called whenever a node is expanded, the hasChildren field is true, and the children field is empty. 23 | The result will be loaded into the node's children attribute. 24 |

25 | 26 |

Example

27 | 28 | {{ javascript }} 29 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/async-guide/async-guide.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/async-guide/async-guide.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/async-guide/async-guide.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AsyncGuideComponent } from './async-guide.component'; 4 | 5 | describe('AsyncGuideComponent', () => { 6 | let component: AsyncGuideComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ AsyncGuideComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AsyncGuideComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/async-guide/async-guide.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-async-guide', 5 | templateUrl: './async-guide.component.html', 6 | styleUrls: ['./async-guide.component.scss'] 7 | }) 8 | export class AsyncGuideComponent { 9 | 10 | javascript = 11 | ` 12 | options = { 13 | getChildren: (node:TreeNode) => { 14 | return request('/api/children/' + node.id); 15 | } 16 | } 17 | 18 | nodes = [ 19 | { 20 | name: 'asyncRoot', 21 | hasChildren: true 22 | }, 23 | { 24 | name: 'root2', 25 | children: [ 26 | { 27 | name: 'leaf', 28 | hasChildren: false 29 | } 30 | ] 31 | } 32 | ] 33 | `; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/async-guide/async/async.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/async-guide/async/async.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/async-guide/async/async.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/async-guide/async/async.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AsyncComponent } from './async.component'; 4 | 5 | describe('AsyncComponent', () => { 6 | let component: AsyncComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ AsyncComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AsyncComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/async-guide/async/async.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ITreeOptions } from 'angular-tree-component'; 3 | 4 | @Component({ 5 | selector: 'app-async', 6 | templateUrl: './async.component.html', 7 | styleUrls: ['./async.component.scss'] 8 | }) 9 | export class AsyncComponent { 10 | 11 | options: ITreeOptions = { 12 | getChildren: this.getChildren.bind(this), 13 | useCheckbox: true 14 | }; 15 | 16 | nodes: any[] = []; 17 | 18 | asyncChildren = [ 19 | { 20 | name: 'child1', 21 | hasChildren: true 22 | }, { 23 | name: 'child2' 24 | } 25 | ]; 26 | 27 | constructor() { 28 | this.nodes = [ 29 | { 30 | name: 'root1', 31 | children: [ 32 | { name: 'child1' } 33 | ] 34 | }, 35 | { 36 | name: 'root2', 37 | hasChildren: true 38 | }, 39 | { 40 | name: 'root3' 41 | } 42 | ]; 43 | } 44 | 45 | getChildren(node: any) { 46 | const newNodes = this.asyncChildren.map((c) => Object.assign({}, c)); 47 | 48 | return new Promise((resolve, reject) => { 49 | setTimeout(() => resolve(newNodes), 1000); 50 | }); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/auto-scroll-guide/auto-scroll-guide.component.html: -------------------------------------------------------------------------------- 1 |

Auto Scrolling

2 | 3 |

When navigating with keys or clicking a node in the edges of the screen, the tree will automatically try to scroll the selected node into view.

4 |

Also, you can call scrollIntoView on any node to make it visible.

5 | 6 |

Inner Scroll

7 |

Auto scrolling is built-in to the tree. Simply wrap the tree with a container that has a height and you're good.

8 |

The tree has an inner element called viewport that is scrollable.

9 | 10 |

External Scroll (Scrolling as part of the body / other container)

11 |

Sometimes you don't want to wrap the tree with a scrollable container, and want the scrolling to be part of a different element.

12 |

Use the scrollContainer option for this purpose as described in Options

13 | 14 |

Demo of scrollContainer

15 |

Source Code

16 |
17 | 18 |
19 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/auto-scroll-guide/auto-scroll-guide.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/auto-scroll-guide/auto-scroll-guide.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/auto-scroll-guide/auto-scroll-guide.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AutoScrollGuideComponent } from './auto-scroll-guide.component'; 4 | 5 | describe('AutoScrollGuideComponent', () => { 6 | let component: AutoScrollGuideComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ AutoScrollGuideComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AutoScrollGuideComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/auto-scroll-guide/auto-scroll-guide.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-auto-scroll-guide', 5 | templateUrl: './auto-scroll-guide.component.html', 6 | styleUrls: ['./auto-scroll-guide.component.scss'] 7 | }) 8 | export class AutoScrollGuideComponent { 9 | } 10 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/auto-scroll-guide/auto-scroll/auto-scroll.component.html: -------------------------------------------------------------------------------- 1 |
2 |
Padding
3 |
4 | 10 |
11 |
12 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/auto-scroll-guide/auto-scroll/auto-scroll.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/auto-scroll-guide/auto-scroll/auto-scroll.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/auto-scroll-guide/auto-scroll/auto-scroll.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AutoScrollComponent } from './auto-scroll.component'; 4 | 5 | describe('AutoScrollComponent', () => { 6 | let component: AutoScrollComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ AutoScrollComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AutoScrollComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/auto-scroll-guide/auto-scroll/auto-scroll.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ITreeOptions } from 'angular-tree-component'; 3 | 4 | @Component({ 5 | selector: 'app-auto-scroll', 6 | templateUrl: './auto-scroll.component.html', 7 | styleUrls: ['./auto-scroll.component.scss'] 8 | }) 9 | export class AutoScrollComponent implements OnInit { 10 | 11 | nodes: any[] = []; 12 | options: ITreeOptions = { 13 | scrollContainer: document.body.parentElement 14 | }; 15 | 16 | ngOnInit() { 17 | for (let i = 0; i < 200; i++) { 18 | this.nodes.push({ 19 | name: `rootDynamic${i}`, 20 | subTitle: `root created dynamically ${i}` 21 | }); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/checkboxes-guide/checkboxes-guide.component.html: -------------------------------------------------------------------------------- 1 |

Checkboxes

2 | 3 |

You can specify useCheckboxes option to show checkboxes, and the tree selection APIs to control and access which node is currently selected.

4 | 5 |

Demo

6 |

Source Code

7 |
8 | 9 |
10 | 11 |

By default, parent nodes reflect and control the status of children.

12 |

If you wish to override this behaviour, specify useTriState: false in the options.

13 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/checkboxes-guide/checkboxes-guide.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/checkboxes-guide/checkboxes-guide.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/checkboxes-guide/checkboxes-guide.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CheckboxesGuideComponent } from './checkboxes-guide.component'; 4 | 5 | describe('CheckboxesGuideComponent', () => { 6 | let component: CheckboxesGuideComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ CheckboxesGuideComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CheckboxesGuideComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/checkboxes-guide/checkboxes-guide.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-checkboxes-guide', 5 | templateUrl: './checkboxes-guide.component.html', 6 | styleUrls: ['./checkboxes-guide.component.scss'] 7 | }) 8 | export class CheckboxesGuideComponent { 9 | } 10 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/checkboxes-guide/checkboxes/checkboxes.component.html: -------------------------------------------------------------------------------- 1 |

tri-state checkboxes

2 | 6 | 7 |

The tree is using flexbox.
8 | Switch expander and checkbox with CSS 'order' attribute:

9 | 14 | 15 |

Disable tri-state checkboxes

16 | 20 | 21 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/checkboxes-guide/checkboxes/checkboxes.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/checkboxes-guide/checkboxes/checkboxes.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/checkboxes-guide/checkboxes/checkboxes.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CheckboxesComponent } from './checkboxes.component'; 4 | 5 | describe('CheckboxesComponent', () => { 6 | let component: CheckboxesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ CheckboxesComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CheckboxesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/checkboxes-guide/checkboxes/checkboxes.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ITreeOptions } from 'angular-tree-component'; 3 | 4 | @Component({ 5 | selector: 'app-checkboxes', 6 | templateUrl: './checkboxes.component.html', 7 | styleUrls: ['./checkboxes.component.scss'] 8 | }) 9 | export class CheckboxesComponent { 10 | 11 | nodes = [ 12 | { 13 | name: 'root1', 14 | }, 15 | { 16 | name: 'root2', 17 | children: [ 18 | { name: 'child1' }, 19 | { name: 'child2', children: [ 20 | { name: 'grandchild1' }, 21 | { name: 'grandchild2' } 22 | ] } 23 | ] 24 | }, 25 | { 26 | name: 'asyncroot', 27 | hasChildren: true 28 | } 29 | ]; 30 | 31 | options: ITreeOptions = { 32 | useCheckbox: true, 33 | getChildren: this.getChildren.bind(this) 34 | }; 35 | 36 | optionsDisabled: ITreeOptions = { 37 | useCheckbox: true, 38 | getChildren: this.getChildren.bind(this), 39 | useTriState: false 40 | }; 41 | 42 | getChildren(node: any) { 43 | const newNodes = [ 44 | { 45 | name: 'child1' 46 | }, { 47 | name: 'child2' 48 | } 49 | ]; 50 | 51 | return new Promise((resolve, reject) => { 52 | setTimeout(() => resolve(newNodes), 1000); 53 | }); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/custom-fields-guide/custom-fields-guide.component.html: -------------------------------------------------------------------------------- 1 |

Custom Fields

2 | 3 |

Node field names are customizable using the following options:

4 |
    5 |
  • childrenField
  • 6 |
  • displayField
  • 7 |
  • idField
  • 8 |
  • isExpandedField
  • 9 |
  • hasChildrenField
  • 10 |
11 | 12 |

Example

13 | {{ javascript }} 14 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/custom-fields-guide/custom-fields-guide.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/custom-fields-guide/custom-fields-guide.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/custom-fields-guide/custom-fields-guide.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CustomFieldsGuideComponent } from './custom-fields-guide.component'; 4 | 5 | describe('CustomFieldsGuideComponent', () => { 6 | let component: CustomFieldsGuideComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ CustomFieldsGuideComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CustomFieldsGuideComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/custom-fields-guide/custom-fields-guide.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-custom-fields-guide', 5 | templateUrl: './custom-fields-guide.component.html', 6 | styleUrls: ['./custom-fields-guide.component.scss'] 7 | }) 8 | export class CustomFieldsGuideComponent { 9 | javascript = ` 10 | nodes = [ 11 | { 12 | _id: '1', 13 | title: 'root1', 14 | nodes: [{_id: '3', title: 'child1'}] 15 | }, 16 | { 17 | _id: '2', 18 | title: 'root2' 19 | } 20 | ]; 21 | 22 | options: ITreeOptions = { 23 | idField: '_id', 24 | displayField: 'title', 25 | childrenField: 'nodes' 26 | };`; 27 | } 28 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/drag-drop-guide/drag-drop-guide.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/drag-drop-guide/drag-drop-guide.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/drag-drop-guide/drag-drop-guide.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DragDropGuideComponent } from './drag-drop-guide.component'; 4 | 5 | describe('DragDropGuideComponent', () => { 6 | let component: DragDropGuideComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ DragDropGuideComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(DragDropGuideComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/drag-drop-guide/drag-drop/drag-drop.component.html: -------------------------------------------------------------------------------- 1 |

Allowing to drag only leaf nodes; ctrl-drag to copy

2 | 3 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/drag-drop-guide/drag-drop/drag-drop.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/drag-drop-guide/drag-drop/drag-drop.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/drag-drop-guide/drag-drop/drag-drop.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DragDropComponent } from './drag-drop.component'; 4 | 5 | describe('DragDropComponent', () => { 6 | let component: DragDropComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ DragDropComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(DragDropComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/drag-drop-guide/drag-drop/drag-drop.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ITreeOptions, ITreeState } from 'angular-tree-component'; 3 | import { v4 } from 'uuid'; 4 | 5 | @Component({ 6 | selector: 'app-drag-drop', 7 | templateUrl: './drag-drop.component.html', 8 | styleUrls: ['./drag-drop.component.scss'] 9 | }) 10 | export class DragDropComponent { 11 | 12 | state: ITreeState = { 13 | expandedNodeIds: { 14 | 1: true, 15 | 2: true 16 | }, 17 | hiddenNodeIds: {}, 18 | activeNodeIds: {} 19 | }; 20 | 21 | options: ITreeOptions = { 22 | allowDrag: (node) => node.isLeaf, 23 | getNodeClone: (node) => ({ 24 | ...node.data, 25 | id: v4(), 26 | name: `copy of ${node.data.name}` 27 | }) 28 | }; 29 | 30 | nodes = [ 31 | { 32 | id: 1, 33 | name: 'root1', 34 | children: [ 35 | { name: 'child1' }, 36 | { name: 'child2' } 37 | ] 38 | }, 39 | { 40 | name: 'root2', 41 | id: 2, 42 | children: [ 43 | { name: 'child2.1', children: [] }, 44 | { name: 'child2.2', children: [ 45 | {name: 'grandchild2.2.1'} 46 | ] } 47 | ] 48 | }, 49 | { name: 'root3' }, 50 | { name: 'root4', children: [] }, 51 | { name: 'root5', children: null } 52 | ]; 53 | 54 | } 55 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/expanding-guide/expanding-guide.component.html: -------------------------------------------------------------------------------- 1 |

Expanding nodes on init

2 | 3 |

A common use case is to start with the tree expanded completely.

4 |

You can achieve this by calling treeModel.expandAll() after view init:

5 | 6 | {{ allNodes }} 7 | 8 |

Or to expand / activate specific nodes:

9 | 10 | {{ specific }} 11 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/expanding-guide/expanding-guide.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/expanding-guide/expanding-guide.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/expanding-guide/expanding-guide.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ExpandingGuideComponent } from './expanding-guide.component'; 4 | 5 | describe('ExpandingGuideComponent', () => { 6 | let component: ExpandingGuideComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ ExpandingGuideComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ExpandingGuideComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/expanding-guide/expanding-guide.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-expanding-guide', 5 | templateUrl: './expanding-guide.component.html', 6 | styleUrls: ['./expanding-guide.component.scss'] 7 | }) 8 | export class ExpandingGuideComponent { 9 | 10 | allNodes = ` 11 | 12 | 13 | @Component { 14 | nodes = [...]; 15 | @ViewChild('tree') tree; 16 | 17 | ngAfterViewInit() { 18 | this.tree.treeModel.expandAll(); 19 | } 20 | }`; 21 | 22 | specific = ` 23 | 24 | 25 | @Component { 26 | nodes = [...]; 27 | @ViewChild('tree') tree; 28 | 29 | ngAfterViewInit() { 30 | const someNode = this.tree.treeModel.getNodeById('someId'); 31 | someNode.expand(); 32 | 33 | const firstRoot = this.tree.treeModel.roots[0]; 34 | firstRoot.setActiveAndVisible(); 35 | } 36 | }`; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/filter-guide/filter-guide.component.html: -------------------------------------------------------------------------------- 1 |

Filtering

2 | 3 |

Demo

4 | 5 |

Source Code

6 | 7 |
8 | 9 |
10 | 11 |

Intro

12 |

13 | Filtering on the tree will ensure that if a node is visible, then all its ancestors are also visible. 14 | This is being taken care of by the treeModel 'filterNodes' function. 15 |

16 | 17 |

Filter by function

18 |

19 | The function receives the node and returns true if the node should be hidden, false otherwise. 20 |

21 | {{functionFilter}} 22 | 23 |

Filter by string

24 |

The function filters all nodes whose displayField ('name' by default) contains the given string. The comparison is done case insensitive.

25 | tree.treeModel.filterNodes("text", true); 26 |

27 | Note the second field - true by default. 28 | This flag makes sure all nodes are visible after searching (i.e. expand all relevant ancestors). 29 |

30 | 31 |

Filtering by AP

32 |

33 | You can traverse the tree and do your own magic, and call hide(), show(), or setIsHidden(value) on all nodes as you wish. 34 |

35 | 36 |

Filtering by 2-way binding

37 |

38 | You can bind to the tree state and supply a dictionary of hidden node IDs. 39 | See 2-way binding to state for more information. 40 |

41 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/filter-guide/filter-guide.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/filter-guide/filter-guide.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/filter-guide/filter-guide.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FilterGuideComponent } from './filter-guide.component'; 4 | 5 | describe('FilterGuideComponent', () => { 6 | let component: FilterGuideComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ FilterGuideComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FilterGuideComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/filter-guide/filter-guide.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-filter-guide', 5 | templateUrl: './filter-guide.component.html', 6 | styleUrls: ['./filter-guide.component.scss'] 7 | }) 8 | export class FilterGuideComponent { 9 | functionFilter = 10 | ` 11 | tree.treeModel.filterNodes((node) => { 12 | return !node.data.name.startsWith(text); 13 | }); 14 | `; 15 | } 16 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/filter-guide/filter/filter.component.html: -------------------------------------------------------------------------------- 1 |

Filter

2 | 3 | 4 | 5 | 6 |

Filter By Function (Fuzzy Search)

7 | 8 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/filter-guide/filter/filter.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/filter-guide/filter/filter.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/filter-guide/filter/filter.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FilterComponent } from './filter.component'; 4 | 5 | describe('FilterComponent', () => { 6 | let component: FilterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ FilterComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FilterComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/filter-guide/filter/filter.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { TreeModel, TreeNode } from 'angular-tree-component'; 3 | 4 | @Component({ 5 | selector: 'app-filter', 6 | templateUrl: './filter.component.html', 7 | styleUrls: ['./filter.component.scss'] 8 | }) 9 | export class FilterComponent { 10 | 11 | options = { 12 | useCheckbox: true 13 | }; 14 | nodes = [ 15 | { 16 | name: 'North America', 17 | children: [ 18 | { name: 'United States', children: [ 19 | {name: 'New York'}, 20 | {name: 'California'}, 21 | {name: 'Florida'} 22 | ] }, 23 | { name: 'Canada' } 24 | ] 25 | }, 26 | { 27 | name: 'South America', 28 | children: [ 29 | { name: 'Argentina', children: [] }, 30 | { name: 'Brazil' } 31 | ] 32 | }, 33 | { 34 | name: 'Europe', 35 | children: [ 36 | { name: 'England' }, 37 | { name: 'Germany' }, 38 | { name: 'France' }, 39 | { name: 'Italy' }, 40 | { name: 'Spain' } 41 | ] 42 | } 43 | ]; 44 | 45 | filterFn(value: string, treeModel: TreeModel) { 46 | treeModel.filterNodes((node: TreeNode) => fuzzysearch(value, node.data.name)); 47 | } 48 | } 49 | 50 | 51 | function fuzzysearch (needle: string, haystack: string) { 52 | const haystackLC = haystack.toLowerCase(); 53 | const needleLC = needle.toLowerCase(); 54 | 55 | const hlen = haystack.length; 56 | const nlen = needleLC.length; 57 | 58 | if (nlen > hlen) { 59 | return false; 60 | } 61 | if (nlen === hlen) { 62 | return needleLC === haystackLC; 63 | } 64 | outer: for (let i = 0, j = 0; i < nlen; i++) { 65 | const nch = needleLC.charCodeAt(i); 66 | 67 | while (j < hlen) { 68 | if (haystackLC.charCodeAt(j++) === nch) { 69 | continue outer; 70 | } 71 | } 72 | return false; 73 | } 74 | return true; 75 | } 76 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/large-tree-guide/large-tree-guide.component.html: -------------------------------------------------------------------------------- 1 |

Large Trees & Virtual Scroll

2 | 3 |

When having a large amount of nodes and experiencing performance issues, it is recommended to use the virtual scroll option.

4 |

To use this option, one must supply the height of the container, and the height of each node in the tree.

5 |

You can also specify height for the dropSlot which is located between nodes.

6 |

Example:

7 | 8 | {{ html }} 9 | {{ javascript }} 10 | 11 |

Hidden trees

12 |

13 | If the tree is hidden (for example inside tab or modal), it will not be rendered when it becomes visible. 14 | After it becomes visible (preferably using setTimeout) - you need to call tree.sizeChanged(), which recalculates the rendered nodes according to the actual viewport size. 15 |

16 | 17 |

Demo

18 |

Initializing 100,000 nodes, please be patient...

19 |

Source Code

20 |
21 | 22 |
23 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/large-tree-guide/large-tree-guide.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/large-tree-guide/large-tree-guide.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/large-tree-guide/large-tree-guide.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LargeTreeGuideComponent } from './large-tree-guide.component'; 4 | 5 | describe('LargeTreeGuideComponent', () => { 6 | let component: LargeTreeGuideComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ LargeTreeGuideComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LargeTreeGuideComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/large-tree-guide/large-tree-guide.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-large-tree-guide', 5 | templateUrl: './large-tree-guide.component.html', 6 | styleUrls: ['./large-tree-guide.component.scss'] 7 | }) 8 | export class LargeTreeGuideComponent { 9 | 10 | html = ` 11 |
12 | 13 |
`; 14 | 15 | javascript = ` 16 | // Fixed node height 17 | options = { 18 | useVirtualScroll: true, 19 | nodeHeight: 22 20 | } 21 | 22 | // Or using a function 23 | options = { 24 | useVirtualScroll: true, 25 | nodeHeight: (node: TreeNode) => node.myHeight, 26 | dropSlotHeight: 3 27 | }`; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/large-tree-guide/large-tree/large-tree.component.html: -------------------------------------------------------------------------------- 1 |
2 | 8 | 9 |
10 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/large-tree-guide/large-tree/large-tree.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/large-tree-guide/large-tree/large-tree.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/large-tree-guide/large-tree/large-tree.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LargeTreeComponent } from './large-tree.component'; 4 | 5 | describe('LargeTreeComponent', () => { 6 | let component: LargeTreeComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ LargeTreeComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LargeTreeComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/large-tree-guide/large-tree/large-tree.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ITreeOptions } from 'angular-tree-component'; 3 | 4 | @Component({ 5 | selector: 'app-large-tree', 6 | templateUrl: './large-tree.component.html', 7 | styleUrls: ['./large-tree.component.scss'] 8 | }) 9 | export class LargeTreeComponent { 10 | nodes: any[]; 11 | 12 | options: ITreeOptions = { 13 | nodeHeight: 23, 14 | useVirtualScroll: true 15 | }; 16 | 17 | constructor() { 18 | this.nodes = new Array(1000).fill(null).map((item, i) => ({ 19 | id: `${i}`, 20 | name: `rootDynamic${i}`, 21 | children: new Array(100).fill(null).map((item, n) => ({ 22 | id: `${i}.${n}`, 23 | name: `rootChildDynamic${i}.${n}` 24 | })) 25 | })); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/redux-guide/redux-guide.component.html: -------------------------------------------------------------------------------- 1 |

Redux / Immutable Data

2 | 3 |

Use ID

4 | 5 |

Working with the tree using immutable data is possible.

6 |

Make sure that:

7 |
    8 |
  1. You provide a unique id property on each node
  2. 9 |
  3. If you have a different key property, then set the idField in the options
  4. 10 |
  5. You override drop action as stated below
  6. 11 |
12 | 13 |

Override drop action

14 |

Drag and drop by default mutates the children.

15 |

If working with immutable data, you must override the action and supply your custom behaviour:

16 | {{ options }} 17 | 18 |

Rebuilding the tree

19 |

Every time the nodes array changes, the entire tree model is rebuilt.

20 |

This might be costly if you have a huge amount of nodes that change very frequently.

21 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/redux-guide/redux-guide.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/redux-guide/redux-guide.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/redux-guide/redux-guide.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ReduxGuideComponent } from './redux-guide.component'; 4 | 5 | describe('ReduxGuideComponent', () => { 6 | let component: ReduxGuideComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ ReduxGuideComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ReduxGuideComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/redux-guide/redux-guide.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-redux-guide', 5 | templateUrl: './redux-guide.component.html', 6 | styleUrls: ['./redux-guide.component.scss'] 7 | }) 8 | export class ReduxGuideComponent { 9 | options = ` 10 | options = { 11 | actionMapping: { 12 | mouse: { 13 | drop: (tree: TreeModel, node: TreeNode, $event: any, {from , to}: {from: any, to: any}) => { 14 | // custom action. parameters: from = node, to = {parent, index} 15 | } 16 | } 17 | } 18 | }`; 19 | } 20 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/rtl-guide/rtl-guide.component.html: -------------------------------------------------------------------------------- 1 |

RTL

2 |

You can make the tree right to left easily by supplying an 'rtl' boolean option.

3 | 4 |

Demo

5 |

Source Code

6 |
7 | 8 |
9 | 10 | {{ html }} 11 | {{ javascript }} 12 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/rtl-guide/rtl-guide.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/rtl-guide/rtl-guide.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/rtl-guide/rtl-guide.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { RtlGuideComponent } from './rtl-guide.component'; 4 | 5 | describe('RtlGuideComponent', () => { 6 | let component: RtlGuideComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ RtlGuideComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(RtlGuideComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/rtl-guide/rtl-guide.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-rtl-guide', 5 | templateUrl: './rtl-guide.component.html', 6 | styleUrls: ['./rtl-guide.component.scss'] 7 | }) 8 | export class RtlGuideComponent { 9 | html = ``; 10 | javascript = ` 11 | options = { 12 | rtl: true 13 | }; 14 | `; 15 | } 16 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/rtl-guide/rtl/rtl.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/rtl-guide/rtl/rtl.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/rtl-guide/rtl/rtl.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/rtl-guide/rtl/rtl.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { RtlComponent } from './rtl.component'; 4 | 5 | describe('RtlComponent', () => { 6 | let component: RtlComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ RtlComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(RtlComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/rtl-guide/rtl/rtl.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-rtl', 5 | templateUrl: './rtl.component.html', 6 | styleUrls: ['./rtl.component.scss'] 7 | }) 8 | export class RtlComponent { 9 | 10 | options = { 11 | rtl: true 12 | }; 13 | 14 | nodes = [ 15 | { 16 | name: 'עץ תיקיות', 17 | children: [ 18 | { name: 'קובץ 1' }, 19 | { name: 'קובץ 2' } 20 | ] 21 | }, 22 | { 23 | name: 'עוד עץ', 24 | children: [ 25 | { name: 'עלה', children: [] }, 26 | { name: 'ענף', children: [ 27 | {name: 'בן של ענף'} 28 | ] } 29 | ] 30 | } 31 | ]; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/update-guide/update-guide.component.html: -------------------------------------------------------------------------------- 1 |

Updating the tree

2 | 3 |

Changing node attributes

4 |

Changing any attributes on the node itself will be reflected immediately, since change detection will force the tree node components to re-render.

5 | 6 |

Adding / Removing nodes

7 |

After adding or removing nodes from the tree, one must call the update method on the treeModel for it to take affect.

8 |

For example:

9 | {{ adding }} 10 |

This is due to the fact that the treeModel builds its own model around the nodes data, to calculate node levels, paths, parent links etc. So it must be informed of any change to the nodes' structure.

11 |

Calling update will have no effect on the tree status, i.e. current selected node, current focused node, current expanded nodes, etc.

12 | 13 |

Working with immutable data

14 |

15 | Changing the reference to nodes will also trigger an update automatically. 16 | So if you work for example with immutable data and replace the nodes array instead of mutating it - there is no need to call any method. 17 |

18 | {{ immutable }} 19 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/update-guide/update-guide.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/app/guides/update-guide/update-guide.component.scss -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/update-guide/update-guide.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UpdateGuideComponent } from './update-guide.component'; 4 | 5 | describe('UpdateGuideComponent', () => { 6 | let component: UpdateGuideComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ UpdateGuideComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(UpdateGuideComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/guides/update-guide/update-guide.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-update-guide', 5 | templateUrl: './update-guide.component.html', 6 | styleUrls: ['./update-guide.component.scss'] 7 | }) 8 | export class UpdateGuideComponent { 9 | 10 | adding = 11 | ` 12 | 13 | 14 | class MyComponent { 15 | nodes = [{ name: 'node' }]; 16 | 17 | @ViewChild(TreeComponent) 18 | private tree: TreeComponent; 19 | 20 | addNode() { 21 | this.nodes.push({ name: 'another node' }); 22 | this.tree.treeModel.update(); 23 | } 24 | } 25 | `; 26 | 27 | immutable = 28 | ` 29 | 30 | 31 | nodes = [...] 32 | 33 | addNode(newNode) { 34 | // Just add node and replace nodes variable: 35 | this.nodes = [...this.nodes, newNode]; 36 | } 37 | `; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/layout/footer/footer.component.html: -------------------------------------------------------------------------------- 1 | 12 | 13 |

Powered by the Community. Code licensed under an MIT-style License.

14 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/layout/footer/footer.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | justify-content: center; 6 | margin: 0 0 40px; 7 | 8 | >* { 9 | color: #fff; 10 | } 11 | } 12 | 13 | .footer-blocks { 14 | display: flex; 15 | justify-content: center; 16 | text-align: left; 17 | margin: 0 0 40px; 18 | } 19 | 20 | .footer-block { 21 | margin: 0 24px; 22 | vertical-align: top; 23 | } 24 | 25 | a { 26 | color: #fff; 27 | } 28 | 29 | h3 { 30 | color: #fff; 31 | text-transform: uppercase; 32 | } 33 | 34 | ul { 35 | margin: 0; 36 | padding: 0; 37 | 38 | li { 39 | list-style-type: none; 40 | padding: 4px 0; 41 | text-align: left; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/layout/footer/footer.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { FooterComponent } from './footer.component'; 4 | 5 | describe('FooterComponent', () => { 6 | let component: FooterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FooterComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FooterComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/layout/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-footer', 5 | templateUrl: './footer.component.html', 6 | styleUrls: ['./footer.component.scss'] 7 | }) 8 | export class FooterComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/layout/layout.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FooterComponent } from './footer/footer.component'; 4 | import { NavItemComponent } from './nav-item/nav-item.component'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { RouterModule } from '@angular/router'; 7 | 8 | @NgModule({ 9 | declarations: [FooterComponent, NavItemComponent], 10 | exports: [ 11 | FooterComponent, 12 | NavItemComponent 13 | ], 14 | imports: [ 15 | CommonModule, 16 | MatIconModule, 17 | RouterModule 18 | ] 19 | }) 20 | export class LayoutModule { } 21 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/layout/nav-item/nav-item.component.html: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 | 11 | {{node.title}} 12 | chevron_right 13 | 14 | 15 | 21 | 22 |
23 | 26 |
27 |
28 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/layout/nav-item/nav-item.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { NavItemComponent } from './nav-item.component'; 4 | 5 | describe('NavItemComponent', () => { 6 | let component: NavItemComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NavItemComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NavItemComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/navigation/navigation.model.ts: -------------------------------------------------------------------------------- 1 | export interface NavigationNode { 2 | title: string; 3 | url?: string; 4 | link?: string[]; 5 | hidden?: boolean; 6 | tooltip?: string; 7 | children?: NavigationNode[]; 8 | } 9 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/navigation/navigation.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { NavigationService } from './navigation.service'; 4 | 5 | describe('NavigationService', () => { 6 | let service: NavigationService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(NavigationService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/navigation/navigation.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { NavigationNode } from './navigation.model'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class NavigationService { 8 | 9 | private gettingStartedRoute: NavigationNode = { 10 | title: 'Getting Started', 11 | url: '' 12 | }; 13 | 14 | private examplesRoute: NavigationNode = { 15 | title: 'Examples', 16 | children: [ 17 | { title: 'Basic Usage', url: 'basic' } 18 | ] 19 | }; 20 | 21 | constructor() { } 22 | } 23 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/shared/logger.service.ts: -------------------------------------------------------------------------------- 1 | import { ErrorHandler, Injectable } from '@angular/core'; 2 | import { environment } from '../../environments/environment'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class Logger { 8 | 9 | constructor(private errorHandler: ErrorHandler) {} 10 | 11 | log(value: any, ...rest: any[]) { 12 | if (!environment.production) { 13 | console.log(value, ...rest); 14 | } 15 | } 16 | 17 | error(error: Error) { 18 | this.errorHandler.handleError(error); 19 | } 20 | 21 | warn(value: any, ...rest: any[]) { 22 | console.warn(value, ...rest); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /projects/docs-app/src/app/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | 5 | 6 | @NgModule({ 7 | declarations: [], 8 | imports: [ 9 | CommonModule 10 | ] 11 | }) 12 | export class SharedModule { } 13 | -------------------------------------------------------------------------------- /projects/docs-app/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/assets/.gitkeep -------------------------------------------------------------------------------- /projects/docs-app/src/assets/github-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 14 | 15 | -------------------------------------------------------------------------------- /projects/docs-app/src/assets/styling/0bd50e1-treecss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/assets/styling/0bd50e1-treecss.png -------------------------------------------------------------------------------- /projects/docs-app/src/assets/styling/28d7625-Screen_Shot_2016-11-29_at_12.11.01_PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/assets/styling/28d7625-Screen_Shot_2016-11-29_at_12.11.01_PM.png -------------------------------------------------------------------------------- /projects/docs-app/src/assets/styling/f50955b-Screen_Shot_2016-11-29_at_12.11.52_PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/assets/styling/f50955b-Screen_Shot_2016-11-29_at_12.11.52_PM.png -------------------------------------------------------------------------------- /projects/docs-app/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /projects/docs-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/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /projects/docs-app/src/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/favicon-16x16.png -------------------------------------------------------------------------------- /projects/docs-app/src/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/favicon-32x32.png -------------------------------------------------------------------------------- /projects/docs-app/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/docs-app/src/favicon.ico -------------------------------------------------------------------------------- /projects/docs-app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular Tree Component 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /projects/docs-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() 12 | .bootstrapModule(AppModule) 13 | .catch(err => console.error(err)); 14 | -------------------------------------------------------------------------------- /projects/docs-app/src/styles.scss: -------------------------------------------------------------------------------- 1 | @import "styles/constants"; 2 | @import "styles/mixins"; 3 | @import "styles/typography"; 4 | @import "styles/code"; 5 | @import "styles/scrollbar"; 6 | @import "styles/alert"; 7 | @import "styles/images"; 8 | @import "~@angular/material/prebuilt-themes/indigo-pink.css"; 9 | @import "styles/angular-tree-component.css"; 10 | 11 | // primary: #355677 12 | 13 | html, body { height: 100%; } 14 | body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } 15 | 16 | .demo-container { 17 | background-color: #fff; 18 | border: 0.5px solid #DBDBDB; 19 | border-radius: 5px; 20 | color: #444444; 21 | margin: 16px auto; 22 | padding: 16px; 23 | } 24 | -------------------------------------------------------------------------------- /projects/docs-app/src/styles/_alert.scss: -------------------------------------------------------------------------------- 1 | .alert { 2 | padding: 16px; 3 | margin: 24px 0px; 4 | @include font-size(14); 5 | color: $darkgray; 6 | width: 100%; 7 | box-sizing: border-box; 8 | clear: both; 9 | 10 | h1, h2, h3, h4, h5, h6 { 11 | font-weight: 500; 12 | } 13 | 14 | &.is-critical { 15 | border-left: 8px solid $brightred; 16 | background-color: rgba($brightred, 0.05); 17 | 18 | h1, h2, h3, h4, h5, h6 { 19 | color: $brightred; 20 | } 21 | } 22 | 23 | &.is-important { 24 | border-left: 8px solid $orange; 25 | background-color: rgba($orange, 0.05); 26 | 27 | h1, h2, h3, h4, h5, h6 { 28 | color: $orange; 29 | } 30 | } 31 | 32 | &.is-helpful { 33 | border-left: 8px solid $blue; 34 | background-color: rgba($blue, 0.05); 35 | 36 | h1, h2, h3, h4, h5, h6 { 37 | color: $blue; 38 | } 39 | } 40 | 41 | &.archive-warning { 42 | background-color: $darkred; 43 | border-radius: 4px; 44 | margin-bottom: 1rem; 45 | 46 | * { 47 | color: $white; 48 | } 49 | 50 | a { 51 | color: $white; 52 | font-weight: bold; 53 | text-decoration: underline; 54 | 55 | &:hover { 56 | opacity: 0.9; 57 | } 58 | } 59 | } 60 | 61 | > * { 62 | margin: 8px 16px; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /projects/docs-app/src/styles/_images.scss: -------------------------------------------------------------------------------- 1 | .lightbox { 2 | margin: 0; 3 | margin-top: 14px; 4 | margin-bottom: 14px; 5 | border-radius: 1px; 6 | background: $white; 7 | border: 1px solid $lightgray; 8 | padding: 32px; 9 | box-sizing: border-box; 10 | display: flex; 11 | justify-content: center; 12 | box-shadow: 2px 2px 5px 0 rgba(0, 0, 0, .2); 13 | margin: 16px 0; 14 | background-color: $lightboxgray; 15 | width: 100%; 16 | display: flex; 17 | justify-content: center; 18 | 19 | img { 20 | max-width: 100%; 21 | height: auto; 22 | padding: 8px; 23 | margin: auto; 24 | box-shadow: 0 2px 2px rgba(10, 16, 20, 0.24), 0 0 2px rgba(10, 16, 20, 0.12); 25 | border-radius: 4px; 26 | background-color: $white; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /projects/docs-app/src/styles/_scrollbar.scss: -------------------------------------------------------------------------------- 1 | body::-webkit-scrollbar, mat-sidenav.sidenav::-webkit-scrollbar, .mat-sidenav-content::-webkit-scrollbar { 2 | height: 6px; 3 | width: 6px; 4 | } 5 | 6 | body::-webkit-scrollbar-track, mat-sidenav.sidenav::-webkit-scrollbar-track, .mat-sidenav-content::-webkit-scrollbar-track { 7 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); 8 | } 9 | 10 | body::-webkit-scrollbar-thumb, mat-sidenav.sidenav::-webkit-scrollbar-thumb, .mat-sidenav-content::-webkit-scrollbar-thumb { 11 | background-color: $mediumgray; 12 | outline: 1px solid $darkgray; 13 | } 14 | 15 | .search-results::-webkit-scrollbar, .toc-container::-webkit-scrollbar { 16 | height: 4px; 17 | width: 4px; 18 | } 19 | 20 | .search-results::-webkit-scrollbar-track, .toc-container::-webkit-scrollbar-track { 21 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); 22 | } 23 | 24 | .search-results::-webkit-scrollbar-thumb, .toc-container::-webkit-scrollbar-thumb { 25 | background-color: $mediumgray; 26 | outline: 1px solid slategrey; 27 | } 28 | -------------------------------------------------------------------------------- /projects/docs-app/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: { 11 | context( 12 | path: string, 13 | deep?: boolean, 14 | filter?: RegExp 15 | ): { 16 | keys(): string[]; 17 | (id: string): T; 18 | }; 19 | }; 20 | 21 | // First, initialize the Angular testing environment. 22 | getTestBed().initTestEnvironment( 23 | BrowserDynamicTestingModule, 24 | platformBrowserDynamicTesting() 25 | ); 26 | // Then we find all the tests. 27 | const context = require.context('./', true, /\.spec\.ts$/); 28 | // And load the modules. 29 | context.keys().map(context); 30 | -------------------------------------------------------------------------------- /projects/docs-app/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": ["src/main.ts", "src/polyfills.ts"], 9 | "include": ["src/**/*.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /projects/docs-app/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/spec", 6 | "types": ["jasmine"] 7 | }, 8 | "files": ["src/test.ts", "src/polyfills.ts"], 9 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /projects/docs-app/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [true, "attribute", "app", "camelCase"], 5 | "component-selector": [true, "element", "app", "kebab-case"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /projects/example-app/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed 5 | > 0.5% 6 | last 2 versions 7 | Firefox ESR 8 | not dead 9 | # IE 9-11 -------------------------------------------------------------------------------- /projects/example-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /tmp 5 | /out-tsc 6 | 7 | # dependencies 8 | /node_modules 9 | 10 | # IDEs and editors 11 | /.idea 12 | .project 13 | .classpath 14 | .c9/ 15 | *.launch 16 | .settings/ 17 | *.sublime-workspace 18 | 19 | # IDE - VSCode 20 | .vscode/* 21 | !.vscode/settings.json 22 | !.vscode/tasks.json 23 | !.vscode/launch.json 24 | !.vscode/extensions.json 25 | 26 | # misc 27 | /.sass-cache 28 | /connect.lock 29 | /coverage 30 | /libpeerconnection.log 31 | npm-debug.log 32 | yarn-error.log 33 | testem.log 34 | /typings 35 | 36 | # System Files 37 | .DS_Store 38 | Thumbs.db 39 | 500tech 40 | -------------------------------------------------------------------------------- /projects/example-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 | const { join } = require('path'); 5 | const getBaseKarmaConfig = require('../../karma.conf'); 6 | 7 | module.exports = function(config) { 8 | const baseConfig = getBaseKarmaConfig(); 9 | 10 | config.set({ 11 | ...baseConfig, 12 | coverageIstanbulReporter: { 13 | ...baseConfig.coverageIstanbulReporter, 14 | dir: join(__dirname, '../../coverage/example-app') 15 | } 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /projects/example-app/src/app/actions/actions.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/example-app/src/app/actions/actions.component.css -------------------------------------------------------------------------------- /projects/example-app/src/app/actions/actions.component.html: -------------------------------------------------------------------------------- 1 |

2 | actions works! 3 |

4 | -------------------------------------------------------------------------------- /projects/example-app/src/app/actions/actions.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { ActionsComponent } from './actions.component'; 4 | 5 | describe('ActionsComponent', () => { 6 | let component: ActionsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ActionsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ActionsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/example-app/src/app/api/api.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/example-app/src/app/api/api.component.css -------------------------------------------------------------------------------- /projects/example-app/src/app/api/api.component.html: -------------------------------------------------------------------------------- 1 |

2 | api works! 3 |

4 | -------------------------------------------------------------------------------- /projects/example-app/src/app/api/api.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { ApiComponent } from './api.component'; 4 | 5 | describe('ApiComponent', () => { 6 | let component: ApiComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ApiComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ApiComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/example-app/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | template: ` 6 | 7 | ` 8 | }) 9 | export class AppComponent { 10 | } 11 | -------------------------------------------------------------------------------- /projects/example-app/src/app/async/async.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ITreeOptions, TreeNode} from 'angular-tree-component'; 3 | 4 | @Component({ 5 | selector: 'app-async', 6 | template: ` 7 | 8 | `, 9 | styles: [] 10 | }) 11 | export class AsyncTreeComponent { 12 | options: ITreeOptions = { 13 | getChildren: this.getChildren.bind(this), 14 | useCheckbox: true 15 | }; 16 | 17 | nodes: any[] = []; 18 | 19 | asyncChildren = [ 20 | { 21 | name: 'child1', 22 | hasChildren: true 23 | }, { 24 | name: 'child2' 25 | } 26 | ]; 27 | 28 | constructor() { 29 | this.nodes = [ 30 | { 31 | name: 'root1', 32 | children: [ 33 | { name: 'child1' } 34 | ] 35 | }, 36 | { 37 | name: 'root2', 38 | hasChildren: true 39 | }, 40 | { 41 | name: 'root3' 42 | } 43 | ]; 44 | } 45 | 46 | getChildren(node: any) { 47 | const newNodes = this.asyncChildren.map((c) => Object.assign({}, c)); 48 | 49 | return new Promise((resolve, reject) => { 50 | setTimeout(() => resolve(newNodes), 1000); 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /projects/example-app/src/app/basictree/basictree.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-basictree', 5 | template: ` 6 | 7 |
8 |

Keys:

9 | down | up | left | right | space | enter 10 | `, 11 | styles: [] 12 | }) 13 | export class BasicTreeComponent { 14 | nodes = [ 15 | { 16 | name: 'root1', 17 | children: [ 18 | { name: 'child1' }, 19 | { name: 'child2' } 20 | ] 21 | }, 22 | { 23 | name: 'root2', 24 | children: [ 25 | { name: 'child2.1', children: [] }, 26 | { name: 'child2.2', children: [ 27 | {name: 'grandchild2.2.1'} 28 | ] } 29 | ] 30 | }, 31 | { name: 'root3' }, 32 | { name: 'root4', children: [] }, 33 | { name: 'root5', children: null } 34 | ]; 35 | } 36 | -------------------------------------------------------------------------------- /projects/example-app/src/app/checkboxes/checkboxes.component.ts: -------------------------------------------------------------------------------- 1 | import { ITreeOptions } from 'angular-tree-component'; 2 | import { Component } from '@angular/core'; 3 | 4 | @Component({ 5 | selector: 'app-checkboxes', 6 | template: ` 7 |

tri-state checkboxes

8 | 12 | 13 |

The tree is using flexbox.
14 | Switch expander and checkbox with CSS 'order' attribute:

15 | 20 | 21 |

Disable tri-state checkboxes

22 | 26 | 27 | `, 28 | styles: [ 29 | ] 30 | }) 31 | export class CheckboxesComponent { 32 | nodes = [ 33 | { 34 | name: 'root1', 35 | }, 36 | { 37 | name: 'root2', 38 | children: [ 39 | { name: 'child1' }, 40 | { name: 'child2', children: [ 41 | { name: 'grandchild1' }, 42 | { name: 'grandchild2' } 43 | ] } 44 | ] 45 | }, 46 | { 47 | name: 'asyncroot', 48 | hasChildren: true 49 | } 50 | ]; 51 | 52 | options: ITreeOptions = { 53 | useCheckbox: true, 54 | getChildren: this.getChildren.bind(this) 55 | }; 56 | 57 | optionsDisabled: ITreeOptions = { 58 | useCheckbox: true, 59 | getChildren: this.getChildren.bind(this), 60 | useTriState: false 61 | }; 62 | 63 | getChildren(node: any) { 64 | const newNodes = [ 65 | { 66 | name: 'child1' 67 | }, { 68 | name: 'child2' 69 | } 70 | ]; 71 | 72 | return new Promise((resolve, reject) => { 73 | setTimeout(() => resolve(newNodes), 1000); 74 | }); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /projects/example-app/src/app/contextmenu/contextmenu.component.html: -------------------------------------------------------------------------------- 1 |

2 | contextmenu works! 3 |

4 | -------------------------------------------------------------------------------- /projects/example-app/src/app/contextmenu/contextmenu.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { ContextmenuComponent } from './contextmenu.component'; 4 | 5 | describe('ContextmenuComponent', () => { 6 | let component: ContextmenuComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ContextmenuComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ContextmenuComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/example-app/src/app/drag/drag.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ITreeState, ITreeOptions } from 'angular-tree-component'; 3 | import { v4 } from 'uuid'; 4 | 5 | @Component({ 6 | selector: 'app-drag', 7 | template: ` 8 |

Allowing to drag only leaf nodes; ctrl-drag to copy

9 | 10 | `, 11 | styles: [] 12 | }) 13 | export class DragComponent { 14 | state: ITreeState = { 15 | expandedNodeIds: { 16 | 1: true, 17 | 2: true 18 | }, 19 | hiddenNodeIds: {}, 20 | activeNodeIds: {} 21 | }; 22 | 23 | options: ITreeOptions = { 24 | allowDrag: (node) => node.isLeaf, 25 | getNodeClone: (node) => ({ 26 | ...node.data, 27 | id: v4(), 28 | name: `copy of ${node.data.name}` 29 | }) 30 | }; 31 | 32 | nodes = [ 33 | { 34 | id: 1, 35 | name: 'root1', 36 | children: [ 37 | { name: 'child1' }, 38 | { name: 'child2' } 39 | ] 40 | }, 41 | { 42 | name: 'root2', 43 | id: 2, 44 | children: [ 45 | { name: 'child2.1', children: [] }, 46 | { name: 'child2.2', children: [ 47 | {name: 'grandchild2.2.1'} 48 | ] } 49 | ] 50 | }, 51 | { name: 'root3' }, 52 | { name: 'root4', children: [] }, 53 | { name: 'root5', children: null } 54 | ]; 55 | } 56 | -------------------------------------------------------------------------------- /projects/example-app/src/app/dragover-styling/dragover-styling.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ITreeState, ITreeOptions } from 'angular-tree-component'; 3 | import { v4 } from 'uuid'; 4 | 5 | @Component({ 6 | selector: 'app-dragover-styling', 7 | template: ` 8 |

Disabled styling of nodes on dragover

9 | 15 | `, 16 | styles: [] 17 | }) 18 | export class DragOverStylingComponent { 19 | state: ITreeState = { 20 | expandedNodeIds: { 21 | 1: true, 22 | 2: true 23 | }, 24 | hiddenNodeIds: {}, 25 | activeNodeIds: {} 26 | }; 27 | 28 | options: ITreeOptions = { 29 | allowDrag: node => true, 30 | allowDragoverStyling: false, 31 | getNodeClone: node => ({ 32 | ...node.data, 33 | id: v4(), 34 | name: `copy of ${node.data.name}` 35 | }) 36 | }; 37 | 38 | nodes = [ 39 | { 40 | id: 1, 41 | name: 'root1', 42 | children: [{ name: 'child1' }, { name: 'child2' }] 43 | }, 44 | { 45 | name: 'root2', 46 | id: 2, 47 | children: [ 48 | { name: 'child2.1', children: [] }, 49 | { name: 'child2.2', children: [{ name: 'grandchild2.2.1' }] } 50 | ] 51 | }, 52 | { name: 'root3' }, 53 | { name: 'root4', children: [] }, 54 | { name: 'root5', children: null } 55 | ]; 56 | } 57 | -------------------------------------------------------------------------------- /projects/example-app/src/app/empty/empty.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-empty', 5 | template: ` 6 | 7 | 8 | 9 | `, 10 | styles: [] 11 | }) 12 | export class EmptyComponent { 13 | nodes: any[] = []; 14 | nodes2: any[] | null = null; 15 | 16 | loadNodes() { 17 | this.nodes = [{ name: 'node' }]; 18 | this.nodes2 = [{ name: 'node' }]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /projects/example-app/src/app/fields/fields.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ITreeOptions } from 'angular-tree-component'; 3 | 4 | @Component({ 5 | selector: 'app-fields', 6 | template: ` 7 |

Overriding displayField & nodeClass

8 | 9 | `, 10 | styles: [ 11 | ] 12 | }) 13 | export class FieldsComponent { 14 | nodes = [ 15 | { 16 | _id: '1', 17 | title: 'root1', 18 | className: 'root1Class', 19 | nodes: [{_id: '3', title: 'child1', className: 'root1Class'}] 20 | }, 21 | { 22 | _id: '2', 23 | title: 'root2', 24 | className: 'root2Class' 25 | } 26 | ]; 27 | 28 | options: ITreeOptions = { 29 | displayField: 'title', 30 | idField: '_id', 31 | childrenField: 'nodes', 32 | nodeClass: (node) => node.data.className 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /projects/example-app/src/app/rtl/rtl-tree.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-basictree', 5 | template: ` 6 | 7 | `, 8 | encapsulation: ViewEncapsulation.None, 9 | styles: [] 10 | }) 11 | export class RtlTreeComponent { 12 | options = { 13 | rtl: true 14 | }; 15 | 16 | nodes = [ 17 | { 18 | name: 'עץ תיקיות', 19 | children: [ 20 | { name: 'קובץ 1' }, 21 | { name: 'קובץ 2' } 22 | ] 23 | }, 24 | { 25 | name: 'עוד עץ', 26 | children: [ 27 | { name: 'עלה', children: [] }, 28 | { name: 'ענף', children: [ 29 | {name: 'בן של ענף'} 30 | ] } 31 | ] 32 | } 33 | ]; 34 | } 35 | -------------------------------------------------------------------------------- /projects/example-app/src/app/save-restore/save-restore.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ITreeState } from 'angular-tree-component'; 3 | 4 | const getChildren = () => new Promise((resolve) => { 5 | setTimeout(() => resolve([ 6 | { id: 5, name: 'child2.1', children: [] }, 7 | { id: 6, name: 'child2.2', children: [ 8 | { id: 7, name: 'grandchild2.2.1' } 9 | ] } 10 | ]), 2000); 11 | }); 12 | 13 | @Component({ 14 | selector: 'app-saverestore', 15 | template: ` 16 | 17 | 18 | `, 19 | styles: [] 20 | }) 21 | export class SaveRestoreComponent { 22 | get state(): ITreeState { 23 | return localStorage.treeState && JSON.parse(localStorage.treeState); 24 | } 25 | set state(state: ITreeState) { 26 | localStorage.treeState = JSON.stringify(state); 27 | } 28 | 29 | options = { 30 | getChildren 31 | }; 32 | 33 | nodes = [ 34 | { 35 | id: 1, 36 | name: 'root1', 37 | children: [ 38 | { id: 2, name: 'child1' }, 39 | { id: 3, name: 'child2' } 40 | ] 41 | }, 42 | { 43 | id: 4, 44 | name: 'root2', 45 | hasChildren: true 46 | } 47 | ]; 48 | 49 | } 50 | -------------------------------------------------------------------------------- /projects/example-app/src/app/scrollcontainer/scrollcontainer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit } from '@angular/core'; 2 | import { TreeNode, TreeModel, TREE_ACTIONS, KEYS, IActionMapping, ITreeOptions } from 'angular-tree-component'; 3 | 4 | @Component({ 5 | selector: 'app-scrollcontainer', 6 | styles: [ 7 | ], 8 | template: ` 9 |
Padding
10 |
11 | 17 |
18 | ` 19 | }) 20 | export class ScrollContainerComponent implements OnInit { 21 | nodes: any[] = []; 22 | options: ITreeOptions = { 23 | scrollContainer: document.body.parentElement 24 | }; 25 | constructor() { 26 | } 27 | ngOnInit() { 28 | for (let i = 0; i < 200; i++) { 29 | this.nodes.push({ 30 | name: `rootDynamic${i}`, 31 | subTitle: `root created dynamically ${i}` 32 | }); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /projects/example-app/src/app/virtualscroll/virtualscroll.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/example-app/src/app/virtualscroll/virtualscroll.component.css -------------------------------------------------------------------------------- /projects/example-app/src/app/virtualscroll/virtualscroll.component.html: -------------------------------------------------------------------------------- 1 |

2 | virtualscroll works! 3 |

4 | -------------------------------------------------------------------------------- /projects/example-app/src/app/virtualscroll/virtualscroll.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { VirtualscrollComponent } from './virtualscroll.component'; 4 | 5 | describe('VirtualscrollComponent', () => { 6 | let component: VirtualscrollComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ VirtualscrollComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(VirtualscrollComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/example-app/src/app/virtualscroll/virtualscroll.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { TreeNode, TreeModel, ITreeOptions } from 'angular-tree-component'; 3 | 4 | @Component({ 5 | selector: 'app-virtualscroll', 6 | styles: [ 7 | ], 8 | template: ` 9 |
10 | 11 | 17 | 18 |
19 | ` 20 | }) 21 | export class VirtualscrollComponent { 22 | nodes: any[]; 23 | 24 | options: ITreeOptions = { 25 | nodeHeight: 23, 26 | useVirtualScroll: true 27 | }; 28 | 29 | constructor() { 30 | this.nodes = new Array(1000).fill(null).map((item, i) => ({ 31 | id: `${i}`, 32 | name: `rootDynamic${i}`, 33 | children: new Array(100).fill(null).map((item, n) => ({ 34 | id: `${i}.${n}`, 35 | name: `rootChildDynamic${i}.${n}` 36 | })) 37 | })); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /projects/example-app/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/example-app/src/assets/.gitkeep -------------------------------------------------------------------------------- /projects/example-app/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /projects/example-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 | * In development mode, to ignore zone related error stack frames such as 11 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can 12 | * import the following file, but please comment it out in production mode 13 | * because it will have performance impact when throw error 14 | */ 15 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 16 | -------------------------------------------------------------------------------- /projects/example-app/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CirclonGroup/angular-tree-component/19b58abe33dcbddbcba80c80ed26babfb7d43a16/projects/example-app/src/favicon.ico -------------------------------------------------------------------------------- /projects/example-app/src/index.build.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cli 6 | 7 | 8 | 9 | 10 | 11 | 12 | Loading... 13 | 14 | 15 | -------------------------------------------------------------------------------- /projects/example-app/src/index.dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cli 6 | 7 | 8 | 9 | 10 | 11 | 12 | Loading... 13 | 14 | 15 | -------------------------------------------------------------------------------- /projects/example-app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cli 6 | 7 | 8 | 9 | 10 | 11 | 12 | Loading... 13 | 14 | 15 | -------------------------------------------------------------------------------- /projects/example-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.log(err)); 13 | -------------------------------------------------------------------------------- /projects/example-app/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | // TODO: link to dist version 3 | @import 'angular-tree-component.css'; 4 | .reverse tree-node-expander { 5 | order: 0; 6 | } 7 | .reverse tree-node-checkbox { 8 | order: 1; 9 | } 10 | .reverse .node-content-wrapper { 11 | order: 2; 12 | } 13 | .root1Class { color: blue } 14 | .root2Class { color: red } 15 | -------------------------------------------------------------------------------- /projects/example-app/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /projects/example-app/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/app", 5 | "module": "es2015", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.ts" 14 | ], 15 | "exclude": [ 16 | "src/test.ts", 17 | "**/*.spec.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /projects/example-app/tsconfig.es5.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/app", 5 | "target": "es5", 6 | "types": [] 7 | }, 8 | "files": ["src/main.ts", "src/polyfills.ts"], 9 | "include": ["src/**/*.ts"], 10 | "exclude": ["src/test.ts", "**/*.spec.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /projects/example-app/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "module": "commonjs", 6 | "types": [ 7 | "jasmine", 8 | "node" 9 | ] 10 | }, 11 | "files": [ 12 | "test.ts", 13 | "polyfills.ts" 14 | ], 15 | "include": [ 16 | "src/**/*.spec.ts", 17 | "src/**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /projects/example-app/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | const Jasmine2HtmlReporter = require('protractor-jasmine2-html-reporter'); 2 | 3 | exports.config = { 4 | framework: 'jasmine', 5 | seleniumAddress: 'http://localhost:4444/wd/hub', 6 | specs: ['e2e/*.spec.js'], 7 | capabilities: { 8 | browserName: 'chrome' 9 | }, 10 | onPrepare: function() { 11 | jasmine.getEnv().addReporter( 12 | new Jasmine2HtmlReporter({ 13 | savePath: 'e2eResults' 14 | }) 15 | ); 16 | }, 17 | useAllAngular2AppRoots: true 18 | } -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import nodeResolve from 'rollup-plugin-node-resolve'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import autoExternal from 'rollup-plugin-auto-external'; 4 | import uglify from 'rollup-plugin-uglify'; 5 | 6 | export default { 7 | format: 'umd', 8 | moduleName: 'angular-tree-component', 9 | plugins: [ 10 | nodeResolve({ jsnext: true, main: true, module: true }), 11 | commonjs(), 12 | autoExternal(), 13 | uglify() 14 | ], 15 | sourceMap: true, 16 | external: [ 17 | '@angular/core', 18 | '@angular/common' 19 | ], 20 | onwarn: function (warning) { 21 | const skip_codes = [ 22 | 'THIS_IS_UNDEFINED', 23 | 'MISSING_GLOBAL_NAME' 24 | ]; 25 | if ( skip_codes.indexOf(warning.code) != -1 ) return; 26 | console.error(warning); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "module": "es2020", 5 | "moduleResolution": "node", 6 | "target": "es2015", 7 | "outDir": "/dist/out-tsc", 8 | "allowSyntheticDefaultImports": true, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "inlineSourceMap": true, 12 | "inlineSources": true, 13 | "declaration": true, 14 | "skipLibCheck": true, 15 | "stripInternal": true, 16 | "typeRoots": ["node_modules/@types"], 17 | "lib": [ 18 | "es2017", 19 | "dom" 20 | ], 21 | "paths": { 22 | "angular-tree-component": [ 23 | "dist/angular-tree-component", 24 | "dist/angular-tree-component" 25 | ], 26 | "angular-tree-component/*": [ 27 | "dist/angular-tree-component/*", 28 | "dist/angular-tree-component/*" 29 | ] 30 | } 31 | }, 32 | "compileOnSave": false, 33 | "buildOnSave": false, 34 | "angularCompilerOptions": { 35 | "skipTemplateCodegen": true, 36 | "enableIvy": false 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode": "modules", 3 | "name": "angular2-tree-component", 4 | "out": "doc", 5 | "theme": "default", 6 | "ignoreCompilerErrors": "true", 7 | "experimentalDecorators": "true", 8 | "emitDecoratorMetadata": "true", 9 | "target": "ES5", 10 | "moduleResolution": "node", 11 | "preserveConstEnums": "true", 12 | "stripInternal": "true", 13 | "suppressExcessPropertyErrors": "true", 14 | "suppressImplicitAnyIndexErrors": "true", 15 | "module": "commonjs" 16 | } 17 | --------------------------------------------------------------------------------