├── .dockerignore ├── .github └── workflows │ ├── audit.yml │ ├── ci.yml │ └── storybook.yml ├── .gitignore ├── ARCHITECTURE.md ├── CHANGELOG.md ├── DEVELOP.md ├── Dockerfile ├── LICENSE ├── README.md ├── dc ├── docker-compose.yml ├── lerna.json ├── package-lock.json ├── package.json ├── projects ├── client-api-react │ ├── .babelrc │ ├── .eslintrc │ ├── .storybook │ │ ├── main.js │ │ ├── manager-head.html │ │ ├── manager.js │ │ ├── preview.js │ │ └── theme.js │ ├── DEVELOP.md │ ├── README.md │ ├── assets │ │ ├── index.css │ │ └── index.less │ ├── examples │ │ ├── default.html │ │ ├── default.js │ │ ├── dynamic.html │ │ └── dynamic.js │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── rollup.config.js │ └── src │ │ ├── bg.js │ │ ├── bindings.js │ │ ├── index.js │ │ ├── stories │ │ ├── Graphistry.stories.jsx │ │ ├── GraphistryJS.stories.jsx │ │ ├── GraphistryUpload.stories.jsx │ │ ├── Introduction.mdx │ │ ├── Introduction.stories.js │ │ └── assets │ │ │ ├── banner_transparent_colored.png │ │ │ ├── code-brackets.svg │ │ │ ├── colors.svg │ │ │ ├── comments.svg │ │ │ ├── direction.svg │ │ │ ├── favicon.ico │ │ │ ├── flow.svg │ │ │ ├── plugin.svg │ │ │ ├── repo.svg │ │ │ └── stackalt.svg │ │ └── usePrevious.js ├── client-api │ ├── .eslintrc │ ├── README.md │ ├── examples │ │ ├── default.html │ │ ├── default.js │ │ ├── example.html │ │ ├── toggles-deprecated.html │ │ └── toggles.html │ ├── index.js │ ├── jsdoc-conf.json │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── index.js │ │ └── rxjs.js ├── cra-template │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── rollup.config.js │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ └── setupTests.js ├── cra-test-18 │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── reportWebVitals.js │ │ └── setupTests.js ├── cra-test │ ├── .gitignore │ ├── DEVELOP.md │ ├── README.md │ ├── craco.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── SidebarSelection.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── reportWebVitals.js │ │ └── setupTests.js ├── js-upload-api │ ├── .eslintignore │ ├── .eslintrc.cjs │ ├── README.json │ ├── package-lock.json │ ├── package.json │ ├── package.template.json │ ├── src │ │ ├── AbstractClient.ts │ │ ├── Client.ts │ │ ├── ClientPKey.ts │ │ ├── Dataset.ts │ │ ├── File.ts │ │ ├── Privacy.ts │ │ ├── index.ts │ │ └── types.ts │ ├── tsconfig.cjs.json │ └── tsconfig.json ├── node-api-test-cjs │ ├── DEVELOP.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ └── index.js ├── node-api-test │ ├── DEVELOP.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ └── index.js └── node-api │ ├── .eslintignore │ ├── .eslintrc.cjs │ ├── .vscode │ └── launch.json │ ├── DEVELOP.md │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── package.template.json │ ├── src │ ├── index.ts │ └── version.ts │ ├── tsconfig.cjs.json │ └── tsconfig.json └── tools ├── extract-dist.sh ├── jsdocs.sh ├── node-tsdocs.sh └── storybook.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | .github 2 | .git 3 | .gitignore 4 | .bak 5 | .pyc 6 | node_modules/ 7 | Dockerfile 8 | docker-compose.yml 9 | 10 | dist/ 11 | tools/ 12 | docs/ 13 | 14 | **/node_modules 15 | 16 | docs-build/ 17 | .github/ 18 | projects/client-api/dist 19 | projects/client-api/docs 20 | projects/client-api/jsdocs/ 21 | projects/client-api/www/ 22 | projects/client-api-react/www/ 23 | projects/client-api-react/dist 24 | projects/js-upload-api/dist 25 | projects/js-upload-api/docs 26 | projects/node-api/dist 27 | projects/node-api/docs 28 | DEVELOP.md 29 | README.md 30 | -------------------------------------------------------------------------------- /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | name: Audit 2 | on: 3 | 4 | push: 5 | paths: [ 6 | "projects/client-api/package.json", 7 | "projects/client-api/package-lock.json", 8 | "projects/client-api-react/package.json", 9 | "projects/client-api-react/package-lock.json", 10 | "projects/node-api/package.json", 11 | "projects/node-api/package-lock.json" 12 | ] # Trigger the action only when files change in the folders defined here 13 | tags: 14 | 15 | #Test main bidaily @ 1a 16 | schedule: 17 | - cron: '0 1 1-31/2 * *' 18 | 19 | workflow_dispatch: 20 | 21 | jobs: 22 | audit: 23 | runs-on: ubuntu-latest 24 | strategy: 25 | max-parallel: 2 26 | matrix: 27 | project: [js-upload-api, client-api, client-api-react, node-api] 28 | steps: 29 | - name: Checkout 🛎️ 30 | uses: actions/checkout@v2.3.1 31 | with: 32 | persist-credentials: false 33 | 34 | - name: audit project 35 | working-directory: "projects/${{ matrix.project }}" 36 | run: npm audit --production 37 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | #See: 2 | # - https://dev.to/xaviercanchal/automatic-versioning-in-a-lerna-monorepo-using-github-actions-4hij 3 | # - https://github.com/graphistry/pygraphistry/blob/master/.github/workflows/ci.yml 4 | 5 | name: CI 6 | 7 | on: 8 | #Regular dev 9 | push: 10 | paths: [ 11 | "projects/js-upload-api/**", 12 | "projects/client-api/.eslintrc", 13 | "projects/client-api/index.js", 14 | "projects/client-api/package.json", 15 | "projects/client-api/package-lock.json", 16 | "projects/client-api/src/**", 17 | "projects/client-api-react/assets/**", 18 | "projects/client-api-react/src/**", 19 | "projects/client-api-react/.babelrc", 20 | "projects/client-api-react/.eslintrc", 21 | "projects/client-api-react/index.js", 22 | "projects/client-api-react/package.json", 23 | "projects/client-api-react/package-lock.json", 24 | "projects/client-api-react/rollup.config.js", 25 | "lerna.json", 26 | "package.json", 27 | "package-lock.json", 28 | "docker-compose.yml", 29 | "Dockerfile" 30 | ] 31 | pull_request: 32 | types: [opened, synchronize] # Workflow triggering events 33 | 34 | #Enable UI-driven branch testing 35 | workflow_dispatch: 36 | 37 | #Test main bidaily @ 1a 38 | schedule: 39 | - cron: '0 1 1-31/2 * *' 40 | 41 | 42 | jobs: 43 | ci: 44 | runs-on: ubuntu-latest 45 | env: 46 | COMPOSE_DOCKER_CLI_BUILD: 1 47 | DOCKER_BUILDKIT: 1 48 | 49 | steps: 50 | - name: "Checkout" 51 | uses: actions/checkout@v2 52 | with: 53 | fetch-depth: 0 54 | 55 | - name: "Build and lint" 56 | run: | 57 | docker-compose build 58 | 59 | - name: "Lint" 60 | run: | 61 | docker-compose run --rm --entrypoint=/usr/local/bin/node graphistry-js ./node_modules/lerna/dist/cli.js run lint 62 | 63 | #- name: Storybook 🔧 64 | # run: ./tools/storybook.sh 65 | 66 | #- name: JSDocs 🔧 67 | # run: ./tools/jsdocs.sh 68 | 69 | - name: "Ensure binaries" 70 | run: | 71 | ./tools/extract-dist.sh 72 | -------------------------------------------------------------------------------- /.github/workflows/storybook.yml: -------------------------------------------------------------------------------- 1 | name: Storybook 2 | on: 3 | push: 4 | paths: [ 5 | ".github/workflows/storybook.yml", 6 | "projects/js-upload-api/**", 7 | "projects/client-api/**", 8 | "projects/client-api-react/.storybook/**", 9 | "projects/client-api-react/assets/**", 10 | "projects/client-api-react/examples/**", 11 | "projects/client-api-react/src/stories/**", 12 | "projects/client-api-react/package.json", 13 | "projects/client-api-react/package-lock.json", 14 | "projects/client-api-react/eslintrc", 15 | "projects/node-api/src/**", 16 | "projects/node-api/package.json", 17 | "projects/node-api/README.md" 18 | ] # Trigger the action only when files change in the folders defined here 19 | tags: 20 | workflow_dispatch: 21 | 22 | jobs: 23 | test-build-and-deploy: 24 | runs-on: ubuntu-latest 25 | env: 26 | COMPOSE_DOCKER_CLI_BUILD: 1 27 | DOCKER_BUILDKIT: 1 28 | steps: 29 | - name: Checkout 🛎️ 30 | uses: actions/checkout@v3 31 | with: 32 | persist-credentials: false 33 | - name: Build 34 | run: | 35 | docker compose build 36 | 37 | - name: Storybook 🔧 38 | run: ./tools/storybook.sh 39 | 40 | - name: JSDocs 🔧 41 | run: ./tools/jsdocs.sh 42 | 43 | - name: Node TSDocs 🔧 44 | run: ./tools/node-tsdocs.sh 45 | 46 | build-and-deploy: 47 | if: github.ref == 'refs/heads/master' 48 | needs: [ test-build-and-deploy ] 49 | runs-on: ubuntu-latest 50 | env: 51 | COMPOSE_DOCKER_CLI_BUILD: 1 52 | DOCKER_BUILDKIT: 1 53 | steps: 54 | - name: Checkout 🛎️ 55 | uses: actions/checkout@v3 56 | with: 57 | persist-credentials: false 58 | - name: Build 59 | run: | 60 | docker compose build 61 | 62 | - name: Storybook 🔧 63 | run: ./tools/storybook.sh 64 | 65 | - name: JSDocs 🔧 66 | run: ./tools/jsdocs.sh 67 | 68 | - name: Node TSDocs 🔧 69 | run: ./tools/node-tsdocs.sh 70 | 71 | - name: Examples 🔧 72 | run: cp -r projects/client-api/examples docs-build/examples 73 | 74 | - name: Deploy 🚀 75 | uses: JamesIves/github-pages-deploy-action@4.1.5 76 | with: 77 | branch: docs 78 | folder: docs-build 79 | target-folder: docs # The folder that we serve our files from 80 | #token is implicit 81 | #clean is implicit -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Logs 4 | logs 5 | *.log 6 | yarn.lock 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (http://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # Typescript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | # rc-tools folders 64 | dist 65 | docs 66 | assets/**/*.css 67 | build 68 | lib 69 | es 70 | docs-build 71 | projects/client-api/jsdocs/ 72 | projects/client-api/www/ 73 | projects/client-api-react/www/ 74 | projects/node-api/dist/ 75 | projects/node-api/docs/ 76 | -------------------------------------------------------------------------------- /ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | # Graphistry JavaScript API Architecture 2 | 3 | ## Monorepo 4 | 5 | Lerna with eslint & esbuild drives two npm packages: 6 | 7 | * `graphistry-client`: rxjs-based vanilla reactive JS API with minimal dependencies 8 | * `graphistry-client-react`: React wrapper around `graphistry-client` 9 | 10 | ## Libraries 11 | 12 | We strive to keep the libraries compatible across RxJS and React versions: 13 | 14 | * `graphistry-client`: Bundles its own tree-shaken `rxjs` version, and it is unpinned so you can also reuse in your own packages. No `react` dependency. For convenience, we reexport a minimal useful set of `rxjs` methods. 15 | * `graphistry-client-react`: Depends on `graphistry-client`, including for any use of `rxjs`. Version of `react` is unpinned. 16 | 17 | ## Automated 18 | 19 | Builds are containerized, including for local dev, and automated via github actions 20 | 21 | ## JSDocs & Storybook 22 | 23 | Docs are automated via JSDocs (`graphistry-client`) and Storybook (`graphistry-client` + `graphistry-client-react`) 24 | 25 | They auto-publish to [graphistry.github.io/graphistry-js](https://graphistry.github.io/graphistry-js/) as branch [#docs](https://github.com/graphistry/graphistry-js/tree/docs) 26 | 27 | ## Bundled 28 | 29 | Both libraries bundle into multiple formats (esm, cjs, iife), optional minification, and optional externing (rxjs+react) as part of the npm publish 30 | 31 | Every Graphistry enterprise release hosts the coordinated JS library versions 32 | 33 | ## Hosting 34 | 35 | The docs run in multiple locations: 36 | 37 | * Standalone 38 | * [https://graphistry.github.io/graphistry-js/](github) 39 | * [https://hub.graphistry.com/docs](Graphistry Hub) -------------------------------------------------------------------------------- /DEVELOP.md: -------------------------------------------------------------------------------- 1 | # GraphistryJS Developer Docs 2 | 3 | ## TL;DR 4 | 5 | Docker compose (`docker compose` or `docker-compose`, with or without `sudo`): 6 | 7 | ```bash 8 | ./dc build 9 | ./dc up storybook # http://localhost:6006 10 | ``` 11 | 12 | and 13 | 14 | ```bash 15 | docker-compose run --rm --entrypoint=/usr/local/bin/node graphistry-js ./node_modules/lerna/dist/cli.js run lint 16 | ``` 17 | 18 | # Docs 19 | 20 | We use Storybook for React, plugged into github pages 21 | 22 | See [Deploy Storybook to GitHub Pages](https://dev.to/kouts/deploy-storybook-to-github-pages-3bij) for a great tutorial: 23 | 24 | * Storybook builds get checked into branch `docs` and hosted at [graphistry.github.io/graphistry-js](https://graphistry.github.io/graphistry-js) 25 | 26 | * For local development: 27 | 28 | ```bash 29 | docker compose build 30 | docker compose up storybook 31 | # http://localhost:6006 32 | # All demos work except uploads 33 | ``` 34 | 35 | * To mimic gha building: 36 | 37 | ```bash 38 | docker compose build 39 | ./tools/storybook.sh 40 | ./tools/jsdocs.sh 41 | ``` 42 | 43 | => 44 | 45 | ``` 46 | ./docs-build # storybook 47 | ./docs-build/jsdocs # jsdocs 48 | ``` 49 | 50 | # Example dev setup for `client-api` / `react-client-api` 51 | 52 | Terminal A 53 | - `cd projects/client-api` 54 | - `npm link @graphistry/client-api` 55 | - `npm run build:esm:min:full:watch` 56 | 57 | Terminal B 58 | - `cd projects/client-api-react` 59 | - `npm link @graphistry/client-api-react` 60 | - `npm link @graphistry/client-api` 61 | - `npm run build:rollup-watch` 62 | 63 | Terminal C 64 | - `cd projects/cra-test` 65 | - `npm link @graphistry/client-api-react` 66 | - `npm start` 67 | 68 | # Docker 69 | 70 | ## Environment 71 | 72 | Enable buildkit for Docker and docker compose plugin, such as in your `.profile`: 73 | 74 | ```bash 75 | export DOCKER_BUILDKIT=1 76 | export COMPOSE_DOCKER_CLI_BUILD=1 77 | ``` 78 | 79 | For convenience, we setup `./dc` to do `docker compose` commands this for you, such as `./dc build` 80 | 81 | ## Build 82 | 83 | ```bash 84 | docker compose build 85 | ``` 86 | 87 | ## Run 88 | 89 | ```bash 90 | docker compose run --rm graphistry-js 91 | ``` 92 | 93 | or 94 | 95 | ```bash 96 | docker run --rm -it graphistry/graphistry:latest 97 | ``` 98 | 99 | => 100 | 101 | ``` 102 | root@8f18f077e0b6:/opt/graphistry-js# 103 | ``` 104 | 105 | ## Extract binaries 106 | 107 | You can build natively to get per-project binaries (see below), or via docker: 108 | 109 | `./tools/extract-dist.sh` => `projects/client-api[-react]/dist` 110 | 111 | This copies from `graphistry/graphistry-js:latest` into host project folders 112 | 113 | You may want to run `docker rm graphistry-js-tmp` if a stale container 114 | 115 | ## Native 116 | 117 | ```bash 118 | apt get install jq 119 | npm install 120 | npm run bootstrap 121 | npm run build 122 | npm run lint 123 | ( cd projects/client-api-react && ./node_modules/.bin/start-storybook -p 6006 ) 124 | # => http://localhost:6006/ 125 | ``` 126 | 127 | ## Dependencies 128 | 129 | We use `package-lock.json` as part of aiding reproducibility 130 | 131 | Make sure you have a recent `node` and `npm` -- we have not containerized these steps yet 132 | 133 | To regenerate: 134 | - go to a project or the root level 135 | - run `npm run lock:fix` 136 | - check in the updated `package-lock.json` 137 | 138 | 139 | # Publish for public consumption (Maintainer only) 140 | 141 | 0. Login to npm: `npm login` 142 | 143 | 1. Build from a local checkout of branch with a corresponding PR: 144 | 145 | Ex: 146 | 147 | ```bash 148 | ./dc build 149 | ./tools/extract-dist.sh 150 | ``` 151 | 152 | 2. Ensure git clean (`git status`) + npm auth (`npm login`) 153 | 154 | 3. Publish: 155 | 156 | ```bash 157 | lerna version X.Y.Z-alpha.2 158 | lerna publish # if failed: lerna publish from-package 159 | ``` 160 | 161 | As an even bigger hammer, to update even unchanged dependencies, try `lerna publish X.Y.Z --force-publish` 162 | 163 | 4. Merge the PR in github 164 | 165 | This publishes ghpages 166 | 167 | 5. Most likely, you also want to update version dependencies in: 168 | 169 | * pivot-app 170 | * viz-app 171 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.13.0-slim as base 2 | WORKDIR /opt/graphistry-js 3 | 4 | RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \ 5 | --mount=target=/var/cache/apt,type=cache,sharing=locked \ 6 | rm -f /etc/apt/apt.conf.d/docker-clean \ 7 | && apt-get update \ 8 | && apt-get install -y jq \ 9 | && jq --version 10 | 11 | COPY lerna.json package.json package-lock.json ./ 12 | RUN --mount=type=cache,target=/usr/src/app/.npm \ 13 | npm set cache /usr/src/app/.npm \ 14 | && echo "=== Installing build tools ===" \ 15 | && npm install 16 | COPY \ 17 | projects/client-api/package.json \ 18 | projects/client-api/package-lock.json \ 19 | /opt/graphistry-js/projects/client-api/ 20 | COPY \ 21 | projects/client-api-react/package.json \ 22 | projects/client-api-react/package-lock.json \ 23 | /opt/graphistry-js/projects/client-api-react/ 24 | COPY \ 25 | projects/js-upload-api/package.json \ 26 | projects/js-upload-api/package-lock.json \ 27 | /opt/graphistry-js/projects/js-upload-api/ 28 | COPY \ 29 | projects/node-api/package.json \ 30 | projects/node-api/package-lock.json \ 31 | /opt/graphistry-js/projects/node-api/ 32 | 33 | # Rebuild esbuild due to exec format err: https://github.com/evanw/esbuild/issues/1223 34 | 35 | 36 | RUN --mount=type=cache,target=/usr/src/app/.npm\ 37 | echo "=== Installing and linking project dependencies ===" \ 38 | && npm run bootstrap \ 39 | && ( cd projects/client-api && npm rebuild esbuild ) \ 40 | && ( cd projects/client-api-react && npm rebuild esbuild ) \ 41 | && ( cd projects/js-upload-api && npm rebuild esbuild ) \ 42 | && ( cd projects/node-api && npm rebuild esbuild ) 43 | 44 | # Shared src 45 | COPY \ 46 | projects/js-upload-api/package.template.json \ 47 | projects/js-upload-api/tsconfig.json \ 48 | projects/js-upload-api/tsconfig.cjs.json \ 49 | /opt/graphistry-js/projects/js-upload-api/ 50 | COPY \ 51 | projects/js-upload-api/src \ 52 | /opt/graphistry-js/projects/js-upload-api/src 53 | RUN \ 54 | echo "=== Building shared dep @graphistry/js-upload-api ===" \ 55 | && cd /opt/graphistry-js/projects/js-upload-api \ 56 | && npm i --no-fund \ 57 | && npm run build 58 | 59 | # ############################################################################# 60 | 61 | FROM base as base_js 62 | WORKDIR /opt/graphistry-js 63 | COPY projects/client-api /opt/graphistry-js/projects/client-api 64 | RUN echo "=== Building client-api ===" \ 65 | && ./node_modules/lerna/dist/cli.js run build --scope="@graphistry/client-api" 66 | 67 | # ############################################################################# 68 | 69 | FROM base_js as base_react 70 | WORKDIR /opt/graphistry-js 71 | COPY projects/client-api-react /opt/graphistry-js/projects/client-api-react 72 | RUN echo "=== Building client-api-react ===" \ 73 | && ./node_modules/lerna/dist/cli.js run lint --scope="@graphistry/client-api-react" \ 74 | && ./node_modules/lerna/dist/cli.js run build --scope="@graphistry/client-api-react" 75 | 76 | # ############################################################################# 77 | 78 | FROM base as base_node 79 | WORKDIR /opt/graphistry-js 80 | COPY projects/node-api/src /opt/graphistry-js/projects/node-api/src 81 | COPY \ 82 | projects/node-api/.eslintignore \ 83 | projects/node-api/.eslintrc.cjs \ 84 | projects/node-api/package.template.json \ 85 | projects/node-api/tsconfig.json \ 86 | projects/node-api/tsconfig.cjs.json \ 87 | /opt/graphistry-js/projects/node-api/ 88 | RUN echo "=== Building node-api ===" \ 89 | && ( cd projects/node-api) \ 90 | && ( npm install) \ 91 | && ( cd projects/js-upload-api && npm link) \ 92 | && ( cd projects/node-api && npm link '@graphistry/js-upload-api') \ 93 | && ./node_modules/lerna/dist/cli.js run build --scope="@graphistry/node-api" \ 94 | && echo "--- Removing symbolic link before next docker layer ---" \ 95 | && ( cd projects/node-api && npm unlink '@graphistry/js-upload-api') 96 | 97 | # ############################################################################# 98 | 99 | FROM base 100 | WORKDIR /opt/graphistry-js 101 | 102 | COPY \ 103 | projects/js-upload-api/.eslintignore \ 104 | projects/js-upload-api/.eslintrc.cjs \ 105 | /opt/graphistry-js/projects/js-upload-api/ 106 | 107 | COPY --from=base_js \ 108 | /opt/graphistry-js/projects/client-api \ 109 | /opt/graphistry-js/projects/client-api 110 | RUN echo "== Final js client" \ 111 | && find /opt/graphistry-js/projects/client-api 112 | 113 | COPY --from=base_react \ 114 | /opt/graphistry-js/projects/client-api-react \ 115 | /opt/graphistry-js/projects/client-api-react 116 | RUN echo "== Final react client" \ 117 | && find /opt/graphistry-js/projects/client-api-react 118 | 119 | COPY --from=base_node \ 120 | /opt/graphistry-js/projects/node-api \ 121 | /opt/graphistry-js/projects/node-api 122 | RUN echo "== Final node client" \ 123 | && find /opt/graphistry-js/projects/node-api 124 | 125 | RUN (cd projects/js-upload-api && npm link) \ 126 | && (cd projects/node-api && npm link '@graphistry/js-upload-api') -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | --- 5 | 6 | [![Latest docs](https://img.shields.io/badge/docs-latest-brightgreen)](https://graphistry.github.io/graphistry-js/) 7 | [![npm](https://img.shields.io/npm/v/@graphistry/client-api?label=%40graphistry%2Fclient-api&logo=npm)](https://www.npmjs.com/package/ient-api) 8 | [![npm](https://img.shields.io/npm/v/@graphistry/client-api?label=%40graphistry%2Fclient-api-react&logo=npm)](https://www.npmjs.com/package/@graphistry/client-api-react) 9 | ![GitHub](https://img.shields.io/github/license/graphistry/graphistry-js) 10 | 11 | ![CI main](https://github.com/graphistry/graphistry-js/workflows/CI/badge.svg) ![Security audit](https://github.com/graphistry/graphistry-js/workflows/Audit/badge.svg) ![CI docs](https://github.com/graphistry/graphistry-js/workflows/Storybook/badge.svg) 12 | 13 | [](https://join.slack.com/t/graphistry-community/shared_invite/zt-53ik36w2-fpP0Ibjbk7IJuVFIRSnr6g) 14 | ![Twitter Follow](https://img.shields.io/twitter/follow/graphistry) 15 | 16 | --- 17 | 18 | # GraphistryJS 19 | ## Web libraries for uploading and embedding graph visualizations 20 | 21 | - [@graphistry/client-api](projects/client-api) - Pure JS API for manipulating Graphistry visualizations 22 | - [Storybook](https://graphistry.github.io/graphistry-js/?path=/story/graphistry-vanilla-js) 23 | - [@graphistry/client-api-react](projects/client-api-react) - Graphistry vizualization React component 24 | - [Storybook](https://graphistry.github.io/graphistry-js/?path=/story/graphistry-react-style--predefined-dataset) 25 | - [@graphistry/node-api](projects/node-api) - Node bindings to upload into Graphistry ecosystem 26 | - [@graphistry/js-upload-api](projects/js-upload-api) - Pure JS library for graph upload 27 | - [@graphistry/cra-test](projects/cra-test) - Example react app using these libraries 28 | 29 | Dev guide for contributors: [DEVELOP.md](./DEVELOP.md) 30 | 31 |

