├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── .husky ├── .gitignore ├── commit-msg └── pre-commit ├── .prettierrc.js ├── LICENSE ├── README.md ├── lerna.json ├── package.json ├── packages ├── trrack-core │ ├── .eslintrc.js │ ├── .prettierrc.js │ ├── CHANGELOG.md │ ├── LICENSE │ ├── package.json │ ├── src │ │ ├── Provenance │ │ │ ├── ActionCreator.ts │ │ │ ├── FirebaseFunctions.ts │ │ │ ├── ProvenanceCreator.ts │ │ │ └── ProvenanceGraphFunction.ts │ │ ├── Types │ │ │ ├── Action.ts │ │ │ ├── Nodes.ts │ │ │ ├── Observers.ts │ │ │ ├── Provenance.ts │ │ │ ├── ProvenanceGraph.ts │ │ │ └── Serializers.ts │ │ ├── Utils │ │ │ ├── DeepCopy.ts │ │ │ ├── Differ.ts │ │ │ ├── defaultDeserializer.ts │ │ │ ├── defaultSerializer.ts │ │ │ ├── generateTimeStamp.ts │ │ │ └── generateUUID.ts │ │ └── index.ts │ ├── test │ │ ├── Actions.spec.ts │ │ ├── Firebase.spec.ts │ │ ├── Observers.spec.ts │ │ ├── Provenance.node.spec.ts │ │ ├── Provenance.spec.ts │ │ ├── ProvenanceGraph.spec.ts │ │ ├── SerializedProvenance.spec.ts │ │ └── helper.ts │ ├── tsconfig.json │ ├── tsdx.config.js │ └── typedoc.json ├── trrack-examples │ ├── basic-js-example │ │ ├── CHANGELOG.md │ │ ├── index.html │ │ └── package.json │ ├── basic-vis-example │ │ ├── CHANGELOG.md │ │ ├── index.html │ │ └── package.json │ ├── index.html │ ├── lesMisExample │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── data │ │ │ └── miserables.json │ │ ├── index.html │ │ ├── package.json │ │ ├── src │ │ │ ├── FDBar.ts │ │ │ ├── FDGraph.ts │ │ │ └── ProvenanceSetup.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── lesMisExampleFirebaseIntegration │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── data │ │ │ └── miserables.json │ │ ├── index.html │ │ ├── package.json │ │ ├── src │ │ │ ├── FDBar.ts │ │ │ ├── FDGraph.ts │ │ │ └── ProvenanceSetup.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── simpleExample │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src │ │ │ ├── provenanceSetup.ts │ │ │ └── scatterplot.ts │ │ ├── styles │ │ │ └── styles.css │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── simpleExampleAddAnnotation │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src │ │ │ ├── provenanceSetup.ts │ │ │ └── scatterplot.ts │ │ ├── styles │ │ │ └── styles.css │ │ ├── tsconfig.json │ │ └── webpack.config.js │ └── simpleExampleEphemeral │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src │ │ ├── provenanceSetup.ts │ │ └── scatterplot.ts │ │ ├── styles │ │ └── styles.css │ │ ├── tsconfig.json │ │ └── webpack.config.js └── trrack-vis │ ├── .eslintignore │ ├── .github │ └── workflows │ │ ├── main.yml │ │ └── size.yml │ ├── .gitignore │ ├── .storybook │ ├── main.js │ └── preview.js │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── src │ ├── Utils │ │ ├── BundleMap.ts │ │ ├── EventConfig.ts │ │ ├── TreeLayout.ts │ │ ├── findBackboneBundleNodes.ts │ │ ├── findBundleParent.ts │ │ └── translate.ts │ ├── components │ │ ├── BackboneNode.tsx │ │ ├── BookmarkListView.tsx │ │ ├── BookmarkNode.tsx │ │ ├── BookmarkToggle.tsx │ │ ├── BookmarkTransitions.tsx │ │ ├── BundleTransitions.tsx │ │ ├── Link.tsx │ │ ├── LinkTransitions.ts │ │ ├── NodeTransitions.ts │ │ ├── ProvVis.tsx │ │ ├── ProvVisCreator.tsx │ │ ├── Styles.ts │ │ └── UndoRedoButton.tsx │ └── index.tsx │ ├── stories │ ├── Nodes.tsx │ ├── NonReactTrrack.tsx │ ├── Some.stories.tsx │ └── Trrack.tsx │ ├── test │ └── blah.test.tsx │ ├── tsconfig.json │ ├── tsdx.config.js │ └── typedoc.json ├── trrack.code-workspace ├── trrack_architecture.png ├── trrack_overview.png └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | #root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | max_line_length = 100 10 | indent_size = 2 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | packages/trrack-vis/**/*.ts 2 | packages/trrack-vis/**/*.tsx 3 | *.spec.ts -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2020: true, 5 | node: true, 6 | jest: true, 7 | }, 8 | extends: [ 9 | 'eslint:recommended', 10 | 'plugin:import/typescript', 11 | 'plugin:@typescript-eslint/recommended', 12 | 'plugin:prettier/recommended', 13 | 'prettier', 14 | ], 15 | parser: '@typescript-eslint/parser', 16 | parserOptions: { 17 | ecmaVersion: 2020, 18 | sourceType: 'module', 19 | }, 20 | plugins: ['@typescript-eslint'], 21 | rules: { 22 | 'linebreak-style': ['error', 'unix'], 23 | quotes: ['error', 'single'], 24 | semi: ['error', 'always'], 25 | 'import/extensions': 'off', 26 | 'no-param-reassign': 0, 27 | 'import/no-cycle': 0, 28 | 'no-use-before-define': 'off', 29 | '@typescript-eslint/explicit-function-return-type': 'off', 30 | 'arrow-parens': ['error', 'always'], 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Publish & Deploy 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | publish-core: 8 | name: Publish trrack-core 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | node-versions: [12.x] 13 | fail-fast: false 14 | steps: 15 | - uses: actions/checkout@v2 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v1 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | 24 | - name: Fetch all 25 | run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* 26 | 27 | - name: Install Packages 28 | run: yarn bootstrap 29 | 30 | - name: Build Libraries 31 | run: yarn build 32 | 33 | - name: Build examples 34 | run: yarn build:examples 35 | 36 | - name: Make build directory 37 | run: mkdir build 38 | 39 | - name: Copy examples 40 | run: for f in `find ./packages/trrack-examples -type d -mindepth 1 -maxdepth 1`; do mkdir -p ./build/${f} && cp -r ${f}/dist ./build/${f}/; done 41 | 42 | - name: Build docs 43 | run: yarn docs 44 | 45 | - name: Copy docs to build 46 | run: | 47 | cp -r ./packages/trrack-core/docs ./build/trrack-docs 48 | cp -r ./packages/trrack-vis/docs ./build/trrack-vis-docs 49 | 50 | - name: Copy index 51 | run: cp ./packages/trrack-examples/index.html ./build/ 52 | 53 | - name: Deploy Website 54 | uses: peaceiris/actions-gh-pages@v3 55 | with: 56 | github_token: ${{ secrets.GITHUB_TOKEN }} 57 | publish_dir: ./build 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .nyc_output 4 | .DS_Store 5 | *.log 6 | .vscode 7 | .idea 8 | dist 9 | compiled 10 | .awcache 11 | .rpt2_cache 12 | docs 13 | *.d.ts 14 | *.js.map 15 | *.js 16 | 17 | # Byte-compiled / optimized / DLL files 18 | __pycache__/ 19 | *.py[cod] 20 | *$py.class 21 | 22 | .vscode 23 | 24 | # C extensions 25 | *.so 26 | 27 | # Distribution / packaging 28 | .Python 29 | build/ 30 | develop-eggs/ 31 | dist/ 32 | downloads/ 33 | eggs/ 34 | .eggs/ 35 | lib/ 36 | lib64/ 37 | parts/ 38 | sdist/ 39 | var/ 40 | wheels/ 41 | pip-wheel-metadata/ 42 | share/python-wheels/ 43 | *.egg-info/ 44 | .installed.cfg 45 | *.egg 46 | MANIFEST 47 | 48 | # PyInstaller 49 | # Usually these files are written by a python script from a template 50 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 51 | *.manifest 52 | *.spec 53 | 54 | # Installer logs 55 | pip-log.txt 56 | pip-delete-this-directory.txt 57 | 58 | # Unit test / coverage reports 59 | htmlcov/ 60 | .tox/ 61 | .nox/ 62 | .coverage 63 | .coverage.* 64 | .cache 65 | nosetests.xml 66 | coverage.xml 67 | *.cover 68 | .hypothesis/ 69 | .pytest_cache/ 70 | 71 | # Translations 72 | *.mo 73 | *.pot 74 | 75 | # Django stuff: 76 | *.log 77 | local_settngs.py 78 | db.sqlite3 79 | db.sqlite3-journal 80 | 81 | # Flask stuff: 82 | instance/ 83 | .webassets-cache 84 | 85 | # Scrapy stuff: 86 | .scrapy 87 | 88 | # Sphinx documentation 89 | docs/_build/ 90 | 91 | # PyBuilder 92 | target/ 93 | 94 | # Jupyter Notebook 95 | .ipynb_checkpoints 96 | 97 | # IPython 98 | profile_default/ 99 | ipython_config.py 100 | 101 | # pyenv 102 | .python-version 103 | 104 | # pipenv 105 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 106 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 107 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 108 | # install all needed dependencies. 109 | #Pipfile.lock 110 | 111 | # celery beat schedule file 112 | celerybeat-schedule 113 | 114 | # SageMath parsed files 115 | *.sage.py 116 | 117 | # Environments 118 | .env 119 | .venv 120 | env/ 121 | venv/ 122 | ENV/ 123 | env.bak/ 124 | venv.bak/ 125 | 126 | # Spyder project settings 127 | .spyderproject 128 | .spyproject 129 | 130 | # Rope project settings 131 | .ropeproject 132 | 133 | # mkdocs documentation 134 | /site 135 | 136 | # mypy 137 | .mypy_cache/ 138 | .dmypy.json 139 | dmypy.json 140 | 141 | # Pyre type checker 142 | .pyre/ 143 | 144 | # Project specific files 145 | files/ 146 | results/ 147 | node_modules/ 148 | .DS_Store 149 | 150 | yarn.lock 151 | package-lock.json 152 | jest-stare/ 153 | storybook-static/ 154 | 155 | !webpack.config.js 156 | !tsdx.config.js 157 | !.prettierrc.js 158 | !.eslintrc.js -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn run lint 5 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | singleQuote: true, 5 | arrowParens: 'always', 6 | printWidth: 120, 7 | }; 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Visualization Design Lab 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/**" 4 | ], 5 | "npmClient": "yarn", 6 | "useWorkspaces": true, 7 | "version": "independent", 8 | "command": { 9 | "publish": { 10 | "conventionalCommits": true 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "private": true, 4 | "author": "Kiran Gadhave ", 5 | "description": "", 6 | "keywords": [ 7 | "trrack" 8 | ], 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/visdesignlab/trrack.git" 12 | }, 13 | "devDependencies": { 14 | "@commitlint/cli": "^11.0.0", 15 | "@commitlint/config-conventional": "^11.0.0", 16 | "@typescript-eslint/eslint-plugin": "^4.22.1", 17 | "@typescript-eslint/parser": "^4.22.1", 18 | "commitizen": "^4.2.1", 19 | "cross-env": "^7.0.2", 20 | "cz-conventional-changelog": "3.3.0", 21 | "eslint": "^7.25.0", 22 | "eslint-config-prettier": "^8.3.0", 23 | "eslint-plugin-import": "^2.22.1", 24 | "eslint-plugin-jest": "^24.0.2", 25 | "eslint-plugin-prettier": "^3.4.0", 26 | "git-cz": "4.7.1", 27 | "husky": "^6.0.0", 28 | "lerna": "^3.22.1", 29 | "lint-staged": "^10.4.0", 30 | "prettier": "^2.2.1" 31 | }, 32 | "workspaces": { 33 | "packages": [ 34 | "packages/**" 35 | ], 36 | "nohoist": [ 37 | "**/tsdx", 38 | "**/tsdx/**" 39 | ] 40 | }, 41 | "scripts": { 42 | "bootstrap": "npx lerna bootstrap --use-workspaces", 43 | "build": "lerna exec --scope @visdesignlab/trrack --scope @visdesignlab/trrack-vis -- yarn build", 44 | "start": "lerna run start --parallel --stream --scope @visdesignlab/trrack --scope @visdesignlab/trrack-vis -- --noClean", 45 | "prestart": "yarn run build", 46 | "storybook:vis": "lerna exec --scope @visdesignlab/trrack-vis -- yarn storybook", 47 | "test:core:watch": "lerna exec --scope @visdesignlab/trrack -- yarn test:watch", 48 | "lint": "lerna run lint -- --fix", 49 | "commit": "git cz", 50 | "release:pre": "CI=true cross-env HUSKY_BYPASS=true lerna version --conventional-commits --conventional-prerelease --no-commit-hooks", 51 | "release:graduate": "CI=true cross-env HUSKY_BYPASS=true lerna version --conventional-commits --conventional-graduate --no-commit-hooks", 52 | "docs": "lerna run docs --scope @visdesignlab/trrack --scope @visdesignlab/trrack-vis", 53 | "build:examples": "lerna run build --ignore @visdesignlab/trrack --ignore @visdesignlab/trrack-vis", 54 | "prepare": "husky install" 55 | }, 56 | "commitlint": { 57 | "extends": [ 58 | "@commitlint/config-conventional" 59 | ] 60 | }, 61 | "config": { 62 | "commitizen": { 63 | "path": "./node_modules/git-cz" 64 | } 65 | }, 66 | "resolutions": { 67 | "@types/react": "16.9.51", 68 | "@types/react-dom": "16.9.8" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/trrack-core/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2020: true, 5 | node: true, 6 | jest: true, 7 | }, 8 | extends: [ 9 | 'eslint:recommended', 10 | 'plugin:import/typescript', 11 | 'plugin:@typescript-eslint/recommended', 12 | 'plugin:prettier/recommended', 13 | 'prettier', 14 | ], 15 | parser: '@typescript-eslint/parser', 16 | parserOptions: { 17 | ecmaVersion: 2020, 18 | sourceType: 'module', 19 | }, 20 | plugins: ['@typescript-eslint'], 21 | rules: { 22 | 'linebreak-style': ['error', 'unix'], 23 | quotes: ['error', 'single'], 24 | semi: ['error', 'always'], 25 | 'import/extensions': 'off', 26 | 'no-param-reassign': 0, 27 | 'import/no-cycle': 0, 28 | 'no-use-before-define': 'off', 29 | '@typescript-eslint/explicit-function-return-type': 'off', 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /packages/trrack-core/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | singleQuote: true, 5 | arrowParens: 'always', 6 | printWidth: 120, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/trrack-core/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [2.0.0](https://github.com/visdesignlab/trrack/compare/@visdesignlab/trrack@2.0.0-alpha.10...@visdesignlab/trrack@2.0.0) (2022-12-16) 7 | 8 | **Note:** Version bump only for package @visdesignlab/trrack 9 | 10 | 11 | 12 | 13 | 14 | # [2.0.0-alpha.10](https://github.com/visdesignlab/trrack/compare/@visdesignlab/trrack@2.0.0-alpha.9...@visdesignlab/trrack@2.0.0-alpha.10) (2021-05-07) 15 | 16 | 17 | ### Bug Fixes 18 | 19 | * defaultDeserialzer ([48cead8](https://github.com/visdesignlab/trrack/commit/48cead87e58e5ff7aec198cf569cc50cc0a9ef6f)) 20 | 21 | 22 | 23 | 24 | 25 | # [2.0.0-alpha.9](https://github.com/visdesignlab/trrack/compare/@visdesignlab/trrack@2.0.0-alpha.8...@visdesignlab/trrack@2.0.0-alpha.9) (2021-05-07) 26 | 27 | 28 | ### Bug Fixes 29 | 30 | * Trrackvis style fixes ([b78db15](https://github.com/visdesignlab/trrack/commit/b78db155373a7b667ce45a801a0c55b05aea5617)) 31 | 32 | 33 | 34 | 35 | 36 | # [2.0.0-alpha.8](https://github.com/visdesignlab/trrack/compare/@visdesignlab/trrack@2.0.0-alpha.7...@visdesignlab/trrack@2.0.0-alpha.8) (2021-05-07) 37 | 38 | 39 | ### Features 40 | 41 | * Serializing support ([27a7f0f](https://github.com/visdesignlab/trrack/commit/27a7f0fada3b68a045d6a8b6dcfe5ece0b49ac8a)) 42 | 43 | 44 | 45 | 46 | 47 | # [2.0.0-alpha.7](https://github.com/visdesignlab/trrack/compare/@visdesignlab/trrack@2.0.0-alpha.6...@visdesignlab/trrack@2.0.0-alpha.7) (2020-12-16) 48 | 49 | **Note:** Version bump only for package @visdesignlab/trrack 50 | 51 | 52 | 53 | 54 | 55 | # [2.0.0-alpha.6](https://github.com/visdesignlab/trrack/compare/@visdesignlab/trrack@2.0.0-alpha.5...@visdesignlab/trrack@2.0.0-alpha.6) (2020-12-16) 56 | 57 | **Note:** Version bump only for package @visdesignlab/trrack 58 | 59 | 60 | 61 | 62 | 63 | # [2.0.0-alpha.5](https://github.com/visdesignlab/trrack/compare/@visdesignlab/trrack@2.0.0-alpha.4...@visdesignlab/trrack@2.0.0-alpha.5) (2020-10-21) 64 | 65 | 66 | ### Bug Fixes 67 | 68 | * 🐛 Updated bugs with loadFromUrl ([7374e4c](https://github.com/visdesignlab/trrack/commit/7374e4c5f9e55eb3965bc8ba6ada05b0bc256d8d)) 69 | 70 | 71 | 72 | 73 | 74 | # [2.0.0-alpha.4](https://github.com/visdesignlab/trrack/compare/@visdesignlab/trrack@2.0.0-alpha.3...@visdesignlab/trrack@2.0.0-alpha.4) (2020-10-20) 75 | 76 | **Note:** Version bump only for package @visdesignlab/trrack 77 | 78 | 79 | 80 | 81 | 82 | # [2.0.0-alpha.3](https://github.com/visdesignlab/trrack/compare/@visdesignlab/trrack@2.0.0-alpha.2...@visdesignlab/trrack@2.0.0-alpha.3) (2020-10-20) 83 | 84 | 85 | ### Bug Fixes 86 | 87 | * 🐛 Fixed lint errors ([7858b5b](https://github.com/visdesignlab/trrack/commit/7858b5b9ec9754391ff68741056cf6992fe37e07)) 88 | 89 | 90 | 91 | 92 | 93 | # [2.0.0-alpha.2](https://github.com/visdesignlab/trrack/compare/@visdesignlab/trrack@2.0.0-alpha.1...@visdesignlab/trrack@2.0.0-alpha.2) (2020-10-19) 94 | 95 | **Note:** Version bump only for package @visdesignlab/trrack 96 | 97 | 98 | 99 | 100 | 101 | # [2.0.0-alpha.1](https://github.com/visdesignlab/trrack/compare/@visdesignlab/trrack@2.0.0-alpha.0...@visdesignlab/trrack@2.0.0-alpha.1) (2020-10-19) 102 | 103 | **Note:** Version bump only for package @visdesignlab/trrack 104 | 105 | 106 | 107 | 108 | 109 | # 2.0.0-alpha.0 (2020-10-19) 110 | 111 | 112 | ### Bug Fixes 113 | 114 | * 🐛 Fix bug with arguments not spreading ([4a18d69](https://github.com/visdesignlab/trrack/commit/4a18d69757d6bd68f2b6cbfbf42cfdb3807fec87)) 115 | * 🐛 Fixed build process + added dependency for examples ([572e8cd](https://github.com/visdesignlab/trrack/commit/572e8cd8675003030ac942036201868383569835)) 116 | * 🐛 Removed old code directories ([37f280a](https://github.com/visdesignlab/trrack/commit/37f280a15d167ff5d4f6c79aa1e2cd51ea7ea017)) 117 | 118 | 119 | ### Features 120 | 121 | * 🎸 Added multiple API changes ([df306f4](https://github.com/visdesignlab/trrack/commit/df306f42ef26568aa533ce3592a0142650da5e34)), closes [#47](https://github.com/visdesignlab/trrack/issues/47) 122 | * 🎸 adding undo/redo ([e9fcc1b](https://github.com/visdesignlab/trrack/commit/e9fcc1b760951440aa21235e8820ac7ed2efada6)) 123 | * 🎸 Fixing trrack-core and adding trrack vis ([169145c](https://github.com/visdesignlab/trrack/commit/169145cb4f7d3a880c61d5f073115d7d898a62a8)) 124 | * 🎸 Integrated trrack-vis ([57d337e](https://github.com/visdesignlab/trrack/commit/57d337e60eb9b7d4059e23bf9e827c8e872c6a04)) 125 | 126 | 127 | ### BREAKING CHANGES 128 | 129 | * 🧨 Actions API, Artifacts and Observers 130 | -------------------------------------------------------------------------------- /packages/trrack-core/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Kiran Gadhave 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /packages/trrack-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@visdesignlab/trrack", 3 | "publishConfig": { 4 | "access": "public" 5 | }, 6 | "version": "2.0.0", 7 | "author": "Kiran Gadhave ", 8 | "description": "", 9 | "keywords": [], 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/visdesignlab/trrack.git" 13 | }, 14 | "main": "dist/index.js", 15 | "module": "dist/trrack.esm.js", 16 | "typings": "dist/index.d.ts", 17 | "license": "MIT", 18 | "files": [ 19 | "dist", 20 | "src" 21 | ], 22 | "engines": { 23 | "node": ">=10" 24 | }, 25 | "scripts": { 26 | "start": "tsdx watch", 27 | "build": "tsdx build --format cjs,esm,umd --name trrack", 28 | "test": "tsdx test --coverage", 29 | "test:watch": "tsdx test --coverage --verbose --watch", 30 | "docs": "typedoc --out docs --target es6 --mode file src", 31 | "lint": "tsdx lint src", 32 | "prepare": "tsdx build --format cjs,esm,umd --name trrack", 33 | "size": "size-limit", 34 | "analyze": "size-limit --why" 35 | }, 36 | "prettier": { 37 | "semi": true, 38 | "trailingComma": "all", 39 | "singleQuote": true, 40 | "arrowParens": "always" 41 | }, 42 | "jest": { 43 | "coveragePathIgnorePatterns": [ 44 | "/node_modules/", 45 | "/test/" 46 | ], 47 | "coverageThreshold": { 48 | "global": { 49 | "branches": 80, 50 | "functions": 95, 51 | "lines": 95, 52 | "statements": 95 53 | } 54 | }, 55 | "collectCoverageFrom": [ 56 | "src/**/*.{js,ts}", 57 | "!src/**/index.{js,ts}" 58 | ] 59 | }, 60 | "size-limit": [ 61 | { 62 | "path": "dist/trrack_dev.cjs.production.min.js", 63 | "limit": "10 KB" 64 | }, 65 | { 66 | "path": "dist/trrack_dev.esm.js", 67 | "limit": "10 KB" 68 | } 69 | ], 70 | "devDependencies": { 71 | "@size-limit/preset-small-lib": "^4.6.0", 72 | "@types/jest": "^26.0.23", 73 | "@types/lz-string": "^1.3.33", 74 | "@types/serialize-javascript": "^4.0.0", 75 | "size-limit": "^4.6.0", 76 | "tsdx": "^0.14.0", 77 | "tslib": "^2.0.1", 78 | "typedoc": "^0.19.2", 79 | "typescript": "^4.0.3" 80 | }, 81 | "dependencies": { 82 | "@types/deep-diff": "^1.0.0", 83 | "deep-diff": "^1.0.2", 84 | "firebase": "^7.15.5", 85 | "lz-string": "^1.4.4", 86 | "mobx": "^6.0.1" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /packages/trrack-core/src/Provenance/ActionCreator.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable func-names */ 2 | /* eslint-disable no-underscore-dangle */ 3 | import { action, toJS } from 'mobx'; 4 | import { ActionFunction, ActionObject, ActionSaveStateMode, ActionType } from '../Types/Action'; 5 | import { Meta } from '../Types/Nodes'; 6 | 7 | /** 8 | * 9 | * @template T State of the application 10 | * @template S Represents the given event types in your application. 11 | * Event types are used to differentiate between different actions that create nodes. 12 | * 13 | * @param func Defines the function which will be executed on provenance apply 14 | * 15 | */ 16 | 17 | // TODO:: Switch Args and S here. 18 | export default function createAction( 19 | func: ActionFunction, 20 | ): ActionObject { 21 | let _label: string | undefined; 22 | let _actionType: ActionType = 'Regular'; 23 | let _stateSaveMode: ActionSaveStateMode = 'Diff'; 24 | let _eventType: S; 25 | let _meta: Meta = {}; 26 | 27 | const actionObject: ActionObject = (...args: Args) => { 28 | return { 29 | apply: action((state: T, label?: string) => { 30 | if (!_label) throw new Error('Please specify a default label when you create the action'); 31 | 32 | if (!label) label = _label; 33 | 34 | func(state, ...args); 35 | return { 36 | state: toJS(state), 37 | label: label, 38 | stateSaveMode: _stateSaveMode, 39 | actionType: _actionType, 40 | eventType: _eventType, 41 | meta: _meta, 42 | }; 43 | }), 44 | }; 45 | }; 46 | 47 | actionObject.setLabel = function (label: string) { 48 | _label = label; 49 | return this; 50 | }; 51 | 52 | actionObject.setActionType = function (actionType: ActionType) { 53 | _actionType = actionType; 54 | return this; 55 | }; 56 | 57 | actionObject.saveStateMode = function (mode: ActionSaveStateMode) { 58 | _stateSaveMode = mode; 59 | return this; 60 | }; 61 | 62 | actionObject.setEventType = function (evtType: S) { 63 | _eventType = evtType; 64 | return this; 65 | }; 66 | 67 | actionObject.setMetaData = function (m: Meta) { 68 | _meta = m; 69 | return this; 70 | }; 71 | 72 | return actionObject; 73 | } 74 | -------------------------------------------------------------------------------- /packages/trrack-core/src/Provenance/FirebaseFunctions.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 3 | /* eslint-disable @typescript-eslint/no-explicit-any */ 4 | import firebase from 'firebase/app'; 5 | import 'firebase/database'; 6 | import { ProvenanceGraph } from '../Types/ProvenanceGraph'; 7 | 8 | export function initializeFirebase(config: any) { 9 | const app: firebase.app.App = 10 | firebase.apps.length === 0 ? firebase.initializeApp(config) : firebase.app(); 11 | 12 | const db = firebase.database(app); 13 | 14 | return { 15 | config, 16 | app, 17 | db, 18 | }; 19 | } 20 | 21 | export function logToFirebase(rtd: firebase.database.Database) { 22 | const addedNodes: string[] = []; 23 | 24 | return (graph: ProvenanceGraph) => { 25 | const path = `${graph.root}`; 26 | const nodes = Object.keys(graph.nodes); 27 | const nodeToAdd: string[] = []; 28 | 29 | nodes.forEach((node) => { 30 | if (!addedNodes.includes(node)) { 31 | nodeToAdd.push(node); 32 | addedNodes.push(node); 33 | } 34 | }); 35 | 36 | nodeToAdd.forEach((node) => { 37 | const actualNode = JSON.parse(JSON.stringify(graph.nodes[node])); 38 | 39 | rtd 40 | .ref(`${path}/nodes/${node}`) 41 | .set(actualNode) 42 | .catch((err) => { 43 | // eslint-disable-next-line no-console 44 | console.warn(err); 45 | throw new Error('Something went wrong while logging.'); 46 | }); 47 | }); 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /packages/trrack-core/src/Types/Action.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | import { Meta } from './Nodes'; 4 | 5 | /** 6 | * 7 | * __Ephemeral__ for actions which are shortlived. e.g. hovering 8 | * 9 | * __Regular__ for all you other actions. 10 | * 11 | */ 12 | export type ActionType = 'Ephemeral' | 'Regular'; 13 | 14 | /** 15 | * 16 | * __Complete__ for saving the entire state 17 | * 18 | * __Diff__ for using diff algorithm 19 | * 20 | */ 21 | export type ActionSaveStateMode = 'Complete' | 'Diff'; 22 | 23 | /** 24 | * @typeParam T Type of application state 25 | * @typeParam Args Represents the types for the arguments 26 | * 27 | * @param T Application state 28 | */ 29 | export type ActionFunction = (state: T, ...args: Args) => void; 30 | 31 | export type ActionReturnType = { 32 | state: T; 33 | label: string; 34 | stateSaveMode: ActionSaveStateMode; 35 | actionType: ActionType; 36 | eventType: S; 37 | meta: Meta; 38 | }; 39 | 40 | export type ApplyObject = { 41 | apply: (state: T, label?: string) => ActionReturnType; 42 | }; 43 | 44 | export type ActionObject = { 45 | /** 46 | * Edits the label associated with this action 47 | */ 48 | setLabel: (label: string) => ActionObject; 49 | /** 50 | * Changes the action type to either "Regular" or "Ephemeral" 51 | * Ephemeral actions are skipped with the ephemeral undo/redo, 52 | * and appear clustered in trrackvis 53 | */ 54 | setActionType: (actionType: ActionType) => ActionObject; 55 | /** 56 | * Manually changes the state save for this node to either "Diff" or "Complete" 57 | * Typically better to let trrack decide how to store your node 58 | */ 59 | saveStateMode: (mode: ActionSaveStateMode) => ActionObject; 60 | /** 61 | * Manually changes the state save for this action to either "Diff" or "Complete" 62 | * Typically better to let trrack decide how to store your node 63 | */ 64 | setEventType: (eventType: S) => ActionObject; 65 | /** 66 | * Adds custom metadata to the metadata associated with this action 67 | * See Nodes.ts for more documentation on the NodeMetadata type. 68 | */ 69 | setMetaData: (metadata: Meta) => ActionObject; 70 | } & ((...args: Args) => ApplyObject); 71 | -------------------------------------------------------------------------------- /packages/trrack-core/src/Types/Nodes.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-shadow */ 2 | import { applyChange, Diff } from 'deep-diff'; 3 | import { toJS } from 'mobx'; 4 | import deepCopy from '../Utils/DeepCopy'; 5 | import { ActionType } from './Action'; 6 | import { ProvenanceGraph } from './ProvenanceGraph'; 7 | import { JsonValue } from './Serializers'; 8 | 9 | export type DiffExport = Diff; 10 | 11 | export type NodeID = string; 12 | 13 | export type Meta = { [key: string]: any }; 14 | 15 | export type NodeMetadata = { 16 | createdOn?: number; 17 | eventType: S | 'Root'; 18 | } & Meta; 19 | 20 | type BaseArtifact = { 21 | timestamp: number; 22 | }; 23 | 24 | export type Annotation = BaseArtifact & { 25 | annotation: string; 26 | }; 27 | 28 | export type Artifact = BaseArtifact & { 29 | artifact: A; 30 | }; 31 | 32 | export type Artifacts = { 33 | annotations: Annotation[]; 34 | customArtifacts: Artifact[]; 35 | }; 36 | 37 | export interface BaseNode { 38 | id: NodeID; 39 | label: string; 40 | metadata: NodeMetadata; 41 | children: NodeID[]; 42 | actionType: ActionType; 43 | bookmarked: boolean; 44 | } 45 | 46 | export interface RootNode extends BaseNode { 47 | state: JsonValue; 48 | } 49 | 50 | export interface ChildNode extends BaseNode { 51 | parent: NodeID; 52 | artifacts: Artifacts; 53 | } 54 | 55 | export interface StateNode extends RootNode, ChildNode {} 56 | 57 | export interface DiffNode extends ChildNode { 58 | diffs: Diff[]; 59 | lastStateNode: NodeID; 60 | } 61 | 62 | export type ProvenanceNode = RootNode | StateNode | DiffNode; 63 | 64 | export type Nodes = { [key: string]: ProvenanceNode }; 65 | 66 | export type CurrentNode = ProvenanceNode; 67 | 68 | /** 69 | * Function for checking if a node is a state node. 70 | * @template T Represents the given state of an application as defined in initProvenance. 71 | * @template S Represents the given event types in your application. 72 | * Event types are used to differentiate between different actions that create nodes. 73 | * @template A Represents the given "extra" type for storing metadata. 74 | * Extra is a way to store customized metadata. 75 | * @param _opts: Given node to check if it is a state node. 76 | */ 77 | export function isStateNode(node: ProvenanceNode): node is StateNode { 78 | return 'parent' in node && 'state' in node; 79 | } 80 | 81 | /** 82 | * Function for checking if a node is a diff node. 83 | * @template T Represents the given state of an application as defined in initProvenance. 84 | * @template S Represents the given event types in your application. 85 | * Event types are used to differentiate between different actions that create nodes. 86 | * @template A Represents the given "extra" type for storing metadata. 87 | * Extra is a way to store customized metadata. 88 | * @param _opts: Given node to check if it is a diff node. 89 | */ 90 | export function isDiffNode(node: ProvenanceNode): node is DiffNode { 91 | return 'diffs' in node; 92 | } 93 | 94 | /** 95 | * Function for checking if a node is a child node. 96 | * @template T Represents the given state of an application as defined in initProvenance. 97 | * @template S Represents the given event types in your application. 98 | * Event types are used to differentiate between different actions that create nodes. 99 | * @template A Represents the given "extra" type for storing metadata. 100 | * Extra is a way to store customized metadata. 101 | * @param _opts: Given node to check if it is a child node. 102 | */ 103 | export function isChildNode( 104 | node: ProvenanceNode, 105 | ): node is DiffNode | StateNode { 106 | return 'parent' in node; 107 | } 108 | 109 | /** 110 | * Function for checking if a node is the root node. 111 | * @template T Represents the given state of an application as defined in initProvenance. 112 | * @template S Represents the given event types in your application. 113 | * Event types are used to differentiate between different actions that create nodes. 114 | * @template A Represents the given "extra" type for storing metadata. 115 | * Extra is a way to store customized metadata. 116 | * @param _opts: Given node to check if it is root. 117 | */ 118 | export function isRootNode(node: ProvenanceNode): node is RootNode { 119 | return node.label === 'Root'; 120 | } 121 | 122 | /** 123 | `* Retrieve the state of a node. ` 124 | * @template T Represents the given state of an application as defined in initProvenance. 125 | * @template S Represents the given event types in your application. 126 | * Event types are used to differentiate between different actions that create nodes. 127 | * @template A Represents the given "extra" type for storing metadata. 128 | * Extra is a way to store customized metadata. 129 | * @param graph: Provenance Graph which we are searching for node in 130 | * @param _opts: Node which we want the state of 131 | */ 132 | export function getState( 133 | graph: ProvenanceGraph, 134 | node: ProvenanceNode, 135 | ): JsonValue { 136 | if (isRootNode(node) || isStateNode(node)) { 137 | return toJS(node.state); 138 | } 139 | 140 | // eslint-disable-next-line no-underscore-dangle 141 | const _state = toJS((graph.nodes[node.lastStateNode] as StateNode).state); 142 | 143 | const state = deepCopy(_state); 144 | 145 | // what is this for? 146 | node.diffs.forEach((diff) => { 147 | applyChange(state, null, diff); 148 | }); 149 | 150 | return state; 151 | } 152 | -------------------------------------------------------------------------------- /packages/trrack-core/src/Types/Observers.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | import { ProvenanceGraph } from './ProvenanceGraph'; 4 | 5 | export type ChangeType = 'CurrentChanged' | 'NodeAdded' | 'Any'; 6 | 7 | export type GlobalObserver = (graph?: ProvenanceGraph, changeType?: ChangeType) => void; 8 | 9 | export type ObserverExpression = (state: T) => P; 10 | 11 | export type ObserverEffect