32 | 33 | # Graphistry - Visual Graph Intelligence 34 | 35 | GPU and AI acceleration for interactive visualization of large graphs. Used accross multiple industries for security, fraud, supply chain, social media analysis and more. Graphistry supports live explorations of large datasets by running server side GPUs to stream into a custom WebGL rendering engine. This enables graph metrics and dynamic layout of up to 8MM nodes and edges at a time, most older client GPUs smoothly support somewhere between 100K and 1MM elements. 36 | 37 | You can 38 | 39 | - [Start with a free Graphistry Hub account](https://www.graphistry.com/get-started) 40 | - [Create Python notebooks with PyGraphistry](https://github.com/graphistry/pygraphistry) 41 | - [Build StreamLit dashboards in Graph-App-Kit](https://github.com/graphistry/graph-app-kit) 42 | - [Directly interact with Graphistry REST APIs](https://hub.graphistry.com/docs/api/) 43 | - [Launch your own Graphistry server with just a few clicks](https://www.graphistry.com/get-started) 44 | 45 |

46 | 47 | # JavaScript clients: Vanilla JS, React, & Node 48 | 49 | ## @graphistry/client-api 50 | 51 | 52 | 53 | Pure JavaScript API for manipulating Graphistry visualizations in the browser with async-friendly APIs 54 | 55 | ```bash 56 | npm install '@graphistry/client-api' 57 | ``` 58 | 59 | ```javascript 60 | import { graphistryJS } from "@graphistry/client-api"; // + variants for different bundling formats 61 | const g = graphistryJS(elt); 62 | ``` 63 | 64 | See [@graphistry/client-api project](projects/client-api/README.md) and [interactive storybook docs](https://graphistry.github.io/graphistry-js/?path=/story/graphistry-vanilla-js) 65 | 66 |