= (state?: P, previousState?: P) => void; 12 | -------------------------------------------------------------------------------- /packages/trrack-core/src/Types/ProvenanceGraph.ts: -------------------------------------------------------------------------------- 1 | import { NodeID, Nodes } from './Nodes'; 2 | 3 | /** 4 | * @template T Represents the given state of an application as defined in initProvenance. 5 | * @template S Represents the given event types in your application. 6 | * Event types are used to differentiate between different actions that create nodes. 7 | * @template A Represents the given "extra" type for storing metadata. 8 | * Extra is a way to store customized metadata. 9 | */ 10 | export interface ProvenanceGraph { 11 | nodes: Nodes; 12 | current: NodeID; 13 | root: NodeID; 14 | name?: string; 15 | } 16 | -------------------------------------------------------------------------------- /packages/trrack-core/src/Types/Serializers.ts: -------------------------------------------------------------------------------- 1 | export type JsonValue = { 2 | [key: string]: JsonValue | number | string | unknown[]; 3 | }; 4 | 5 | export type Serializer = (obj: T) => JsonValue; 6 | 7 | export type Deserializer = (obj: JsonValue) => T; 8 | -------------------------------------------------------------------------------- /packages/trrack-core/src/Utils/DeepCopy.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-shadow */ 2 | // TODO:: Fix typing, target shouldnt be of type T. 3 | 4 | // eslint-disable-next-line no-unused-vars 5 | export default function deepCopy(target: T): T { 6 | return JSON.parse(JSON.stringify(target)); 7 | } 8 | -------------------------------------------------------------------------------- /packages/trrack-core/src/Utils/Differ.ts: -------------------------------------------------------------------------------- 1 | import { diff } from 'deep-diff'; 2 | 3 | export default function differ(obj1: T, obj2: T) { 4 | return diff(obj1, obj2); 5 | } 6 | -------------------------------------------------------------------------------- /packages/trrack-core/src/Utils/defaultDeserializer.ts: -------------------------------------------------------------------------------- 1 | import { JsonValue } from '../Types/Serializers'; 2 | 3 | function defaultDeserializer(o: JsonValue): T { 4 | const str = JSON.stringify(o); 5 | const obj = JSON.parse(str, (_, val) => { 6 | if (!val) return val; 7 | 8 | if (val.type && val.type === 'Set') { 9 | return new Set(val.arr); 10 | } 11 | if (val.type && val.type === 'Map') { 12 | return new Map(Object.entries(val.obj)); 13 | } 14 | return val; 15 | }); 16 | 17 | return obj; 18 | } 19 | 20 | export default defaultDeserializer; 21 | -------------------------------------------------------------------------------- /packages/trrack-core/src/Utils/defaultSerializer.ts: -------------------------------------------------------------------------------- 1 | import { toJS } from 'mobx'; 2 | import { JsonValue } from '../Types/Serializers'; 3 | 4 | function defaultSerializer(obj: T): JsonValue { 5 | if (!obj) return {}; 6 | 7 | const str = JSON.stringify(toJS(obj), (_, val) => { 8 | if (val instanceof Set) { 9 | return { 10 | type: 'Set', 11 | arr: Array.from(val), 12 | }; 13 | } 14 | if (val instanceof Map) { 15 | return { 16 | type: 'Map', 17 | obj: Object.fromEntries(val), 18 | }; 19 | } 20 | return val; 21 | }); 22 | 23 | return JSON.parse(str); 24 | } 25 | 26 | export default defaultSerializer; 27 | -------------------------------------------------------------------------------- /packages/trrack-core/src/Utils/generateTimeStamp.ts: -------------------------------------------------------------------------------- 1 | export default function generateTimeStamp(): number { 2 | return new Date().getTime(); 3 | } 4 | -------------------------------------------------------------------------------- /packages/trrack-core/src/Utils/generateUUID.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-bitwise */ 2 | export default function generateUUID(): string { 3 | let d = new Date().getTime(); 4 | 5 | d += new Date().valueOf(); 6 | 7 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { 8 | const r = (d + Math.random() * 16) % 16 | 0; 9 | d = Math.floor(d / 16); 10 | return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /packages/trrack-core/src/index.ts: -------------------------------------------------------------------------------- 1 | import initProvenance from './Provenance/ProvenanceCreator'; 2 | import { ActionType, ActionObject, ActionFunction } from './Types/Action'; 3 | import createAction from './Provenance/ActionCreator'; 4 | import { ProvenanceGraph } from './Types/ProvenanceGraph'; 5 | import { 6 | isStateNode, 7 | getState, 8 | NodeID, 9 | StateNode, 10 | isChildNode, 11 | Nodes, 12 | ProvenanceNode, 13 | DiffNode, 14 | ChildNode, 15 | Meta, 16 | Artifact, 17 | Artifacts, 18 | Annotation, 19 | } from './Types/Nodes'; 20 | import { Provenance } from './Types/Provenance'; 21 | 22 | export { 23 | initProvenance, 24 | ActionType, 25 | ActionObject, 26 | ActionFunction, 27 | createAction, 28 | ProvenanceGraph, 29 | isStateNode, 30 | getState, 31 | NodeID, 32 | StateNode, 33 | ChildNode, 34 | Nodes, 35 | ProvenanceNode, 36 | Provenance, 37 | DiffNode, 38 | isChildNode, 39 | Meta, 40 | Artifacts, 41 | Artifact, 42 | Annotation, 43 | }; 44 | -------------------------------------------------------------------------------- /packages/trrack-core/test/Actions.spec.ts: -------------------------------------------------------------------------------- 1 | import { ActionType } from '../src'; 2 | import { isChildNode, Meta } from '../src/Types/Nodes'; 3 | import { 4 | initialState, 5 | setupProvenanceAndAction, 6 | setupTodoManager, 7 | } from './helper'; 8 | 9 | describe('action object is valid', () => { 10 | const { action } = setupProvenanceAndAction(initialState); 11 | 12 | it('action object should not be null ', () => { 13 | expect(action).not.toBeNull(); 14 | }); 15 | }); 16 | 17 | describe('apply action should work properly', () => { 18 | it('should throw error when applying action without setting label', () => { 19 | const { provenance, action } = setupProvenanceAndAction( 20 | initialState, 21 | false 22 | ); 23 | 24 | expect(() => provenance.apply(action())).toThrowError( 25 | new Error('Please specify a default label when you create the action') 26 | ); 27 | }); 28 | 29 | it('new node should have correct label', () => { 30 | const { provenance, action } = setupProvenanceAndAction(initialState); 31 | const label = 'Increase counter by 1'; 32 | action.setLabel(label); 33 | provenance.apply(action()); 34 | 35 | expect(provenance.current.label).toBe(label); 36 | }); 37 | 38 | it('new node should have correct custom label', () => { 39 | const { provenance, action } = setupProvenanceAndAction(initialState); 40 | const label = 'Increase counter by 1'; 41 | const customLabel = 'Custom'; 42 | action.setLabel(label); 43 | provenance.apply(action(), customLabel); 44 | 45 | expect(provenance.current.label).toBe(customLabel); 46 | }); 47 | 48 | it('new node should have correct action type', () => { 49 | const { provenance, action } = setupProvenanceAndAction(initialState); 50 | const label = 'Increase counter by 1'; 51 | action.setLabel(label).setActionType('Ephemeral'); 52 | provenance.apply(action()); 53 | 54 | expect(provenance.current.actionType).toBe('Ephemeral'); 55 | }); 56 | 57 | it('should increment counter value by 1', () => { 58 | const { provenance, action } = setupProvenanceAndAction(initialState); 59 | 60 | const originalValue = provenance.getState(provenance.current).counter; 61 | provenance.apply(action.setLabel('Increase Counter')()); 62 | const newValue = provenance.getState(provenance.current).counter; 63 | 64 | expect(newValue - originalValue).toEqual(1); 65 | }); 66 | 67 | it('should change message according to argument', () => { 68 | const { provenance, changeMessageAction } = setupProvenanceAndAction( 69 | initialState 70 | ); 71 | 72 | const msg = 'Hello, World!'; 73 | provenance.apply(changeMessageAction(msg)); 74 | const newMessage = provenance.getState(provenance.current).message; 75 | expect(newMessage).toEqual(msg); 76 | }); 77 | 78 | it('should set metadata', () => { 79 | const { provenance, action } = setupProvenanceAndAction(initialState); 80 | 81 | const val = 'Hello, World!'; 82 | const meta: Meta = { 83 | testMetaData: val, 84 | }; 85 | 86 | provenance.apply(action.setLabel('Increment counter').setMetaData(meta)()); 87 | 88 | const currentNode = provenance.current; 89 | if (isChildNode(currentNode)) { 90 | expect(currentNode.metadata.testMetaData).toEqual(val); 91 | } else { 92 | throw new Error('Should not be root node'); 93 | } 94 | }); 95 | 96 | it('should set event type', () => { 97 | const { provenance, action } = setupProvenanceAndAction(initialState); 98 | 99 | provenance.apply( 100 | action.setLabel('Increment Counter').setEventType('IncreaseCounter')() 101 | ); 102 | 103 | const currentNode = provenance.current; 104 | if (isChildNode(currentNode)) { 105 | expect(currentNode.metadata.eventType).toEqual('IncreaseCounter'); 106 | } else { 107 | throw new Error('Should not be root node'); 108 | } 109 | }); 110 | 111 | it('should save diff', () => { 112 | const { provenance, changeName, addTodoAction } = setupTodoManager(); 113 | 114 | provenance.apply(changeName('New Name')); 115 | provenance.apply( 116 | addTodoAction({ 117 | title: 'Task 1', 118 | description: 'This is a test task', 119 | status: 'incomplete', 120 | }) 121 | ); 122 | expect('diffs' in provenance.current).toEqual(true); 123 | }); 124 | 125 | it('should save complete state', () => { 126 | const { provenance, changeName, addTodoAction } = setupTodoManager(); 127 | 128 | provenance.apply(changeName('New Name')); 129 | provenance.apply( 130 | addTodoAction.saveStateMode('Complete')({ 131 | title: 'Task 1', 132 | description: 'This is a test task', 133 | status: 'incomplete', 134 | }) 135 | ); 136 | 137 | expect('state' in provenance.current).toEqual(true); 138 | }); 139 | }); 140 | -------------------------------------------------------------------------------- /packages/trrack-core/test/Firebase.spec.ts: -------------------------------------------------------------------------------- 1 | import { initializeFirebase } from '../src/Provenance/FirebaseFunctions'; 2 | 3 | const firebaseConfig = { 4 | apiKey: 'AIzaSyDDSC6jbcXkv5zcB-SGGTMYVryYo4q_0JI', 5 | authDomain: 'trrack-testing.firebaseapp.com', 6 | databaseURL: 'https://trrack-testing.firebaseio.com', 7 | projectId: 'trrack-testing', 8 | storageBucket: 'trrack-testing.appspot.com', 9 | messagingSenderId: '39631851502', 10 | appId: '1:39631851502:web:9da7375282f0b27e1b48ff', 11 | }; 12 | 13 | describe('Test', () => { 14 | it('works', () => { 15 | const { db } = initializeFirebase(firebaseConfig); 16 | const ref = db.ref(); 17 | ref.on('value', (snap) => { 18 | console.log(snap.val()); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/trrack-core/test/Provenance.node.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment node 3 | */ 4 | import { initialState, setupProvenanceAndAction } from './helper'; 5 | 6 | describe('done: should load state from url when available', () => { 7 | it('should throw error when "window" environment is not detected', () => { 8 | global.window = null as any; 9 | expect(() => setupProvenanceAndAction(initialState, true)).toThrow(); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/trrack-core/test/ProvenanceGraph.spec.ts: -------------------------------------------------------------------------------- 1 | import { isStateNode } from '../src'; 2 | import { initialState, setupProvenanceAndAction } from './helper'; 3 | 4 | describe('provenance graph has correct shape', () => { 5 | const { provenance } = setupProvenanceAndAction(initialState); 6 | 7 | it('provenance graph should not be null', () => expect(provenance.graph).not.toBeNull()); 8 | 9 | it('provenance graph should have root', () => expect(provenance.graph.root).not.toBeNull()); 10 | 11 | it('provenance graph should have current', () => expect(provenance.graph.current).not.toBeNull()); 12 | 13 | it('provenance graph should have nodes', () => expect(provenance.graph.nodes).not.toBeNull()); 14 | }); 15 | 16 | describe('provenance graph has correct values', () => { 17 | const { provenance } = setupProvenanceAndAction(initialState); 18 | 19 | it('should have one node', () => expect(Object.keys(provenance.graph.nodes)).toHaveLength(1)); 20 | 21 | it('should have a root node', () => expect(provenance.root).not.toBeNull()); 22 | 23 | it('root and current node should be same', () => { 24 | const { current, root } = provenance; 25 | expect(current.id).toEqual(root.id); 26 | }); 27 | 28 | it('Current node is not a state node', () => expect(isStateNode(provenance.current)).toBeFalsy()); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/trrack-core/test/helper.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 3 | import { createAction, initProvenance } from '../src'; 4 | 5 | export function print(obj: any) { 6 | console.log(JSON.stringify(obj, null, 2)); 7 | } 8 | 9 | export type State = { 10 | counter: number; 11 | message: string; 12 | testArr: string[]; 13 | }; 14 | 15 | export type Events = 'IncreaseCounter' | 'DecreaseCounter' | 'UpdateMessage'; 16 | 17 | export const initialState: State = { 18 | counter: 0, 19 | message: 'Init', 20 | testArr: ['First Element'], 21 | }; 22 | 23 | export function setupProvenanceAndAction( 24 | initState: State = initialState, 25 | loadFromUrl = false, 26 | skipDone = false 27 | ) { 28 | const provenance = initProvenance(initState, { loadFromUrl }); 29 | 30 | const action = createAction((state: State) => { 31 | state.counter += 1; 32 | }); 33 | 34 | const ephemeralAction = createAction((state) => { 35 | state.counter -= 1; 36 | }) 37 | .setLabel('Ephemeral decrease counter') 38 | .setActionType('Ephemeral'); 39 | 40 | const changeMessageAction = createAction( 41 | (state: State, msg: string) => { 42 | state.message = msg; 43 | } 44 | ).setLabel('Change Message'); 45 | 46 | if (!skipDone) provenance.done(); 47 | 48 | return { 49 | provenance, 50 | action, 51 | changeMessageAction, 52 | ephemeralAction, 53 | }; 54 | } 55 | 56 | export type Todo = { 57 | title: string; 58 | description: string; 59 | status: 'complete' | 'incomplete'; 60 | }; 61 | 62 | export type TodoManager = { 63 | user: string; 64 | todos: Todo[]; 65 | totalTodos: number; 66 | totalCompleted: number; 67 | totalIncomplete: number; 68 | logs: Set; 69 | map: Map; 70 | }; 71 | 72 | export type TodoEvents = 73 | | 'SetName' 74 | | 'AddTodo' 75 | | 'RemoveTodo' 76 | | 'MarkComplete' 77 | | 'MarkIncomplete' 78 | | 'Log'; 79 | 80 | export type TodoArtifacts = { 81 | notes: string; 82 | }; 83 | 84 | export const initialTodoState: TodoManager = { 85 | user: 'Test User', 86 | todos: [], 87 | totalTodos: 0, 88 | totalIncomplete: 0, 89 | totalCompleted: 0, 90 | logs: new Set(['log']), 91 | map: new Map(), 92 | }; 93 | 94 | export function setupTodoManager() { 95 | const provenance = initProvenance({ 96 | user: 'Test User', 97 | todos: [], 98 | totalTodos: 0, 99 | totalIncomplete: 0, 100 | totalCompleted: 0, 101 | logs: new Set(['log']), 102 | map: new Map(), 103 | }); 104 | 105 | const changeName = createAction( 106 | (state, name: string) => { 107 | state.user = name; 108 | } 109 | ) 110 | .setLabel('Change Name') 111 | .setEventType('SetName'); 112 | 113 | const addTodoAction = createAction( 114 | (state, todo: Todo) => { 115 | state.todos.push(todo); 116 | if (todo.status === 'incomplete') state.totalIncomplete += 1; 117 | else state.totalCompleted += 1; 118 | state.totalTodos += 1; 119 | } 120 | ) 121 | .setLabel('Add Todo') 122 | .setEventType('AddTodo'); 123 | 124 | const addLogAction = createAction( 125 | (state, log: string) => { 126 | state.logs.add(log); 127 | } 128 | ) 129 | .setLabel('Log') 130 | .setEventType('Log'); 131 | 132 | const addMapAction = createAction( 133 | (state, key, value) => { 134 | state.map.set(key, value); 135 | } 136 | ).setLabel('Map'); 137 | 138 | provenance.done(); 139 | return { 140 | provenance, 141 | changeName, 142 | addTodoAction, 143 | addLogAction, 144 | addMapAction, 145 | initState: initialTodoState, 146 | }; 147 | } 148 | export class Test { 149 | arr: string[] = []; 150 | 151 | constructor(arr: string[]) { 152 | this.arr = arr; 153 | } 154 | 155 | add(str: string) { 156 | this.arr.push(str); 157 | } 158 | 159 | remove(str: string) { 160 | this.arr = this.arr.filter((a) => a !== str); 161 | } 162 | 163 | size() { 164 | return this.arr.length; 165 | } 166 | 167 | elements() { 168 | return this.arr; 169 | } 170 | 171 | static serialize(obj: Test) { 172 | return { arr: obj.arr }; 173 | } 174 | 175 | static deserialize(val: any) { 176 | return new Test(val.arr); 177 | } 178 | } 179 | 180 | export function customSerializerTest() { 181 | const provenance = initProvenance(new Test([]), { 182 | _serializer: Test.serialize, 183 | _deserializer: Test.deserialize, 184 | }); 185 | 186 | const action = createAction((state, str) => { 187 | state.add(str); 188 | }).setLabel('Add'); 189 | 190 | provenance.done(); 191 | return { provenance, action }; 192 | } 193 | -------------------------------------------------------------------------------- /packages/trrack-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src", "types"], 3 | "compilerOptions": { 4 | "module": "esnext", 5 | "target": "ESNext", 6 | "lib": ["dom", "esnext"], 7 | "importHelpers": true, 8 | "declaration": true, 9 | "sourceMap": true, 10 | "rootDir": "./src", 11 | "strict": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "moduleResolution": "node", 17 | "jsx": "react", 18 | "esModuleInterop": true, 19 | "skipLibCheck": true, 20 | "forceConsistentCasingInFileNames": true, 21 | "noEmit": true, 22 | "experimentalDecorators": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/trrack-core/tsdx.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // This function will run for each entry/format/env combination 3 | rollup(config, options) { 4 | if (config.output.format === 'umd') { 5 | config.output.globals['lz-string'] = 'LZString'; 6 | config.output.globals['deep-diff'] = 'DeepDiff'; 7 | } 8 | return config; // always return a config. 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/trrack-core/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode":"file", 3 | "out": "docs" 4 | } 5 | -------------------------------------------------------------------------------- /packages/trrack-examples/basic-js-example/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/visdesignlab/trrack/compare/basic-js-example@1.0.1-alpha.4...basic-js-example@1.0.1) (2022-12-16) 7 | 8 | **Note:** Version bump only for package basic-js-example 9 | 10 | 11 | 12 | 13 | 14 | ## [1.0.1-alpha.4](https://github.com/visdesignlab/trrack/compare/basic-js-example@1.0.1-alpha.3...basic-js-example@1.0.1-alpha.4) (2021-05-07) 15 | 16 | **Note:** Version bump only for package basic-js-example 17 | 18 | 19 | 20 | 21 | 22 | ## [1.0.1-alpha.3](https://github.com/visdesignlab/trrack/compare/basic-js-example@1.0.1-alpha.2...basic-js-example@1.0.1-alpha.3) (2021-05-07) 23 | 24 | **Note:** Version bump only for package basic-js-example 25 | 26 | 27 | 28 | 29 | 30 | ## [1.0.1-alpha.2](https://github.com/visdesignlab/trrack/compare/basic-js-example@1.0.1-alpha.1...basic-js-example@1.0.1-alpha.2) (2021-05-07) 31 | 32 | **Note:** Version bump only for package basic-js-example 33 | 34 | 35 | 36 | 37 | 38 | ## [1.0.1-alpha.1](https://github.com/visdesignlab/trrack/compare/basic-js-example@1.0.1-alpha.0...basic-js-example@1.0.1-alpha.1) (2020-12-16) 39 | 40 | **Note:** Version bump only for package basic-js-example 41 | 42 | 43 | 44 | 45 | 46 | ## 1.0.1-alpha.0 (2020-11-04) 47 | 48 | 49 | ### Bug Fixes 50 | 51 | * 🐛 Build process for basic examples ([2d755bc](https://github.com/visdesignlab/trrack/commit/2d755bc2e91034456b9b01e12f7516d215312eeb)) 52 | * 🐛 Made example private ([3047f95](https://github.com/visdesignlab/trrack/commit/3047f9542be9ce11f26637c30e460f4360102bed)) 53 | -------------------------------------------------------------------------------- /packages/trrack-examples/basic-js-example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Basic Trrack Example with JS 6 | 7 | 8 | 9 |