67 | 68 | ## @graphistry/client-api-react 69 | 70 | 71 | 72 | React component for manipulating Graphistry visualizations in the browser 73 | 74 | ```bash 75 | npm install '@graphistry/client-api-react' 76 | ``` 77 | 78 | ```javascript 79 | import { Graphistry } from '@graphistry/client-api-react';` // + variants for different bundling formats 80 | 81 | ``` 82 | 83 | See [@graphistry/client-api-react project](projects/client-api-react/README.md), [interactive storybook docs](https://graphistry.github.io/graphistry-js/), and [Create React App project sample](projects/cra-test/README.md) 84 | 85 |

86 | 87 | ## @graphistry/node-api 88 | 89 | 90 | 91 | **@graphistry/node-api**: Node.js bindings, including optional Typescript support, for creating visualizations and generating URLs 92 | 93 | ```bash 94 | npm install '@graphistry/node-api' 95 | ``` 96 | 97 | ```javascript 98 | import { Client, Dataset, EdgeFile, NodeFile } from "@graphistry/node-api"; 99 | const client = new Client(user, pass); 100 | const ds = new Dataset(bindings, new EdgeFile(edges)); 101 | await ds.upload(client); 102 | ``` 103 | 104 | See [@graphistry/node-api project](projects/node-api/README.md) and [API docs & examples](https://graphistry.github.io/graphistry-js/node-tsdocs/) 105 | 106 |

107 | 108 | # Graphistry's Architecture 109 | 110 | You can think of Graphistry as a live data version of Google Maps. 111 | 112 | Clientside (client-api & client-api-react): 113 | 114 | - Graphistry runs as an embedded iframe that streams live with your Graphistry server 115 | - GraphistryJS runs as a lightweight JavaScript library in your application. It simplifies creating the iframe, uploading data for visualization as needed, and sending the iframe messages to control style and interactions 116 | - GraphistryJS can be used to upload and view new visualizations, or run sessions for existing uploads, including those from other clients 117 | - User and GraphistryJS interactions will transparently leverage the Graphistry server as needed, such as for loading a graph, running analytics, drilling into data, and saving settings 118 | 119 | Serverside (node-api): 120 | 121 | - GraphistryJS can be used to upload new files and stitch them into graph datasets 122 | - The resulting server item IDs can be sent to browsers for embedding either as iframe URLs or GraphistryJS IDs, or additional server-side manipulations 123 | 124 | ## Decoupling uploads from downloads 125 | 126 | To support server-acceleration and fast interactions, Graphistry decouples uploads from downloads 127 | 128 | ### Uploads: 129 | 130 | - Multiple upload formats are possible, but we recommend parquet & arrow for the best performance and high reliability 131 | - Uploads are possible from browser clients (CSP/CORS support), but we generally recommend server<>server communications for better speed 132 | - Different datasets may reuse the same file. Datasets are generally just a small amount of metadata, so for best performance, try to upload new datasets for existing files, instead of reuploading the files as well. 133 | 134 | ### Downloads: 135 | 136 | - Client sessions connect to previously uploaded datasets and their files 137 | - Client session configurations can override settings initially defined during the upload phase 138 | 139 | ## Security 140 | 141 | - You can configure your Graphistry server to run as http, https, or both 142 | - Uploads require authentication 143 | - The `node-api` client already uses the new JWT-based protocol ("API 3") 144 | - Deprecated: The clientside JavaScript convenience APIs still use the deprecrated "API 1" protocol (key-based), which lacks JWT-based authentication and authorization 145 | - We recommend clients instead use `fetch` or other HTTP callers to directly invoke the REST API: See how the `node-api` performs it 146 | - The client JavaScript APIs will updated to the new JWT method alongside recent CORS and SSO updates; contact staff if you desire assistance 147 | - Sessions are based on unguessable web keys: sharing a secret ID means sharing read access 148 | - Datasets are immutable and thus their integrity is safe for sharing, while session state (e.g., filters) are writable: share a copy when in doubt 149 | -------------------------------------------------------------------------------- /dc: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | ############## 4 | # 5 | # Helper for running docker compose commands 6 | # ex: follow logs - ./dc logs -f 7 | # 8 | ############## 9 | 10 | DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker compose $@ 11 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.5" 2 | 3 | networks: 4 | grph_net: 5 | name: grph_net 6 | ipam: 7 | driver: default 8 | 9 | x-build-kwargs: 10 | &build_kwargs 11 | args: 12 | - APP_BUILD_TAG=${APP_BUILD_TAG:-latest} 13 | context: . 14 | 15 | x-production-options: 16 | &production_opts 17 | networks: 18 | - grph_net 19 | logging: 20 | driver: json-file 21 | options: 22 | compress: "true" 23 | max-size: "10m" 24 | max-file: "10" 25 | mode: "non-blocking" 26 | max-buffer-size: "5m" 27 | tag: "{{.ImageName}}/{{.Name}}/{{.ID}}" 28 | # https://github.com/seccomp/libseccomp/issues/153 29 | security_opt: # Purely for local dev / ci perf 30 | - seccomp:unconfined 31 | 32 | services: 33 | 34 | graphistry-js: 35 | << : *production_opts 36 | image: graphistry/graphistry-js:${APP_BUILD_TAG:-latest} 37 | build: 38 | << : *build_kwargs 39 | dockerfile: ./Dockerfile 40 | init: true 41 | 42 | storybook: 43 | << : *production_opts 44 | image: graphistry/graphistry-js:${APP_BUILD_TAG:-latest} 45 | build: 46 | << : *build_kwargs 47 | dockerfile: ./Dockerfile 48 | ports: 49 | - "6006:6006" 50 | init: true 51 | working_dir: /opt/graphistry-js/projects/client-api-react 52 | environment: 53 | - NODE_OPTIONS=--max_old_space_size=6144 54 | - NODE_ENV=development 55 | - STORYBOOK_DISABLE_TELEMETRY=1 56 | command: 57 | - npm 58 | - run 59 | - storybook 60 | volumes: 61 | # TODO does not seem to get picked up 62 | - ./projects/client-api/src:/opt/graphistry-js/projects/client-api/src:ro 63 | - ./projects/client-api-react/package.json:/opt/graphistry-js/projects/client-api-react/package.json:ro 64 | - ./projects/client-api-react/src:/opt/graphistry-js/projects/client-api-react/src:ro 65 | - ./projects/client-api-react/assets:/opt/graphistry-js/projects/client-api-react/assets:ro 66 | - ./projects/client-api-react/.storybook:/opt/graphistry-js/projects/client-api-react/.storybook:ro -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "5.1.6", 3 | "packages": [ 4 | "projects/client-api", 5 | "projects/client-api-react", 6 | "projects/cra-test", 7 | "projects/cra-test-18", 8 | "projects/cra-template", 9 | "projects/js-upload-api", 10 | "projects/node-api", 11 | "projects/node-api-test", 12 | "projects/node-api-test-cjs" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphistry-js", 3 | "version": "1.0.0", 4 | "description": "Libraries for embedding, controlling, and reacting to Graphistry visualizations", 5 | "main": "", 6 | "scripts": { 7 | "lerna": "lerna", 8 | "bootstrap": "lerna bootstrap", 9 | "deploy": "lerna publish", 10 | "build": "lerna run build", 11 | "build:umd": "lerna run build:umd", 12 | "build:prod": "lerna run build:prod", 13 | "build:examples": "lerna run build:examples", 14 | "lint": "lerna run lint", 15 | "clean": "lerna clean && lerna run clean && npm run clean:artifacts && npm run clean:install", 16 | "clean:artifacts": "rm -rf dist && rm -rf docs && rm -rf docs-build", 17 | "clean:install": "rm -rf node_modules", 18 | "lock:fix": "npm i --package-lock-only" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/graphistry/graphistry-js.git" 23 | }, 24 | "author": "Graphistry, Inc", 25 | "license": "Apache-2.0", 26 | "bugs": { 27 | "url": "https://github.com/graphistry/graphistry-js/issues" 28 | }, 29 | "homepage": "https://github.com/graphistry/graphistry-js#readme", 30 | "devDependencies": { 31 | "eslint": "7.32.0", 32 | "lerna": "^6.5.1" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /projects/client-api-react/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/env", ["@babel/preset-react", {"runtime": "automatic"}]] 3 | } -------------------------------------------------------------------------------- /projects/client-api-react/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:react/recommended", 5 | ], 6 | "parserOptions": { 7 | "sourceType": "module", 8 | "ecmaVersion": 9, 9 | "ecmaFeatures": { 10 | "experimentalObjectRestSpread": true 11 | } 12 | }, 13 | "env": { 14 | "browser": true, 15 | "es6": true 16 | }, 17 | "settings": { 18 | "react": { 19 | "version": "16.8.6" 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /projects/client-api-react/.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "stories": ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], 3 | 4 | "addons": ["@storybook/addon-essentials", { 5 | name: '@storybook/addon-storysource', 6 | options: { 7 | loaderOptions: { 8 | injectStoryParameters: false, 9 | }, 10 | }, 11 | }, "@storybook/addon-links", "@storybook/addon-mdx-gfm"], 12 | 13 | webpackFinal: async (config, { configType }) => { 14 | console.dir(config.plugins, { depth: null }) || config; 15 | return config; 16 | }, 17 | 18 | framework: { 19 | name: "@storybook/react-webpack5", 20 | options: {} 21 | }, 22 | 23 | docs: { 24 | autodocs: true 25 | } 26 | } -------------------------------------------------------------------------------- /projects/client-api-react/.storybook/manager-head.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /projects/client-api-react/.storybook/manager.js: -------------------------------------------------------------------------------- 1 | // .storybook/manager.js 2 | 3 | import { addons } from '@storybook/addons'; 4 | import theme from './theme'; 5 | 6 | addons.setConfig({ 7 | theme: theme, 8 | }); -------------------------------------------------------------------------------- /projects/client-api-react/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import theme from './theme'; 2 | 3 | export const parameters = { 4 | actions: { argTypesRegex: "^on[A-Z].*" }, 5 | controls: { 6 | matchers: { 7 | color: /(background|color)$/i, 8 | date: /Date$/, 9 | }, 10 | }, 11 | docs: { 12 | theme: theme 13 | } 14 | } -------------------------------------------------------------------------------- /projects/client-api-react/.storybook/theme.js: -------------------------------------------------------------------------------- 1 | // .storybook/YourTheme.js 2 | 3 | import { create } from '@storybook/theming'; 4 | 5 | export default create({ 6 | base: 'light', 7 | brandTitle: 'Graphistry JS docs', 8 | brandUrl: 'https://www.graphistry.com', 9 | brandImage: 'https://hub.graphistry.com/static/assets/images/logo/banner_transparent_colored.png', 10 | }); -------------------------------------------------------------------------------- /projects/client-api-react/DEVELOP.md: -------------------------------------------------------------------------------- 1 | # @graphistry/client-api-react Developer Guide 2 | 3 | ## Architecture 4 | 5 | *`esbuild` (and `rollup` + `babel` for `umd`) + `eslint` 6 | * multiple output targets for embedded and standalone use 7 | * see root instructions for containerized + storybook 8 | 9 | ## Build 10 | 11 | Uses `esbuild` for most, `rollup` for `umd`: 12 | 13 | ```bash 14 | npm run build 15 | # -> dist/index.{cjs,iife,esm,umd}.js 16 | # -> dist/index.full.{cjs,iife,esm}.js 17 | ``` 18 | 19 | The `index.full.*.js` variants inline `React` for standalone use, while the main variant relies on linking to an externally provided version by the embedding environment. 20 | 21 | ## Regenerate package lock 22 | 23 | ```bash 24 | npm run lock:fix 25 | ``` -------------------------------------------------------------------------------- /projects/client-api-react/README.md: -------------------------------------------------------------------------------- 1 | # Graphistry's Client API React Component 2 | 3 | ## Automatically upload and visualize datasets using React, powered by Graphistry's GPU graph visualization infrastructure. 4 | 5 | ### Install 6 | 7 | `npm install @graphistry/client-api-react` 8 | 9 | ### Usage 10 | 11 | ```javascript 12 | import React from 'react'; 13 | import ReactDOM from 'react-dom'; 14 | 15 | import { Graphistry } from '@graphistry/client-api-react'; 16 | //... Specify bundle format when default is incompatible: @graphistry/client-api-react/dist/index.{esm,cjs,iife}.js 17 | 18 | import '@graphistry/client-api-react/assets/index.less'; 19 | 20 | ReactDOM.render( 21 | , 23 | document.getElementById('react-root') 24 | ); 25 | 26 | ``` 27 | ### Additional React examples 28 | 29 | * API: [Interactive storybook](https://graphistry.github.io/graphistry-js/) 30 | * Project structure: [Create React App sample project](../cra-test/README.md) 31 | 32 | ### Versions 33 | 34 | * `@graphistry/client-api-react` wraps `@graphistry/client-api`, so use aligned versions 35 | * By default, `React` is linked as an external dependency (16.8+, 17.x, 18.x) 36 | * You can use multiple module formats: `@graphistry/client-api-react` (default) or pick a specific format via `@graphistry/client-api-react/dist/index.{esm,cjs,iife}.js` 37 | * You can also use the library standalone, where React is already inlined: `@graphistry/client-api-react/dist/index.full.{esm,cjs,iife}.js` 38 | -------------------------------------------------------------------------------- /projects/client-api-react/assets/index.css: -------------------------------------------------------------------------------- 1 | .graphistry-container { 2 | width: 100%; 3 | height: 100%; 4 | min-height: 20em; 5 | position: relative; 6 | box-sizing: border-box; 7 | } 8 | .graphistry-iframe { 9 | width: 100%; 10 | height: 100%; 11 | min-height: 20em; 12 | overflow: hidden; 13 | border: 0px solid #DDD; 14 | box-sizing: border-box; 15 | } 16 | .graphistry-loading-placeholder { 17 | width: 100%; 18 | height: 100%; 19 | overflow: hidden; 20 | position: absolute; 21 | } 22 | .graphistry-loading-placeholder-nav { 23 | width: 100%; 24 | height: 41px; 25 | background-color: #ffffff; 26 | border-bottom: 1px solid #cccccc; 27 | } 28 | .graphistry-loading-placeholder-content { 29 | width: 100%; 30 | height: calc(100% - 42px); 31 | display: table; 32 | } 33 | .graphistry-loading-placeholder-message { 34 | font-size: 12px; 35 | font-weight: bold; 36 | display: table-cell; 37 | vertical-align: middle; 38 | text-align: center; 39 | box-shadow: inset -4px 3px 18px 0px rgba(0, 0, 0, 0.13); 40 | background-image: repeating-linear-gradient(45deg, #f8f8f8, #f8f8f8 30px, #F6F6F6 30px, #F6F6F6 60px); 41 | } 42 | .graphistry-loading-placeholder-spinner { 43 | -webkit-animation: graphistry-loading-placeholder-animation-spin 400ms infinite linear; 44 | -o-animation: graphistry-loading-placeholder-animation-spin 400ms infinite linear; 45 | animation: graphistry-loading-placeholder-animation-spin 400ms infinite linear; 46 | width: 16px; 47 | height: 16px; 48 | box-sizing: border-box; 49 | border-radius: 50%; 50 | border: 2px solid #ccc; 51 | border-right-color: #333; 52 | display: inline-block; 53 | position: relative; 54 | vertical-align: middle; 55 | text-align: center; 56 | } 57 | @keyframes graphistry-loading-placeholder-animation-spin { 58 | to { 59 | transform: rotate(1turn); 60 | } 61 | } 62 | @-webkit-keyframes graphistry-loading-placeholder-animation-spin { 63 | to { 64 | -webkit-transform: rotate(1turn); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /projects/client-api-react/assets/index.less: -------------------------------------------------------------------------------- 1 | .graphistry-container { 2 | width: 100%; 3 | height: 100%; 4 | min-height: 20em; 5 | position: relative; 6 | box-sizing: border-box; 7 | } 8 | 9 | .graphistry-iframe { 10 | width: 100%; 11 | height: 100%; 12 | min-height: 20em; 13 | overflow: hidden; 14 | border: 0px solid #DDD; 15 | box-sizing: border-box; 16 | } 17 | 18 | .graphistry-loading-placeholder { 19 | width: 100%; 20 | height: 100%; 21 | overflow: hidden; 22 | position: absolute; 23 | } 24 | 25 | .graphistry-loading-placeholder-nav { 26 | width: 100%; 27 | height: 41px; 28 | background-color: #ffffff; 29 | border-bottom: 1px solid #cccccc; 30 | } 31 | 32 | .graphistry-loading-placeholder-content { 33 | width: 100%; 34 | height: ~"calc(100% - 42px)"; 35 | display: table; 36 | } 37 | 38 | .graphistry-loading-placeholder-message { 39 | font-size: 12px; 40 | font-weight: bold; 41 | //background: linear-gradient(135deg, #f8f8f8 0%,#eeeeee 100%); 42 | display: table-cell; 43 | vertical-align: middle; 44 | text-align: center; 45 | box-shadow: inset -4px 3px 18px 0px rgba(0,0,0,0.13); 46 | // padding-bottom: 30px; 47 | background-image: repeating-linear-gradient(45deg, #f8f8f8, #f8f8f8 30px, #F6F6F6 30px, #F6F6F6 60px); 48 | } 49 | 50 | .graphistry-loading-placeholder-spinner { 51 | -webkit-animation: graphistry-loading-placeholder-animation-spin 400ms infinite linear; 52 | -o-animation: graphistry-loading-placeholder-animation-spin 400ms infinite linear; 53 | animation: graphistry-loading-placeholder-animation-spin 400ms infinite linear; 54 | width: 16px; 55 | height: 16px; 56 | box-sizing: border-box; 57 | border-radius: 50%; 58 | border: 2px solid #ccc; 59 | border-right-color: #333; 60 | display: inline-block; 61 | position: relative; 62 | vertical-align: middle; 63 | text-align: center; 64 | } 65 | 66 | @keyframes graphistry-loading-placeholder-animation-spin { 67 | to { 68 | transform: rotate(1turn); 69 | } 70 | } 71 | @-webkit-keyframes graphistry-loading-placeholder-animation-spin { 72 | to { 73 | -webkit-transform: rotate(1turn); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /projects/client-api-react/examples/default.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphistry/graphistry-js/2192e0835456c180101d0be20296fee94a43be28/projects/client-api-react/examples/default.html -------------------------------------------------------------------------------- /projects/client-api-react/examples/default.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {Graphistry} from '@graphistry/client-api-react'; 4 | import '@graphistry/client-api-react/assets/index.less'; 5 | 6 | const container = document.getElementById('__react-content'); 7 | container.style['height'] = `400px`; 8 | 9 | ReactDOM.render( 10 | , 20 | container 21 | ); 22 | -------------------------------------------------------------------------------- /projects/client-api-react/examples/dynamic.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphistry/graphistry-js/2192e0835456c180101d0be20296fee94a43be28/projects/client-api-react/examples/dynamic.html -------------------------------------------------------------------------------- /projects/client-api-react/examples/dynamic.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {Graphistry} from '@graphistry/client-api-react'; 4 | import '@graphistry/client-api-react/assets/index.less'; 5 | 6 | const container = document.getElementById('__react-content'); 7 | container.style['height'] = `400px`; 8 | 9 | ReactDOM.render( 10 | , 31 | container 32 | ); 33 | -------------------------------------------------------------------------------- /projects/client-api-react/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src'); 2 | -------------------------------------------------------------------------------- /projects/client-api-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@graphistry/client-api-react", 3 | "version": "5.1.6", 4 | "repository": { 5 | "type": "git", 6 | "url": "git+https://github.com/graphistry/graphistry-js.git" 7 | }, 8 | "homepage": "https://github.com/graphistry/graphistry-js/projects/client-api-react#readme", 9 | "private": false, 10 | "main": "dist/index.cjs.min.js", 11 | "module": "dist/index.esm.min.js", 12 | "source": "src/index.js", 13 | "files": [ 14 | "assets", 15 | "dist", 16 | "docs", 17 | "README.md" 18 | ], 19 | "description": "Embed a Graphistry visualization via React", 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject", 24 | "build": "npm run build:rollup && npm run build:esbuild", 25 | "lock:fix": "npm i --package-lock-only", 26 | "clean": "npm run clean:install && npm run clean:builds && npm run clean:cache && npm run clean:docs", 27 | "clean:install": "rm -rf node_modules", 28 | "clean:builds": "rm -rf dist && rm -rf www && rm -rf .next", 29 | "clean:cache": "rm -rf .cache", 30 | "clean:docs": "rimraf docs jsdocs docs-build", 31 | "build:rollup": "rollup -c", 32 | "build:rollup-watch": "rollup -c -w", 33 | "build:esbuild": "npm run build:dev && npm run build:linked && npm run build:full", 34 | "build:dev": "esbuild --bundle src/index.js --target=es6 --loader:.js=jsx --outfile=www/main.js --sourcemap", 35 | "build:linked": "npm run build:linked:js && npm run build:linked:min:js", 36 | "build:linked:js": "npm run build:cjs:js && npm run build:esm:js && npm run build:umd:js && npm run build:iife:js", 37 | "build:cjs:js": "esbuild --bundle src/index.js --sourcemap --target=es6 --loader:.js=jsx --format=cjs --external:react --external:react-dom --outfile=dist/index.cjs.js", 38 | "build:esm:js:broken": "esbuild --bundle src/index.js --sourcemap --target=es6 --loader:.js=jsx --format=esm --external:react --external:react-dom --outfile=dist/index.esm.js", 39 | "build:esm:js": "echo 'relying on rollup -c'", 40 | "build:umd:js": "echo 'relying on rollup -c'", 41 | "build:iife:js": "esbuild --bundle src/index.js --sourcemap --target=es6 --loader:.js=jsx --format=iife --external:react --external:react-dom --outfile=dist/index.iife.js --global-name=Graphistry", 42 | "build:linked:min:js": "npm run build:cjs:min:js && npm run build:esm:min:js && npm run build:umd:min:js && npm run build:iife:min:js", 43 | "build:cjs:min:js": "esbuild --bundle src/index.js --minify --sourcemap --target=es6 --loader:.js=jsx --format=cjs --external:react --external:react-dom --outfile=dist/index.cjs.min.js", 44 | "build:esm:min:js:broken": "esbuild --bundle src/index.js --minify --sourcemap --target=es6 --loader:.js=jsx --format=esm --external:react --external:react-dom --outfile=dist/index.esm.min.js", 45 | "build:esm:min:js": "echo 'relying on rollup -c'", 46 | "build:umd:min:js": "echo 'relying on rollup -c'", 47 | "build:iife:min:js": "esbuild --bundle src/index.js --minify --sourcemap --target=es6 --loader:.js=jsx --format=iife --external:react --external:react-dom --outfile=dist/index.iife.min.js --global-name=Graphistry", 48 | "build:full": "npm run build:full:js && npm run build:full:min:js", 49 | "build:full:js": "npm run build:cjs:js:full && npm run build:esm:js:full && npm run build:iife:js:full", 50 | "build:cjs:js:full": "esbuild --bundle src/index.js --sourcemap --target=es6 --loader:.js=jsx --format=cjs --outfile=dist/index.full.cjs.js", 51 | "build:esm:js:full": "esbuild --bundle src/index.js --sourcemap --target=es6 --loader:.js=jsx --format=esm --outfile=dist/index.full.esm.js", 52 | "build:iife:js:full": "esbuild --bundle src/index.js --sourcemap --target=es6 --loader:.js=jsx --format=iife --outfile=dist/index.full.iife.js --global-name=Graphistry", 53 | "build:full:min:js": "npm run build:cjs:js:full && npm run build:esm:js:full && npm run build:iife:js:full", 54 | "build:cjs:min:js:full": "esbuild --bundle src/index.js --minify --sourcemap --target=es6 --loader:.js=jsx --format=cjs --outfile=dist/index.full.cjs.min.js", 55 | "build:esm:min:js:full": "esbuild --bundle src/index.js --minify --sourcemap --target=es6 --loader:.js=jsx --format=esm --outfile=dist/index.full.esm.min.js", 56 | "build:iife:min:js:full": "esbuild --bundle src/index.js --minify --sourcemap --target=es6 --loader:.js=jsx --format=iife --outfile=dist/index.full.iife.min.js --global-name=Graphistry", 57 | "build:docs": "rimraf docs && jsdoc -c jsdoc-conf.json -R ./README.md -d docs", 58 | "prebuild:docs": "rimraf docs", 59 | "lint": "eslint src", 60 | "storybook": "storybook dev -p 6006", 61 | "build-storybook": "storybook build --disable-telemetry -o docs-build -s ./assets" 62 | }, 63 | "filesESBuild": [ 64 | "dist", 65 | "docs", 66 | "assets" 67 | ], 68 | "keywords": [], 69 | "author": "Graphistry, Inc ", 70 | "license": "ISC", 71 | "dependencies": { 72 | "@graphistry/client-api": "^5.1.6", 73 | "crypto-browserify": "3.12.0", 74 | "prop-types": ">=15.6.0", 75 | "shallowequal": "1.1.0", 76 | "uuid": "9.0.0" 77 | }, 78 | "peerDependencies": { 79 | "react": ">=16.8.6", 80 | "react-dom": ">=16.8.6" 81 | }, 82 | "eslintConfig": { 83 | "extends": [ 84 | "react-app", 85 | "react-app/jest" 86 | ] 87 | }, 88 | "browserslist": { 89 | "production": [ 90 | ">0.2%", 91 | "not dead", 92 | "not op_mini all" 93 | ], 94 | "development": [ 95 | "last 1 chrome version", 96 | "last 1 firefox version", 97 | "last 1 safari version" 98 | ] 99 | }, 100 | "devDependencies": { 101 | "@babel/cli": "^7.17.10", 102 | "@babel/core": "^7.17.10", 103 | "@babel/preset-env": "^7.17.10", 104 | "@babel/preset-react": "^7.16.0", 105 | "@rollup/plugin-babel": "^5.3.1", 106 | "@rollup/plugin-commonjs": "^21.1.0", 107 | "@rollup/plugin-node-resolve": "^13.3.0", 108 | "@rollup/plugin-replace": "^6.0.2", 109 | "@storybook/addon-actions": "^7.6.12", 110 | "@storybook/addon-docs": "^7.6.12", 111 | "@storybook/addon-essentials": "^7.6.12", 112 | "@storybook/addon-links": "^7.6.12", 113 | "@storybook/addon-mdx-gfm": "^7.6.12", 114 | "@storybook/addon-storysource": "^7.6.12", 115 | "@storybook/addons": "^7.6.12", 116 | "@storybook/blocks": "^7.6.12", 117 | "@storybook/react": "^7.6.12", 118 | "@storybook/react-webpack5": "^7.6.12", 119 | "@storybook/theming": "^7.6.12", 120 | "@testing-library/jest-dom": "^5.16.4", 121 | "@testing-library/react": "^12.1.5", 122 | "@testing-library/user-event": "^14.2.0", 123 | "babel-loader": "^8.2.5", 124 | "cross-env": "^7.0.3", 125 | "esbuild": "0.14.39", 126 | "eslint": "7.32.0", 127 | "eslint-plugin-react": "7.26.1", 128 | "jsdoc": "^4.0.2", 129 | "jsdoc-babel": "~0.5.0", 130 | "npm-run-all": "^4.1.5", 131 | "react": ">=16.8.6", 132 | "react-dom": ">=16.8.6", 133 | "react-scripts": "^5.0.1", 134 | "rimraf": "3.0.2", 135 | "rollup": "^2.72.1", 136 | "rollup-plugin-delete": "^2.0.0", 137 | "rollup-plugin-peer-deps-external": "^2.2.4", 138 | "rollup-plugin-terser": "^7.0.2", 139 | "storybook": "^7.6.12" 140 | }, 141 | "gitHead": "949ca04aa87b399e0185c64d7a6810780a841deb" 142 | } 143 | -------------------------------------------------------------------------------- /projects/client-api-react/rollup.config.js: -------------------------------------------------------------------------------- 1 | import { babel } from '@rollup/plugin-babel'; 2 | import external from 'rollup-plugin-peer-deps-external'; 3 | import del from 'rollup-plugin-delete'; 4 | import pkg from './package.json'; 5 | import commonjs from '@rollup/plugin-commonjs'; 6 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 7 | import replace from '@rollup/plugin-replace'; 8 | import { terser } from "rollup-plugin-terser"; 9 | 10 | const globals = { 11 | 'react': 'React', 12 | 'react-dom': 'ReactDOM', 13 | 'prop-types': 'PropTypes' 14 | }; 15 | const name = "Graphistry"; 16 | const replaceNodeEnvStr = defaultNodeEnv => `typeof process !== 'undefined' && process.env && process.env.NODE_ENV ? process.env.NODE_ENV : "${defaultNodeEnv}"`; 17 | 18 | export default { 19 | input: pkg.source, 20 | output: [ 21 | //{ globals, file: pkg.main, format: 'cjs' }, 22 | { globals, file: 'dist/index.esm.js', format: 'esm', plugins: [replace({'process.env.NODE_ENV': replaceNodeEnvStr('development')})] }, 23 | { globals, file: 'dist/index.esm.min.js', format: 'esm', plugins: [replace({'process.env.NODE_ENV': replaceNodeEnvStr('production')}), terser()] }, 24 | //{ globals, file: pkg.module, format: 'iife', name }, 25 | { globals, file: `dist/index.umd.js`, format: 'umd', name, plugins: [replace({'process.env.NODE_ENV': replaceNodeEnvStr('development')})] }, 26 | { globals, file: `dist/index.umd.min.js`, format: 'umd', name, plugins: [replace({'process.env.NODE_ENV': replaceNodeEnvStr('production')}), terser()] } 27 | ], 28 | plugins: [ 29 | nodeResolve({ 30 | extensions: [".js"], 31 | }), 32 | external(), 33 | babel({ 34 | exclude: 'node_modules/**', 35 | babelHelpers: 'bundled' 36 | }), 37 | commonjs(), 38 | del({ targets: ['dist/*'] }), 39 | ], 40 | external: Object.keys(pkg.peerDependencies || {}), 41 | }; -------------------------------------------------------------------------------- /projects/client-api-react/src/bindings.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | //FIXME https://github.com/storybookjs/storybook/issues/14092 4 | //See corresponding issue in index.js 5 | const panelNames = ['filters', 'exclusions', 'scene', 'labels', 'layout']; 6 | 7 | // react prop name :: [ react type, default name, maybe alt JS updateSetting prop, maybe alt JS method ] 8 | const bindingsTable = { 9 | 10 | backgroundColor: [ PropTypes.string, 'defaultBackgroundColor', 'background', undefined], 11 | labelOpacity: [ PropTypes.number, 'defaultLabelOpacity', undefined, undefined], 12 | labelColor: [ PropTypes.string, 'defaultLabelColor', undefined, undefined], 13 | labelBackground: [ PropTypes.string, 'defaultLabelBackground', undefined, undefined], 14 | pointSize: [ PropTypes.number, 'defaultPointSize', undefined, undefined], 15 | pointStrokeWidth: [ PropTypes.number, 'defaultPointStrokeWidth', undefined, undefined], 16 | neighborhoodHighlight: [ PropTypes.string, 'defaultNeighborhoodHighlight', undefined, undefined], 17 | neighborhoodHighlightHops: [ PropTypes.number, 'defaultNeighborhoodHighlightHops', undefined, undefined], 18 | edgeCurvature: [ PropTypes.number, 'defaultEdgeCurvature', undefined, undefined], 19 | edgeOpacity: [ PropTypes.number, 'defaultEdgeOpacity', undefined, undefined], 20 | pointOpacity: [ PropTypes.number, 'defaultPointOpacity', undefined, undefined], 21 | showArrows: [ PropTypes.bool, 'defaultShowArrows', undefined, undefined], 22 | showCollections: [ PropTypes.bool, 'defaultShowCollections', undefined, undefined], 23 | showLabels: [ PropTypes.bool, 'defaultShowLabels', 'labelEnabled', undefined], 24 | showToolbar: [ PropTypes.bool, 'defaultShowToolbar', undefined, undefined], 25 | showInspector: [ PropTypes.bool, 'defaultShowInspector', undefined, 'toggleInspector'], 26 | showTimebars: [ PropTypes.bool, 'defaultShowTimebars', undefined, 'toggleTimebars'], 27 | showHistograms: [ PropTypes.bool, 'defaultShowHistograms', undefined, 'toggleHistograms'], 28 | pruneOrphans: [ PropTypes.bool, 'defaultPruneOrphans', undefined, undefined], 29 | showLabelOnHover: [ PropTypes.bool, 'defaultShowLabelOnHover', 'labelHighlightEnabled', undefined], 30 | showLabelPropertiesOnHover: [ PropTypes.bool, 'defaultShowLabelPropertiesOnHover', 'labelPropertiesEnabled', undefined], 31 | showLabelInspector: [ PropTypes.bool, 'defaultShowLabelInspector', 'labelInspectorEnabled', undefined], 32 | showLabelActions: [ PropTypes.bool, 'defaultShowLabelActions', 'labelShowActions', undefined], 33 | showPointsOfInterest: [ PropTypes.bool, 'defaultShowPointsOfInterest', 'labelPOI', undefined], 34 | showPointsOfInterestLabel: [ PropTypes.bool, 'defaultShowPointsOfInterestLabel', 'labelLabelPOI', undefined], 35 | pointsOfInterestMax: [ PropTypes.number, 'defaultPointsOfInterestMax', 'labelPOIMax', undefined], 36 | linLog: [ PropTypes.bool, 'defaultLinLog', undefined, undefined], 37 | lockedX: [ PropTypes.bool, 'defaultLockedX', undefined, undefined], 38 | lockedY: [ PropTypes.bool, 'defaultLockedY', undefined, undefined], 39 | lockedR: [ PropTypes.bool, 'defaultLockedR', undefined, undefined], 40 | strongGravity: [ PropTypes.bool, 'defaultStrongGravity', undefined, undefined], 41 | dissuadeHubs: [ PropTypes.bool, 'defaultDissuadeHubs', undefined, undefined], 42 | edgeInfluence: [ PropTypes.oneOf([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), 'defaultEdgeInfluence', undefined, undefined], 43 | precisionVsSpeed: [ PropTypes.oneOf([ -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]), 'defaultPrecisionVsSpeed', undefined, undefined], 44 | gravity: [ PropTypes.oneOf(Array.from({ length: 100 }, (x, i) => i + 1)), 'defaultGravity', undefined, undefined], 45 | scalingRatio: [ PropTypes.oneOf(Array.from({ length: 100 }, (x, i) => i + 1)), 'defaultScalingRatio', undefined, undefined], 46 | 47 | setTogglePanel: [ PropTypes.array, undefined, undefined, 'togglePanel'], 48 | 49 | ticks: [PropTypes.number, undefined, undefined, 'tickClustering'], 50 | 51 | filters: [PropTypes.arrayOf(PropTypes.string), undefined, undefined, 'addFilters'], 52 | filter: [PropTypes.string, undefined, undefined, 'addFilter'], 53 | exclusions: [PropTypes.arrayOf(PropTypes.string), undefined, undefined, 'addExclusions'], 54 | exclusion: [PropTypes.string, undefined, undefined, 'addExclusion'], 55 | 56 | encodePointSize: [ 57 | PropTypes.oneOfType([ 58 | PropTypes.string, 59 | PropTypes.array, 60 | ]), 61 | 'encodeDefaultPointSize', 62 | undefined, 63 | 'encodePointSize' 64 | ], 65 | 66 | encodePointColor: [ 67 | PropTypes.oneOfType([ 68 | PropTypes.string, 69 | PropTypes.array 70 | ]), 71 | 'encodeDefaultPointColor', 72 | undefined, 73 | 'encodePointColor' 74 | ], 75 | encodeEdgeColor: [ 76 | PropTypes.oneOfType([ 77 | PropTypes.string, 78 | PropTypes.array 79 | ]), 80 | 'encodeDefaultEdgeColor', 81 | undefined, 82 | 'encodeEdgeColor' 83 | ], 84 | 85 | encodeAxis: [ 86 | PropTypes.object, 87 | undefined, 88 | undefined, 89 | 'encodeAxis' 90 | ], 91 | 92 | encodePointIcons: [ 93 | PropTypes.oneOfType([ 94 | PropTypes.string, 95 | PropTypes.array 96 | ]), 97 | 'encodeDefaultPointIcons', 98 | undefined, 99 | 'encodePointIcons' 100 | ], 101 | encodeEdgeIcons: [ 102 | PropTypes.oneOfType([ 103 | PropTypes.string, 104 | PropTypes.array 105 | ]), 106 | 'encodeDefaultEdgeIcons', 107 | undefined, 108 | 'encodeEdgeIcons' 109 | ], 110 | }; 111 | 112 | 113 | //[ {name: String, nameDefault: String, reactType: PropType, jsName: String, jsCommand: ?String} ] 114 | const bindings = 115 | Object.keys(bindingsTable).map( name => { 116 | const [ reactType, nameDefault, jsName, jsCommand ] = bindingsTable[name]; 117 | return { 118 | name, 119 | nameDefault, 120 | reactType, 121 | jsName: jsName || name, 122 | jsCommand 123 | }; 124 | }); 125 | 126 | // TODO: infer this from chainList in client-api 127 | const calls = bindings.map(b => b.jsCommand).filter(Boolean).concat([ 128 | 'setSelectionExternal', 129 | 'setHighlightExternal', 130 | 'resetFilters', 131 | 'resetExclusions' 132 | ]) 133 | 134 | export { bindings, panelNames, calls }; -------------------------------------------------------------------------------- /projects/client-api-react/src/stories/GraphistryUpload.stories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import '../../assets/index.css'; 3 | 4 | import { Graphistry } from '../index'; 5 | 6 | export default { 7 | title: 'Graphistry: React upload', 8 | component: Graphistry, 9 | }; 10 | 11 | export const Empty = {}; 12 | export const PredefinedDataset = { 13 | render: (args) => , 14 | }; 15 | 16 | const defaultSettings = { 17 | //dataset: 'Miserables', 18 | play: 1, 19 | showSplashScreen: true, 20 | }; 21 | 22 | export const UploadEdges = { 23 | render: (args) => ( 24 | 39 | ), 40 | }; 41 | 42 | export const UploadEdgesAndNodes = { 43 | render: (args) => ( 44 | 63 | ), 64 | }; 65 | -------------------------------------------------------------------------------- /projects/client-api-react/src/stories/Introduction.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/blocks'; 2 | import Code from './assets/code-brackets.svg'; 3 | import Colors from './assets/colors.svg'; 4 | import Comments from './assets/comments.svg'; 5 | import Direction from './assets/direction.svg'; 6 | import Flow from './assets/flow.svg'; 7 | import Plugin from './assets/plugin.svg'; 8 | import Repo from './assets/repo.svg'; 9 | import StackAlt from './assets/stackalt.svg'; 10 | import * as IntroductionStories from './Introduction.stories'; 11 | 12 | import GraphistryLogo from './assets/banner_transparent_colored.png'; 13 | import GraphistryFavicon from './assets/favicon.ico'; 14 | 15 | 16 | 17 | 122 | 123 | # Welcome to Graphistry's JavaScript clients 124 | 125 | Browse example stories by navigating the left sidebar. 126 | Each example's source code is visible from its `story` tab. 127 | 128 | Try modifying different properties of a configuration using the `Controls` panel. 129 | In addition, open your browser's developer panel to watch the network traffic and debug messages as you interact. 130 | 131 | ## Graphistry React API Docs 132 | 133 | Explore the React stories on the left 134 | 135 | See also the [React stories source code](https://github.com/graphistry/graphistry-js/blob/master/projects/client-api-react/src/stories/Graphistry.stories.jsx) and [sample Create React App project](https://github.com/graphistry/graphistry-js/tree/master/projects/cra-test) 136 | 137 | ## Graphistry Browser JavaScript API Docs 138 | 139 | Explore the vanilla JS stories on the left 140 | 141 | See also the [browser JavaScript API reference](/jsdocs), [main RxJS docs](https://rxjs.dev), and [JS stories source code](https://github.com/graphistry/graphistry-js/blob/master/projects/client-api-react/src/stories/GraphistryJS.stories.jsx) 142 | 143 | ## Graphistry Node.js JavaScript API Docs 144 | 145 | Explore the [Node.js API reference and examples](node-tsdocs) 146 | 147 | ## Multiple distributions 148 | 149 | The browser libraries are packaged in multiple formats to match different module formats. 150 | 151 | Check the documentation for more information in each client, and contact the Graphistry team for assistance on yours. 152 | -------------------------------------------------------------------------------- /projects/client-api-react/src/stories/Introduction.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default { 4 | title: 'Introduction: Graphistry JavaScript APIs', 5 | }; 6 | 7 | export const SomeStory = () =>
Story content
; -------------------------------------------------------------------------------- /projects/client-api-react/src/stories/assets/banner_transparent_colored.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphistry/graphistry-js/2192e0835456c180101d0be20296fee94a43be28/projects/client-api-react/src/stories/assets/banner_transparent_colored.png -------------------------------------------------------------------------------- /projects/client-api-react/src/stories/assets/code-brackets.svg: -------------------------------------------------------------------------------- 1 | illustration/code-brackets -------------------------------------------------------------------------------- /projects/client-api-react/src/stories/assets/colors.svg: -------------------------------------------------------------------------------- 1 | illustration/colors -------------------------------------------------------------------------------- /projects/client-api-react/src/stories/assets/comments.svg: -------------------------------------------------------------------------------- 1 | illustration/comments -------------------------------------------------------------------------------- /projects/client-api-react/src/stories/assets/direction.svg: -------------------------------------------------------------------------------- 1 | illustration/direction -------------------------------------------------------------------------------- /projects/client-api-react/src/stories/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphistry/graphistry-js/2192e0835456c180101d0be20296fee94a43be28/projects/client-api-react/src/stories/assets/favicon.ico -------------------------------------------------------------------------------- /projects/client-api-react/src/stories/assets/flow.svg: -------------------------------------------------------------------------------- 1 | illustration/flow -------------------------------------------------------------------------------- /projects/client-api-react/src/stories/assets/plugin.svg: -------------------------------------------------------------------------------- 1 | illustration/plugin -------------------------------------------------------------------------------- /projects/client-api-react/src/stories/assets/repo.svg: -------------------------------------------------------------------------------- 1 | illustration/repo -------------------------------------------------------------------------------- /projects/client-api-react/src/stories/assets/stackalt.svg: -------------------------------------------------------------------------------- 1 | illustration/stackalt -------------------------------------------------------------------------------- /projects/client-api-react/src/usePrevious.js: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect } from 'react' // eslint-disable-line no-unused-vars 2 | 3 | 4 | //https://blog.logrocket.com/how-to-get-previous-props-state-with-react-hooks/ 5 | function usePrevious(value) { 6 | const ref = useRef(); 7 | useEffect(() => { 8 | ref.current = value; 9 | }); 10 | return ref.current; 11 | } 12 | 13 | export { usePrevious }; -------------------------------------------------------------------------------- /projects/client-api/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended" 4 | ], 5 | "parserOptions": { 6 | "sourceType": "module", 7 | "ecmaVersion": 9, 8 | "ecmaFeatures": { 9 | "experimentalObjectRestSpread": true 10 | } 11 | }, 12 | "env": { 13 | "browser": true, 14 | "es6": true 15 | } 16 | } -------------------------------------------------------------------------------- /projects/client-api/README.md: -------------------------------------------------------------------------------- 1 | # Graphistry's JavaScript Client API 2 | 3 | ## Graphistry's client-side interactions API makes it easy for developers to interact with embedded graph visualizations. 4 | 5 | ### Installation 6 | 7 | The JS package supports commonjs, esm, and iffe formats. See [package.json](package.json) for `dist/` folder contents. 8 | 9 | The `rxjs` version is an unpinned peer dependency: 10 | - The bundled versions `dist/index.{cjs,esm,iife}.min.js` keep `rxjs` as an external package 11 | - The bundled versions `dist/index.full.{cjs,esm,iife}.min.js` include it 12 | - Use `dist/index.full.iife.min.js` for ` 25 | 46 | 47 | -------------------------------------------------------------------------------- /projects/client-api/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src'); 2 | -------------------------------------------------------------------------------- /projects/client-api/jsdoc-conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true, 4 | "dictionaries": ["jsdoc"] 5 | }, 6 | "source": { 7 | "include": ["src/index.js"] 8 | }, 9 | "plugins": [ 10 | "plugins/markdown", 11 | "node_modules/jsdoc-babel" 12 | ], 13 | "opts": { 14 | "encoding": "utf8", 15 | "private": true, 16 | "recurse": true, 17 | "template": "./node_modules/clean-jsdoc-theme" 18 | }, 19 | "babel": { 20 | "presets": ["@babel/preset-env"] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /projects/client-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@graphistry/client-api", 3 | "version": "5.1.6", 4 | "description": "Client-side API for interacting with a Graphistry embedded visualization.", 5 | "jsnext:main": "dist/index.js", 6 | "config": { 7 | "port": 8000, 8 | "entry": { 9 | "GraphistryJS": [ 10 | "./src/index.js" 11 | ] 12 | }, 13 | "default": "./dist/index.full.esm.min.js" 14 | }, 15 | "main": "dist/index.full.cjs.min.js", 16 | "module": "dist/index.full.esm.min.js", 17 | "source": "src/index.js", 18 | "files": [ 19 | "dist", 20 | "docs", 21 | "jsdocs", 22 | "src", 23 | "README.md" 24 | ], 25 | "scripts": { 26 | "build": "npm run build:js && npm run build:js:full && npm run build:docs", 27 | "build:link:watch": "esbuild --watch --bundle src/index.js --minify --sourcemap --target=es6 --format=esm --outfile=dist/index.full.esm.min.js", 28 | "build:js": "npm run build:js-core && npm run build:js-aliases", 29 | "build:js-core": "npm run build:cjs && npm run build:esm && npm run build:iife && npm run build:iife-raw", 30 | "build:js-aliases": "cp dist/index.esm.min.js dist/index.min.mjs && cp dist/index.esm.min.js.map dist/index.min.mjs.map", 31 | "build:cjs": "esbuild --bundle src/index.js --minify --sourcemap --target=es6 --external:rxjs --format=cjs --outfile=dist/index.cjs.min.js", 32 | "build:esm": "esbuild --bundle src/index.js --minify --sourcemap --target=es6 --external:rxjs --format=esm --outfile=dist/index.esm.min.js", 33 | "build:iife": "esbuild --bundle src/index.js --minify --sourcemap --target=es6 --external:rxjs --format=iife --outfile=dist/index.iife.min.js --global-name=GraphistryModule", 34 | "build:iife-raw": "esbuild --bundle src/index.js --sourcemap --target=es6 --external:rxjs --format=iife --outfile=dist/index.iife.js --global-name=GraphistryModule", 35 | "build:js:full": "npm run build:js-core:full && npm run build:js-core:min:full && npm run build:js-aliases:full && npm run build:js-aliases:min:full", 36 | "build:js-core:full": "npm run build:cjs:full && npm run build:esm:full && npm run build:iife:full", 37 | "build:js-aliases:full": "npm run build:js-aliases:full-esm && npm run build:js-aliases:full-rest", 38 | "build:js-aliases:full-esm": "cp dist/index.full.esm.js dist/index.full.mjs && cp dist/index.full.esm.js.map dist/index.full.mjs.map", 39 | "build:js-aliases:full-rest": "cp dist/index.full.iife.js dist/GraphistryJS.js && cp dist/index.full.iife.js.map dist/GraphistryJS.js.map", 40 | "build:cjs:full": "esbuild --bundle src/index.js --sourcemap --target=es6 --format=cjs --outfile=dist/index.full.cjs.js", 41 | "build:esm:full": "esbuild --bundle src/index.js --sourcemap --target=es6 --format=esm --outfile=dist/index.full.esm.js", 42 | "build:iife:full": "esbuild --bundle src/index.js --sourcemap --target=es6 --format=iife --outfile=dist/index.full.iife.js --global-name=GraphistryModule", 43 | "build:js-core:min:full": "npm run build:cjs:min:full && npm run build:esm:min:full && npm run build:iife:min:full", 44 | "build:js-aliases:min:full": "npm run build:js-aliases:min:full-esm && npm run build:js-aliases:min:full-rest", 45 | "build:js-aliases:min:full-esm": "cp dist/index.full.esm.min.js dist/index.full.min.mjs && cp dist/index.full.esm.min.js.map dist/index.full.min.mjs.map", 46 | "build:js-aliases:min:full-rest": "cp dist/index.full.iife.min.js dist/GraphistryJS.min.js && cp dist/index.full.iife.min.js.map dist/GraphistryJS.min.js.map && cp dist/index.full.iife.js dist/GraphistryJS.js && cp dist/index.full.iife.js.map dist/GraphistryJS.js.map", 47 | "build:cjs:min:full": "esbuild --bundle src/index.js --minify --sourcemap --target=es6 --format=cjs --outfile=dist/index.full.cjs.min.js", 48 | "build:esm:min:full": "esbuild --bundle src/index.js --minify --sourcemap --target=es6 --format=esm --outfile=dist/index.full.esm.min.js", 49 | "build:esm:min:full:watch": "esbuild --watch --bundle src/index.js --minify --sourcemap --target=es6 --format=esm --outfile=dist/index.full.esm.min.js", 50 | "build:iife:min:full": "esbuild --bundle src/index.js --minify --sourcemap --target=es6 --format=iife --outfile=dist/index.full.iife.min.js --global-name=GraphistryModule", 51 | "build:docs": "rimraf docs && mkdir -p docs && jsdoc -c jsdoc-conf.json -R ./README.md -d docs", 52 | "lock:fix": "npm i --package-lock-only", 53 | "prebuild:docs": "rimraf docs", 54 | "lint": "eslint src", 55 | "clean": "npm run clean:install && npm run clean:build && npm run clean:docs", 56 | "clean:install": "rimraf node_modules", 57 | "clean:build": "rimraf dist es libs", 58 | "clean:docs": "rimraf docs jsdocs" 59 | }, 60 | "repository": { 61 | "type": "git", 62 | "url": "git+https://github.com/graphistry/graphistry-js.git" 63 | }, 64 | "keywords": [ 65 | "graphistry", 66 | "graph-viz" 67 | ], 68 | "author": "Graphistry, Inc ", 69 | "license": "Apache-2.0", 70 | "bugs": { 71 | "url": "https://github.com/graphistry/graphistry-js/issues" 72 | }, 73 | "homepage": "https://github.com/graphistry/graphistry-js/projects/client-api#readme", 74 | "devDependencies": { 75 | "@babel/core": "^7.17.10", 76 | "@babel/preset-env": "7.17.10", 77 | "clean-jsdoc-theme": "^4.2.6", 78 | "esbuild": "0.14.39", 79 | "eslint": "7.32.0", 80 | "jsdoc": "^4.0.2", 81 | "jsdoc-babel": "~0.5.0", 82 | "rimraf": "3.0.2" 83 | }, 84 | "dependencies": { 85 | "@graphistry/falcor-json-graph": "^2.9.10", 86 | "@graphistry/falcor-model-rxjs": "2.11.0", 87 | "@graphistry/falcor-socket-datasource": "2.11.3", 88 | "@graphistry/js-upload-api": "^5.1.6", 89 | "shallowequal": "1.1.0" 90 | }, 91 | "peerDependencies": { 92 | "rxjs": ">=7.2.0" 93 | }, 94 | "gitHead": "949ca04aa87b399e0185c64d7a6810780a841deb" 95 | } 96 | -------------------------------------------------------------------------------- /projects/client-api/src/rxjs.js: -------------------------------------------------------------------------------- 1 | import { 2 | AsyncSubject, 3 | catchError, 4 | forkJoin, 5 | fromEvent, 6 | of, 7 | Observable, 8 | pipe, 9 | retryWhen, 10 | ReplaySubject, 11 | BehaviorSubject, 12 | Subject, 13 | timer, 14 | finalize, 15 | throwError 16 | } from 'rxjs'; 17 | 18 | import { ajax } from 'rxjs/ajax'; 19 | 20 | import { 21 | concatMap, 22 | delay, 23 | distinctUntilChanged, 24 | filter, 25 | first, 26 | isEmpty, 27 | last, 28 | map, 29 | mergeMap, 30 | mergeAll, 31 | pairwise, 32 | scan, 33 | share, 34 | shareReplay, 35 | startWith, 36 | switchMap, 37 | take, 38 | takeLast, 39 | tap 40 | } from 'rxjs/operators'; 41 | 42 | 43 | // ////////////////////////////////////////////////////////////////////////////// 44 | export { 45 | ajax, 46 | AsyncSubject, 47 | catchError, 48 | concatMap, 49 | delay, 50 | distinctUntilChanged, 51 | filter, 52 | first, 53 | forkJoin, 54 | fromEvent, 55 | isEmpty, 56 | last, 57 | map, 58 | mergeMap, 59 | mergeAll, 60 | Observable, 61 | of, 62 | pairwise, 63 | pipe, 64 | ReplaySubject, 65 | retryWhen, 66 | scan, 67 | share, 68 | shareReplay, 69 | BehaviorSubject, 70 | startWith, 71 | Subject, 72 | switchMap, 73 | take, 74 | takeLast, 75 | tap, 76 | timer, 77 | finalize, 78 | throwError 79 | }; 80 | 81 | -------------------------------------------------------------------------------- /projects/cra-template/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/env", ["@babel/preset-react", {"runtime": "automatic"}]] 3 | } -------------------------------------------------------------------------------- /projects/cra-template/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /projects/cra-template/README.md: -------------------------------------------------------------------------------- 1 | # Graphistry sample project 2 | 3 | CRA-based sample app that embeds Graphistry (React) 4 | 5 | ```bash 6 | npm i 7 | npm update && npm run build 8 | ``` -------------------------------------------------------------------------------- /projects/cra-template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@graphistry/cra-template", 3 | "version": "5.1.6", 4 | "private": true, 5 | "main": "dist/index.cjs.js", 6 | "module": "dist/index.esm.js", 7 | "source": "src/index.js", 8 | "files": [ 9 | "dist", 10 | "README.md" 11 | ], 12 | "dependencies": { 13 | "@testing-library/jest-dom": "^5.16.4", 14 | "@testing-library/react": "^12.1.5", 15 | "@testing-library/user-event": "^12.8.3" 16 | }, 17 | "peerDependencies": { 18 | "react": ">=16.8.6", 19 | "react-dom": ">=16.8.6", 20 | "react-scripts": "4.0.3" 21 | }, 22 | "scripts": { 23 | "start": "react-scripts start", 24 | "build:cra": "react-scripts build", 25 | "test": "react-scripts test", 26 | "eject": "react-scripts eject", 27 | "clean": "rm -rf dist && rm -rf node_modules && rm -rf .cache && rm -rf .next", 28 | "build": "rollup -c", 29 | "build:watch": "rollup -c -w", 30 | "start-playground": "cd playground && npm run start", 31 | "i-all": "npm i && cd playground && npm i", 32 | "dev": "npm-run-all --parallel build-watch start-playground", 33 | "lock:fix": "npm i --package-lock-only" 34 | }, 35 | "eslintConfig": { 36 | "extends": [ 37 | "react-app", 38 | "react-app/jest" 39 | ] 40 | }, 41 | "browserslist": { 42 | "production": [ 43 | ">0.2%", 44 | "not dead", 45 | "not op_mini all" 46 | ], 47 | "development": [ 48 | "last 1 chrome version", 49 | "last 1 firefox version", 50 | "last 1 safari version" 51 | ] 52 | }, 53 | "devDependencies": { 54 | "@babel/cli": "^7.17.0", 55 | "@babel/core": "^7.17.10", 56 | "@babel/preset-env": "^7.17.10", 57 | "@babel/preset-react": "^7.16.0", 58 | "@rollup/plugin-babel": "^5.3.1", 59 | "@rollup/plugin-commonjs": "^21.1.0", 60 | "@rollup/plugin-node-resolve": "^13.3.0", 61 | "cross-env": "^7.0.3", 62 | "npm-run-all": "^4.1.5", 63 | "rollup": "^2.72.1", 64 | "rollup-plugin-delete": "^2.0.0", 65 | "rollup-plugin-peer-deps-external": "^2.2.4" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /projects/cra-template/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphistry/graphistry-js/2192e0835456c180101d0be20296fee94a43be28/projects/cra-template/public/favicon.ico -------------------------------------------------------------------------------- /projects/cra-template/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /projects/cra-template/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphistry/graphistry-js/2192e0835456c180101d0be20296fee94a43be28/projects/cra-template/public/logo192.png -------------------------------------------------------------------------------- /projects/cra-template/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphistry/graphistry-js/2192e0835456c180101d0be20296fee94a43be28/projects/cra-template/public/logo512.png -------------------------------------------------------------------------------- /projects/cra-template/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /projects/cra-template/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /projects/cra-template/rollup.config.js: -------------------------------------------------------------------------------- 1 | import { babel } from '@rollup/plugin-babel'; 2 | import external from 'rollup-plugin-peer-deps-external'; 3 | import del from 'rollup-plugin-delete'; 4 | import pkg from './package.json'; 5 | import commonjs from '@rollup/plugin-commonjs'; 6 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 7 | 8 | const globals = { 9 | 'react': 'React', 10 | 'react-dom': 'ReactDOM', 11 | 'prop-types': 'PropTypes' 12 | }; 13 | const name = "CRATemplate"; 14 | 15 | export default { 16 | input: pkg.source, 17 | output: [ 18 | { globals, sourcemap: true, file: 'dist/index.cjs.js', format: 'cjs' }, 19 | { globals, sourcemap: true, file: 'dist/index.esm.js', format: 'esm' }, 20 | { globals, sourcemap: true, file: 'dist/index.iife.js', format: 'iife', name }, 21 | { globals, sourcemap: true, file: 'dist/index.umd.js', format: 'umd', name } 22 | ], 23 | plugins: [ 24 | nodeResolve({ 25 | extensions: [".js"], 26 | }), 27 | external(), 28 | babel({ 29 | exclude: 'node_modules/**', 30 | babelHelpers: 'bundled' 31 | }), 32 | commonjs(), 33 | del({ targets: ['dist/*'] }), 34 | ], 35 | external: Object.keys(pkg.peerDependencies || {}), 36 | }; -------------------------------------------------------------------------------- /projects/cra-template/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /projects/cra-template/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useRef, useState, useEffect, useCallback } from 'react' 2 | 3 | window.ReactComponent = React; 4 | 5 | function App() { 6 | const [count, setCount] = useState(0); // FIXME: https://reactjs.org/link/invalid-hook-call 7 | console.log('cra-template app::render', {React}); 8 | return ( 9 |
10 |
11 |