10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 39 | 40 | -------------------------------------------------------------------------------- /packages/trrack-examples/basic-js-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basic-js-example", 3 | "private": true, 4 | "version": "1.0.1", 5 | "main": "index.js", 6 | "author": "Kiran Gadhave", 7 | "license": "MIT", 8 | "scripts": { 9 | "build": "rm -rf ./dist && mkdir -p ../dist-basic-js && cp -r ./* ../dist-basic-js && mv ../dist-basic-js ./dist && rm -rf ./dist/package.json" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/trrack-examples/basic-vis-example/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/visdesignlab/trrack/compare/basic-vis-example@1.0.1-alpha.4...basic-vis-example@1.0.1) (2022-12-16) 7 | 8 | **Note:** Version bump only for package basic-vis-example 9 | 10 | 11 | 12 | 13 | 14 | ## [1.0.1-alpha.4](https://github.com/visdesignlab/trrack/compare/basic-vis-example@1.0.1-alpha.3...basic-vis-example@1.0.1-alpha.4) (2021-05-07) 15 | 16 | **Note:** Version bump only for package basic-vis-example 17 | 18 | 19 | 20 | 21 | 22 | ## [1.0.1-alpha.3](https://github.com/visdesignlab/trrack/compare/basic-vis-example@1.0.1-alpha.2...basic-vis-example@1.0.1-alpha.3) (2021-05-07) 23 | 24 | **Note:** Version bump only for package basic-vis-example 25 | 26 | 27 | 28 | 29 | 30 | ## [1.0.1-alpha.2](https://github.com/visdesignlab/trrack/compare/basic-vis-example@1.0.1-alpha.1...basic-vis-example@1.0.1-alpha.2) (2021-05-07) 31 | 32 | **Note:** Version bump only for package basic-vis-example 33 | 34 | 35 | 36 | 37 | 38 | ## [1.0.1-alpha.1](https://github.com/visdesignlab/trrack/compare/basic-vis-example@1.0.1-alpha.0...basic-vis-example@1.0.1-alpha.1) (2020-12-16) 39 | 40 | **Note:** Version bump only for package basic-vis-example 41 | 42 | 43 | 44 | 45 | 46 | ## 1.0.1-alpha.0 (2020-11-04) 47 | 48 | 49 | ### Bug Fixes 50 | 51 | * 🐛 Build process for basic examples ([2d755bc](https://github.com/visdesignlab/trrack/commit/2d755bc2e91034456b9b01e12f7516d215312eeb)) 52 | -------------------------------------------------------------------------------- /packages/trrack-examples/basic-vis-example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Basic Trrack Vis Example with JS 6 | 7 | 8 |
9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 141 | 142 | -------------------------------------------------------------------------------- /packages/trrack-examples/basic-vis-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basic-vis-example", 3 | "private": true, 4 | "version": "1.0.1", 5 | "main": "index.js", 6 | "author": "Kiran Gadhave", 7 | "license": "MIT", 8 | "scripts": { 9 | "build": "rm -rf ./dist && mkdir -p ../dist-basic-vis && cp -r ./* ../dist-basic-vis && mv ../dist-basic-vis ./dist && rm -rf ./dist/package.json" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExample/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [1.1.0](https://github.com/visdesignlab/trrack/compare/example-lesmis@1.1.0-alpha.9...example-lesmis@1.1.0) (2022-12-16) 7 | 8 | **Note:** Version bump only for package example-lesmis 9 | 10 | 11 | 12 | 13 | 14 | # [1.1.0-alpha.9](https://github.com/visdesignlab/trrack/compare/example-lesmis@1.1.0-alpha.8...example-lesmis@1.1.0-alpha.9) (2021-05-07) 15 | 16 | **Note:** Version bump only for package example-lesmis 17 | 18 | 19 | 20 | 21 | 22 | # [1.1.0-alpha.8](https://github.com/visdesignlab/trrack/compare/example-lesmis@1.1.0-alpha.7...example-lesmis@1.1.0-alpha.8) (2021-05-07) 23 | 24 | **Note:** Version bump only for package example-lesmis 25 | 26 | 27 | 28 | 29 | 30 | # [1.1.0-alpha.7](https://github.com/visdesignlab/trrack/compare/example-lesmis@1.1.0-alpha.6...example-lesmis@1.1.0-alpha.7) (2021-05-07) 31 | 32 | **Note:** Version bump only for package example-lesmis 33 | 34 | 35 | 36 | 37 | 38 | # [1.1.0-alpha.6](https://github.com/visdesignlab/trrack/compare/example-lesmis@1.1.0-alpha.5...example-lesmis@1.1.0-alpha.6) (2020-12-16) 39 | 40 | **Note:** Version bump only for package example-lesmis 41 | 42 | 43 | 44 | 45 | 46 | # [1.1.0-alpha.5](https://github.com/visdesignlab/trrack/compare/example-lesmis@1.1.0-alpha.4...example-lesmis@1.1.0-alpha.5) (2020-12-16) 47 | 48 | **Note:** Version bump only for package example-lesmis 49 | 50 | 51 | 52 | 53 | 54 | # [1.1.0-alpha.4](https://github.com/visdesignlab/trrack/compare/example-lesmis@1.1.0-alpha.3...example-lesmis@1.1.0-alpha.4) (2020-11-04) 55 | 56 | **Note:** Version bump only for package example-lesmis 57 | 58 | 59 | 60 | 61 | 62 | # [1.1.0-alpha.3](https://github.com/visdesignlab/trrack/compare/example-lesmis@1.1.0-alpha.2...example-lesmis@1.1.0-alpha.3) (2020-11-04) 63 | 64 | **Note:** Version bump only for package example-lesmis 65 | 66 | 67 | 68 | 69 | 70 | # [1.1.0-alpha.2](https://github.com/visdesignlab/trrack/compare/example-lesmis@1.1.0-alpha.1...example-lesmis@1.1.0-alpha.2) (2020-10-21) 71 | 72 | **Note:** Version bump only for package example-lesmis 73 | 74 | 75 | 76 | 77 | 78 | # [1.1.0-alpha.1](https://github.com/visdesignlab/trrack/compare/example-lesmis@1.1.0-alpha.0...example-lesmis@1.1.0-alpha.1) (2020-10-20) 79 | 80 | **Note:** Version bump only for package example-lesmis 81 | 82 | 83 | 84 | 85 | 86 | # 1.1.0-alpha.0 (2020-10-20) 87 | 88 | 89 | ### Bug Fixes 90 | 91 | * 🐛 Added landing page and links ([98afd5d](https://github.com/visdesignlab/trrack/commit/98afd5d0537e49dce82b3cddb3e7547c2d3ffa84)) 92 | * 🐛 Fixed build process + added dependency for examples ([572e8cd](https://github.com/visdesignlab/trrack/commit/572e8cd8675003030ac942036201868383569835)) 93 | * 🐛 making example circles work on firefox, button style ([28e3e20](https://github.com/visdesignlab/trrack/commit/28e3e20063e40a3fc45ea1bbbeffab41f72ea4e3)) 94 | * 🐛 more syntax changes, example updates ([b6dc7ff](https://github.com/visdesignlab/trrack/commit/b6dc7ff5d7d7f8fcc669d46837e4c37210d7e32a)) 95 | 96 | 97 | ### Features 98 | 99 | * 🎸 adding examples ([994edd7](https://github.com/visdesignlab/trrack/commit/994edd76ec1be5d7aef9b3d17e097868817a702f)) 100 | * 🎸 Fixed examples and build process ([647b978](https://github.com/visdesignlab/trrack/commit/647b9789dd04a37c70395d08e547fc82adcccab7)) 101 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExample/README.md: -------------------------------------------------------------------------------- 1 | To run this example, use the following commands 2 | 3 | npm install 4 | npm start 5 | 6 | Will host the application at localhost:8080 7 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExample/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Examples 6 | 7 | 8 | 9 | 10 | 11 | 134 | 135 | 136 | 137 |
Les Misérables Character Co-Occurence
138 |
139 | 140 | 141 |
142 |
143 | 144 |
145 |
146 | 147 |
148 |
149 |
150 |
151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExample/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-lesmis", 3 | "private": true, 4 | "version": "1.1.0", 5 | "description": "Examples for using provenance library", 6 | "main": "index.js", 7 | "scripts": { 8 | "build": "webpack", 9 | "test": "test", 10 | "start": "webpack-dev-server --mode development" 11 | }, 12 | "license": "ISC", 13 | "dependencies": { 14 | "@types/react": "^16.9.38", 15 | "@types/react-dom": "^16.9.8", 16 | "@visdesignlab/trrack": "^2.0.0", 17 | "@visdesignlab/trrack-vis": "^2.0.0", 18 | "d3": "^5.12.0", 19 | "font-awesome": "^4.7.0", 20 | "http-server": "^0.11.1", 21 | "react": "^16.13.1", 22 | "react-dom": "^16.13.1", 23 | "react-move": "^6.1.0", 24 | "semantic-ui-react": "^0.88.2", 25 | "typestyle": "^2.1.0" 26 | }, 27 | "devDependencies": { 28 | "@types/d3": "^5.7.2", 29 | "awesome-typescript-loader": "^5.2.1", 30 | "clean-webpack-plugin": "^3.0.0", 31 | "copy-webpack-plugin": "^6.2.1", 32 | "css-loader": "^5.0.0", 33 | "html-webpack-plugin": "^4.5.0", 34 | "prettier": "^1.18.2", 35 | "style-loader": "^2.0.0", 36 | "ts-loader": "^8.0.5", 37 | "tslint": "^5.19.0", 38 | "tslint-config-prettier": "^1.18.0", 39 | "typescript": "^4.0.3", 40 | "webpack": "^4.0.0", 41 | "webpack-cli": "^4.0.0", 42 | "webpack-dev-server": "^3.8.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExample/src/FDBar.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | /* eslint-disable class-methods-use-this */ 3 | import * as d3 from 'd3'; 4 | 5 | export default class Bars { 6 | private graph = undefined; 7 | 8 | private hoverOver: (id: string) => void; 9 | 10 | private hoverOut: () => void; 11 | 12 | private select: (id: string) => void; 13 | 14 | constructor(graph, hoverOver, hoverOut, select) { 15 | this.graph = graph; 16 | 17 | this.hoverOver = hoverOver; 18 | this.hoverOut = hoverOut; 19 | this.select = select; 20 | 21 | this.drawBar(); 22 | } 23 | 24 | drawBar() { 25 | const nodeMap = new Map(); 26 | 27 | for (let i = 0; i < this.graph.links.length; i += 1) { 28 | if (nodeMap.get(this.graph.links[i].source.id) === undefined) { 29 | nodeMap.set(this.graph.links[i].source.id, 1); 30 | } else { 31 | nodeMap.set(this.graph.links[i].source.id, nodeMap.get(this.graph.links[i].source.id) + 1); 32 | } 33 | if (nodeMap.get(this.graph.links[i].target.id) === undefined) { 34 | nodeMap.set(this.graph.links[i].target.id, 1); 35 | } else { 36 | nodeMap.set(this.graph.links[i].target.id, nodeMap.get(this.graph.links[i].target.id) + 1); 37 | } 38 | } 39 | 40 | const bars = d3.select('#bars'); 41 | 42 | const width:number = +bars.attr('width') - 10; 43 | const height:number = +bars.attr('height') - 10; 44 | 45 | const edgePadding = 50; 46 | const bottomPadding = 90; 47 | const rightPadding = 4; 48 | const barPadding = 1; 49 | 50 | const g = bars.append('g'); 51 | 52 | const scale = d3.scaleLinear(); 53 | 54 | let keyList:string[] = Array.from(nodeMap.keys()); 55 | const values:number[] = Array.from(nodeMap.values()); 56 | 57 | keyList = keyList.sort((x, y) => { 58 | if (nodeMap.get(x) < nodeMap.get(y)) { 59 | return 1; 60 | } if (nodeMap.get(x) > nodeMap.get(y)) { 61 | return -1; 62 | } 63 | return 0; 64 | }); 65 | 66 | scale.domain([0, Math.max(...values)]); 67 | scale.range([0, height - bottomPadding * 2]); 68 | 69 | // setting up the bar chart, as well as the bar chart on click and hover. 70 | g 71 | .selectAll('rect') 72 | .data(keyList) 73 | .enter() 74 | .append('rect') 75 | .attr('x', (d : any, i) => i * ((width - edgePadding) / keyList.length) + barPadding + edgePadding - rightPadding) 76 | .attr('y', (d : any) => height - scale(nodeMap.get(d)) - bottomPadding) 77 | .attr('width', (width - edgePadding) / keyList.length - barPadding) 78 | .attr('height', (d : any) => scale(nodeMap.get(d))) 79 | .attr('id', (d:any) => `${d}B`) 80 | .classed('bar', true) 81 | .on('click', (d) => this.select(d)) 82 | .on('mouseover', (d) => this.hoverOver(d)) 83 | .on('mouseout', (d) => this.hoverOut()); 84 | 85 | const fakeScale = d3.scaleLinear(); 86 | 87 | fakeScale.domain([Math.max(...values), 0]); 88 | fakeScale.range([0, height - bottomPadding * 2]); 89 | 90 | const yAxis = d3.axisLeft(fakeScale); 91 | 92 | const xScale = d3.scaleBand() 93 | .domain(keyList) 94 | .range([1 + edgePadding - rightPadding, 1 + width - rightPadding]); 95 | 96 | const xAxis = d3.axisBottom(xScale); 97 | // These are the axis 98 | g.append('g') 99 | .attr('class', 'axis') 100 | .attr('transform', `translate(${edgePadding - 6},${bottomPadding})`) 101 | .call(yAxis); 102 | 103 | // the y axis 104 | g.append('g') 105 | .attr('class', 'axis') 106 | .attr('transform', `translate(0,${height - bottomPadding + 1})`) 107 | .call(xAxis) 108 | .selectAll('text') 109 | .style('text-anchor', 'end') 110 | .attr('font-size', '11px') 111 | .attr('dx', '-1em') 112 | .attr('dy', '-.45em') 113 | .attr('transform', 'rotate(-90)'); 114 | // The title 115 | g.append('text') 116 | .attr('transform', 'rotate(-90)') 117 | .attr('y', 0) 118 | .attr('x', 0 - (height / 2)) 119 | .attr('dy', '1em') 120 | .style('text-anchor', 'middle') 121 | .text('Node Degree'); 122 | } 123 | 124 | selectBar(id) { 125 | this.deselectAllBars(); 126 | 127 | d3.select('#bars') 128 | .selectAll(`rect[id='${id}B']`) 129 | .classed('barSelected', true); 130 | } 131 | 132 | deselectAllBars() { 133 | d3.select('#bars') 134 | .selectAll('.barSelected') 135 | .classed('barSelected', false); 136 | } 137 | 138 | hoverBar(id) { 139 | d3.select('#bars') 140 | .selectAll(`rect[id='${id}B']`) 141 | .classed('barHover', true); 142 | } 143 | 144 | dehoverBars() { 145 | d3.select('#bars') 146 | .selectAll('.barHover') 147 | .classed('barHover', false); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExample/src/FDGraph.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | /* eslint-disable prefer-destructuring */ 3 | /* eslint-disable class-methods-use-this */ 4 | /* eslint-disable no-unused-vars */ 5 | import * as d3 from 'd3'; 6 | 7 | export default class Graph { 8 | graph:any; 9 | 10 | private hoverOver: (id: any) => void; 11 | 12 | private hoverOut: () => void; 13 | 14 | private select: (id: any) => void; 15 | 16 | private dragEnded: (d: any) => void; 17 | 18 | constructor(graph, hoverOver, hoverOut, select, dragEnded) { 19 | this.graph = graph; 20 | 21 | this.hoverOver = hoverOver; 22 | this.hoverOut = hoverOut; 23 | this.select = select; 24 | this.dragEnded = dragEnded; 25 | 26 | this.drawGraph(); 27 | } 28 | 29 | drawGraph() { 30 | const svg = d3.select('#viz'); 31 | 32 | svg.selectAll('g').remove(); 33 | 34 | const color = d3.scaleOrdinal(d3.schemeCategory10); 35 | 36 | svg.append('button') 37 | .attr('class', 'undoRedoButton'); 38 | 39 | const link = svg.append('g') 40 | .attr('class', 'links') 41 | .selectAll('line') 42 | .data(this.graph.links) 43 | .join('line') 44 | .attr('stroke-width', (d:any) => Math.sqrt(d.value)); 45 | 46 | const node = svg.append('g') 47 | .classed('circleG', true) 48 | .selectAll('circle') 49 | .data(this.graph.nodes) 50 | .join('circle') 51 | .classed('nodes', true) 52 | .attr('id', (d:any) => `${d.id}N`) 53 | .attr('fill', (d:any) => color(d.group)) 54 | .attr('r', 5) 55 | .call(d3.drag() 56 | .on('drag', (d) => this.dragged(d, link, node)) 57 | .on('end', (d) => this.dragEnded(d))) 58 | .on('click', (d) => this.select(d)) 59 | .on('mouseover', (d) => this.hoverOver(d)) 60 | .on('mouseout', (d) => this.hoverOut()); 61 | 62 | node 63 | .append('title') 64 | .text((d) => d.id); 65 | 66 | this.draw(link, node); 67 | } 68 | 69 | dragged(d: any, link, node) { 70 | d.x = d3.event.x; 71 | d.y = d3.event.y; 72 | this.draw(link, node); 73 | } 74 | 75 | draw(link, node) { 76 | link 77 | .attr('x1', (d:any) => d.source.x) 78 | .attr('y1', (d:any) => d.source.y) 79 | .attr('x2', (d:any) => d.target.x) 80 | .attr('y2', (d:any) => d.target.y); 81 | 82 | node 83 | .attr('cx', (d:any) => d.x) 84 | .attr('cy', (d:any) => d.y); 85 | } 86 | 87 | selectNode(id) { 88 | this.deselectAllNodes(); 89 | 90 | d3.select(`circle[id='${id}N']`) 91 | .classed('selectedNode', true) 92 | .attr('r', 10); 93 | 94 | const edges = this.graph.links.filter((d) => d.source.id === id || d.target.id === id); 95 | 96 | edges.forEach((data) => { 97 | d3.select(`circle[id='${data.source.id}N']`).filter((d:any) => d.id !== id) 98 | .classed('nodeEdges', true); 99 | 100 | d3.select(`circle[id='${data.target.id}N']`).filter((d:any) => d.id !== id) 101 | .classed('nodeEdges', true); 102 | }); 103 | } 104 | 105 | deselectAllNodes() { 106 | d3.select('#viz') 107 | .selectAll('circle') 108 | .classed('nodeEdges', false) 109 | .classed('selectedNode', false) 110 | .attr('r', 5); 111 | } 112 | 113 | hoverNode(id) { 114 | d3.select('#viz') 115 | .select(`circle[id='${id}N']`) 116 | .classed('hoverNode', true) 117 | .attr('r', 8); 118 | } 119 | 120 | dehoverNodes() { 121 | const s = d3.select('#viz') 122 | .select('.hoverNode') 123 | .classed('hoverNode', false); 124 | 125 | if (s.classed('selectedNode')) { 126 | s.attr('r', 10); 127 | } else { 128 | s.attr('r', 5); 129 | } 130 | } 131 | 132 | moveNodes(newGraph) { 133 | for (const i of this.graph.nodes) { 134 | i.x = newGraph[i.id][0]; 135 | i.y = newGraph[i.id][1]; 136 | } 137 | 138 | for (const i of this.graph.links) { 139 | i.source.x = newGraph[i.source.id][0]; 140 | i.source.y = newGraph[i.source.id][1]; 141 | i.target.x = newGraph[i.target.id][0]; 142 | i.target.y = newGraph[i.target.id][1]; 143 | } 144 | 145 | const link = d3.select('#viz').select('g.links').selectAll('line'); 146 | 147 | const node = d3.select('#viz').select('g.circleG').selectAll('circle'); 148 | 149 | this.draw(link, node); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExample/src/ProvenanceSetup.ts: -------------------------------------------------------------------------------- 1 | // 2 | import * as d3 from 'd3'; 3 | import { 4 | initProvenance, Provenance, NodeID, createAction, 5 | } from '@visdesignlab/trrack'; 6 | import { ProvVisCreator } from '@visdesignlab/trrack-vis'; 7 | import Bars from './FDBar'; 8 | import Graph from './FDGraph'; 9 | 10 | export interface NodeState { 11 | nodeMap: {}; 12 | selectedNode: string; 13 | } 14 | 15 | const initialState: NodeState = { 16 | nodeMap: {}, 17 | selectedNode: 'none', 18 | }; 19 | 20 | d3.json('./data/miserables.json').then((graph) => { 21 | runSimulation(graph); 22 | 23 | const provenance = setupProvenance(graph); 24 | 25 | const hoverOver = function (currData) { 26 | if (currData.id) { 27 | barVis.hoverBar(currData.id); 28 | graphVis.hoverNode(currData.id); 29 | } else { 30 | barVis.hoverBar(currData); 31 | graphVis.hoverNode(currData); 32 | } 33 | }; 34 | 35 | const hoverOut = function () { 36 | barVis.dehoverBars(); 37 | graphVis.dehoverNodes(); 38 | }; 39 | 40 | /** 41 | * Two callback functions where the actions are applied. Subsequently calls the observers, which 42 | * changes the les mis vis and updates the prov vis. 43 | */ 44 | const selectAction = createAction((state: NodeState, newSelected: string) => { 45 | state.selectedNode = newSelected; 46 | }); 47 | 48 | const select = function (currData) { 49 | selectAction.setLabel(currData.id ? `${currData.id} Selected` : `${currData} Selected`); 50 | 51 | provenance.apply(selectAction(currData.id ? currData.id : currData)); 52 | }; 53 | 54 | const dragAction = createAction((state: NodeState, x: number, y: number, id: string) => { 55 | console.log(id, x, y); 56 | state.nodeMap[id][0] = x; 57 | state.nodeMap[id][1] = y; 58 | }); 59 | 60 | const dragEnded = function (d) { 61 | // Doing this so clicking on node-link doesnt cause two state changes. 62 | const state = provenance.getState(provenance.current); 63 | 64 | if ( 65 | state.nodeMap[d.id][0] >= d.x - 0.1 66 | && state.nodeMap[d.id][0] <= d.x + 0.1 67 | && state.nodeMap[d.id][1] >= d.y - 0.1 68 | && state.nodeMap[d.id][1] <= d.y + 0.1 69 | ) { 70 | return; 71 | } 72 | 73 | dragAction.setLabel(`${d.id} Moved`); 74 | 75 | console.log(d.x, d.y); 76 | 77 | provenance.apply(dragAction(d.x, d.y, d.id)); 78 | }; 79 | 80 | const barVis = new Bars(graph, hoverOver, hoverOut, select); 81 | const graphVis = new Graph(graph, hoverOver, hoverOut, select, dragEnded); 82 | 83 | /* 84 | * Setting up observers. Called on state changed. 85 | */ 86 | 87 | provenance.addObserver( 88 | (state) => state.selectedNode, 89 | () => { 90 | barVis.selectBar(provenance.getState(provenance.current).selectedNode); 91 | graphVis.selectNode(provenance.getState(provenance.current).selectedNode); 92 | }, 93 | ); 94 | 95 | provenance.addObserver( 96 | (state) => state.nodeMap, 97 | () => { 98 | graphVis.moveNodes(provenance.getState(provenance.current).nodeMap); 99 | }, 100 | ); 101 | 102 | provenance.done(); 103 | 104 | /** 105 | * 106 | * Setting up undo/redo keys 107 | * 108 | */ 109 | document.onkeydown = function (e) { 110 | const mac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform); 111 | console.log(mac); 112 | 113 | if (!e.shiftKey && (mac ? e.metaKey : e.ctrlKey) && e.which === 90) { 114 | undo(); 115 | } else if (e.shiftKey && (mac ? e.metaKey : e.ctrlKey) && e.which === 90) { 116 | redo(); 117 | } 118 | }; 119 | 120 | function undo() { 121 | provenance.goBackOneStep(); 122 | } 123 | 124 | function redo() { 125 | if (provenance.current.children.length === 0) { 126 | return; 127 | } 128 | provenance.goToNode(provenance.current.children[provenance.current.children.length - 1]); 129 | } 130 | 131 | ProvVisCreator(document.getElementById('provDiv')!, provenance, (id: NodeID) => { 132 | provenance.goToNode(id); 133 | }); 134 | }); 135 | 136 | /* 137 | * Creates starting state. Is called after running simulations. 138 | * Creates and returns provenance object. 139 | */ 140 | function setupProvenance(graph): Provenance { 141 | const dict = {}; 142 | const arr: any[] = graph.nodes; 143 | 144 | for (let i = 0; i < arr.length; i += 1) { 145 | const curr = arr[i].id; 146 | dict[curr] = [arr[i].x, arr[i].y]; 147 | } 148 | 149 | initialState.nodeMap = dict; 150 | 151 | const provenance = initProvenance(initialState); 152 | 153 | return provenance; 154 | } 155 | 156 | /* 157 | * Runs the force simulation 300 times. Only done at beginning to find initial placement. 158 | * 159 | */ 160 | 161 | function runSimulation(graph) { 162 | const simulation = d3 163 | .forceSimulation() 164 | .force( 165 | 'link', 166 | d3.forceLink().id((d: any) => d.id), 167 | ) 168 | .force('charge', d3.forceManyBody()) 169 | .force('center', d3.forceCenter(800 / 2, 1000 / 2)); 170 | 171 | simulation.nodes(graph.nodes); 172 | 173 | simulation.force>('link').links(graph.links); 174 | 175 | for (let i = 0; i < 300; i += 1) { 176 | simulation.tick(); 177 | } 178 | 179 | return simulation; 180 | } 181 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExample/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": false, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "jsx": "react", 15 | "downlevelIteration": true 16 | }, 17 | "include": ["src/**/*"], 18 | "exclude": ["node_modules", "*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExample/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 5 | const CopyPlugin = require('copy-webpack-plugin'); 6 | 7 | const config = { 8 | entry: './src/ProvenanceSetup.ts', 9 | output: { 10 | path: path.resolve(__dirname, 'dist'), 11 | filename: 'bundle.js', 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | loader: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | { 21 | test: /\.css$/, 22 | use: ['style-loader', 'css-loader'], 23 | }, 24 | ], 25 | }, 26 | resolve: { 27 | extensions: ['.tsx', '.ts', '.js'], 28 | }, 29 | plugins: [ 30 | new CopyPlugin({ 31 | patterns: [{ from: './data', to: './data/' }], 32 | }), 33 | new HtmlWebpackPlugin({ 34 | template: './index.html', 35 | }), 36 | new CleanWebpackPlugin(), 37 | ], 38 | }; 39 | 40 | module.exports = config; 41 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExampleFirebaseIntegration/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [1.1.0](https://github.com/visdesignlab/trrack/compare/example-lesmis-firebase@1.1.0-alpha.9...example-lesmis-firebase@1.1.0) (2022-12-16) 7 | 8 | **Note:** Version bump only for package example-lesmis-firebase 9 | 10 | 11 | 12 | 13 | 14 | # [1.1.0-alpha.9](https://github.com/visdesignlab/trrack/compare/example-lesmis-firebase@1.1.0-alpha.8...example-lesmis-firebase@1.1.0-alpha.9) (2021-05-07) 15 | 16 | **Note:** Version bump only for package example-lesmis-firebase 17 | 18 | 19 | 20 | 21 | 22 | # [1.1.0-alpha.8](https://github.com/visdesignlab/trrack/compare/example-lesmis-firebase@1.1.0-alpha.7...example-lesmis-firebase@1.1.0-alpha.8) (2021-05-07) 23 | 24 | **Note:** Version bump only for package example-lesmis-firebase 25 | 26 | 27 | 28 | 29 | 30 | # [1.1.0-alpha.7](https://github.com/visdesignlab/trrack/compare/example-lesmis-firebase@1.1.0-alpha.6...example-lesmis-firebase@1.1.0-alpha.7) (2021-05-07) 31 | 32 | **Note:** Version bump only for package example-lesmis-firebase 33 | 34 | 35 | 36 | 37 | 38 | # [1.1.0-alpha.6](https://github.com/visdesignlab/trrack/compare/example-lesmis-firebase@1.1.0-alpha.5...example-lesmis-firebase@1.1.0-alpha.6) (2020-12-16) 39 | 40 | **Note:** Version bump only for package example-lesmis-firebase 41 | 42 | 43 | 44 | 45 | 46 | # [1.1.0-alpha.5](https://github.com/visdesignlab/trrack/compare/example-lesmis-firebase@1.1.0-alpha.4...example-lesmis-firebase@1.1.0-alpha.5) (2020-12-16) 47 | 48 | **Note:** Version bump only for package example-lesmis-firebase 49 | 50 | 51 | 52 | 53 | 54 | # [1.1.0-alpha.4](https://github.com/visdesignlab/trrack/compare/example-lesmis-firebase@1.1.0-alpha.3...example-lesmis-firebase@1.1.0-alpha.4) (2020-11-04) 55 | 56 | **Note:** Version bump only for package example-lesmis-firebase 57 | 58 | 59 | 60 | 61 | 62 | # [1.1.0-alpha.3](https://github.com/visdesignlab/trrack/compare/example-lesmis-firebase@1.1.0-alpha.2...example-lesmis-firebase@1.1.0-alpha.3) (2020-11-04) 63 | 64 | **Note:** Version bump only for package example-lesmis-firebase 65 | 66 | 67 | 68 | 69 | 70 | # [1.1.0-alpha.2](https://github.com/visdesignlab/trrack/compare/example-lesmis-firebase@1.1.0-alpha.1...example-lesmis-firebase@1.1.0-alpha.2) (2020-10-21) 71 | 72 | **Note:** Version bump only for package example-lesmis-firebase 73 | 74 | 75 | 76 | 77 | 78 | # [1.1.0-alpha.1](https://github.com/visdesignlab/trrack/compare/example-lesmis-firebase@1.1.0-alpha.0...example-lesmis-firebase@1.1.0-alpha.1) (2020-10-20) 79 | 80 | **Note:** Version bump only for package example-lesmis-firebase 81 | 82 | 83 | 84 | 85 | 86 | # 1.1.0-alpha.0 (2020-10-20) 87 | 88 | 89 | ### Bug Fixes 90 | 91 | * 🐛 Fixed build process + added dependency for examples ([572e8cd](https://github.com/visdesignlab/trrack/commit/572e8cd8675003030ac942036201868383569835)) 92 | * 🐛 Fixing examples ([d574a07](https://github.com/visdesignlab/trrack/commit/d574a07e5708895d8da9a1f472fad49d815e982b)) 93 | * 🐛 making example circles work on firefox, button style ([28e3e20](https://github.com/visdesignlab/trrack/commit/28e3e20063e40a3fc45ea1bbbeffab41f72ea4e3)) 94 | * 🐛 more syntax changes, example updates ([b6dc7ff](https://github.com/visdesignlab/trrack/commit/b6dc7ff5d7d7f8fcc669d46837e4c37210d7e32a)) 95 | 96 | 97 | ### Features 98 | 99 | * 🎸 adding examples ([994edd7](https://github.com/visdesignlab/trrack/commit/994edd76ec1be5d7aef9b3d17e097868817a702f)) 100 | * 🎸 Fixed examples and build process ([647b978](https://github.com/visdesignlab/trrack/commit/647b9789dd04a37c70395d08e547fc82adcccab7)) 101 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExampleFirebaseIntegration/README.md: -------------------------------------------------------------------------------- 1 | To run this example, use the following commands 2 | 3 | npm install 4 | npm start 5 | 6 | Will host the application at localhost:8080 7 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExampleFirebaseIntegration/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Examples 6 | 7 | 8 | 9 | 10 | 11 | 134 | 135 | 136 | 137 |
Les Misérables Character Co-Occurence
138 | 139 | 140 |
141 |
142 | 143 |
144 |
145 | 146 |
147 |
148 |
149 |
150 | 151 | 152 | 153 |
154 | 155 | 156 |
157 | 158 | 159 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExampleFirebaseIntegration/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-lesmis-firebase", 3 | "private": true, 4 | "version": "1.1.0", 5 | "description": "Examples for using provenance library", 6 | "main": "index.js", 7 | "scripts": { 8 | "build": "webpack", 9 | "test": "test", 10 | "start": "webpack-dev-server --mode development --max-http-header-size 256048576 " 11 | }, 12 | "author": "Zach Cutler", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@types/react": "^16.9.38", 16 | "@types/react-dom": "^16.9.8", 17 | "@visdesignlab/trrack": "^2.0.0", 18 | "@visdesignlab/trrack-vis": "^2.0.0", 19 | "d3": "^5.12.0", 20 | "http-server": "^0.11.1", 21 | "react": "^16.13.1", 22 | "react-dom": "^16.13.1", 23 | "react-move": "^6.1.0", 24 | "semantic-ui-react": "^0.88.2", 25 | "typestyle": "^2.1.0" 26 | }, 27 | "devDependencies": { 28 | "@types/d3": "^5.7.2", 29 | "awesome-typescript-loader": "^5.2.1", 30 | "clean-webpack-plugin": "^3.0.0", 31 | "copy-webpack-plugin": "^6.2.1", 32 | "css-loader": "^5.0.0", 33 | "html-webpack-plugin": "^4.5.0", 34 | "prettier": "^1.18.2", 35 | "style-loader": "^2.0.0", 36 | "ts-loader": "^8.0.5", 37 | "tslint": "^5.19.0", 38 | "tslint-config-prettier": "^1.18.0", 39 | "typescript": "^4.0.3", 40 | "webpack": "^4.0.0", 41 | "webpack-cli": "^4.0.0", 42 | "webpack-dev-server": "^3.8.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExampleFirebaseIntegration/src/FDBar.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | /* eslint-disable no-unused-vars */ 3 | import * as d3 from 'd3'; 4 | import { NodeState } from './ProvenanceSetup'; 5 | 6 | export default class Bars { 7 | private graph = undefined; 8 | 9 | private hoverOver: (id: string) => void; 10 | 11 | private hoverOut: () => void; 12 | 13 | private select: (id: string) => void; 14 | 15 | constructor(graph, hoverOver, hoverOut, select) { 16 | this.graph = graph; 17 | 18 | this.hoverOver = hoverOver; 19 | this.hoverOut = hoverOut; 20 | this.select = select; 21 | 22 | this.drawBar(); 23 | } 24 | 25 | drawBar() { 26 | const nodeMap = new Map(); 27 | 28 | for (let i = 0; i < this.graph.links.length; i += 1) { 29 | if (nodeMap.get(this.graph.links[i].source.id) === undefined) { 30 | nodeMap.set(this.graph.links[i].source.id, 1); 31 | } else { 32 | nodeMap.set(this.graph.links[i].source.id, nodeMap.get(this.graph.links[i].source.id) + 1); 33 | } 34 | if (nodeMap.get(this.graph.links[i].target.id) === undefined) { 35 | nodeMap.set(this.graph.links[i].target.id, 1); 36 | } else { 37 | nodeMap.set(this.graph.links[i].target.id, nodeMap.get(this.graph.links[i].target.id) + 1); 38 | } 39 | } 40 | 41 | const bars = d3.select('#bars'); 42 | 43 | const width:number = +bars.attr('width') - 10; 44 | const height:number = +bars.attr('height') - 10; 45 | 46 | const edgePadding = 50; 47 | const bottomPadding = 90; 48 | const rightPadding = 4; 49 | const barPadding = 1; 50 | 51 | const g = bars.append('g'); 52 | 53 | const scale = d3.scaleLinear(); 54 | 55 | let keyList:string[] = Array.from(nodeMap.keys()); 56 | const values:number[] = Array.from(nodeMap.values()); 57 | 58 | keyList = keyList.sort((x, y) => { 59 | if (nodeMap.get(x) < nodeMap.get(y)) { 60 | return 1; 61 | } if (nodeMap.get(x) > nodeMap.get(y)) { 62 | return -1; 63 | } 64 | return 0; 65 | }); 66 | 67 | scale.domain([0, Math.max(...values)]); 68 | scale.range([0, height - bottomPadding * 2]); 69 | 70 | // setting up the bar chart, as well as the bar chart on click and hover. 71 | g 72 | .selectAll('rect') 73 | .data(keyList) 74 | .enter() 75 | .append('rect') 76 | .attr('x', (d : any, i) => i * ((width - edgePadding) / keyList.length) + barPadding + edgePadding - rightPadding) 77 | .attr('y', (d : any) => height - scale(nodeMap.get(d)) - bottomPadding) 78 | .attr('width', (width - edgePadding) / keyList.length - barPadding) 79 | .attr('height', (d : any) => scale(nodeMap.get(d))) 80 | .attr('id', (d:any) => `${d}B`) 81 | .classed('bar', true) 82 | .on('click', (d) => this.select(d)) 83 | .on('mouseover', (d) => this.hoverOver(d)) 84 | .on('mouseout', (d) => this.hoverOut()); 85 | 86 | const fakeScale = d3.scaleLinear(); 87 | 88 | fakeScale.domain([Math.max(...values), 0]); 89 | fakeScale.range([0, height - bottomPadding * 2]); 90 | 91 | const yAxis = d3.axisLeft(fakeScale); 92 | 93 | const xScale = d3.scaleBand() 94 | .domain(keyList) 95 | .range([1 + edgePadding - rightPadding, 1 + width - rightPadding]); 96 | 97 | const xAxis = d3.axisBottom(xScale); 98 | // These are the axis 99 | g.append('g') 100 | .attr('class', 'axis') 101 | .attr('transform', `translate(${edgePadding - 6},${bottomPadding})`) 102 | .call(yAxis); 103 | 104 | // the y axis 105 | g.append('g') 106 | .attr('class', 'axis') 107 | .attr('transform', `translate(0,${height - bottomPadding + 1})`) 108 | .call(xAxis) 109 | .selectAll('text') 110 | .style('text-anchor', 'end') 111 | .attr('font-size', '11px') 112 | .attr('dx', '-1em') 113 | .attr('dy', '-.45em') 114 | .attr('transform', 'rotate(-90)'); 115 | // The title 116 | g.append('text') 117 | .attr('transform', 'rotate(-90)') 118 | .attr('y', 0) 119 | .attr('x', 0 - (height / 2)) 120 | .attr('dy', '1em') 121 | .style('text-anchor', 'middle') 122 | .text('Node Degree'); 123 | } 124 | 125 | selectBar(id) { 126 | this.deselectAllBars(); 127 | 128 | d3.select('#bars') 129 | .selectAll(`rect[id='${id}B']`) 130 | .classed('barSelected', true); 131 | } 132 | 133 | deselectAllBars() { 134 | d3.select('#bars') 135 | .selectAll('.barSelected') 136 | .classed('barSelected', false); 137 | } 138 | 139 | hoverBar(id) { 140 | d3.select('#bars') 141 | .selectAll(`rect[id='${id}B']`) 142 | .classed('barHover', true); 143 | } 144 | 145 | dehoverBars() { 146 | d3.select('#bars') 147 | .selectAll('.barHover') 148 | .classed('barHover', false); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExampleFirebaseIntegration/src/FDGraph.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | /* eslint-disable prefer-destructuring */ 3 | /* eslint-disable class-methods-use-this */ 4 | /* eslint-disable no-unused-vars */ 5 | import * as d3 from 'd3'; 6 | 7 | export default class Graph { 8 | graph: any; 9 | 10 | private hoverOver: (id: any) => void; 11 | 12 | private hoverOut: () => void; 13 | 14 | private select: (id: any) => void; 15 | 16 | private dragEnded: (d: any) => void; 17 | 18 | constructor(graph, hoverOver, hoverOut, select, dragEnded) { 19 | this.graph = graph; 20 | 21 | this.hoverOver = hoverOver; 22 | this.hoverOut = hoverOut; 23 | this.select = select; 24 | this.dragEnded = dragEnded; 25 | 26 | this.drawGraph(); 27 | } 28 | 29 | drawGraph() { 30 | const svg = d3.select('#viz'); 31 | 32 | svg.selectAll('g').remove(); 33 | 34 | const color = d3.scaleOrdinal(d3.schemeCategory10); 35 | 36 | svg.append('button').attr('class', 'undoRedoButton'); 37 | 38 | const link = svg 39 | .append('g') 40 | .attr('class', 'links') 41 | .selectAll('line') 42 | .data(this.graph.links) 43 | .join('line') 44 | .attr('stroke-width', (d: any) => Math.sqrt(d.value)); 45 | 46 | const node = svg 47 | .append('g') 48 | .classed('circleG', true) 49 | .selectAll('circle') 50 | .data(this.graph.nodes) 51 | .join('circle') 52 | .classed('nodes', true) 53 | .attr('id', (d: any) => `${d.id}N`) 54 | .attr('fill', (d: any) => color(d.group)) 55 | .attr('r', 5) 56 | .call( 57 | d3 58 | .drag() 59 | .on('drag', (d) => this.dragged(d, link, node)) 60 | .on('end', (d) => this.dragEnded(d)), 61 | ) 62 | .on('click', (d) => this.select(d)) 63 | .on('mouseover', (d) => this.hoverOver(d)) 64 | .on('mouseout', (d) => this.hoverOut()); 65 | 66 | node.append('title').text((d) => d.id); 67 | 68 | this.draw(link, node); 69 | } 70 | 71 | dragged(d: any, link, node) { 72 | d.x = d3.event.x; 73 | d.y = d3.event.y; 74 | this.draw(link, node); 75 | } 76 | 77 | draw(link, node) { 78 | link 79 | .attr('x1', (d: any) => d.source.x) 80 | .attr('y1', (d: any) => d.source.y) 81 | .attr('x2', (d: any) => d.target.x) 82 | .attr('y2', (d: any) => d.target.y); 83 | 84 | node.attr('cx', (d: any) => d.x).attr('cy', (d: any) => d.y); 85 | } 86 | 87 | selectNode(id) { 88 | this.deselectAllNodes(); 89 | 90 | d3.select(`circle[id='${id}N']`) 91 | .classed('selectedNode', true) 92 | .attr('r', 10); 93 | 94 | const edges = this.graph.links.filter((d) => d.source.id === id || d.target.id === id); 95 | 96 | edges.forEach((data) => { 97 | d3.select(`circle[id='${data.source.id}N']`) 98 | .filter((d: any) => d.id !== id) 99 | .classed('nodeEdges', true); 100 | 101 | d3.select(`circle[id='${data.target.id}N']`) 102 | .filter((d: any) => d.id !== id) 103 | .classed('nodeEdges', true); 104 | }); 105 | } 106 | 107 | deselectAllNodes() { 108 | d3.select('#viz') 109 | .selectAll('circle') 110 | .classed('nodeEdges', false) 111 | .classed('selectedNode', false) 112 | .attr('r', 5); 113 | } 114 | 115 | hoverNode(id) { 116 | d3.select('#viz') 117 | .select(`circle[id='${id}N']`) 118 | .classed('hoverNode', true) 119 | .attr('r', 8); 120 | } 121 | 122 | dehoverNodes() { 123 | const s = d3 124 | .select('#viz') 125 | .select('.hoverNode') 126 | .classed('hoverNode', false); 127 | 128 | if (s.classed('selectedNode')) { 129 | s.attr('r', 10); 130 | } else { 131 | s.attr('r', 5); 132 | } 133 | } 134 | 135 | moveNodes(newGraph) { 136 | for (const i of this.graph.nodes) { 137 | i.x = newGraph[i.id][0]; 138 | i.y = newGraph[i.id][1]; 139 | } 140 | 141 | for (const i of this.graph.links) { 142 | i.source.x = newGraph[i.source.id][0]; 143 | i.source.y = newGraph[i.source.id][1]; 144 | i.target.x = newGraph[i.target.id][0]; 145 | i.target.y = newGraph[i.target.id][1]; 146 | } 147 | 148 | const link = d3 149 | .select('#viz') 150 | .select('g.links') 151 | .selectAll('line'); 152 | 153 | const node = d3 154 | .select('#viz') 155 | .select('g.circleG') 156 | .selectAll('circle'); 157 | 158 | this.draw(link, node); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExampleFirebaseIntegration/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": false, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "jsx": "react", 15 | "downlevelIteration": true 16 | }, 17 | "include": ["src/**/*"], 18 | "exclude": ["node_modules", "*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/trrack-examples/lesMisExampleFirebaseIntegration/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 5 | const CopyPlugin = require('copy-webpack-plugin'); 6 | 7 | const config = { 8 | entry: './src/ProvenanceSetup.ts', 9 | output: { 10 | path: path.resolve(__dirname, 'dist'), 11 | filename: 'bundle.js', 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | loader: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | { 21 | test: /\.css$/, 22 | use: ['style-loader', 'css-loader'], 23 | }, 24 | ], 25 | }, 26 | resolve: { 27 | extensions: ['.tsx', '.ts', '.js'], 28 | }, 29 | plugins: [ 30 | new CopyPlugin({ 31 | patterns: [{ from: './data', to: './data/' }], 32 | }), 33 | new HtmlWebpackPlugin({ 34 | template: './index.html', 35 | }), 36 | new CleanWebpackPlugin(), 37 | ], 38 | }; 39 | 40 | module.exports = config; 41 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExample/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [1.1.0](https://github.com/visdesignlab/trrack/compare/example-simple@1.1.0-alpha.9...example-simple@1.1.0) (2022-12-16) 7 | 8 | **Note:** Version bump only for package example-simple 9 | 10 | 11 | 12 | 13 | 14 | # [1.1.0-alpha.9](https://github.com/visdesignlab/trrack/compare/example-simple@1.1.0-alpha.8...example-simple@1.1.0-alpha.9) (2021-05-07) 15 | 16 | **Note:** Version bump only for package example-simple 17 | 18 | 19 | 20 | 21 | 22 | # [1.1.0-alpha.8](https://github.com/visdesignlab/trrack/compare/example-simple@1.1.0-alpha.7...example-simple@1.1.0-alpha.8) (2021-05-07) 23 | 24 | **Note:** Version bump only for package example-simple 25 | 26 | 27 | 28 | 29 | 30 | # [1.1.0-alpha.7](https://github.com/visdesignlab/trrack/compare/example-simple@1.1.0-alpha.6...example-simple@1.1.0-alpha.7) (2021-05-07) 31 | 32 | **Note:** Version bump only for package example-simple 33 | 34 | 35 | 36 | 37 | 38 | # [1.1.0-alpha.6](https://github.com/visdesignlab/trrack/compare/example-simple@1.1.0-alpha.5...example-simple@1.1.0-alpha.6) (2020-12-16) 39 | 40 | **Note:** Version bump only for package example-simple 41 | 42 | 43 | 44 | 45 | 46 | # [1.1.0-alpha.5](https://github.com/visdesignlab/trrack/compare/example-simple@1.1.0-alpha.4...example-simple@1.1.0-alpha.5) (2020-12-16) 47 | 48 | **Note:** Version bump only for package example-simple 49 | 50 | 51 | 52 | 53 | 54 | # [1.1.0-alpha.4](https://github.com/visdesignlab/trrack/compare/example-simple@1.1.0-alpha.3...example-simple@1.1.0-alpha.4) (2020-11-04) 55 | 56 | **Note:** Version bump only for package example-simple 57 | 58 | 59 | 60 | 61 | 62 | # [1.1.0-alpha.3](https://github.com/visdesignlab/trrack/compare/example-simple@1.1.0-alpha.2...example-simple@1.1.0-alpha.3) (2020-11-04) 63 | 64 | **Note:** Version bump only for package example-simple 65 | 66 | 67 | 68 | 69 | 70 | # [1.1.0-alpha.2](https://github.com/visdesignlab/trrack/compare/example-simple@1.1.0-alpha.1...example-simple@1.1.0-alpha.2) (2020-10-21) 71 | 72 | 73 | ### Bug Fixes 74 | 75 | * 🐛 Updated examples ([db48f8c](https://github.com/visdesignlab/trrack/commit/db48f8c17b9a76ff03d8e6b4c07ba22a3d8da322)) 76 | 77 | 78 | 79 | 80 | 81 | # [1.1.0-alpha.1](https://github.com/visdesignlab/trrack/compare/example-simple@1.1.0-alpha.0...example-simple@1.1.0-alpha.1) (2020-10-20) 82 | 83 | **Note:** Version bump only for package example-simple 84 | 85 | 86 | 87 | 88 | 89 | # 1.1.0-alpha.0 (2020-10-20) 90 | 91 | 92 | ### Bug Fixes 93 | 94 | * 🐛 Added landing page and links ([98afd5d](https://github.com/visdesignlab/trrack/commit/98afd5d0537e49dce82b3cddb3e7547c2d3ffa84)) 95 | * 🐛 Fixed build process + added dependency for examples ([572e8cd](https://github.com/visdesignlab/trrack/commit/572e8cd8675003030ac942036201868383569835)) 96 | * 🐛 linting trrack-vis and changing syntax ([13216bf](https://github.com/visdesignlab/trrack/commit/13216bf8e707ecb74431510efa940d895f292f66)) 97 | * 🐛 making example circles work on firefox, button style ([28e3e20](https://github.com/visdesignlab/trrack/commit/28e3e20063e40a3fc45ea1bbbeffab41f72ea4e3)) 98 | * 🐛 more syntax changes, example updates ([b6dc7ff](https://github.com/visdesignlab/trrack/commit/b6dc7ff5d7d7f8fcc669d46837e4c37210d7e32a)) 99 | * 🐛 simpleExample scatterplot cleanup ([c44486f](https://github.com/visdesignlab/trrack/commit/c44486f6ce92ca45bd2f30fd232daf74fad5458b)) 100 | 101 | 102 | ### Features 103 | 104 | * 🎸 adding examples ([994edd7](https://github.com/visdesignlab/trrack/commit/994edd76ec1be5d7aef9b3d17e097868817a702f)) 105 | * 🎸 Fixed examples and build process ([647b978](https://github.com/visdesignlab/trrack/commit/647b9789dd04a37c70395d08e547fc82adcccab7)) 106 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExample/README.md: -------------------------------------------------------------------------------- 1 | To run this example, use the following commands 2 | 3 | npm install 4 | npm start 5 | 6 | Will host the application at localhost:8080 7 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExample/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple Example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 20 | 21 |
22 | 23 |
24 | 25 | 26 |
27 |
28 |
29 | 30 |
31 |
32 |
33 | 34 |
35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExample/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-simple", 3 | "version": "1.1.0", 4 | "private": true, 5 | "description": "Examples for using provenance library", 6 | "scripts": { 7 | "build": "webpack", 8 | "test": "test", 9 | "start": "webpack-dev-server --mode development" 10 | }, 11 | "dependencies": { 12 | "@visdesignlab/trrack": "^2.0.0", 13 | "@visdesignlab/trrack-vis": "^2.0.0", 14 | "d3": "^5.12.0", 15 | "font-awesome": "^4.7.0", 16 | "typestyle": "^2.1.0" 17 | }, 18 | "devDependencies": { 19 | "@types/d3": "^5.7.2", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "clean-webpack-plugin": "^3.0.0", 22 | "css-loader": "^5.0.0", 23 | "html-webpack-plugin": "^4.5.0", 24 | "prettier": "^1.18.2", 25 | "style-loader": "^2.0.0", 26 | "ts-loader": "^8.0.5", 27 | "tslint": "^5.19.0", 28 | "tslint-config-prettier": "^1.18.0", 29 | "typescript": "^4.0.3", 30 | "webpack": "^4.0.0", 31 | "webpack-cli": "^4.0.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExample/src/provenanceSetup.ts: -------------------------------------------------------------------------------- 1 | import { initProvenance, NodeID, createAction } from '@visdesignlab/trrack'; 2 | import '../styles/styles.css'; 3 | import { ProvVisCreator } from '@visdesignlab/trrack-vis'; 4 | 5 | import Scatterplot from './scatterplot'; 6 | 7 | /** 8 | * interface representing the state of the application 9 | */ 10 | export interface NodeState { 11 | selectedQuartet: string; 12 | selectedNode: string; 13 | hoveredNode: string; 14 | } 15 | 16 | /** 17 | * Initial state 18 | */ 19 | 20 | const initialState: NodeState = { 21 | selectedQuartet: 'I', 22 | selectedNode: 'none', 23 | hoveredNode: 'none', 24 | }; 25 | 26 | type EventTypes = 'Change Quartet' | 'Select Node' | 'Hover Node'; 27 | 28 | // initialize provenance with the first state 29 | const prov = initProvenance(initialState, { 30 | loadFromUrl: false, 31 | }); 32 | 33 | // Set up apply action functions for each of the 3 actions that affect state 34 | 35 | /** 36 | * Function called when the quartet number is changed. Applies an action to provenance. 37 | * This is a complex action, meaning it always stores a state node. 38 | */ 39 | 40 | const quartetUpdateAction = createAction( 41 | (state: NodeState, newQuartet: string) => { 42 | state.selectedQuartet = newQuartet; 43 | }, 44 | ); 45 | 46 | const changeQuartetUpdate = function (newQuartet: string) { 47 | quartetUpdateAction 48 | .setLabel(`Quartet ${newQuartet} Selected`) 49 | .setEventType('Change Quartet') 50 | .saveStateMode('Complete'); 51 | 52 | prov.apply(quartetUpdateAction(newQuartet)); 53 | }; 54 | 55 | /** 56 | * Function called when a node is selected. Applies an action to provenance. 57 | */ 58 | 59 | const nodeSelectAction = createAction( 60 | (state: NodeState, newSelected: string) => { 61 | state.selectedNode = newSelected; 62 | }, 63 | ); 64 | 65 | const selectNodeUpdate = function (newSelected: string) { 66 | nodeSelectAction.setLabel(`${newSelected} Selected`).setEventType('Select Node'); 67 | 68 | prov.apply(nodeSelectAction(newSelected)); 69 | }; 70 | 71 | /** 72 | * Function called when a node is hovered. Applies an action to provenance. 73 | */ 74 | 75 | const hoverAction = createAction( 76 | (state: NodeState, newHover: string) => { 77 | state.hoveredNode = newHover; 78 | return state; 79 | }, 80 | ); 81 | 82 | const hoverNodeUpdate = function (newHover: string) { 83 | hoverAction 84 | .setLabel(newHover === '' ? 'Hover Removed' : `${newHover} Hovered`) 85 | .setEventType('Hover Node'); 86 | 87 | prov.apply(hoverAction(newHover)); 88 | }; 89 | 90 | // Create our scatterplot class which handles the actual vis. Pass it our three action functions 91 | // so it can use them when appropriate. 92 | const scatterplot = new Scatterplot(changeQuartetUpdate, selectNodeUpdate, hoverNodeUpdate); 93 | 94 | // Create function to pass to the ProvVis library for when a node is selected in the graph. 95 | // For our purposes, were simply going to jump to the selected node. 96 | const visCallback = function (newNode: NodeID) { 97 | prov.goToNode(newNode); 98 | }; 99 | 100 | // Set up observers for the three keys in state. These observers will get called either when 101 | // an applyAction function changes the associated keys value. 102 | 103 | // Also will be called when an internal graph change such as goBackNSteps, goBackOneStep or goToNode 104 | // change the keys value. 105 | 106 | /** 107 | * Observer for when the quartet state is changed. Calls changeQuartet in scatterplot to update vis. 108 | */ 109 | prov.addObserver( 110 | (state) => state.selectedQuartet, 111 | () => { 112 | scatterplot.changeQuartet(prov.getState(prov.current).selectedQuartet); 113 | }, 114 | ); 115 | 116 | /** 117 | * Observer for when the selected node state is changed. 118 | * Calls selectNode in scatterplot to update vis. 119 | */ 120 | prov.addObserver( 121 | (state) => state.selectedNode, 122 | () => { 123 | scatterplot.selectNode(prov.getState(prov.current).selectedNode); 124 | }, 125 | ); 126 | 127 | /** 128 | * Observer for when the hovered node state is changed. Calls hoverNode in scatterplot to update vis. 129 | */ 130 | prov.addObserver( 131 | (state) => state.hoveredNode, 132 | () => { 133 | scatterplot.hoverNode(prov.getState(prov.current).hoveredNode); 134 | }, 135 | ); 136 | 137 | prov.done(); 138 | 139 | // Setup ProvVis once initially 140 | ProvVisCreator(document.getElementById('provDiv')!, prov, visCallback); 141 | 142 | // Undo function which simply goes one step backwards in the graph. 143 | function undo() { 144 | prov.goBackOneStep(); 145 | } 146 | 147 | // Redo function which traverses down the tree one step. 148 | function redo() { 149 | if (prov.current.children.length === 0) { 150 | return; 151 | } 152 | prov.goForwardOneStep(); 153 | } 154 | 155 | // Setting up undo/redo hotkey to typical buttons 156 | document.onkeydown = function (e) { 157 | const mac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform); 158 | 159 | if (!e.shiftKey && (mac ? e.metaKey : e.ctrlKey) && e.which === 90) { 160 | undo(); 161 | } else if (e.shiftKey && (mac ? e.metaKey : e.ctrlKey) && e.which === 90) { 162 | redo(); 163 | } 164 | }; 165 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExample/src/scatterplot.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | /* eslint-disable no-unused-vars */ 3 | import * as d3 from 'd3'; 4 | 5 | export default class Scatterplot { 6 | margin:any; 7 | 8 | width:number; 9 | 10 | height:number; 11 | 12 | quartetNum:string; 13 | 14 | data:any[]; 15 | 16 | svg:d3.Selection; 17 | 18 | xScale:d3.ScaleLinear; 19 | 20 | yScale:d3.ScaleLinear; 21 | 22 | constructor( 23 | changeQuartetFunc: (s:string) => void, 24 | selectNodeFunc: (s:string) => void, 25 | hoverNodeFunc: (s:string) => void, 26 | ) { 27 | this.margin = {}; 28 | this.width = 0; 29 | this.height = 0; 30 | this.quartetNum = ''; 31 | this.data = []; 32 | this.svg = d3.select('#mainDiv') 33 | .append('svg'); 34 | this.xScale = d3.scaleLinear(); 35 | this.yScale = d3.scaleLinear(); 36 | 37 | d3.csv('https://gist.githubusercontent.com/ericbusboom/b2ac1d366c005cd2ed8c/raw/c92c66e43d144fa9c29dbd602d5af6988e8db533/anscombes.csv') 38 | .then((d) => { 39 | this.data = d; 40 | this.margin = { 41 | top: 20, right: 20, bottom: 20, left: 20, 42 | }; 43 | this.width = 800 - this.margin.left - this.margin.right; 44 | this.height = 800 - this.margin.top - this.margin.bottom; 45 | 46 | this.quartetNum = 'I'; 47 | 48 | this.xScale.domain([ 49 | d3.min(this.data, (innerD) => +innerD.x)!, 50 | d3.max(this.data, (innerD) => +innerD.x)!, 51 | ]); 52 | this.xScale.range([50, 750]); 53 | 54 | this.yScale.domain([ 55 | d3.min(this.data, (innerD) => +innerD.y)!, 56 | d3.max(this.data, (innerD) => +innerD.y)!, 57 | ]); 58 | this.yScale.range([50, 750]); 59 | 60 | d3.select('#quartets') 61 | .on('change', function () { 62 | changeQuartetFunc((this as HTMLSelectElement).value); 63 | }); 64 | 65 | this.initializeVis(selectNodeFunc, hoverNodeFunc); 66 | }); 67 | } 68 | 69 | /** 70 | * Creates an svg and draws the initial visualization 71 | */ 72 | 73 | initializeVis(selectNodeFunc: (s:string) => void, hoverNodeFunc: (s:string) => void) { 74 | this.svg 75 | .attr('width', this.width + this.margin.left + this.margin.right) 76 | .attr('height', this.height + this.margin.top + this.margin.bottom); 77 | 78 | const currData = this.data.filter((d) => d.dataset === this.quartetNum); 79 | 80 | this.svg.selectAll('circle') 81 | .data(currData) 82 | .enter() 83 | .append('circle') 84 | .attr('class', 'normalNode') 85 | .attr('id', (d) => `node_${d.id}`) 86 | .attr('cx', (d) => this.xScale(+d.x)) 87 | .attr('cy', (d) => this.height - this.yScale(+d.y)) 88 | .attr('r', 7) 89 | .on('click', (d) => selectNodeFunc(`node_${d.id}`)) 90 | .on('mouseover', (d) => hoverNodeFunc(`node_${d.id}`)) 91 | .on('mouseout', (d) => hoverNodeFunc('')); 92 | } 93 | 94 | /** 95 | * Filters the data so that only points associated with the new quartet are used 96 | * Updates each circle and transitions them to their new position 97 | */ 98 | 99 | changeQuartet(newQuartet:string) { 100 | this.quartetNum = newQuartet; 101 | 102 | const currData = this.data.filter((d) => d.dataset === this.quartetNum); 103 | 104 | this.svg.selectAll('circle') 105 | .data(currData) 106 | .attr('id', (d) => `node_${d.id}`) 107 | .transition() 108 | .duration(750) 109 | .attr('cx', (d) => this.xScale(+d.x)) 110 | .attr('cy', (d) => this.height - this.yScale(+d.y)); 111 | } 112 | 113 | /** 114 | * Ensures the previously selected node is no longer selected 115 | * Selects the new node 116 | */ 117 | 118 | selectNode(selectedNode:string) { 119 | d3.select('.selectedNode') 120 | .classed('selectedNode', false) 121 | .attr('r', 7); 122 | 123 | d3.select(`#${selectedNode}`) 124 | .classed('selectedNode', true) 125 | .attr('r', 10); 126 | } 127 | 128 | /** 129 | * Ensures the previously hovered node is no longer hovered 130 | * If hoverNode is not empty, hovers the new node 131 | */ 132 | 133 | hoverNode(hoverNode:string) { 134 | d3.select('.hoverNode') 135 | .classed('hoverNode', false); 136 | 137 | if (hoverNode !== '') { 138 | d3.select(`#${hoverNode}`) 139 | .classed('hoverNode', true); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExample/styles/styles.css: -------------------------------------------------------------------------------- 1 | .normalNode 2 | { 3 | fill: #333333; 4 | } 5 | 6 | .selectedNode 7 | { 8 | fill: blue 9 | } 10 | 11 | .hoverNode{ 12 | fill: blue; 13 | opacity: .5 14 | } 15 | 16 | #mainDiv{ 17 | border: 1px solid black; 18 | max-height: 800px; 19 | display: flex; 20 | float:left 21 | } 22 | 23 | #quartDiv{ 24 | display: flex; 25 | justify-content: center; 26 | margin-bottom: 10px; 27 | } 28 | 29 | #provDiv{ 30 | max-width: 300px; 31 | display: flex; 32 | float: right; 33 | } 34 | 35 | #parent{ 36 | display: flex; 37 | justify-content: center; 38 | } 39 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExample/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": false, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "jsx": "react", 15 | "downlevelIteration": true 16 | }, 17 | "include": ["src/**/*"], 18 | "exclude": ["node_modules", "*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExample/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 5 | 6 | const config = { 7 | entry: './src/provenanceSetup.ts', 8 | output: { 9 | path: path.resolve(__dirname, 'dist'), 10 | filename: 'bundle.js', 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.tsx?$/, 16 | loader: 'ts-loader', 17 | exclude: /node_modules/, 18 | }, 19 | { 20 | test: /\.css$/, 21 | use: ['style-loader', 'css-loader'], 22 | }, 23 | ], 24 | }, 25 | resolve: { 26 | extensions: ['.tsx', '.ts', '.js'], 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: './index.html', 31 | }), 32 | new CleanWebpackPlugin(), 33 | ], 34 | }; 35 | 36 | module.exports = config; 37 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleAddAnnotation/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [1.1.0](https://github.com/visdesignlab/trrack/compare/example-simple-annotation@1.1.0-alpha.9...example-simple-annotation@1.1.0) (2022-12-16) 7 | 8 | **Note:** Version bump only for package example-simple-annotation 9 | 10 | 11 | 12 | 13 | 14 | # [1.1.0-alpha.9](https://github.com/visdesignlab/trrack/compare/example-simple-annotation@1.1.0-alpha.8...example-simple-annotation@1.1.0-alpha.9) (2021-05-07) 15 | 16 | **Note:** Version bump only for package example-simple-annotation 17 | 18 | 19 | 20 | 21 | 22 | # [1.1.0-alpha.8](https://github.com/visdesignlab/trrack/compare/example-simple-annotation@1.1.0-alpha.7...example-simple-annotation@1.1.0-alpha.8) (2021-05-07) 23 | 24 | **Note:** Version bump only for package example-simple-annotation 25 | 26 | 27 | 28 | 29 | 30 | # [1.1.0-alpha.7](https://github.com/visdesignlab/trrack/compare/example-simple-annotation@1.1.0-alpha.6...example-simple-annotation@1.1.0-alpha.7) (2021-05-07) 31 | 32 | **Note:** Version bump only for package example-simple-annotation 33 | 34 | 35 | 36 | 37 | 38 | # [1.1.0-alpha.6](https://github.com/visdesignlab/trrack/compare/example-simple-annotation@1.1.0-alpha.5...example-simple-annotation@1.1.0-alpha.6) (2020-12-16) 39 | 40 | **Note:** Version bump only for package example-simple-annotation 41 | 42 | 43 | 44 | 45 | 46 | # [1.1.0-alpha.5](https://github.com/visdesignlab/trrack/compare/example-simple-annotation@1.1.0-alpha.4...example-simple-annotation@1.1.0-alpha.5) (2020-12-16) 47 | 48 | **Note:** Version bump only for package example-simple-annotation 49 | 50 | 51 | 52 | 53 | 54 | # [1.1.0-alpha.4](https://github.com/visdesignlab/trrack/compare/example-simple-annotation@1.1.0-alpha.3...example-simple-annotation@1.1.0-alpha.4) (2020-11-04) 55 | 56 | **Note:** Version bump only for package example-simple-annotation 57 | 58 | 59 | 60 | 61 | 62 | # [1.1.0-alpha.3](https://github.com/visdesignlab/trrack/compare/example-simple-annotation@1.1.0-alpha.2...example-simple-annotation@1.1.0-alpha.3) (2020-11-04) 63 | 64 | **Note:** Version bump only for package example-simple-annotation 65 | 66 | 67 | 68 | 69 | 70 | # [1.1.0-alpha.2](https://github.com/visdesignlab/trrack/compare/example-simple-annotation@1.1.0-alpha.1...example-simple-annotation@1.1.0-alpha.2) (2020-10-21) 71 | 72 | **Note:** Version bump only for package example-simple-annotation 73 | 74 | 75 | 76 | 77 | 78 | # [1.1.0-alpha.1](https://github.com/visdesignlab/trrack/compare/example-simple-annotation@1.1.0-alpha.0...example-simple-annotation@1.1.0-alpha.1) (2020-10-20) 79 | 80 | **Note:** Version bump only for package example-simple-annotation 81 | 82 | 83 | 84 | 85 | 86 | # 1.1.0-alpha.0 (2020-10-20) 87 | 88 | 89 | ### Bug Fixes 90 | 91 | * 🐛 Fixed build process + added dependency for examples ([572e8cd](https://github.com/visdesignlab/trrack/commit/572e8cd8675003030ac942036201868383569835)) 92 | * 🐛 making example circles work on firefox, button style ([28e3e20](https://github.com/visdesignlab/trrack/commit/28e3e20063e40a3fc45ea1bbbeffab41f72ea4e3)) 93 | * 🐛 more syntax changes, example updates ([b6dc7ff](https://github.com/visdesignlab/trrack/commit/b6dc7ff5d7d7f8fcc669d46837e4c37210d7e32a)) 94 | 95 | 96 | ### Features 97 | 98 | * 🎸 adding examples ([994edd7](https://github.com/visdesignlab/trrack/commit/994edd76ec1be5d7aef9b3d17e097868817a702f)) 99 | * 🎸 Fixed examples and build process ([647b978](https://github.com/visdesignlab/trrack/commit/647b9789dd04a37c70395d08e547fc82adcccab7)) 100 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleAddAnnotation/README.md: -------------------------------------------------------------------------------- 1 | To run this example, use the following commands 2 | 3 | npm install 4 | npm start 5 | 6 | Will host the application at localhost:8080 7 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleAddAnnotation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Examples 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 20 | 21 |
22 | 23 |
24 | 25 | 26 |
27 | 28 |
29 | 30 | 31 |
32 |
33 |
34 | 35 |
36 |
37 |
38 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleAddAnnotation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-simple-annotation", 3 | "private": true, 4 | "version": "1.1.0", 5 | "description": "Examples for using provenance library", 6 | "main": "index.js", 7 | "scripts": { 8 | "build": "webpack", 9 | "test": "test", 10 | "start": "webpack-dev-server --mode development" 11 | }, 12 | "author": "Zach Cutler", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@visdesignlab/trrack": "^2.0.0", 16 | "@visdesignlab/trrack-vis": "^2.0.0", 17 | "d3": "^5.12.0", 18 | "font-awesome": "^4.7.0", 19 | "http-server": "^0.12.0", 20 | "typestyle": "^2.1.0" 21 | }, 22 | "devDependencies": { 23 | "@types/d3": "^5.7.2", 24 | "awesome-typescript-loader": "^5.2.1", 25 | "clean-webpack-plugin": "^3.0.0", 26 | "css-loader": "^5.0.0", 27 | "html-webpack-plugin": "^4.5.0", 28 | "prettier": "^1.18.2", 29 | "style-loader": "^2.0.0", 30 | "ts-loader": "^8.0.5", 31 | "tslint": "^5.19.0", 32 | "tslint-config-prettier": "^1.18.0", 33 | "typescript": "^4.0.3", 34 | "webpack": "^4.0.0", 35 | "webpack-cli": "^4.0.0", 36 | "webpack-dev-server": "^3.8.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleAddAnnotation/src/provenanceSetup.ts: -------------------------------------------------------------------------------- 1 | import { initProvenance, NodeID, createAction } from '@visdesignlab/trrack'; 2 | import '../styles/styles.css'; 3 | import * as d3 from 'd3'; 4 | 5 | import { ProvVisCreator } from '@visdesignlab/trrack-vis'; 6 | 7 | import Scatterplot from './scatterplot'; 8 | 9 | /** 10 | * interface representing the state of the application 11 | */ 12 | export interface NodeState { 13 | selectedQuartet: string; 14 | selectedNode: string; 15 | hoveredNode: string; 16 | } 17 | 18 | /** 19 | * Initial state 20 | */ 21 | 22 | const initialState: NodeState = { 23 | selectedQuartet: 'I', 24 | selectedNode: 'none', 25 | hoveredNode: 'none', 26 | }; 27 | 28 | type EventTypes = 'Change Quartet' | 'Select Node' | 'Hover Node'; 29 | 30 | // initialize provenance with the first state 31 | const prov = initProvenance(initialState, { 32 | loadFromUrl: false, 33 | }); 34 | 35 | // Set up apply action functions for each of the 3 actions that affect state 36 | 37 | /** 38 | * Function called when the quartet number is changed. Applies an action to provenance. 39 | * This is a complex action, meaning it always stores a state node. 40 | */ 41 | 42 | const quartetUpdateAction = createAction( 43 | (state: NodeState, newQuartet: string) => { 44 | state.selectedQuartet = newQuartet; 45 | }, 46 | ); 47 | 48 | const changeQuartetUpdate = function (newQuartet: string) { 49 | quartetUpdateAction 50 | .setLabel(`Quartet ${newQuartet} Selected`) 51 | .setEventType('Change Quartet') 52 | .saveStateMode('Complete'); 53 | 54 | prov.apply(quartetUpdateAction(newQuartet)); 55 | }; 56 | 57 | /** 58 | * Function called when a node is selected. Applies an action to provenance. 59 | */ 60 | 61 | const nodeSelectAction = createAction( 62 | (state: NodeState, newSelected: string) => { 63 | state.selectedNode = newSelected; 64 | }, 65 | ); 66 | 67 | const selectNodeUpdate = function (newSelected: number) { 68 | nodeSelectAction.setLabel(`${newSelected} Selected`).setEventType('Select Node'); 69 | 70 | prov.apply(nodeSelectAction(newSelected)); 71 | }; 72 | 73 | /** 74 | * Function called when a node is hovered. Applies an action to provenance. 75 | */ 76 | 77 | const hoverAction = createAction( 78 | (state: NodeState, newHover: string) => { 79 | state.hoveredNode = newHover; 80 | return state; 81 | }, 82 | ); 83 | 84 | const hoverNodeUpdate = function (newHover: string) { 85 | hoverAction 86 | .setLabel(newHover === '' ? 'Hover Removed' : `${newHover} Hovered`) 87 | .setEventType('Hover Node'); 88 | 89 | prov.apply(hoverAction(newHover)); 90 | }; 91 | 92 | // Create our scatterplot class which handles the actual vis. Pass it our three action functions 93 | // so it can use them when appropriate. 94 | const scatterplot = new Scatterplot(changeQuartetUpdate, selectNodeUpdate, hoverNodeUpdate); 95 | 96 | // Create function to pass to the ProvVis library for when a node is selected in the graph. 97 | // For our purposes, were simply going to jump to the selected node. 98 | const visCallback = function (newNode: NodeID) { 99 | prov.goToNode(newNode); 100 | }; 101 | 102 | // Set up observers for the three keys in state. 103 | // These observers will get called either when an applyAction 104 | // function changes the associated keys value. 105 | 106 | // Also will be called when an internal graph change such as goBackNSteps, goBackOneStep or goToNode 107 | // change the keys value. 108 | 109 | /** 110 | * Observer for when the quartet state is changed. Calls changeQuartet in scatterplot to update vis. 111 | */ 112 | prov.addObserver( 113 | (state) => state.selectedQuartet, 114 | () => { 115 | scatterplot.changeQuartet(prov.getState(prov.current).selectedQuartet); 116 | }, 117 | ); 118 | 119 | /** 120 | * Observer for when the selected node state is changed. 121 | * Calls selectNode in scatterplot to update vis. 122 | */ 123 | prov.addObserver( 124 | (state) => state.selectedNode, 125 | () => { 126 | scatterplot.selectNode(prov.getState(prov.current).selectedNode); 127 | }, 128 | ); 129 | 130 | /** 131 | * Observer for when the hovered node state is changed. Calls hoverNode in scatterplot to update vis. 132 | */ 133 | prov.addObserver( 134 | (state) => state.hoveredNode, 135 | () => { 136 | scatterplot.hoverNode(prov.getState(prov.current).hoveredNode); 137 | }, 138 | ); 139 | 140 | prov.done(); 141 | 142 | // Setup ProvVis once initially 143 | ProvVisCreator(document.getElementById('provDiv')!, prov, visCallback); 144 | // Undo function which simply goes one step backwards in the graph. 145 | function undo() { 146 | prov.goBackOneStep(); 147 | } 148 | 149 | // Redo function which traverses down the tree one step. 150 | function redo() { 151 | if (prov.current.children.length === 0) { 152 | return; 153 | } 154 | prov.goForwardOneStep(); 155 | } 156 | 157 | d3.select('#annotationButton').on('click', () => { 158 | const val = (d3.select('#annotateInput').node() as HTMLInputElement).value; 159 | 160 | prov.addAnnotation(val); 161 | }); 162 | 163 | // Setting up undo/redo hotkey to typical buttons 164 | document.onkeydown = function (e) { 165 | const mac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform); 166 | 167 | if (!e.shiftKey && (mac ? e.metaKey : e.ctrlKey) && e.which === 90) { 168 | undo(); 169 | } else if (e.shiftKey && (mac ? e.metaKey : e.ctrlKey) && e.which === 90) { 170 | redo(); 171 | } 172 | }; 173 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleAddAnnotation/src/scatterplot.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | /* eslint-disable no-unused-vars */ 3 | import * as d3 from 'd3'; 4 | 5 | export default class Scatterplot { 6 | margin:any; 7 | 8 | width:number; 9 | 10 | height:number; 11 | 12 | quartetNum:string; 13 | 14 | data:any[]; 15 | 16 | svg:d3.Selection; 17 | 18 | xScale:d3.ScaleLinear; 19 | 20 | yScale:d3.ScaleLinear; 21 | 22 | constructor( 23 | changeQuartetFunc: (s:string) => void, 24 | selectNodeFunc: (s:number, x:number, y:number) => void, 25 | hoverNodeFunc: (s:string) => void, 26 | ) { 27 | this.margin = {}; 28 | this.width = 0; 29 | this.height = 0; 30 | this.quartetNum = ''; 31 | this.data = []; 32 | this.svg = d3.select('#mainDiv') 33 | .append('svg'); 34 | this.xScale = d3.scaleLinear(); 35 | this.yScale = d3.scaleLinear(); 36 | 37 | d3.csv('https://gist.githubusercontent.com/ericbusboom/b2ac1d366c005cd2ed8c/raw/c92c66e43d144fa9c29dbd602d5af6988e8db533/anscombes.csv') 38 | .then((data) => { 39 | this.data = data; 40 | this.margin = { 41 | top: 20, right: 20, bottom: 20, left: 20, 42 | }; 43 | this.width = 800 - this.margin.left - this.margin.right; 44 | this.height = 800 - this.margin.top - this.margin.bottom; 45 | 46 | this.quartetNum = 'I'; 47 | 48 | this.xScale.domain([d3.min(this.data, (d) => +d.x)!, d3.max(this.data, (d) => +d.x)!]); 49 | this.xScale.range([50, 750]); 50 | 51 | this.yScale.domain([d3.min(this.data, (d) => +d.y)!, d3.max(this.data, (d) => +d.y)!]); 52 | this.yScale.range([50, 750]); 53 | 54 | d3.select('#quartets') 55 | .on('change', function () { 56 | changeQuartetFunc((this as HTMLSelectElement).value); 57 | }); 58 | 59 | this.initializeVis(selectNodeFunc, hoverNodeFunc); 60 | }); 61 | } 62 | 63 | /** 64 | * Creates an svg and draws the initial visualization 65 | */ 66 | 67 | initializeVis( 68 | selectNodeFunc: (s:number, x:number, y:number) => void, 69 | hoverNodeFunc: (s:string) => void, 70 | ) { 71 | this.svg 72 | .attr('width', this.width + this.margin.left + this.margin.right) 73 | .attr('height', this.height + this.margin.top + this.margin.bottom); 74 | 75 | const currData = this.data.filter((d) => d.dataset === this.quartetNum); 76 | 77 | this.svg.selectAll('circle') 78 | .data(currData) 79 | .enter() 80 | .append('circle') 81 | .attr('class', 'normalNode') 82 | .attr('id', (d) => `node_${d.id}`) 83 | .attr('cx', (d) => this.xScale(+d.x)) 84 | .attr('cy', (d) => this.height - this.yScale(+d.y)) 85 | .attr('r', 7) 86 | .on('click', (d) => selectNodeFunc(d.id, d.x, d.y)) 87 | .on('mouseover', (d) => hoverNodeFunc(`node_${d.id}`)) 88 | .on('mouseout', (d) => hoverNodeFunc('')); 89 | } 90 | 91 | /** 92 | * Filters the data so that only points associated with the new quartet are used 93 | * Updates each circle and transitions them to their new position 94 | */ 95 | 96 | changeQuartet(newQuartet:string) { 97 | this.quartetNum = newQuartet; 98 | 99 | const currData = this.data.filter((d) => d.dataset === this.quartetNum); 100 | 101 | this.svg.selectAll('circle') 102 | .data(currData) 103 | .attr('id', (d) => `node_${d.id}`) 104 | .transition() 105 | .duration(750) 106 | .attr('cx', (d) => this.xScale(+d.x)) 107 | .attr('cy', (d) => this.height - this.yScale(+d.y)); 108 | } 109 | 110 | /** 111 | * Ensures the previously selected node is no longer selected 112 | * Selects the new node 113 | */ 114 | 115 | selectNode(selectedNode:string) { 116 | d3.select('.selectedNode') 117 | .classed('selectedNode', false) 118 | .attr('r', 7); 119 | 120 | d3.select(`#${selectedNode}`) 121 | .classed('selectedNode', true) 122 | .attr('r', 10); 123 | } 124 | 125 | /** 126 | * Ensures the previously hovered node is no longer hovered 127 | * If hoverNode is not empty, hovers the new node 128 | */ 129 | 130 | hoverNode(hoverNode:string) { 131 | d3.select('.hoverNode') 132 | .classed('hoverNode', false); 133 | 134 | if (hoverNode !== '') { 135 | d3.select(`#${hoverNode}`) 136 | .classed('hoverNode', true); 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleAddAnnotation/styles/styles.css: -------------------------------------------------------------------------------- 1 | .normalNode 2 | { 3 | fill: #333333; 4 | } 5 | 6 | .selectedNode 7 | { 8 | fill: blue 9 | } 10 | 11 | .hoverNode{ 12 | fill: blue; 13 | opacity: .5 14 | } 15 | 16 | #annotationBox{ 17 | margin-left:5px; 18 | } 19 | 20 | #mainDiv{ 21 | border: 1px solid black; 22 | max-height: 800px; 23 | display: flex; 24 | float:left 25 | } 26 | 27 | #quartDiv{ 28 | display: flex; 29 | justify-content: center; 30 | margin-bottom: 10px; 31 | } 32 | 33 | #provDiv{ 34 | max-width: 300px; 35 | display: flex; 36 | float: right; 37 | } 38 | 39 | #parent{ 40 | display: flex; 41 | justify-content: center; 42 | } 43 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleAddAnnotation/tsconfig.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "compilerOptions": { 4 | "target": "es5", 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "esModuleInterop": true, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": false, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "jsx": "react", 16 | "downlevelIteration": true 17 | }, 18 | "include": ["src"], 19 | "exclude": ["node_modules", "*.d.ts"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleAddAnnotation/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 5 | 6 | const config = { 7 | entry: './src/provenanceSetup.ts', 8 | output: { 9 | path: path.resolve(__dirname, 'dist'), 10 | filename: 'bundle.js', 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.tsx?$/, 16 | loader: 'ts-loader', 17 | exclude: /node_modules/, 18 | }, 19 | { 20 | test: /\.css$/, 21 | use: ['style-loader', 'css-loader'], 22 | }, 23 | ], 24 | }, 25 | resolve: { 26 | extensions: ['.tsx', '.ts', '.js'], 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: './index.html', 31 | }), 32 | new CleanWebpackPlugin(), 33 | ], 34 | }; 35 | 36 | module.exports = config; 37 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleEphemeral/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [1.1.0](https://github.com/visdesignlab/trrack/compare/example-simple-ephemeral@1.1.0-alpha.9...example-simple-ephemeral@1.1.0) (2022-12-16) 7 | 8 | **Note:** Version bump only for package example-simple-ephemeral 9 | 10 | 11 | 12 | 13 | 14 | # [1.1.0-alpha.9](https://github.com/visdesignlab/trrack/compare/example-simple-ephemeral@1.1.0-alpha.8...example-simple-ephemeral@1.1.0-alpha.9) (2021-05-07) 15 | 16 | **Note:** Version bump only for package example-simple-ephemeral 17 | 18 | 19 | 20 | 21 | 22 | # [1.1.0-alpha.8](https://github.com/visdesignlab/trrack/compare/example-simple-ephemeral@1.1.0-alpha.7...example-simple-ephemeral@1.1.0-alpha.8) (2021-05-07) 23 | 24 | **Note:** Version bump only for package example-simple-ephemeral 25 | 26 | 27 | 28 | 29 | 30 | # [1.1.0-alpha.7](https://github.com/visdesignlab/trrack/compare/example-simple-ephemeral@1.1.0-alpha.6...example-simple-ephemeral@1.1.0-alpha.7) (2021-05-07) 31 | 32 | **Note:** Version bump only for package example-simple-ephemeral 33 | 34 | 35 | 36 | 37 | 38 | # [1.1.0-alpha.6](https://github.com/visdesignlab/trrack/compare/example-simple-ephemeral@1.1.0-alpha.5...example-simple-ephemeral@1.1.0-alpha.6) (2020-12-16) 39 | 40 | **Note:** Version bump only for package example-simple-ephemeral 41 | 42 | 43 | 44 | 45 | 46 | # [1.1.0-alpha.5](https://github.com/visdesignlab/trrack/compare/example-simple-ephemeral@1.1.0-alpha.4...example-simple-ephemeral@1.1.0-alpha.5) (2020-12-16) 47 | 48 | **Note:** Version bump only for package example-simple-ephemeral 49 | 50 | 51 | 52 | 53 | 54 | # [1.1.0-alpha.4](https://github.com/visdesignlab/trrack/compare/example-simple-ephemeral@1.1.0-alpha.3...example-simple-ephemeral@1.1.0-alpha.4) (2020-11-04) 55 | 56 | **Note:** Version bump only for package example-simple-ephemeral 57 | 58 | 59 | 60 | 61 | 62 | # [1.1.0-alpha.3](https://github.com/visdesignlab/trrack/compare/example-simple-ephemeral@1.1.0-alpha.2...example-simple-ephemeral@1.1.0-alpha.3) (2020-11-04) 63 | 64 | **Note:** Version bump only for package example-simple-ephemeral 65 | 66 | 67 | 68 | 69 | 70 | # [1.1.0-alpha.2](https://github.com/visdesignlab/trrack/compare/example-simple-ephemeral@1.1.0-alpha.1...example-simple-ephemeral@1.1.0-alpha.2) (2020-10-21) 71 | 72 | **Note:** Version bump only for package example-simple-ephemeral 73 | 74 | 75 | 76 | 77 | 78 | # [1.1.0-alpha.1](https://github.com/visdesignlab/trrack/compare/example-simple-ephemeral@1.1.0-alpha.0...example-simple-ephemeral@1.1.0-alpha.1) (2020-10-20) 79 | 80 | **Note:** Version bump only for package example-simple-ephemeral 81 | 82 | 83 | 84 | 85 | 86 | # 1.1.0-alpha.0 (2020-10-20) 87 | 88 | 89 | ### Bug Fixes 90 | 91 | * 🐛 Fixed build process + added dependency for examples ([572e8cd](https://github.com/visdesignlab/trrack/commit/572e8cd8675003030ac942036201868383569835)) 92 | * 🐛 making example circles work on firefox, button style ([28e3e20](https://github.com/visdesignlab/trrack/commit/28e3e20063e40a3fc45ea1bbbeffab41f72ea4e3)) 93 | * 🐛 more syntax changes, example updates ([b6dc7ff](https://github.com/visdesignlab/trrack/commit/b6dc7ff5d7d7f8fcc669d46837e4c37210d7e32a)) 94 | 95 | 96 | ### Features 97 | 98 | * 🎸 adding examples ([994edd7](https://github.com/visdesignlab/trrack/commit/994edd76ec1be5d7aef9b3d17e097868817a702f)) 99 | * 🎸 Fixed examples and build process ([647b978](https://github.com/visdesignlab/trrack/commit/647b9789dd04a37c70395d08e547fc82adcccab7)) 100 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleEphemeral/README.md: -------------------------------------------------------------------------------- 1 | To run this example, use the following commands 2 | 3 | npm install 4 | npm start 5 | 6 | Will host the application at localhost:8080 7 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleEphemeral/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Examples 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 20 | 21 |
22 | 23 |
24 | 25 | 26 |
27 |
28 |
29 | 30 |
31 |
32 |
33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleEphemeral/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-simple-ephemeral", 3 | "private": true, 4 | "version": "1.1.0", 5 | "description": "Examples for using provenance library", 6 | "main": "index.js", 7 | "scripts": { 8 | "build": "webpack", 9 | "test": "test", 10 | "start": "webpack-dev-server --mode development" 11 | }, 12 | "license": "ISC", 13 | "dependencies": { 14 | "@visdesignlab/trrack": "^2.0.0", 15 | "@visdesignlab/trrack-vis": "^2.0.0", 16 | "d3": "^5.12.0", 17 | "font-awesome": "^4.7.0", 18 | "http-server": "^0.12.0", 19 | "typestyle": "^2.1.0" 20 | }, 21 | "devDependencies": { 22 | "@types/d3": "^5.7.2", 23 | "awesome-typescript-loader": "^5.2.1", 24 | "clean-webpack-plugin": "^3.0.0", 25 | "css-loader": "^5.0.0", 26 | "html-webpack-plugin": "^4.5.0", 27 | "prettier": "^1.18.2", 28 | "style-loader": "^2.0.0", 29 | "ts-loader": "^8.0.5", 30 | "tslint": "^5.19.0", 31 | "tslint-config-prettier": "^1.18.0", 32 | "typescript": "^4.0.3", 33 | "webpack": "^5.1.2", 34 | "webpack-cli": "^4.0.0", 35 | "webpack-dev-server": "^3.8.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleEphemeral/src/provenanceSetup.ts: -------------------------------------------------------------------------------- 1 | import { initProvenance, NodeID, createAction } from '@visdesignlab/trrack'; 2 | 3 | import { ProvVisCreator } from '@visdesignlab/trrack-vis'; 4 | import '../styles/styles.css'; 5 | import Scatterplot from './scatterplot'; 6 | 7 | /** 8 | * interface representing the state of the application 9 | */ 10 | export interface NodeState { 11 | selectedQuartet: string; 12 | selectedNode: string; 13 | hoveredNode: string; 14 | } 15 | 16 | /** 17 | * Initial state 18 | */ 19 | 20 | const initialState: NodeState = { 21 | selectedQuartet: 'I', 22 | selectedNode: 'none', 23 | hoveredNode: 'none', 24 | }; 25 | 26 | type EventTypes = 'Change Quartet' | 'Select Node' | 'Hover Node'; 27 | 28 | // initialize provenance with the first state 29 | const prov = initProvenance(initialState, { 30 | loadFromUrl: false, 31 | }); 32 | 33 | // Set up apply action functions for each of the 3 actions that affect state 34 | 35 | /** 36 | * Function called when the quartet number is changed. Applies an action to provenance. 37 | * This is a complex action, meaning it always stores a state node. 38 | */ 39 | 40 | const quartetUpdateAction = createAction( 41 | (state: NodeState, newQuartet: string) => { 42 | state.selectedQuartet = newQuartet; 43 | }, 44 | ); 45 | 46 | const changeQuartetUpdate = function (newQuartet: string) { 47 | quartetUpdateAction 48 | .setLabel(`Quartet ${newQuartet} Selected`) 49 | .setEventType('Change Quartet') 50 | .saveStateMode('Complete'); 51 | 52 | prov.apply(quartetUpdateAction(newQuartet)); 53 | }; 54 | 55 | /** 56 | * Function called when a node is selected. Applies an action to provenance. 57 | */ 58 | 59 | const nodeSelectAction = createAction( 60 | (state: NodeState, newSelected: string) => { 61 | state.selectedNode = newSelected; 62 | }, 63 | ); 64 | 65 | const selectNodeUpdate = function (newSelected: string) { 66 | nodeSelectAction.setLabel(`${newSelected} Selected`).setEventType('Select Node'); 67 | 68 | prov.apply(nodeSelectAction(newSelected)); 69 | }; 70 | 71 | /** 72 | * Function called when a node is hovered. Applies an action to provenance. 73 | */ 74 | 75 | const hoverAction = createAction( 76 | (state: NodeState, newHover: string) => { 77 | state.hoveredNode = newHover; 78 | return state; 79 | }, 80 | ); 81 | 82 | const hoverNodeUpdate = function (newHover: string) { 83 | hoverAction 84 | .setLabel(newHover === '' ? 'Hover Removed' : `${newHover} Hovered`) 85 | .setEventType('Hover Node') 86 | .setActionType('Ephemeral'); 87 | 88 | prov.apply(hoverAction(newHover)); 89 | }; 90 | 91 | // Create our scatterplot class which handles the actual vis. Pass it our three action functions 92 | // so it can use them when appropriate. 93 | const scatterplot = new Scatterplot(changeQuartetUpdate, selectNodeUpdate, hoverNodeUpdate); 94 | 95 | // Create function to pass to the ProvVis library for when a node is selected in the graph. 96 | // For our purposes, were simply going to jump to the selected node. 97 | const visCallback = function (newNode: NodeID) { 98 | prov.goToNode(newNode); 99 | }; 100 | 101 | // Set up observers for the three keys in state. 102 | // These observers will get called either when an applyAction 103 | // function changes the associated keys value. 104 | 105 | // Also will be called when an internal graph change such as goBackNSteps, goBackOneStep or goToNode 106 | // change the keys value. 107 | 108 | /** 109 | * Observer for when the quartet state is changed. Calls changeQuartet in scatterplot to update vis. 110 | */ 111 | prov.addObserver( 112 | (state) => state.selectedQuartet, 113 | () => { 114 | scatterplot.changeQuartet(prov.getState(prov.current).selectedQuartet); 115 | }, 116 | ); 117 | 118 | /** 119 | * Observer for when the selected node state is changed. 120 | * Calls selectNode in scatterplot to update vis. 121 | */ 122 | prov.addObserver( 123 | (state) => state.selectedNode, 124 | () => { 125 | scatterplot.selectNode(prov.getState(prov.current).selectedNode); 126 | }, 127 | ); 128 | 129 | /** 130 | * Observer for when the hovered node state is changed. Calls hoverNode in scatterplot to update vis. 131 | */ 132 | prov.addObserver( 133 | (state) => state.hoveredNode, 134 | () => { 135 | scatterplot.hoverNode(prov.getState(prov.current).hoveredNode); 136 | }, 137 | ); 138 | 139 | prov.done(); 140 | 141 | // Setup ProvVis once initially 142 | ProvVisCreator(document.getElementById('provDiv')!, prov, visCallback, true, true); 143 | 144 | // Undo function which simply goes one step backwards in the graph. 145 | function undo() { 146 | prov.goBackToNonEphemeral(); 147 | } 148 | 149 | // Redo function which traverses down the tree one step. 150 | function redo() { 151 | if (prov.current.children.length === 0) { 152 | return; 153 | } 154 | prov.goForwardToNonEphemeral(); 155 | } 156 | 157 | // Setting up undo/redo hotkey to typical buttons 158 | document.onkeydown = function (e) { 159 | const mac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform); 160 | 161 | if (!e.shiftKey && (mac ? e.metaKey : e.ctrlKey) && e.which === 90) { 162 | undo(); 163 | } else if (e.shiftKey && (mac ? e.metaKey : e.ctrlKey) && e.which === 90) { 164 | redo(); 165 | } 166 | }; 167 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleEphemeral/src/scatterplot.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | /* eslint-disable no-unused-vars */ 3 | import * as d3 from 'd3'; 4 | 5 | export default class Scatterplot { 6 | margin:any; 7 | 8 | width:number; 9 | 10 | height:number; 11 | 12 | quartetNum:string; 13 | 14 | data:any[]; 15 | 16 | svg:d3.Selection; 17 | 18 | xScale:d3.ScaleLinear; 19 | 20 | yScale:d3.ScaleLinear; 21 | 22 | constructor( 23 | changeQuartetFunc: (s:string) => void, 24 | selectNodeFunc: (s:string) => void, 25 | hoverNodeFunc: (s:string) => void, 26 | ) { 27 | this.margin = {}; 28 | this.width = 0; 29 | this.height = 0; 30 | this.quartetNum = ''; 31 | this.data = []; 32 | this.svg = d3.select('#mainDiv') 33 | .append('svg'); 34 | this.xScale = d3.scaleLinear(); 35 | this.yScale = d3.scaleLinear(); 36 | 37 | d3.csv('https://gist.githubusercontent.com/ericbusboom/b2ac1d366c005cd2ed8c/raw/c92c66e43d144fa9c29dbd602d5af6988e8db533/anscombes.csv') 38 | .then((d) => { 39 | this.data = d; 40 | this.margin = { 41 | top: 20, right: 20, bottom: 20, left: 20, 42 | }; 43 | this.width = 800 - this.margin.left - this.margin.right; 44 | this.height = 800 - this.margin.top - this.margin.bottom; 45 | 46 | this.quartetNum = 'I'; 47 | 48 | this.xScale.domain([ 49 | d3.min(this.data, (innerD) => +innerD.x)!, 50 | d3.max(this.data, (innerD) => +innerD.x)!, 51 | ]); 52 | this.xScale.range([50, 750]); 53 | 54 | this.yScale.domain([ 55 | d3.min(this.data, (innerD) => +innerD.y)!, 56 | d3.max(this.data, (innerD) => +innerD.y)!, 57 | ]); 58 | this.yScale.range([50, 750]); 59 | 60 | d3.select('#quartets') 61 | .on('change', function () { 62 | changeQuartetFunc((this as HTMLSelectElement).value); 63 | }); 64 | 65 | this.initializeVis(selectNodeFunc, hoverNodeFunc); 66 | }); 67 | } 68 | 69 | /** 70 | * Creates an svg and draws the initial visualization 71 | */ 72 | 73 | initializeVis(selectNodeFunc: (s:string) => void, hoverNodeFunc: (s:string) => void) { 74 | this.svg 75 | .attr('width', this.width + this.margin.left + this.margin.right) 76 | .attr('height', this.height + this.margin.top + this.margin.bottom); 77 | 78 | const currData = this.data.filter((d) => d.dataset === this.quartetNum); 79 | 80 | this.svg.selectAll('circle') 81 | .data(currData) 82 | .enter() 83 | .append('circle') 84 | .attr('class', 'normalNode') 85 | .attr('id', (d) => `node_${d.id}`) 86 | .attr('cx', (d) => this.xScale(+d.x)) 87 | .attr('cy', (d) => this.height - this.yScale(+d.y)) 88 | .attr('r', 7) 89 | .on('click', (d) => selectNodeFunc(`node_${d.id}`)) 90 | .on('mouseover', (d) => hoverNodeFunc(`node_${d.id}`)) 91 | .on('mouseout', (d) => hoverNodeFunc('')); 92 | } 93 | 94 | /** 95 | * Filters the data so that only points associated with the new quartet are used 96 | * Updates each circle and transitions them to their new position 97 | */ 98 | 99 | changeQuartet(newQuartet:string) { 100 | this.quartetNum = newQuartet; 101 | 102 | const currData = this.data.filter((d) => d.dataset === this.quartetNum); 103 | 104 | this.svg.selectAll('circle') 105 | .data(currData) 106 | .attr('id', (d) => `node_${d.id}`) 107 | .transition() 108 | .duration(750) 109 | .attr('cx', (d) => this.xScale(+d.x)) 110 | .attr('cy', (d) => this.height - this.yScale(+d.y)); 111 | } 112 | 113 | /** 114 | * Ensures the previously selected node is no longer selected 115 | * Selects the new node 116 | */ 117 | 118 | selectNode(selectedNode:string) { 119 | d3.select('.selectedNode') 120 | .classed('selectedNode', false) 121 | .attr('r', 7); 122 | 123 | d3.select(`#${selectedNode}`) 124 | .classed('selectedNode', true) 125 | .attr('r', 10); 126 | } 127 | 128 | /** 129 | * Ensures the previously hovered node is no longer hovered 130 | * If hoverNode is not empty, hovers the new node 131 | */ 132 | 133 | hoverNode(hoverNode:string) { 134 | d3.select('.hoverNode') 135 | .classed('hoverNode', false); 136 | 137 | if (hoverNode !== '') { 138 | d3.select(`#${hoverNode}`) 139 | .classed('hoverNode', true); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleEphemeral/styles/styles.css: -------------------------------------------------------------------------------- 1 | .normalNode 2 | { 3 | fill: #333333; 4 | } 5 | 6 | .selectedNode 7 | { 8 | fill: blue 9 | } 10 | 11 | .hoverNode{ 12 | fill: blue; 13 | opacity: .5 14 | } 15 | 16 | #mainDiv{ 17 | border: 1px solid black; 18 | max-height: 800px; 19 | display: flex; 20 | float:left 21 | } 22 | 23 | #quartDiv{ 24 | display: flex; 25 | justify-content: center; 26 | margin-bottom: 10px; 27 | } 28 | 29 | #provDiv{ 30 | max-width: 300px; 31 | display: flex; 32 | float: right; 33 | } 34 | 35 | #parent{ 36 | display: flex; 37 | justify-content: center; 38 | } 39 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleEphemeral/tsconfig.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "compilerOptions": { 4 | "target": "es5", 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "esModuleInterop": true, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": false, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "jsx": "react", 16 | "downlevelIteration": true 17 | }, 18 | "include": ["src"], 19 | "exclude": ["node_modules", "*.d.ts"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/trrack-examples/simpleExampleEphemeral/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 5 | 6 | const config = { 7 | entry: './src/provenanceSetup.ts', 8 | output: { 9 | path: path.resolve(__dirname, 'dist'), 10 | filename: 'bundle.js', 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.tsx?$/, 16 | loader: 'ts-loader', 17 | exclude: /node_modules/, 18 | }, 19 | { 20 | test: /\.css$/, 21 | use: ['style-loader', 'css-loader'], 22 | }, 23 | ], 24 | }, 25 | resolve: { 26 | extensions: ['.tsx', '.ts', '.js'], 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: './index.html', 31 | }), 32 | new CleanWebpackPlugin(), 33 | ], 34 | }; 35 | 36 | module.exports = config; 37 | -------------------------------------------------------------------------------- /packages/trrack-vis/.eslintignore: -------------------------------------------------------------------------------- 1 | src/ -------------------------------------------------------------------------------- /packages/trrack-vis/.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push] 3 | jobs: 4 | build: 5 | name: Build, lint, and test on Node ${{ matrix.node }} and ${{ matrix.os }} 6 | 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | node: ['10.x', '12.x', '14.x'] 11 | os: [ubuntu-latest, windows-latest, macOS-latest] 12 | 13 | steps: 14 | - name: Checkout repo 15 | uses: actions/checkout@v2 16 | 17 | - name: Use Node ${{ matrix.node }} 18 | uses: actions/setup-node@v1 19 | with: 20 | node-version: ${{ matrix.node }} 21 | 22 | - name: Install deps and build (with cache) 23 | uses: bahmutov/npm-install@v1 24 | 25 | - name: Lint 26 | run: yarn lint 27 | 28 | - name: Test 29 | run: yarn test --ci --coverage --maxWorkers=2 30 | 31 | - name: Build 32 | run: yarn build 33 | -------------------------------------------------------------------------------- /packages/trrack-vis/.github/workflows/size.yml: -------------------------------------------------------------------------------- 1 | name: size 2 | on: [pull_request] 3 | jobs: 4 | size: 5 | runs-on: ubuntu-latest 6 | env: 7 | CI_JOB_NUMBER: 1 8 | steps: 9 | - uses: actions/checkout@v1 10 | - uses: andresz1/size-limit-action@v1 11 | with: 12 | github_token: ${{ secrets.GITHUB_TOKEN }} 13 | -------------------------------------------------------------------------------- /packages/trrack-vis/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | .cache 5 | dist 6 | -------------------------------------------------------------------------------- /packages/trrack-vis/.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ['../stories/**/*.stories.@(ts|tsx)'], 3 | addons: ['@storybook/addon-links', '@storybook/addon-essentials'], 4 | // https://storybook.js.org/docs/react/configure/typescript#mainjs-configuration 5 | typescript: { 6 | check: true, // type-check stories during Storybook build 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /packages/trrack-vis/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | // https://storybook.js.org/docs/react/writing-stories/parameters#global-parameters 2 | export const parameters = { 3 | // https://storybook.js.org/docs/react/essentials/actions#automatically-matching-args 4 | actions: { argTypesRegex: '^on.*' }, 5 | }; 6 | -------------------------------------------------------------------------------- /packages/trrack-vis/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [2.0.0](https://github.com/visdesignlab/trrack-vis/compare/@visdesignlab/trrack-vis@2.0.0-alpha.12...@visdesignlab/trrack-vis@2.0.0) (2022-12-16) 7 | 8 | **Note:** Version bump only for package @visdesignlab/trrack-vis 9 | 10 | 11 | 12 | 13 | 14 | # [2.0.0-alpha.12](https://github.com/visdesignlab/trrack-vis/compare/@visdesignlab/trrack-vis@2.0.0-alpha.11...@visdesignlab/trrack-vis@2.0.0-alpha.12) (2021-05-07) 15 | 16 | **Note:** Version bump only for package @visdesignlab/trrack-vis 17 | 18 | 19 | 20 | 21 | 22 | # [2.0.0-alpha.11](https://github.com/visdesignlab/trrack-vis/compare/@visdesignlab/trrack-vis@2.0.0-alpha.10...@visdesignlab/trrack-vis@2.0.0-alpha.11) (2021-05-07) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * Trrackvis style fixes ([b78db15](https://github.com/visdesignlab/trrack-vis/commit/b78db155373a7b667ce45a801a0c55b05aea5617)) 28 | 29 | 30 | 31 | 32 | 33 | # [2.0.0-alpha.10](https://github.com/visdesignlab/trrack-vis/compare/@visdesignlab/trrack-vis@2.0.0-alpha.9...@visdesignlab/trrack-vis@2.0.0-alpha.10) (2021-05-07) 34 | 35 | 36 | ### Features 37 | 38 | * Serializing support ([27a7f0f](https://github.com/visdesignlab/trrack-vis/commit/27a7f0fada3b68a045d6a8b6dcfe5ece0b49ac8a)) 39 | 40 | 41 | 42 | 43 | 44 | # [2.0.0-alpha.9](https://github.com/visdesignlab/trrack-vis/compare/@visdesignlab/trrack-vis@2.0.0-alpha.8...@visdesignlab/trrack-vis@2.0.0-alpha.9) (2020-12-16) 45 | 46 | **Note:** Version bump only for package @visdesignlab/trrack-vis 47 | 48 | 49 | 50 | 51 | 52 | # [2.0.0-alpha.8](https://github.com/visdesignlab/trrack-vis/compare/@visdesignlab/trrack-vis@2.0.0-alpha.7...@visdesignlab/trrack-vis@2.0.0-alpha.8) (2020-12-16) 53 | 54 | **Note:** Version bump only for package @visdesignlab/trrack-vis 55 | 56 | 57 | 58 | 59 | 60 | # [2.0.0-alpha.7](https://github.com/visdesignlab/trrack-vis/compare/@visdesignlab/trrack-vis@2.0.0-alpha.6...@visdesignlab/trrack-vis@2.0.0-alpha.7) (2020-11-04) 61 | 62 | **Note:** Version bump only for package @visdesignlab/trrack-vis 63 | 64 | 65 | 66 | 67 | 68 | # [2.0.0-alpha.6](https://github.com/visdesignlab/trrack-vis/compare/@visdesignlab/trrack-vis@2.0.0-alpha.5...@visdesignlab/trrack-vis@2.0.0-alpha.6) (2020-11-04) 69 | 70 | **Note:** Version bump only for package @visdesignlab/trrack-vis 71 | 72 | 73 | 74 | 75 | 76 | # [2.0.0-alpha.5](https://github.com/visdesignlab/trrack-vis/compare/@visdesignlab/trrack-vis@2.0.0-alpha.4...@visdesignlab/trrack-vis@2.0.0-alpha.5) (2020-10-21) 77 | 78 | **Note:** Version bump only for package @visdesignlab/trrack-vis 79 | 80 | 81 | 82 | 83 | 84 | # [2.0.0-alpha.4](https://github.com/visdesignlab/trrack-vis/compare/@visdesignlab/trrack-vis@2.0.0-alpha.3...@visdesignlab/trrack-vis@2.0.0-alpha.4) (2020-10-20) 85 | 86 | **Note:** Version bump only for package @visdesignlab/trrack-vis 87 | 88 | 89 | 90 | 91 | 92 | # [2.0.0-alpha.3](https://github.com/visdesignlab/trrack-vis/compare/@visdesignlab/trrack-vis@2.0.0-alpha.2...@visdesignlab/trrack-vis@2.0.0-alpha.3) (2020-10-20) 93 | 94 | 95 | ### Bug Fixes 96 | 97 | * 🐛 Fixed lint errors ([7858b5b](https://github.com/visdesignlab/trrack-vis/commit/7858b5b9ec9754391ff68741056cf6992fe37e07)) 98 | * 🐛 Made react and react-dom peer dependencies ([da0b24d](https://github.com/visdesignlab/trrack-vis/commit/da0b24d3d953a8f31ae4ac97644409c5b00129ca)) 99 | 100 | 101 | 102 | 103 | 104 | # [2.0.0-alpha.2](https://github.com/visdesignlab/trrack-vis/compare/@visdesignlab/trrack-vis@2.0.0-alpha.1...@visdesignlab/trrack-vis@2.0.0-alpha.2) (2020-10-19) 105 | 106 | **Note:** Version bump only for package @visdesignlab/trrack-vis 107 | 108 | 109 | 110 | 111 | 112 | # [2.0.0-alpha.1](https://github.com/visdesignlab/trrack-vis/compare/@visdesignlab/trrack-vis@2.0.0-alpha.0...@visdesignlab/trrack-vis@2.0.0-alpha.1) (2020-10-19) 113 | 114 | **Note:** Version bump only for package @visdesignlab/trrack-vis 115 | 116 | 117 | 118 | 119 | 120 | # 2.0.0-alpha.0 (2020-10-19) 121 | 122 | 123 | ### Bug Fixes 124 | 125 | * 🐛 Fixed build process + added dependency for examples ([572e8cd](https://github.com/visdesignlab/trrack-vis/commit/572e8cd8675003030ac942036201868383569835)) 126 | * 🐛 linting trrack-vis and changing syntax ([13216bf](https://github.com/visdesignlab/trrack-vis/commit/13216bf8e707ecb74431510efa940d895f292f66)) 127 | * 🐛 making example circles work on firefox, button style ([28e3e20](https://github.com/visdesignlab/trrack-vis/commit/28e3e20063e40a3fc45ea1bbbeffab41f72ea4e3)) 128 | * 🐛 more syntax changes, example updates ([b6dc7ff](https://github.com/visdesignlab/trrack-vis/commit/b6dc7ff5d7d7f8fcc669d46837e4c37210d7e32a)) 129 | 130 | 131 | ### Features 132 | 133 | * 🎸 adding examples ([994edd7](https://github.com/visdesignlab/trrack-vis/commit/994edd76ec1be5d7aef9b3d17e097868817a702f)) 134 | * 🎸 changing styling, tabs, undo/redo buttons to materialui ([be5b029](https://github.com/visdesignlab/trrack-vis/commit/be5b0293e7b68708fe1cfc94f9b356a9ee064291)) 135 | * 🎸 Fixing trrack-core and adding trrack vis ([169145c](https://github.com/visdesignlab/trrack-vis/commit/169145cb4f7d3a880c61d5f073115d7d898a62a8)) 136 | * 🎸 Integrated trrack-vis ([57d337e](https://github.com/visdesignlab/trrack-vis/commit/57d337e60eb9b7d4059e23bf9e827c8e872c6a04)) 137 | * 🎸 Updated trrack-vis ([f254f8d](https://github.com/visdesignlab/trrack-vis/commit/f254f8d996095b9e0dc2c1e6c26a59d185d30aee)) 138 | 139 | 140 | ### BREAKING CHANGES 141 | 142 | * 🧨 Updates the API for trrack-vis, trrack-vis now automatically connects to 143 | trrack 144 | -------------------------------------------------------------------------------- /packages/trrack-vis/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Kiran Gadhave 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. -------------------------------------------------------------------------------- /packages/trrack-vis/README.md: -------------------------------------------------------------------------------- 1 | # trrack-vis Library 2 | 3 | This library is intended to be used with trrack, a provenance tracking library, which can be found [here](https://github.com/visdesignlab/trrack). Trrack-vis can be used to visualize the non-linear provenance graph, as well as change nodes within the graph. Trrack-vis is designed to be highly customizable, allowing for the size and position of the graph to be customized, custom icons to be used in the graph, custom ways to visualize annotations, and the grouping of nodes. 4 | 5 | Here are multiple [examples](https://github.com/visdesignlab/trrack-examples) using trrack and trrack-vis. 6 | 7 | For documentation, see http://vdl.sci.utah.edu/trrack-examples/api/trrack-vis 8 | 9 | 10 | ## Installation 11 | 12 | - NPM 13 | 14 | ```bash 15 | npm install --save-dev @visdesignlab/trrack-vis 16 | ``` 17 | 18 | - Yarn 19 | 20 | ```bash 21 | yarn add @visdesignlab/trrack-vis 22 | ``` 23 | 24 | ```html 25 | 26 | 27 | ``` 28 | -------------------------------------------------------------------------------- /packages/trrack-vis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@visdesignlab/trrack-vis", 3 | "version": "2.0.0", 4 | "description": "", 5 | "keywords": [], 6 | "main": "dist/index.js", 7 | "module": "dist/trrackvis.esm.js", 8 | "typings": "dist/index.d.ts", 9 | "files": [ 10 | "dist", 11 | "src" 12 | ], 13 | "author": "Kiran Gadhave ", 14 | "repository": { 15 | "type": "git", 16 | "url": "git@github.com:visdesignlab/trrack-vis.git" 17 | }, 18 | "license": "MIT", 19 | "engines": { 20 | "node": ">=10" 21 | }, 22 | "scripts": { 23 | "start": "tsdx watch", 24 | "build": "tsdx build --format cjs,esm,umd --name trrackvis", 25 | "test": "tsdx test --passWithNoTests", 26 | "prepare": "tsdx build --format cjs,esm,umd --name trrackvis", 27 | "size": "size-limit", 28 | "analyze": "size-limit --why", 29 | "storybook": "start-storybook -p 6006", 30 | "build-storybook": "build-storybook", 31 | "docs": "typedoc --out docs --target es6 --mode file src" 32 | }, 33 | "peerDependencies": { 34 | "react": ">=16", 35 | "react-dom": ">=16.13.1" 36 | }, 37 | "husky": { 38 | "hooks": { 39 | "pre-commit": "tsdx lint" 40 | } 41 | }, 42 | "prettier": { 43 | "printWidth": 80, 44 | "semi": true, 45 | "singleQuote": true, 46 | "trailingComma": "es5" 47 | }, 48 | "size-limit": [ 49 | { 50 | "path": "dist/trrack-vis.cjs.production.min.js", 51 | "limit": "500 KB" 52 | }, 53 | { 54 | "path": "dist/trrack-vis.esm.js", 55 | "limit": "500 KB" 56 | } 57 | ], 58 | "devDependencies": { 59 | "@babel/core": "^7.11.6", 60 | "@size-limit/preset-small-lib": "^4.6.0", 61 | "@storybook/addon-essentials": "^6.0.26", 62 | "@storybook/addon-info": "^5.3.21", 63 | "@storybook/addon-links": "^6.0.26", 64 | "@storybook/addons": "^6.0.26", 65 | "@storybook/react": "^6.0.26", 66 | "@types/d3": "^5.7.2", 67 | "@types/react": "^16.9.51", 68 | "@types/react-dom": "^16.9.8", 69 | "babel-loader": "^8.1.0", 70 | "husky": "^4.3.0", 71 | "lodash.camelcase": "^4.3.0", 72 | "react": "^16.13.1", 73 | "react-dom": "^16.13.1", 74 | "react-is": "^16.13.1", 75 | "size-limit": "^4.6.0", 76 | "tsdx": "^0.14.0", 77 | "tslib": "^2.0.2", 78 | "typescript": "^4.0.3" 79 | }, 80 | "dependencies": { 81 | "@storybook/react": "^6.0.26", 82 | "@visdesignlab/trrack": "^2.0.0", 83 | "d3": "^5.15.0", 84 | "mobx": "^6.0.1", 85 | "mobx-react": "^6.1.7", 86 | "react-contenteditable": "^3.3.5", 87 | "react-move": "^6.1.0", 88 | "semantic-ui-css": "^2.4.1", 89 | "semantic-ui-react": "^0.88.2", 90 | "typedoc": "^0.19.2", 91 | "typestyle": "^2.0.4" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/Utils/BundleMap.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | export type Bundle = { 3 | metadata: any; 4 | bundleLabel: string; 5 | bunchedNodes: string[]; 6 | }; 7 | 8 | export type BundleMap = { [key: string]: Bundle }; 9 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/Utils/EventConfig.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | import { ReactChild } from 'react'; 3 | 4 | export type Config = { 5 | regularGlyph: ReactChild; 6 | currentGlyph: ReactChild; 7 | backboneGlyph: ReactChild; 8 | bundleGlyph: ReactChild; 9 | }; 10 | 11 | export type EventConfig = { 12 | [key: string]: Partial; 13 | }; 14 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/Utils/TreeLayout.ts: -------------------------------------------------------------------------------- 1 | import { ProvenanceNode } from '@visdesignlab/trrack'; 2 | import { HierarchyNode } from 'd3'; 3 | import { StratifiedMap } from '../components/ProvVis'; 4 | 5 | export type TreeNode = HierarchyNode; 6 | 7 | export interface ExtendedHierarchyNode 8 | extends HierarchyNode> { 9 | column: number; 10 | } 11 | 12 | export type ExtendedStratifiedMap = { 13 | [key: string]: ExtendedHierarchyNode; 14 | }; 15 | 16 | export function treeLayout( 17 | nodes: StratifiedMap, 18 | current: string, 19 | root: string 20 | ) { 21 | const depthMap: { [key: string]: any } = {}; 22 | 23 | const currentPath = getPathTo(nodes, root, current); 24 | 25 | DFS(nodes, root, depthMap, currentPath); 26 | 27 | return currentPath; 28 | } 29 | 30 | function DFS( 31 | nodes: StratifiedMap, 32 | node: string, 33 | depthMap: any, 34 | currentPath: string[] 35 | ) { 36 | const explored = new Set(); 37 | 38 | const toExplore = []; 39 | 40 | let currDepth = 0; 41 | 42 | toExplore.push(nodes[node]); 43 | 44 | while (toExplore.length > 0) { 45 | const temp: any = toExplore.pop(); 46 | 47 | if (!explored.has(temp.id)) { 48 | temp.width = currDepth; 49 | depthMap[temp.id] = temp.width; 50 | explored.add(temp.id); 51 | } else { 52 | temp.width = depthMap[temp.id]; 53 | } 54 | 55 | if (temp.children) { 56 | toExplore.push( 57 | ...temp.children.sort((a: any, b: any) => { 58 | const aIncludes = currentPath.includes(a.id) ? 1 : 0; 59 | const bIncludes = currentPath.includes(b.id) ? 1 : 0; 60 | return aIncludes - bIncludes; 61 | }) 62 | ); 63 | } else { 64 | currDepth += 1; 65 | } 66 | } 67 | } 68 | 69 | export function getPathTo( 70 | nodes: StratifiedMap, 71 | from: string, 72 | to: string 73 | ): string[] { 74 | const path: string[] = []; 75 | 76 | search(nodes, from, to, path); 77 | 78 | return [from, ...path.reverse()]; 79 | } 80 | 81 | function search( 82 | nodes: StratifiedMap, 83 | node: string, 84 | final: string, 85 | path: string[] 86 | ) { 87 | if (!nodes[node]) return false; 88 | 89 | if (node === final) { 90 | path.push(node); 91 | return true; 92 | } 93 | 94 | const children = nodes[node].children || []; 95 | 96 | // eslint-disable-next-line no-restricted-syntax 97 | for (const child of children) { 98 | if (search(nodes, child.id!, final, path)) { 99 | path.push(child.id!); 100 | return true; 101 | } 102 | } 103 | 104 | return false; 105 | } 106 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/Utils/findBackboneBundleNodes.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | /* eslint-disable guard-for-in */ 3 | 4 | import { BundleMap } from './BundleMap'; 5 | 6 | export default function findBackboneBundleNodes( 7 | nodeMap: any, 8 | bundleMap?: BundleMap 9 | ): string[] { 10 | const backboneBundleNodes = []; 11 | 12 | for (const bundle in bundleMap) { 13 | let flag = true; 14 | 15 | if (nodeMap[bundle].width !== 0) { 16 | flag = false; 17 | } 18 | 19 | for (const i of bundleMap[bundle].bunchedNodes) { 20 | if (nodeMap[i].width !== 0) { 21 | flag = false; 22 | } 23 | } 24 | 25 | if (flag) { 26 | backboneBundleNodes.push(bundle); 27 | for (const n of bundleMap[bundle].bunchedNodes) { 28 | backboneBundleNodes.push(n); 29 | } 30 | } 31 | } 32 | 33 | return backboneBundleNodes; 34 | } 35 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/Utils/findBundleParent.ts: -------------------------------------------------------------------------------- 1 | import { BundleMap } from './BundleMap'; 2 | 3 | export default function findBundleParent( 4 | nodeId: string, 5 | bundleMap?: BundleMap 6 | ): string[] { 7 | const parentList = []; 8 | // eslint-disable-next-line no-restricted-syntax 9 | for (const bundle in bundleMap) { 10 | if (bundleMap[bundle].bunchedNodes.includes(nodeId)) { 11 | parentList.push(bundle); 12 | } 13 | } 14 | 15 | return parentList; 16 | } 17 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/Utils/translate.ts: -------------------------------------------------------------------------------- 1 | export default function translate(x: number, y: number) { 2 | return `translate(${x}, ${y})`; 3 | } 4 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/components/BookmarkListView.tsx: -------------------------------------------------------------------------------- 1 | import { NodeID, ProvenanceGraph } from '@visdesignlab/trrack'; 2 | import React from 'react'; 3 | import { NodeGroup } from 'react-move'; 4 | import { EventConfig } from '../Utils/EventConfig'; 5 | import translate from '../Utils/translate'; 6 | import BookmarkNode from './BookmarkNode'; 7 | import BookmarkTransitions from './BookmarkTransitions'; 8 | 9 | export interface BookmarkListViewConfig { 10 | graph?: ProvenanceGraph; 11 | eventConfig?: EventConfig; 12 | currentNode: NodeID; 13 | } 14 | 15 | function BookmarkListView({ 16 | graph, 17 | eventConfig, 18 | currentNode, 19 | }: BookmarkListViewConfig) { 20 | if (graph === undefined) { 21 | return null; 22 | } 23 | 24 | const gutter = 15; 25 | const verticalSpace = 50; 26 | 27 | const bookmarks = []; 28 | 29 | const xOffset = gutter; 30 | const yOffset = verticalSpace; 31 | 32 | // eslint-disable-next-line no-restricted-syntax 33 | for (const j in graph.nodes) { 34 | if (graph.nodes[j].bookmarked) { 35 | bookmarks.push(graph.nodes[j]); 36 | } 37 | } 38 | 39 | return ( 40 | d.label} 43 | {...BookmarkTransitions(xOffset, yOffset, bookmarks)} 44 | > 45 | {(innerBookmarks) => ( 46 | <> 47 | {innerBookmarks.map((bookmark) => { 48 | const { data: d, key, state } = bookmark; 49 | 50 | return ( 51 | 52 | 59 | 60 | ); 61 | })} 62 | 63 | )} 64 | 65 | ); 66 | } 67 | 68 | export default BookmarkListView; 69 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/components/BookmarkNode.tsx: -------------------------------------------------------------------------------- 1 | import { StateNode } from '@visdesignlab/trrack'; 2 | import React from 'react'; 3 | import { Animate } from 'react-move'; 4 | import { EventConfig } from '../Utils/EventConfig'; 5 | import { treeColor } from './Styles'; 6 | 7 | interface BookmarkNodeProps { 8 | current: boolean; 9 | node: StateNode; 10 | nodeMap: any; 11 | editAnnotations: boolean; 12 | eventConfig?: EventConfig; 13 | } 14 | 15 | function BookmarkNode({ 16 | current, 17 | node, 18 | eventConfig, 19 | }: BookmarkNodeProps) { 20 | const radius = 5; 21 | const strokeWidth = 2; 22 | const textSize = 15; 23 | 24 | const cursorStyle = { 25 | cursor: 'pointer', 26 | } as React.CSSProperties; 27 | 28 | let glyph = ( 29 | 35 | ); 36 | 37 | const dropDownAdded = false; 38 | const { eventType } = node.metadata; 39 | 40 | if (eventConfig) { 41 | const { currentGlyph, backboneGlyph } = eventConfig[eventType]; 42 | 43 | if (current) { 44 | glyph = ( 45 | 46 | {currentGlyph} 47 | 48 | ); 49 | } else { 50 | glyph = ( 51 | 52 | {backboneGlyph} 53 | 54 | ); 55 | } 56 | } 57 | 58 | let label = ''; 59 | let annotate = ''; 60 | 61 | if ( 62 | node.artifacts && 63 | node.artifacts.annotations.length > 0 && 64 | node.artifacts.annotations[0].annotation.length > 0 65 | ) { 66 | annotate = node.artifacts.annotations[0].annotation; 67 | } 68 | 69 | label = node.label; 70 | 71 | if (annotate.length > 20) annotate = `${annotate.substr(0, 20)}..`; 72 | 73 | if (label.length > 20) label = `${label.substr(0, 20)}..`; 74 | 75 | return ( 76 | 82 | {() => ( 83 | <> 84 | 85 | {glyph} 86 | 87 | 95 | {label} 96 | 97 | 98 | 106 | {annotate} 107 | 108 | 109 | 110 | )} 111 | 112 | ); 113 | } 114 | 115 | export default BookmarkNode; 116 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/components/BookmarkToggle.tsx: -------------------------------------------------------------------------------- 1 | import { ProvenanceGraph } from '@visdesignlab/trrack'; 2 | 3 | import React from 'react'; 4 | 5 | export interface BookmarkToggleConfig { 6 | graph?: ProvenanceGraph; 7 | bookmarkView: boolean; 8 | // eslint-disable-next-line no-unused-vars 9 | setBookmarkView: (b: boolean) => void; 10 | } 11 | 12 | function BookmarkToggle({ 13 | graph, 14 | bookmarkView, 15 | setBookmarkView, 16 | }: BookmarkToggleConfig) { 17 | if (graph === undefined) { 18 | return null; 19 | } 20 | 21 | return ( 22 |
23 | { 29 | setBookmarkView(!bookmarkView); 30 | }} 31 | readOnly 32 | /> 33 | 36 |
37 | ); 38 | } 39 | 40 | export default BookmarkToggle; 41 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/components/BookmarkTransitions.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-plusplus */ 2 | export default function BookmarkTransitions( 3 | xOffset: number, 4 | yOffset: number, 5 | nodeList: any[] 6 | ) { 7 | xOffset = -xOffset; 8 | 9 | const start = (data: any) => { 10 | const index = nodeList.findIndex(d => d.id === data.id); 11 | 12 | const x = 0; 13 | const y = 40 * index; 14 | 15 | return { x, y: y - yOffset, opacity: 0 }; 16 | }; 17 | 18 | const enter = (data: any) => { 19 | const index = nodeList.findIndex(d => d.id === data.id); 20 | 21 | const x = 0; 22 | const y = 40 * index; 23 | 24 | return { 25 | x: [x], 26 | y: [y], 27 | opactiy: 1, 28 | }; 29 | }; 30 | 31 | const update = (data: any) => { 32 | const index = nodeList.findIndex(d => d.id === data.id); 33 | 34 | const x = 0; 35 | const y = 40 * index; 36 | 37 | return { 38 | x: [x], 39 | y: [y], 40 | opactiy: 1, 41 | }; 42 | }; 43 | 44 | return { 45 | enter, 46 | leave: start, 47 | update, 48 | start, 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/components/BundleTransitions.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-plusplus */ 2 | import { getX } from './LinkTransitions'; 3 | import { BundleMap } from '../Utils/BundleMap'; 4 | 5 | export default function bundleTransitions( 6 | xOffset: number, 7 | yOffset: number, 8 | clusterOffset: number, 9 | backboneOffset: number, 10 | duration = 500, 11 | expandedClusterList: string[], 12 | stratifiedMap: any, 13 | nodeList: any[], 14 | annotationOpen: number, 15 | annotationHeight: number, 16 | bundleMap?: BundleMap 17 | ) { 18 | xOffset = -xOffset; 19 | backboneOffset = -backboneOffset; 20 | const start = () => ({ x: 0, y: 0, opacity: 0 }); 21 | 22 | const enter = (data: any) => { 23 | const validity = true; 24 | 25 | const x = getX(stratifiedMap[data].width, xOffset, backboneOffset); 26 | 27 | // let backboneBundleNodes = findBackboneBundleNodes(stratifiedMap, bundleMap) 28 | 29 | let highestDepth = stratifiedMap[data].depth; 30 | let lowestDepth = stratifiedMap[data].depth; 31 | 32 | if (bundleMap && expandedClusterList.includes(data)) { 33 | for (let i = 0; i < bundleMap[data].bunchedNodes.length; i++) { 34 | // if(stratifiedMap[bundleMap[data].bunchedNodes[i]].width != 0) 35 | // { 36 | // validity = false; 37 | // } 38 | if ( 39 | stratifiedMap[bundleMap[data].bunchedNodes[i]] && 40 | stratifiedMap[bundleMap[data].bunchedNodes[i]].depth < highestDepth 41 | ) { 42 | highestDepth = stratifiedMap[bundleMap[data].bunchedNodes[i]].depth; 43 | } 44 | 45 | if ( 46 | stratifiedMap[bundleMap[data].bunchedNodes[i]] && 47 | stratifiedMap[bundleMap[data].bunchedNodes[i]].depth > lowestDepth 48 | ) { 49 | lowestDepth = stratifiedMap[bundleMap[data].bunchedNodes[i]].depth; 50 | } 51 | } 52 | } 53 | 54 | let y = yOffset * highestDepth; 55 | 56 | if (annotationOpen !== -1 && highestDepth > annotationOpen) { 57 | y += annotationHeight; 58 | } 59 | 60 | let height = 0; 61 | 62 | // eslint-disable-next-line no-restricted-syntax 63 | for (const j in bundleMap![data].bunchedNodes) { 64 | if (stratifiedMap[bundleMap![data].bunchedNodes[j]]) { 65 | height++; 66 | } 67 | } 68 | 69 | height *= clusterOffset; 70 | 71 | if (!expandedClusterList.includes(data)) { 72 | height = 10; 73 | } 74 | 75 | if ( 76 | annotationOpen !== -1 && 77 | annotationOpen >= highestDepth && 78 | annotationOpen <= lowestDepth 79 | ) { 80 | height += annotationHeight; 81 | } 82 | 83 | return { 84 | x: [x], 85 | y: [y], 86 | opacity: [!expandedClusterList.includes(data) ? 0 : 1], 87 | timing: { duration }, 88 | validity, 89 | height, 90 | }; 91 | }; 92 | 93 | const update = (data: any) => { 94 | const validity = true; 95 | 96 | const x = getX(stratifiedMap[data].width, xOffset, backboneOffset); 97 | 98 | // let backboneBundleNodes = findBackboneBundleNodes(stratifiedMap, bundleMap) 99 | 100 | let highestDepth = stratifiedMap[data].depth; 101 | let lowestDepth = stratifiedMap[data].depth; 102 | 103 | if (bundleMap && expandedClusterList.includes(data)) { 104 | for (let i = 0; i < bundleMap[data].bunchedNodes.length; i++) { 105 | // if(stratifiedMap[bundleMap[data].bunchedNodes[i]].width != 0) 106 | // { 107 | // validity = false; 108 | // } 109 | if ( 110 | stratifiedMap[bundleMap[data].bunchedNodes[i]] && 111 | stratifiedMap[bundleMap[data].bunchedNodes[i]].depth < highestDepth 112 | ) { 113 | highestDepth = stratifiedMap[bundleMap[data].bunchedNodes[i]].depth; 114 | } 115 | 116 | if ( 117 | stratifiedMap[bundleMap[data].bunchedNodes[i]] && 118 | stratifiedMap[bundleMap[data].bunchedNodes[i]].depth > lowestDepth 119 | ) { 120 | lowestDepth = stratifiedMap[bundleMap[data].bunchedNodes[i]].depth; 121 | } 122 | } 123 | } 124 | 125 | let y = yOffset * highestDepth; 126 | 127 | if (annotationOpen !== -1 && highestDepth > annotationOpen) { 128 | y += annotationHeight; 129 | } 130 | 131 | let height = 0; 132 | // eslint-disable-next-line no-restricted-syntax 133 | for (const j in bundleMap![data].bunchedNodes) { 134 | if (stratifiedMap[bundleMap![data].bunchedNodes[j]]) { 135 | height++; 136 | } 137 | } 138 | 139 | height *= clusterOffset; 140 | 141 | if (!expandedClusterList.includes(data)) { 142 | height = 10; 143 | } 144 | 145 | if ( 146 | annotationOpen !== -1 && 147 | annotationOpen >= highestDepth && 148 | annotationOpen <= lowestDepth 149 | ) { 150 | height += annotationHeight; 151 | } 152 | 153 | return { 154 | x: [x], 155 | y: [y], 156 | opacity: [!expandedClusterList.includes(data) ? 0 : 1], 157 | timing: { duration }, 158 | validity, 159 | height: [height], 160 | }; 161 | }; 162 | return { 163 | enter, 164 | leave: start, 165 | update, 166 | start, 167 | }; 168 | } 169 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/components/Link.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | type LinkProps = {} & React.SVGProps; 4 | 5 | const Link: FC = (props: LinkProps) => ; 6 | 7 | export default Link; 8 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/components/LinkTransitions.ts: -------------------------------------------------------------------------------- 1 | export default function linkTransitions( 2 | xOffset: number, 3 | yOffset: number, 4 | clusterOffset: number, 5 | backboneOffset: number, 6 | duration = 500, 7 | annotationOpen: number, 8 | annotationHeight: number 9 | ) { 10 | xOffset = -xOffset; 11 | backboneOffset = -backboneOffset; 12 | const start = () => ({ 13 | x1: 0, 14 | x2: 0, 15 | y1: 0, 16 | y2: 0, 17 | opacity: 0, 18 | }); 19 | 20 | const enter = (data: any) => { 21 | let clusteredNodesInFront = 0; 22 | 23 | // eslint-disable-next-line max-len 24 | clusteredNodesInFront = 25 | clusteredNodesInFront === 0 26 | ? clusteredNodesInFront 27 | : clusteredNodesInFront - 1; 28 | 29 | const { source, target } = data; 30 | const x1 = getX(source.width, xOffset, backboneOffset); 31 | const x2 = getX(target.width, xOffset, backboneOffset); 32 | 33 | let y1 = 34 | yOffset * source.depth - 35 | (yOffset - clusterOffset) * clusteredNodesInFront; 36 | let y2 = 37 | yOffset * target.depth - 38 | (yOffset - clusterOffset) * clusteredNodesInFront; 39 | 40 | if ( 41 | annotationOpen !== -1 && 42 | source.depth > annotationOpen && 43 | source.width === 0 44 | ) { 45 | y1 += annotationHeight; 46 | } 47 | 48 | if ( 49 | annotationOpen !== -1 && 50 | target.depth > annotationOpen && 51 | target.width === 0 52 | ) { 53 | y2 += annotationHeight; 54 | } 55 | 56 | return { 57 | x1, 58 | x2, 59 | y1, 60 | y2, 61 | opacity: 1, 62 | timing: { duration }, 63 | }; 64 | }; 65 | 66 | const update = (data: any) => { 67 | let clusteredNodesInFront = 0; 68 | 69 | // eslint-disable-next-line max-len 70 | clusteredNodesInFront = 71 | clusteredNodesInFront === 0 72 | ? clusteredNodesInFront 73 | : clusteredNodesInFront - 1; 74 | 75 | const { source, target } = data; 76 | const x1 = getX(source.width, xOffset, backboneOffset); 77 | const x2 = getX(target.width, xOffset, backboneOffset); 78 | 79 | let y1 = 80 | yOffset * source.depth - 81 | (yOffset - clusterOffset) * clusteredNodesInFront; 82 | let y2 = 83 | yOffset * target.depth - 84 | (yOffset - clusterOffset) * clusteredNodesInFront; 85 | 86 | if ( 87 | annotationOpen !== -1 && 88 | source.depth > annotationOpen && 89 | source.width === 0 90 | ) { 91 | y1 += annotationHeight; 92 | } 93 | 94 | if ( 95 | annotationOpen !== -1 && 96 | target.depth > annotationOpen && 97 | target.width === 0 98 | ) { 99 | y2 += annotationHeight; 100 | } 101 | 102 | return { 103 | x1: [x1], 104 | y1: [y1], 105 | x2: [x2], 106 | y2: [y2], 107 | opacity: 1, 108 | timing: { duration }, 109 | }; 110 | }; 111 | 112 | return { 113 | enter, 114 | leave: start, 115 | update, 116 | start, 117 | }; 118 | } 119 | 120 | export function getX(width: number, xOffset: number, backboneOffset: number) { 121 | return width > 1 122 | ? (xOffset + backboneOffset) * width - backboneOffset 123 | : (xOffset + backboneOffset) * width; 124 | } 125 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/components/NodeTransitions.ts: -------------------------------------------------------------------------------- 1 | import { getX } from './LinkTransitions'; 2 | 3 | export default function nodeTransitions( 4 | xOffset: number, 5 | yOffset: number, 6 | clusterOffset: number, 7 | backboneOffset: number, 8 | duration = 500, 9 | annotationOpen: number, 10 | annotationHeight: number 11 | ) { 12 | xOffset = -xOffset; 13 | backboneOffset = -backboneOffset; 14 | const start = (data: any) => { 15 | let clusteredNodesInFront = 0; 16 | 17 | const x = getX(data.width, xOffset, backboneOffset); 18 | // eslint-disable-next-line max-len 19 | clusteredNodesInFront = 20 | clusteredNodesInFront === 0 21 | ? clusteredNodesInFront 22 | : clusteredNodesInFront - 1; 23 | 24 | let y = 25 | yOffset * data.depth - (yOffset - clusterOffset) * clusteredNodesInFront; 26 | 27 | if ( 28 | annotationOpen !== -1 && 29 | data.depth > annotationOpen && 30 | data.width === 0 31 | ) { 32 | y += annotationHeight; 33 | } 34 | 35 | return { x, y: y - yOffset, opacity: 0 }; 36 | }; 37 | 38 | const enter = (data: any) => { 39 | let clusteredNodesInFront = 0; 40 | 41 | const x = getX(data.width, xOffset, backboneOffset); 42 | 43 | // eslint-disable-next-line max-len 44 | clusteredNodesInFront = 45 | clusteredNodesInFront === 0 46 | ? clusteredNodesInFront 47 | : clusteredNodesInFront - 1; 48 | 49 | let y = 50 | yOffset * data.depth - (yOffset - clusterOffset) * clusteredNodesInFront; 51 | 52 | if ( 53 | annotationOpen !== -1 && 54 | data.depth > annotationOpen && 55 | data.width === 0 56 | ) { 57 | y += annotationHeight; 58 | } 59 | 60 | return { 61 | x: [x], 62 | y: [y], 63 | opactiy: 1, 64 | timing: { duration }, 65 | }; 66 | }; 67 | 68 | const update = (data: any) => { 69 | let clusteredNodesInFront = 0; 70 | 71 | const x = getX(data.width, xOffset, backboneOffset); 72 | 73 | // eslint-disable-next-line max-len 74 | clusteredNodesInFront = 75 | clusteredNodesInFront === 0 76 | ? clusteredNodesInFront 77 | : clusteredNodesInFront - 1; 78 | 79 | let y = 80 | yOffset * data.depth - (yOffset - clusterOffset) * clusteredNodesInFront; 81 | 82 | if ( 83 | annotationOpen !== -1 && 84 | data.depth > annotationOpen && 85 | data.width === 0 86 | ) { 87 | y += annotationHeight; 88 | } 89 | 90 | return { 91 | x: [x], 92 | y: [y], 93 | opactiy: 1, 94 | timing: { duration }, 95 | }; 96 | }; 97 | 98 | return { 99 | enter, 100 | leave: start, 101 | update, 102 | start, 103 | }; 104 | } 105 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/components/ProvVisCreator.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | import { NodeID, Provenance, ProvenanceGraph } from '@visdesignlab/trrack'; 3 | import { configure } from 'mobx'; 4 | import React from 'react'; 5 | import ReactDOM from 'react-dom'; 6 | import { BundleMap } from '../Utils/BundleMap'; 7 | import { EventConfig } from '../Utils/EventConfig'; 8 | import ProvVis from './ProvVis'; 9 | import UndoRedoButton from './UndoRedoButton'; 10 | 11 | export interface ProvVisConfig { 12 | eventConfig: EventConfig; 13 | editAnnotations: boolean; 14 | bundleMap: BundleMap; 15 | iconOnly: boolean; 16 | iconSize: number; 17 | height: number; 18 | width: number; 19 | sideOffset: number; 20 | backboneGutter: number; 21 | gutter: number; 22 | verticalSpace: number; 23 | regularCircleRadius: number; 24 | backboneCircleRadius: number; 25 | regularCircleStroke: number; 26 | backboneCircleStroke: number; 27 | topOffset: number; 28 | textSize: number; 29 | linkWidth: number; 30 | duration: number; 31 | } 32 | configure({ isolateGlobalState: true }); 33 | export function ProvVisCreator( 34 | node: Element, 35 | prov: Provenance, 36 | callback?: (id: NodeID) => void, 37 | buttons = true, 38 | ephemeralUndo = false, 39 | fauxRoot: NodeID = prov.graph.root, 40 | config: Partial = {} 41 | ) { 42 | prov.addGlobalObserver(() => { 43 | ReactDOM.render( 44 | , 54 | node 55 | ); 56 | }); 57 | 58 | ReactDOM.render( 59 | , 69 | node 70 | ); 71 | } 72 | 73 | export function UndoRedoButtonCreator( 74 | node: Element, 75 | graph: ProvenanceGraph, 76 | undoCallback: () => void, 77 | redoCallback: () => void 78 | ) { 79 | ReactDOM.render( 80 | , 85 | node 86 | ); 87 | } 88 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/components/Styles.ts: -------------------------------------------------------------------------------- 1 | import { style } from 'typestyle'; 2 | 3 | // eslint-disable-next-line import/prefer-default-export 4 | export const treeColor = (current?: boolean) => 5 | style({ 6 | fill: current ? 'rgb(33, 133, 208)' : 'white', 7 | stroke: 'rgb(33, 133, 208)', 8 | }); 9 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/components/UndoRedoButton.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import { ProvenanceGraph } from '@visdesignlab/trrack'; 3 | import React from 'react'; 4 | import { Button } from 'semantic-ui-react'; 5 | import { style } from 'typestyle'; 6 | 7 | export interface UndoRedoConfig { 8 | undoCallback: () => void; 9 | redoCallback: () => void; 10 | graph?: ProvenanceGraph; 11 | } 12 | 13 | function UndoRedoButton({ 14 | graph, 15 | undoCallback, 16 | redoCallback, 17 | }: UndoRedoConfig) { 18 | if (graph === undefined) { 19 | return null; 20 | } 21 | 22 | const isAtRoot = graph.root === graph.current; 23 | const isAtLatest = graph.nodes[graph.current].children.length === 0; 24 | 25 | const margin = { 26 | marginRight: '3px', 27 | } as React.CSSProperties; 28 | 29 | return ( 30 |
31 | 40 | 41 | 50 |
51 | ); 52 | } 53 | 54 | const undoButtonStyle = style({ 55 | marginTop: '2px', 56 | borderRadius: '2px', 57 | display: 'inline-block', 58 | cursor: 'pointer', 59 | fontFamily: 'Lato,Helvetica Neue,Arial,Helvetica,sans-serif', 60 | fontSize: '14px', 61 | marginRight: '1px', 62 | $nest: { 63 | '&:hover': { 64 | backgroundColor: '#6c7c7c', 65 | }, 66 | '&:active': { 67 | backgroundColor: '#6c7c7c', 68 | }, 69 | }, 70 | }); 71 | 72 | const redoButtonStyle = style({ 73 | marginTop: '2px', 74 | borderRadius: '2px', 75 | display: 'inline-block', 76 | cursor: 'pointer', 77 | fontFamily: 'Lato,Helvetica Neue,Arial,Helvetica,sans-serif', 78 | fontSize: '14px', 79 | 80 | $nest: { 81 | '&:hover': { 82 | backgroundColor: '#6c7c7c', 83 | }, 84 | 85 | '&:active': { 86 | backgroundColor: '#6c7c7c', 87 | }, 88 | }, 89 | }); 90 | 91 | export default UndoRedoButton; 92 | -------------------------------------------------------------------------------- /packages/trrack-vis/src/index.tsx: -------------------------------------------------------------------------------- 1 | import ProvVis from './components/ProvVis'; 2 | import { 3 | ProvVisCreator, 4 | UndoRedoButtonCreator, 5 | } from './components/ProvVisCreator'; 6 | import { Config, EventConfig } from './Utils/EventConfig'; 7 | 8 | export { ProvVis, EventConfig, Config, ProvVisCreator, UndoRedoButtonCreator }; 9 | -------------------------------------------------------------------------------- /packages/trrack-vis/stories/Nodes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import translate from '../src/Utils/translate'; 3 | 4 | interface AddTaskGlyphProps { 5 | size?: number; 6 | fill?: string; 7 | scale?: number; 8 | } 9 | 10 | export function AddTaskGlyph({ size = 15, fill = '#ccc' }: AddTaskGlyphProps) { 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | } 22 | 23 | interface ChangeTaskGlyphProps { 24 | size?: number; 25 | fill?: string; 26 | } 27 | 28 | export function ChangeTaskGlyph({ 29 | size = 15, 30 | fill = '#ccc', 31 | }: ChangeTaskGlyphProps) { 32 | return ( 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /packages/trrack-vis/stories/NonReactTrrack.tsx: -------------------------------------------------------------------------------- 1 | import 'semantic-ui-css/semantic.min.css'; 2 | 3 | import { Meta } from '@storybook/react'; 4 | import { 5 | createAction, initProvenance, NodeID, ProvenanceGraph, StateNode, 6 | } from '@visdesignlab/trrack'; 7 | import { observable } from 'mobx'; 8 | import { inject, observer, Provider } from 'mobx-react'; 9 | import React, { FC } from 'react'; 10 | import { Button } from 'semantic-ui-react'; 11 | 12 | import { ProvVis } from '../src'; 13 | import { BundleMap } from '../src/Utils/BundleMap'; 14 | 15 | interface Task { 16 | key: number; 17 | desc: string; 18 | } 19 | 20 | interface DemoState { 21 | tasks: Task[]; 22 | } 23 | 24 | interface DemoAnnotation { 25 | note: string; 26 | } 27 | 28 | const defaultState: DemoState = { 29 | tasks: [], 30 | }; 31 | 32 | type Events = 'Add Task' | 'Change Task'; 33 | 34 | const prov = initProvenance(defaultState); 35 | 36 | prov.done(); 37 | 38 | let taskNo: number = 1; 39 | 40 | const addTask = (desc: string = 'Random Task') => { 41 | const action = createAction((state) => { 42 | state.tasks.push({ key: taskNo, desc }); 43 | }) 44 | .setLabel(`Adding task #: ${taskNo}`) 45 | .setEventType('Add Task'); 46 | 47 | prov.apply(action); 48 | 49 | taskNo += 1; 50 | }; 51 | 52 | function updateTask(taskId: number, desc: string = 'Changed String ') { 53 | const action = createAction((state) => { 54 | const idx = state.tasks.findIndex((d) => d.key === taskId); 55 | if (idx !== -1) { 56 | state.tasks[idx].desc = desc; 57 | } 58 | }) 59 | .setLabel(`Changing task #: ${taskId}`) 60 | .setEventType('Change Task'); 61 | 62 | prov.apply(action); 63 | } 64 | 65 | const undo = () => prov.goBackOneStep(); 66 | const redo = () => prov.goForwardOneStep(); 67 | 68 | const goToNode = (nodeId: NodeID) => { 69 | prov.goToNode(nodeId); 70 | }; 71 | 72 | /// ////////////////////////////////////////////////////////////// 73 | 74 | interface Props { 75 | store?: any; 76 | } 77 | 78 | const BaseComponent: FC = ({ store }: Props) => { 79 | const { 80 | graph, isRoot, isLatest, tasks, 81 | } = store!; 82 | const { root, nodes, current } = graph; 83 | 84 | return ( 85 | <> 86 |
93 | 111 |
112 |
119 | 120 | 123 | 124 | 127 | 128 | 135 | 136 |
137 |
144 |
145 | {tasks.map((d: any) => ( 146 |
147 | {d.key} --- {d.desc} 148 |
149 | ))} 150 |
151 |
152 | 153 | ); 154 | }; 155 | 156 | const BaseComponentWithStore = inject('store')(observer(BaseComponent)); 157 | 158 | // eslint-disable-next-line no-underscore-dangle 159 | const _DemoComponent: FC = () => ( 160 | 161 | 162 | 163 | ); 164 | 165 | export default _DemoComponent; 166 | 167 | const setupInital = () => { 168 | addTask(); 169 | addTask(); 170 | addTask(); 171 | undo(); 172 | undo(); 173 | addTask(); 174 | addTask(); 175 | undo(); 176 | undo(); 177 | addTask(); 178 | addTask(); 179 | addTask(); 180 | addTask(); 181 | addTask(); 182 | addTask(); 183 | undo(); 184 | undo(); 185 | undo(); 186 | undo(); 187 | undo(); 188 | addTask(); 189 | addTask(); 190 | addTask(); 191 | addTask(); 192 | addTask(); 193 | addTask(); 194 | undo(); 195 | undo(); 196 | undo(); 197 | updateTask(1); 198 | updateTask(12); 199 | updateTask(14); 200 | }; 201 | 202 | setupInital(); 203 | -------------------------------------------------------------------------------- /packages/trrack-vis/stories/Some.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/react'; 2 | import React, { FC } from 'react'; 3 | import _DemoComponent from './Trrack'; 4 | 5 | const meta: Meta = { 6 | title: 'Trrack-Vis', 7 | }; 8 | 9 | export default meta; 10 | 11 | export const DemoComponent: FC = () => <_DemoComponent />; 12 | -------------------------------------------------------------------------------- /packages/trrack-vis/test/blah.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { Default as Thing } from '../stories/Thing.stories'; 4 | 5 | describe('Thing', () => { 6 | it('renders without crashing', () => { 7 | const div = document.createElement('div'); 8 | ReactDOM.render(, div); 9 | ReactDOM.unmountComponentAtNode(div); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/trrack-vis/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs 3 | "include": ["src", "types"], 4 | "compilerOptions": { 5 | "module": "esnext", 6 | "lib": ["dom", "esnext"], 7 | "importHelpers": true, 8 | // output .d.ts declaration files for consumers 9 | "declaration": true, 10 | // output .js.map sourcemap files for consumers 11 | "sourceMap": true, 12 | // match output dir to input dir. e.g. dist/index instead of dist/src/index 13 | "rootDir": "./src", 14 | // stricter type-checking for stronger correctness. Recommended by TS 15 | "strict": false, 16 | // linter checks for common issues 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": true, 19 | // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative 20 | // "noUnusedLocals": true, 21 | // "noUnusedParameters": true, 22 | // use Node's module resolution algorithm, instead of the legacy TS one 23 | "moduleResolution": "node", 24 | // transpile JSX to React.createElement 25 | "jsx": "react", 26 | // interop between ESM and CJS modules. Recommended by TS 27 | "esModuleInterop": true, 28 | // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS 29 | "skipLibCheck": true, 30 | // error out if import and file system have a casing mismatch. Recommended by TS 31 | "forceConsistentCasingInFileNames": true, 32 | // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` 33 | "noEmit": true, 34 | "downlevelIteration": true 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/trrack-vis/tsdx.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // This function will run for each entry/format/env combination 3 | rollup(config, options) { 4 | if (config.output.format === 'umd') { 5 | config.output.globals['@visdesignlab/trrack'] = 'trrack'; 6 | config.output.globals.d3 = 'd3'; 7 | config.output.globals['react-move'] = 'ReactMove'; 8 | config.output.globals['semantic-ui-react'] = 'semanticUIReact'; 9 | } 10 | return config; // always return a config. 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /packages/trrack-vis/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode":"file", 3 | "out": "docs" 4 | } 5 | -------------------------------------------------------------------------------- /trrack.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": {} 8 | } 9 | -------------------------------------------------------------------------------- /trrack_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/visdesignlab/trrack/d5c35146667bfc40bd67293a643ef91dc09c2262/trrack_architecture.png -------------------------------------------------------------------------------- /trrack_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/visdesignlab/trrack/d5c35146667bfc40bd67293a643ef91dc09c2262/trrack_overview.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "lib": ["dom", "esnext"], 6 | "importHelpers": true, 7 | "declaration": true, 8 | "sourceMap": true, 9 | "strict": true, 10 | "importsNotUsedAsValues": "remove", 11 | "experimentalDecorators": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "strictBindCallApply": true, 14 | "stripInternal": true, 15 | "resolveJsonModule": true, 16 | "noUnusedParameters": true, 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": true, 19 | "moduleResolution": "node", 20 | "jsx": "react", 21 | "esModuleInterop": true, 22 | "skipLibCheck": true 23 | } 24 | } 25 | --------------------------------------------------------------------------------