12 | My component! 13 |

14 | 20 | Learn React 21 | 22 |
23 |
24 | ); 25 | } 26 | 27 | export { App }; 28 | -------------------------------------------------------------------------------- /projects/cra-template/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /projects/cra-template/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /projects/cra-template/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | //import './index.css'; 4 | import { App } from './App'; 5 | 6 | console.log('cra-template', {React, ReactDOM}); 7 | /* 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ); 14 | */ 15 | 16 | export { App }; -------------------------------------------------------------------------------- /projects/cra-template/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /projects/cra-template/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /projects/cra-test-18/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /projects/cra-test-18/README.md: -------------------------------------------------------------------------------- 1 | # Graphistry in a standard React project 2 | 3 | Mirrors React 17 [Graphistry in a standard React project](../cra-test/README.md) under React 18 -------------------------------------------------------------------------------- /projects/cra-test-18/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test18", 3 | "version": "5.1.6", 4 | "private": true, 5 | "dependencies": { 6 | "@graphistry/client-api-react": "^5.1.6", 7 | "@testing-library/jest-dom": "^5.16.4", 8 | "@testing-library/react": "^13.2.0", 9 | "@testing-library/user-event": "^13.5.0", 10 | "crypto-browserify": "3.12.0", 11 | "react": "^18.1.0", 12 | "react-dom": "^18.1.0", 13 | "react-scripts": "5.0.1", 14 | "stream-browserify": "3.0.0", 15 | "web-vitals": "^2.1.4" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browser": { 30 | "crypto": "crypto-browserify", 31 | "stream": "stream-browserify" 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /projects/cra-test-18/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphistry/graphistry-js/2192e0835456c180101d0be20296fee94a43be28/projects/cra-test-18/public/favicon.ico -------------------------------------------------------------------------------- /projects/cra-test-18/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /projects/cra-test-18/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphistry/graphistry-js/2192e0835456c180101d0be20296fee94a43be28/projects/cra-test-18/public/logo192.png -------------------------------------------------------------------------------- /projects/cra-test-18/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphistry/graphistry-js/2192e0835456c180101d0be20296fee94a43be28/projects/cra-test-18/public/logo512.png -------------------------------------------------------------------------------- /projects/cra-test-18/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /projects/cra-test-18/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /projects/cra-test-18/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /projects/cra-test-18/src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | 3 | import { Graphistry } from '@graphistry/client-api-react'; 4 | 5 | function App() { 6 | return ( 7 |
8 |
9 |

Testing Graphistry

10 | 11 |
12 |
13 | ); 14 | } 15 | 16 | export default App; 17 | -------------------------------------------------------------------------------- /projects/cra-test-18/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /projects/cra-test-18/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /projects/cra-test-18/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /projects/cra-test-18/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /projects/cra-test-18/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /projects/cra-test-18/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /projects/cra-test/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /projects/cra-test/DEVELOP.md: -------------------------------------------------------------------------------- 1 | # Develop 2 | 3 | For local testing, it can help to change the sample project as follows: 4 | 5 | 6 | ## Install 7 | 8 | Link to local `@graphistry/client-api-react`: 9 | 10 | ```bash 11 | lerna bootstrap 12 | ``` 13 | 14 | ## Conflict testing: 15 | 16 | * Add `@graphistry/cra-template` to `cra-test`'s `package.json` and run `lerna bootstrap` again 17 | 18 | * Add the following to your `app.js`: 19 | 20 | ```javascript 21 | import { App as Component } from '@graphistry/cra-template'; 22 | window.ReactApp = React; 23 | console.log('Component', {Component}) 24 | console.debug('same React?', { 25 | RApp: window.ReactApp, 26 | RComp: window.ReactComponent, 27 | same: window.ReactApp === window.ReactComponent.Component 28 | }); 29 | 30 | 31 | ``` 32 | -------------------------------------------------------------------------------- /projects/cra-test/README.md: -------------------------------------------------------------------------------- 1 | # Graphistry in a standard React project 2 | 3 | This demo modifies a standard [Create React App](https://create-react-app.dev/) project to use Graphistry: 4 | 5 | ## React 17 vs 18 6 | 7 | This project uses React 17; see its [sibling React 18 project](../cra-test-18/README.md) for React 18 8 | 9 | ## Try 10 | 11 | ```bash 12 | npm i 13 | npm run start 14 | ``` 15 | 16 | A browser window should launch that loads a React page that embeds a visualization from Graphistry Hub 17 | 18 | ## Key files 19 | 20 | * [package.json](package.json) for installation 21 | * [src/index.js](src/index.js) for global CSS import 22 | * [src/App.js](src/App.js) for `` component import and sample use 23 | 24 | 25 | ## Advanced imports 26 | 27 | See also [@graphistry/client-api-react versions](../client-api-react/README.md#versions) discussion for using different bundle formats published in every GraphistryJS release 28 | 29 | ## Contribute or experiment locally 30 | 31 | See also [DEVELOP.md](DEVELOP.md) -------------------------------------------------------------------------------- /projects/cra-test/craco.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | module.exports = { 3 | webpack: { 4 | alias: { 5 | //https://reactjs.org/link/invalid-hook-call 6 | //https://github.com/facebook/react/issues/13991#issuecomment-435587809 7 | react: path.resolve('./node_modules/react') 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /projects/cra-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@graphistry/client-react-app-cra-test", 3 | "version": "5.1.6", 4 | "private": true, 5 | "dependencies": { 6 | "@craco/craco": "^6.4.2", 7 | "@graphistry/client-api-react": "^5.1.6", 8 | "@testing-library/jest-dom": "^5.16.4", 9 | "@testing-library/react": "^12.1.5", 10 | "@testing-library/user-event": "^14.2.0", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "react-markdown": "^8.0.5", 14 | "react-scripts": "4.0.3", 15 | "web-vitals": "^2.1.4" 16 | }, 17 | "scripts": { 18 | "start": "craco start", 19 | "build": "craco build", 20 | "test": "craco test", 21 | "eject": "react-scripts eject", 22 | "lock:fix": "npm i --package-lock-only" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "react-app", 27 | "react-app/jest" 28 | ], 29 | "rules": { 30 | "react/jsx-uses-react": "off", 31 | "react/react-in-jsx-scope": "off" 32 | } 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.2%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /projects/cra-test/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphistry/graphistry-js/2192e0835456c180101d0be20296fee94a43be28/projects/cra-test/public/favicon.ico -------------------------------------------------------------------------------- /projects/cra-test/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /projects/cra-test/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphistry/graphistry-js/2192e0835456c180101d0be20296fee94a43be28/projects/cra-test/public/logo192.png -------------------------------------------------------------------------------- /projects/cra-test/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphistry/graphistry-js/2192e0835456c180101d0be20296fee94a43be28/projects/cra-test/public/logo512.png -------------------------------------------------------------------------------- /projects/cra-test/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /projects/cra-test/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /projects/cra-test/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | display: flex; 3 | flex-direction: column; 4 | background-color: #282c34; 5 | height: 100%; 6 | width: 100%; 7 | } 8 | 9 | .App-header { 10 | display: flex; 11 | flex-direction: row; 12 | align-items: center; 13 | justify-content: center; 14 | gap: 0.5em; 15 | font-size: 2em; 16 | margin: 0.5em; 17 | color: white; 18 | } 19 | 20 | .Content { 21 | flex-grow: 1; 22 | position: relative; 23 | } 24 | 25 | .graphistry-container { 26 | width: 100%; 27 | height: 100%; 28 | } 29 | 30 | 31 | .sb { 32 | position: absolute; 33 | left: -3px; 34 | top: 120px; 35 | width: 300px; 36 | height: 500px; 37 | max-height: calc(100% - 280px); 38 | border-radius: 4px; 39 | margin: 12px; 40 | box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px; 41 | transition: width 0.1s, height 0.3s; 42 | overflow: hidden; 43 | } 44 | 45 | .sb-closed { 46 | position: absolute; 47 | left: -3px; 48 | top: 120px; 49 | width: 24px; 50 | height: 24px; 51 | border-radius: 4px; 52 | margin: 12px; 53 | box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px; 54 | transition: width 0.3s, height 0.1s; 55 | overflow: hidden; 56 | } 57 | 58 | .sb-toggle { 59 | position: absolute; 60 | top: 0; 61 | left: 0; 62 | width: 24px; 63 | height: 24px; 64 | cursor: pointer; 65 | background-color: white; 66 | border-radius: 4px; 67 | } 68 | 69 | .sb-body { 70 | position: absolute; 71 | top: 0; 72 | left: 0; 73 | width: 300px; 74 | height: 100%; 75 | min-height: calc(100% - 280px); 76 | border-radius: 4px; 77 | overflow: scroll; 78 | border: none; 79 | background-color: white; 80 | 81 | display: flex; 82 | flex-direction: column; 83 | overflow-y: scroll; 84 | } 85 | 86 | .sb-form { 87 | width: 100%; 88 | display: flex; 89 | flex-direction: column; 90 | } 91 | 92 | .sb-result { 93 | flex-grow: 1; 94 | padding: 16px; 95 | overflow-wrap: break-word; 96 | } 97 | 98 | .sb-frame { 99 | width: 100%; 100 | height: 100%; 101 | border: none; 102 | } 103 | 104 | .sb-label { 105 | align-self: flex-end; 106 | font-size: 8px; 107 | color:rgba(0, 0, 0, 0.6); 108 | padding-bottom: 4px; 109 | } 110 | 111 | .select { 112 | height: 18px; 113 | margin: 2px; 114 | color:rgba(0, 0, 0, 0.6); 115 | border-color:rgba(0, 0, 0, 0.2); 116 | border-radius: 4px; 117 | outline: none; 118 | } 119 | 120 | .select:focus { 121 | outline: none; 122 | border-color:rgba(0, 0, 0, 0.6); 123 | } 124 | 125 | .query-input { 126 | border: none; 127 | padding: 8px 0px 4px 0px; 128 | margin: 18px 16px 4px; 129 | border-bottom: solid 1px #ccc; 130 | } 131 | 132 | .query-input:focus { 133 | outline: none; 134 | border-color: #888; 135 | } 136 | 137 | .sb-submit-bar { 138 | display: flex; 139 | margin: 0 12px 12px; 140 | align-items: center; 141 | } 142 | 143 | .sb-flex-grow { 144 | flex-grow: 1; 145 | } 146 | 147 | .sb-icon-button { 148 | cursor: pointer; 149 | fill: rgba(0, 0, 0, 0.4); 150 | } 151 | 152 | .sb-icon-button:hover { 153 | fill: rgba(0, 0, 0, 0.6); 154 | } 155 | 156 | .sb-footer { 157 | color:rgba(0, 0, 0, 0.4); 158 | font-size: 8px; 159 | padding: 4px; 160 | overflow-wrap: break-word; 161 | display: flex; 162 | flex-direction: row; 163 | justify-content: space-between; 164 | align-items: flex-end; 165 | } 166 | 167 | .key-input-label { 168 | cursor: pointer; 169 | } 170 | 171 | .alert { 172 | color: red; 173 | } -------------------------------------------------------------------------------- /projects/cra-test/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useState, useRef } from 'react'; 2 | import './App.css'; 3 | import { Graphistry } from '@graphistry/client-api-react'; 4 | import SidebarSelection from './SidebarSelection'; 5 | 6 | console.debug('app', { Graphistry, React }); 7 | 8 | const LOCAL_DEV = { 9 | graphistryHost: "http://0.0.0.0:3000", 10 | play: 0, 11 | session: "cycle" 12 | } 13 | 14 | // If this is inline it causes graphistry to refresh. 15 | const IFRAME_STYLE = { height: '100%', width: '100%', border: 0 }; 16 | const SELECTION_API_OPTIONS = { pageSize: 10, withColumns: true }; 17 | 18 | function App() { 19 | const graphistryRef = useRef(); 20 | const [selection, setSelection] = useState(undefined); 21 | const [inputSelection, setInputSelection] = useState('{ "point": [], "edge": [0] }'); 22 | const [show, setShow] = useState(false); 23 | 24 | const onUpdateObservableG = useCallback((err, v) => { 25 | console.info('onUpdateObservableG returned', v, '@CRA') 26 | }, []); 27 | 28 | const onSelectionUpdate = useCallback((err, v) => { 29 | setSelection(v); 30 | console.info('onSelectionUpdate returned', v, err, '@CRA') 31 | }, []); 32 | 33 | const onLabelsUpdate = useCallback((err, v) => { 34 | console.info('onLabelsUpdate returned', v, err, '@CRA') 35 | }, []); 36 | 37 | const injectSelection = () => { 38 | const s = JSON.parse(inputSelection); 39 | console.log('Attempting to set selection to ', s, '@CRA'); 40 | graphistryRef.current.setSelectionExternal(s); 41 | } 42 | 43 | const injectHighlight = () => { 44 | const s = JSON.parse(inputSelection); 45 | console.log('Attempting to set highlight to ', s, '@CRA'); 46 | graphistryRef.current.setHighlightExternal(s); 47 | } 48 | 49 | const callAddFilter = () => { 50 | graphistryRef.current.addFilter('point:degree > 0'); 51 | } 52 | 53 | return ( 54 |
55 |
56 | Embeded Graphistry 57 | 58 | 59 | setInputSelection(e.target.value)}/> 60 | 61 | 62 |
63 |
64 | 75 | 76 |
77 |
78 | ); 79 | } 80 | 81 | export default App; 82 | -------------------------------------------------------------------------------- /projects/cra-test/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /projects/cra-test/src/SidebarSelection.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import "./App.css"; 3 | import ReactMarkdown from 'react-markdown' 4 | 5 | const NODE_LABELS = { 6 | 0: "Myriel", 1: "Napoleon", 2: "Mlle Baptistine", 3: "Mme Magloire", 4: "Countess DeLo", 5: "Geborand", 7 | 6: "Champtercier", 7: "Cravatte", 8: "Count", 9: "OldMan", 10: "Labarre", 11: "Valjean", 12: "Marguerite", 8 | 13: "Mme DeR", 14: "Isabeau", 15: "Gervais", 16: "Tholomyes", 17: "Listolier", 18: "Fameuil", 19: "Blacheville", 9 | 20: "Favourite", 21: "Dahlia", 22: "Zephine", 23: "Fantine", 24: "Mme Thenardier", 25: "Thenardier", 26: "Cosette", 10 | 27: "Javert", 28: "Fauchelevent", 29: "Bamatabois", 30: "Perpetue", 31: "Simplice", 32: "Scaufflaire", 11 | 33: "Woman1", 34: "Judge", 35: "Champmathieu", 36: "Brevet", 37: "Chenildieu", 38: "Cochepaille", 12 | 39: "Pontmercy", 40: "Boulatruelle", 41: "Eponine", 42: "Anzelma", 43: "Woman2", 44: "Mother Innocent", 13 | 45: "Gribier", 46: "Jondrette", 47: "Mme Burgon", 48: "Gavroche", 49: "Gillenormand", 50: "Magnon", 14 | 51: "Mlle Gillenormand", 52: "Mme Pontmercy", 53: "Mlle Vaubois", 54: "Lt Gillenormand", 55: "Marius", 15 | 56: "BaronessT", 57: "Mabeuf", 58: "Enjolras", 59: "Combeferre", 60: "Prouvaire", 61: "Feuilly", 16 | 62: "Courfeyrac", 63: "Bahorel", 64: "Bossuet", 65: "Joly", 66: "Grantaire", 67: "Mother Plutarch", 17 | 68: "Gueulemer", 69: "Babet", 70: "Claquesous", 71: "Montparnasse", 72: "Toussaint", 73: "Child1", 18 | 74: "Child2", 75: "Brujon", 76: "Mme Hucheloup" 19 | }; 20 | 21 | 22 | const PREFIX = `Major themes and narritive structure of Les Miserables 23 | `; 24 | 25 | 26 | const CONTEXTS = { 27 | relationship: "The relationships of $?", 28 | scene: "Scenewise analysis of $?", 29 | } 30 | 31 | const formatList = a => a.length ? a.reduce((a, b, i, array) => a + (i < array.length - 1 ? ', ' : ' and ') + b) : ''; 32 | 33 | const ChatIcon = ({c, ...rest}) => ( 34 | 35 | 36 | 37 | ) 38 | 39 | const UpdateIcon = ({c, ...rest}) => ( 40 | 41 | 42 | 43 | ) 44 | 45 | const SubmitIcon = ({c, ...rest}) => ( 46 | 47 | 48 | 49 | ) 50 | 51 | 52 | const Select = ({ options, value, onChange, className }) => ( 53 | 58 | ) 59 | 60 | const QueryInput = ({ query, onChange, onEditingChange }) => ( 61 |