├── .github
└── workflows
│ ├── cd.yml
│ ├── ci.yml
│ ├── cicd.yml
│ ├── doc.yml
│ └── init.yml
├── .gitignore
├── .prettierignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs
├── assets
│ ├── api-overview.svg
│ ├── favicon.svg
│ ├── javascripts
│ │ ├── analytics
│ │ │ └── head.js
│ │ ├── csv2js.js
│ │ ├── extlinks.js
│ │ ├── highlight.js
│ │ └── thumbs.js
│ ├── logo-white.svg
│ ├── readme-example.gif
│ ├── stylesheets
│ │ ├── gallery.css
│ │ ├── highlight.css
│ │ └── vizzu.css
│ └── vizzu-story.gif
├── dev
│ └── index.md
├── examples
│ ├── _basic.md
│ ├── _basic.png
│ ├── _basic
│ │ └── main.js
│ ├── linkedinpoll.md
│ ├── linkedinpoll.png
│ ├── linkedinpoll
│ │ ├── linkedinpoll.csv
│ │ └── main.js
│ ├── population.md
│ ├── population.png
│ ├── population
│ │ ├── main.js
│ │ └── population.csv
│ ├── proglangs.md
│ ├── proglangs.png
│ ├── proglangs
│ │ ├── main.js
│ │ └── proglangs.csv
│ ├── titanic.md
│ ├── titanic.png
│ ├── titanic
│ │ ├── main.js
│ │ └── titanic.csv
│ ├── trumptwitter.md
│ ├── trumptwitter.png
│ ├── trumptwitter
│ │ ├── main.js
│ │ └── trumptwitter.csv
│ ├── usbudget.md
│ ├── usbudget.png
│ └── usbudget
│ │ ├── main.js
│ │ └── usbudget.csv
├── installation.md
├── reference
│ └── README.md
└── tutorial
│ ├── building_blocks.md
│ ├── data.md
│ ├── index.md
│ └── initialization.md
├── eslint.config.mjs
├── package-lock.json
├── package.json
├── pdm.lock
├── pyproject.toml
├── rollup.config.cjs
├── src
├── AnimationQueue.js
├── controllers
│ └── slider.js
├── example
│ ├── data.js
│ ├── index.html
│ ├── index.js
│ ├── loadinganim.svg
│ └── style.js
├── vizzu-controller.js
├── vizzu-player.js
└── vizzu-story.d.ts
├── tests
├── assets
│ ├── chart-params
│ │ ├── animOptions.js
│ │ ├── config.js
│ │ ├── data.js
│ │ ├── filter.js
│ │ └── style.js
│ ├── data-sets
│ │ ├── basic.mjs
│ │ ├── nolabel.mjs
│ │ └── olympics.mjs
│ ├── data-tests
│ │ └── label
│ │ │ ├── basic.cjs
│ │ │ ├── index.cjs
│ │ │ ├── nolabel.cjs
│ │ │ └── olympics.cjs
│ ├── mocks
│ │ ├── vizzu-attribute.js
│ │ ├── vizzu-cdn.js
│ │ ├── vizzu-window.js
│ │ └── vizzu.js
│ └── slides
│ │ ├── asset-functions.js
│ │ ├── more-slides.js
│ │ ├── one-slide-more-steps.js
│ │ ├── one-slide-one-empty-step.js
│ │ ├── one-slide-one-step.js
│ │ └── zero-slide.js
└── vizzu-player
│ ├── e2e
│ ├── jest.config.cjs
│ └── vp.data.label.test.cjs
│ └── unit
│ ├── jest.config.js
│ ├── vp.animationqueue.test.js
│ ├── vp.attributes.test.js
│ └── vp.slides.test.js
└── tools
├── ci
├── markdown_format.py
├── std_check.py
└── version.py
├── docs
├── config.py
├── deploy.py
├── examples
│ └── gen_examples.py
├── mkdocs.yml
├── overrides
│ ├── main.html
│ └── mike
│ │ └── redirect.html
├── pages
│ └── gen_pages.py
└── reference
│ ├── gen_reference.cjs
│ ├── gen_reference.py
│ └── tsconfig.json
└── modules
├── node.py
└── vizzu.py
/.github/workflows/cd.yml:
--------------------------------------------------------------------------------
1 | name: CD
2 |
3 | on:
4 | release:
5 | types: [published]
6 | workflow_call:
7 |
8 | jobs:
9 | publish:
10 | if: ${{ (github.event_name == 'release' && github.event.action == 'published') }}
11 |
12 | runs-on: ubuntu-24.04
13 |
14 | steps:
15 | - name: Checkout repo
16 | uses: actions/checkout@v4
17 | with:
18 | fetch-depth: 1
19 |
20 | - name: Set up Node.js
21 | uses: actions/setup-node@v4
22 | with:
23 | node-version: '22'
24 | cache: 'npm'
25 | cache-dependency-path: 'package-lock.json'
26 |
27 | - name: Set up npm
28 | run: |
29 | npm ci
30 |
31 | - name: Get workflow ID
32 | id: workflow_id
33 | run: |
34 | workflow_data=$(curl -s -X GET \
35 | -H "Accept: application/vnd.github.v3+json" \
36 | -H "Authorization: Bearer ${{ secrets.VIZZUHQ_GITHUB_API }}" \
37 | "https://api.github.com/repos/$GITHUB_REPOSITORY/actions/workflows")
38 | workflow_id=$(echo $workflow_data | jq -r '.workflows[] | select(.name == "CI-CD") | .id')
39 | echo "workflow_id=${workflow_id}" >> $GITHUB_OUTPUT
40 |
41 | - name: Get run ID
42 | id: run_id
43 | run: |
44 | run_data=$(curl -s -X GET \
45 | -H "Accept: application/vnd.github.v3+json" \
46 | -H "Authorization: Bearer ${{ secrets.VIZZUHQ_GITHUB_API }}" \
47 | "https://api.github.com/repos/$GITHUB_REPOSITORY/actions/workflows/${{ steps.workflow_id.outputs.workflow_id }}/runs?branch=main")
48 | run_id=$(echo $run_data | jq -r '.workflow_runs[0].id')
49 | echo "run_id=${run_id}" >> $GITHUB_OUTPUT
50 |
51 | - name: Cache package
52 | uses: actions/cache@v4
53 | with:
54 | path: |
55 | README.md
56 | build/
57 | dist/
58 | key: package_${{ steps.run_id.outputs.run_id }}
59 |
60 | - name: Publish package
61 | run: |
62 | npm config set registry=https://registry.npmjs.org/
63 | npm config set //registry.npmjs.org/:_authToken=${NPM_API_TOKEN}
64 | npm publish
65 | env:
66 | NPM_API_TOKEN: ${{ secrets.NPM_API_TOKEN }}
67 |
68 | - name: Upload package
69 | run: |
70 | echo ${{ secrets.GITHUB_TOKEN }} | gh auth login --with-token
71 | gh release upload ${{ github.event.release.tag_name }} build/*tgz --clobber
72 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | workflow_call:
5 |
6 | jobs:
7 | check_src:
8 | runs-on: ubuntu-24.04
9 |
10 | env:
11 | PUPPETEER_CACHE_DIR: ${{ github.workspace }}/node_modules/.chromium
12 |
13 | steps:
14 | - name: Checkout repo
15 | uses: actions/checkout@v4
16 | with:
17 | fetch-depth: 1
18 |
19 | - name: Set up Node.js
20 | uses: actions/setup-node@v4
21 | with:
22 | node-version: '22'
23 | cache: 'npm'
24 | cache-dependency-path: 'package-lock.json'
25 |
26 | - name: Set up npm
27 | run: |
28 | npm ci
29 |
30 | - name: Format
31 | run: |
32 | npm run format:src
33 |
34 | - name: Lint
35 | run: |
36 | npm run lint:src
37 |
38 | - name: Type
39 | run: |
40 | npm run type:src
41 |
42 | - name: Test
43 | run: |
44 | npm test
45 |
46 | check_docs:
47 | runs-on: ubuntu-24.04
48 |
49 | steps:
50 | - name: Checkout repo
51 | uses: actions/checkout@v4
52 | with:
53 | fetch-depth: 1
54 |
55 | - name: Set up Node.js
56 | uses: actions/setup-node@v4
57 | with:
58 | node-version: '22'
59 | cache: 'npm'
60 | cache-dependency-path: 'package-lock.json'
61 |
62 | - name: Set up npm
63 | run: |
64 | npm ci
65 |
66 | - name: Set up Python
67 | uses: actions/setup-python@v5
68 | with:
69 | python-version: '3.13'
70 |
71 | - name: Cache venv
72 | uses: actions/cache@v4
73 | with:
74 | path: .venv
75 | key: venv_ubuntu24_${{ hashFiles('pdm.lock') }}
76 |
77 | - name: Format
78 | run: |
79 | source .venv/bin/activate
80 | npm run format:docs
81 |
82 | - name: Lint
83 | run: |
84 | source .venv/bin/activate
85 | npm run lint:docs
86 |
87 | check_tools:
88 | runs-on: ubuntu-24.04
89 |
90 | steps:
91 | - name: Checkout repo
92 | uses: actions/checkout@v4
93 | with:
94 | fetch-depth: 1
95 |
96 | - name: Set up Node.js
97 | uses: actions/setup-node@v4
98 | with:
99 | node-version: '22'
100 | cache: 'npm'
101 | cache-dependency-path: 'package-lock.json'
102 |
103 | - name: Set up npm
104 | run: |
105 | npm ci
106 |
107 | - name: Set up Python
108 | uses: actions/setup-python@v5
109 | with:
110 | python-version: '3.13'
111 |
112 | - name: Cache venv
113 | uses: actions/cache@v4
114 | with:
115 | path: .venv
116 | key: venv_ubuntu24_${{ hashFiles('pdm.lock') }}
117 |
118 | - name: Format
119 | run: |
120 | source .venv/bin/activate
121 | npm run format:tools
122 |
123 | - name: Lint
124 | run: |
125 | source .venv/bin/activate
126 | npm run lint:tools
127 |
128 | - name: Type
129 | run: |
130 | source .venv/bin/activate
131 | npm run type:tools
132 |
--------------------------------------------------------------------------------
/.github/workflows/cicd.yml:
--------------------------------------------------------------------------------
1 | name: CI-CD
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 |
9 | jobs:
10 | init:
11 | uses: ./.github/workflows/init.yml
12 |
13 | ci:
14 | uses: ./.github/workflows/ci.yml
15 | needs: init
16 |
17 | doc:
18 | uses: ./.github/workflows/doc.yml
19 | needs: ci
20 |
21 | cd:
22 | uses: ./.github/workflows/cd.yml
23 | secrets: inherit
24 | needs: doc
25 |
--------------------------------------------------------------------------------
/.github/workflows/doc.yml:
--------------------------------------------------------------------------------
1 | name: Doc
2 |
3 | on:
4 | workflow_dispatch:
5 | release:
6 | types: [published]
7 | workflow_call:
8 |
9 | jobs:
10 | build:
11 | if: ${{ !((github.event_name == 'release' && github.event.action == 'published') || github.event_name == 'workflow_dispatch') }}
12 |
13 | runs-on: ubuntu-24.04
14 |
15 | steps:
16 | - name: Checkout repo
17 | uses: actions/checkout@v4
18 | with:
19 | fetch-depth: 1
20 |
21 | - name: Set up Node.js
22 | uses: actions/setup-node@v4
23 | with:
24 | node-version: '22'
25 | cache: 'npm'
26 | cache-dependency-path: 'package-lock.json'
27 |
28 | - name: Set up npm
29 | run: |
30 | npm ci
31 |
32 | - name: Set up Python
33 | uses: actions/setup-python@v5
34 | with:
35 | python-version: '3.13'
36 |
37 | - name: Cache venv
38 | uses: actions/cache@v4
39 | with:
40 | path: .venv
41 | key: venv_ubuntu24_${{ hashFiles('pdm.lock') }}
42 |
43 | - name: Build documentation
44 | run: |
45 | source .venv/bin/activate
46 | npm run build-docs
47 |
48 | deploy:
49 | if: ${{ ((github.event_name == 'release' && github.event.action == 'published') || github.event_name == 'workflow_dispatch') }}
50 |
51 | runs-on: ubuntu-24.04
52 |
53 | steps:
54 | - name: Checkout repo
55 | uses: actions/checkout@v4
56 | with:
57 | fetch-depth: 1
58 |
59 | - name: Set up Node.js
60 | uses: actions/setup-node@v4
61 | with:
62 | node-version: '22'
63 | cache: 'npm'
64 | cache-dependency-path: 'package-lock.json'
65 |
66 | - name: Set up npm
67 | run: |
68 | npm ci
69 |
70 | - name: Set up Python
71 | uses: actions/setup-python@v5
72 | with:
73 | python-version: '3.13'
74 |
75 | - name: Cache venv
76 | uses: actions/cache@v4
77 | with:
78 | path: .venv
79 | key: venv_ubuntu24_${{ hashFiles('pdm.lock') }}
80 |
81 | - name: Configure Git
82 | run: |
83 | git config --global user.name "${{ secrets.VIZZUHQ_GITHUB_USER }}"
84 | git config --global user.email "${{ secrets.VIZZUHQ_GITHUB_EMAIL }}"
85 |
86 | - name: Deploy documentation
87 | run: |
88 | source .venv/bin/activate
89 | git fetch origin gh-pages || echo "gh-pages does not exist"
90 | npm run deploy-docs
91 | git push origin gh-pages
92 |
--------------------------------------------------------------------------------
/.github/workflows/init.yml:
--------------------------------------------------------------------------------
1 | name: Init
2 |
3 | on:
4 | workflow_call:
5 |
6 | jobs:
7 | init_ubuntu24:
8 | runs-on: ubuntu-24.04
9 |
10 | env:
11 | PUPPETEER_CACHE_DIR: ${{ github.workspace }}/node_modules/.chromium
12 |
13 | steps:
14 | - name: Checkout repo
15 | uses: actions/checkout@v4
16 | with:
17 | fetch-depth: 1
18 |
19 | - name: Set up Node.js
20 | uses: actions/setup-node@v4
21 | with:
22 | node-version: '22'
23 | cache: 'npm'
24 | cache-dependency-path: 'package-lock.json'
25 |
26 | - name: Set up npm
27 | run: |
28 | npm ci
29 |
30 | - name: Set up Python
31 | uses: actions/setup-python@v5
32 | with:
33 | python-version: '3.13'
34 |
35 | - name: Cache venv
36 | id: venv_ubuntu24
37 | uses: actions/cache@v4
38 | with:
39 | path: .venv
40 | key: venv_ubuntu24_${{ hashFiles('pdm.lock') }}
41 |
42 | - name: Set up venv
43 | if: steps.venv_ubuntu24.outputs.cache-hit != 'true'
44 | run: |
45 | python3.13 -m venv ".venv"
46 | source .venv/bin/activate
47 | pip install pdm==2.22.3
48 | pdm install
49 |
50 | - name: Build package
51 | run: |
52 | source .venv/bin/activate
53 | npm run build
54 |
55 | - name: Cache package
56 | uses: actions/cache@v4
57 | with:
58 | path: |
59 | README.md
60 | build/
61 | dist/
62 | key: package_${{ github.run_id }}
63 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 |
3 | build
4 | dist
5 |
6 | .coverage
7 |
8 | __pycache__
9 |
10 | .venv
11 | .pdm-python
12 |
13 | site
14 | node_modules
15 | tmp
16 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | *.md
2 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | See [Code of Conduct](https://lib.vizzuhq.com/latest/CODE_OF_CONDUCT/) of the
4 | `Vizzu` community.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Issues
4 |
5 | You can find our open issues in the project's
6 | [issue tracker](https://github.com/vizzuhq/vizzu-story-js/issues). Please let us
7 | know if you find any issues or have any feature requests there.
8 |
9 | ## Contributing
10 |
11 | If you want to contribute to the project, your help is very welcome. Just fork
12 | the project, make your changes and send us a pull request. You can find the
13 | detailed description of how to do this in
14 | [Github's guide to contributing to projects](https://docs.github.com/en/get-started/quickstart/contributing-to-projects).
15 |
16 | Our [Roadmap page](https://github.com/vizzuhq/.github/wiki/Roadmap) is a
17 | comprehensive list of tasks we want to do in the future. It is a good place to
18 | start if you want to contribute to `Vizzu`. In case you have something else in
19 | mind, that's awesome and we are very interested in hearing about it.
20 |
21 | ## CI-CD
22 |
23 | ### Development environment
24 |
25 | For contributing to the project, it is recommended to use `Node.js` `22`.
26 | However, for the documentation we are also using `Python`. If you plan to
27 | contribute to this part of the project, you will need `Python`, preferably
28 | version `3.13`.
29 |
30 | The following steps demonstrate how to set up the development environment on an
31 | `Ubuntu` `24.04` operating system. However, the process can be adapted for other
32 | operating systems as well.
33 |
34 | To start using the `Vizzu-Story` development environment, you need to install
35 | the development dependencies.
36 |
37 | ```sh
38 | npm install
39 | ```
40 |
41 | If you want to work with the documantation too, you need to set up the `Python`
42 | development environment.
43 |
44 | ```sh
45 | python3.13 -m venv ".venv"
46 | source .venv/bin/activate
47 | pip install pdm==2.22.3
48 | pdm install
49 | ```
50 |
51 | The development requirements are installed based on the `package-lock.json` and
52 | `pdm.lock` files. To update the development requirements, you can use the
53 | command `npm run lock`.
54 |
55 | **Note:** For all available `npm` scripts, run `npm run --list`.
56 |
57 | For better development practices, you can set up `pre-commit` and `pre-push`
58 | hooks in your local `Git` repository. The `pre-commit` hook will format the code
59 | automatically, and the `pre-push` hook will run the CI steps before pushing your
60 | changes.
61 |
62 | ```sh
63 | npx husky install
64 | ```
65 |
66 | **Note:** The provided `pre-commit` and `pre-push` hook configuration file is
67 | tailored for `Ubuntu` `24.04`. If you intend to use another operating system,
68 | you may need to create a custom configuration file suitable for that
69 | environment.
70 |
71 | ### CI
72 |
73 | The CI pipeline includes code formatting checks, code analysis, typing
74 | validation, and unit tests for the `Vizzu-Story` project.
75 |
76 | To run the entire CI pipeline, execute the following `npm` script:
77 |
78 | ```sh
79 | npm run ci
80 | ```
81 |
82 | However, if you want to run the CI steps on specific parts of the project, you
83 | can use the following scripts: `ci:src`, `ci:docs`, or `ci:tools`.
84 |
85 | #### Formatting
86 |
87 | You can check the code's formatting using the `format` script:
88 |
89 | ```sh
90 | npm run format
91 | ```
92 |
93 | If you need to fix any formatting issues, you can use the `fix-format` script:
94 |
95 | ```sh
96 | npm run fix-format
97 | ```
98 |
99 | If you wish to format specific parts of the project, you can use the following
100 | scripts: `format:src`, `format:docs`, `format:tools`, or `fix-format:src`,
101 | `fix-format:docs`, `fix-format:tools`.
102 |
103 | #### Code analyses
104 |
105 | To perform code analyses, you can use the `lint` script:
106 |
107 | ```sh
108 | npm run lint
109 | ```
110 |
111 | If you need to run code analyses for specific parts of the project, you can
112 | utilize the following scripts: `lint:src`, `lint:docs`, or `lint:tools`.
113 |
114 | #### Typing
115 |
116 | For type checking, you can use the `type` script:
117 |
118 | ```sh
119 | npm run type
120 | ```
121 |
122 | If you want to check specific parts of the project, you can use the following
123 | scripts: `type:src` or `type:tools`.
124 |
125 | #### Testing
126 |
127 | The project is tested using the `jest` testing framework. To run the tests, you
128 | can use the `test` script:
129 |
130 | ```sh
131 | npm test
132 | ```
133 |
134 | ### Documentation
135 |
136 | To build the documentation, you can use the `build-docs` script:
137 |
138 | ```sh
139 | npm run build-docs
140 | ```
141 |
142 | You can read the online version at
143 | [vizzu-story.vizzuhq.com](https://vizzu-story.vizzuhq.com/latest/).
144 |
145 | ### Release
146 |
147 | `Vizzu-Story` is distributed on
148 | [npm](https://www.npmjs.com/package/vizzu-story). **Note:** You need to be an
149 | administrator to release the project.
150 |
151 | To release `Vizzu-Story`, follow the steps below:
152 |
153 | - You should increase the version number in `package.json`. The version bump
154 | should be in a separated commit.
155 |
156 | - Generate the release notes and publish the new release on
157 | [Releases](https://github.com/vizzuhq/vizzu-story-js/releases).
158 |
159 | **Note:** Publishing a new release will automatically trigger the `cd` workflow
160 | which builds and uploads the `Vizzu-Story` package to
161 | [npm](https://www.npmjs.com/package/vizzu-story).
162 |
163 | Before making a release, you can build and check the package using the `build`
164 | script:
165 |
166 | ```sh
167 | npm run build
168 | ```
169 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Vizzu-Story - Build and present animated data stories
6 |
7 | Documentation
8 | · Examples
9 | · Code reference
10 | · Repository
11 | · Blog
12 |
13 |
14 |
15 | [](https://badge.fury.io/js/vizzu-story)
16 | [](https://packagephobia.com/result?p=vizzu-story)
17 |
18 | # Vizzu-Story
19 |
20 | ## About The Extension
21 |
22 | `Vizzu-Story` is an extension for the
23 | [Vizzu](https://github.com/vizzuhq/vizzu-lib) `JavaScript` library that allows
24 | users to create interactive presentations from the animated data visualizations
25 | built with `Vizzu`.
26 |
27 | The extension provides a `Web Component` that contains the presentation and adds
28 | controls for navigating between slides - predefined stages within the story.
29 |
30 | ## Installation
31 |
32 | Install via [npm](https://www.npmjs.com/package/vizzu-story):
33 |
34 | ```sh
35 | npm install vizzu-story
36 | ```
37 |
38 | Or use it from [CDN](https://www.jsdelivr.com/package/npm/vizzu-story):
39 |
40 | ```javascript
41 | import VizzuPlayer from 'https://cdn.jsdelivr.net/npm/vizzu-story@latest/dist/vizzu-story.min.js'
42 | ```
43 |
44 | ## Usage
45 |
46 | 
47 |
48 | Create a `vizzu-player` element that will contain the rendered story:
49 |
50 | ```
51 |
52 | ```
53 |
54 | In a script module element import the extension from `CDN` or local install:
55 |
56 | ```
57 |
61 | ```
62 |
63 | Add the underlying data for the data story. You can use the same data definition
64 | formats as in the `Vizzu` library, but you must add the entire data set for the
65 | whole story in the initial step; you can not change this later. See
66 | [Data chapter](https://vizzu-story.vizzuhq.com/latest/tutorial/data/) for more
67 | details on data formats.
68 |
69 | ```javascript
70 | const data = {
71 | series: [{
72 | name: 'Foo',
73 | values: ['Alice', 'Bob', 'Ted']
74 | }, {
75 | name: 'Bar',
76 | values: [15, 32, 12]
77 | }, {
78 | name: 'Baz',
79 | values: [5, 3, 2]
80 | }]
81 | }
82 | ```
83 |
84 | Create the data story by defining a sequence of slides. A slide can be a single
85 | chart corresponding to an [animate](https://lib.vizzuhq.com/latest/tutorial/)
86 | call from `Vizzu`. Or a slide can be a sequence of animation calls, in which
87 | case all of these animations will be played until the last one in the sequence,
88 | allowing for more complex transitions between slides.
89 |
90 | ```javascript
91 | const slides = [{
92 | config: {
93 | x: 'Foo',
94 | y: 'Bar'
95 | }
96 | }, {
97 | config: {
98 | color: 'Foo',
99 | x: 'Baz',
100 | geometry: 'circle'
101 | }
102 | }]
103 | ```
104 |
105 | Navigation controls beneath the chart will navigate between the slides. You can
106 | use the `PgUp` and `PgDn` buttons, left and right arrows to navigate between
107 | slides, and the `Home` and `End` buttons to jump to the first or last slide.
108 |
109 | On each chart, you can define the chart configuration and style with the same
110 | objects as in `Vizzu`. However, you can not modify the underlying data between
111 | the slides, only the data filter used.
112 |
113 | ```typescript
114 | interface Chart {
115 | config?: Vizzu.Config.Chart
116 | filter?: Vizzu.Data.FilterCallback | null
117 | style?: Vizzu.Styles.Chart
118 | animOptions?: Vizzu.Anim.Options
119 | }
120 | ```
121 |
122 | Put the data and the slide list into the `story` descriptor object. Here you can
123 | also set the `story` `style` property to set the chart style used for the whole
124 | `story`.
125 |
126 | ```javascript
127 | const story = {
128 | data: data,
129 | slides: slides
130 | }
131 | ```
132 |
133 | Then set up the created element with the configuration object:
134 |
135 | ```javascript
136 | const vp = document.querySelector('vizzu-player')
137 | vp.slides = story
138 | ```
139 |
140 | > [Check out a live example in JSFiddle!](https://jsfiddle.net/VizzuHQ/topcmuyf/3/)
141 |
142 | ## Documentation
143 |
144 | Visit our [Documentation site](https://vizzu-story.vizzuhq.com/latest/) for more
145 | details and a step-by-step tutorial into `Vizzu-Story` or check out our
146 | [Example gallery](https://vizzu-story.vizzuhq.com/latest/examples/).
147 |
148 | ## Contributing
149 |
150 | We welcome contributions to the project; visit our
151 | [Contributing guide](https://vizzu-story.vizzuhq.com/latest/CONTRIBUTING/) for
152 | further info.
153 |
154 | ## Contact
155 |
156 | - Join our Slack:
157 | [vizzu-community.slack.com](https://join.slack.com/t/vizzu-community/shared_invite/zt-w2nqhq44-2CCWL4o7qn2Ns1EFSf9kEg)
158 |
159 |
160 | - Drop us a line at hello@vizzu.io
161 |
162 | - Follow us on Twitter:
163 | [https://twitter.com/VizzuHQ](https://twitter.com/VizzuHQ)
164 |
165 | ## License
166 |
167 | Copyright © 2022-2025 [Vizzu Inc.](https://vizzuhq.com)
168 |
169 | Released under the
170 | [Apache 2.0 License](https://vizzu-story.vizzuhq.com/latest/LICENSE/).
171 |
--------------------------------------------------------------------------------
/docs/assets/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/assets/javascripts/analytics/head.js:
--------------------------------------------------------------------------------
1 | const plausibleBasic = document.createElement('script')
2 | plausibleBasic.setAttribute('defer', '')
3 | plausibleBasic.setAttribute('data-domain', 'vizzu-story.vizzuhq.com')
4 | plausibleBasic.src = 'https://plausible.io/js/script.outbound-links.js'
5 |
6 | const plausibleOutboundLinkTracking = document.createElement('script')
7 | plausibleOutboundLinkTracking.textContent =
8 | 'window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }'
9 |
10 | const documentHeadElement = document.getElementsByTagName('head')[0]
11 | documentHeadElement.appendChild(plausibleBasic)
12 | documentHeadElement.appendChild(plausibleOutboundLinkTracking)
13 |
--------------------------------------------------------------------------------
/docs/assets/javascripts/csv2js.js:
--------------------------------------------------------------------------------
1 | import * as d3 from 'https://cdn.jsdelivr.net/npm/d3@7/+esm'
2 |
3 | class Csv2Js {
4 | static csv(csv, options) {
5 | return new Promise((resolve, reject) => {
6 | if (!options) {
7 | options = {}
8 | }
9 | if (!options.dimensions) {
10 | options.dimensions = []
11 | }
12 | if (!options.measures) {
13 | options.measures = []
14 | }
15 | if (!options.units) {
16 | options.units = {}
17 | }
18 | const detectedDimensions = {}
19 | const data = { series: [], records: [] }
20 | const csvLoaded = d3.csv(csv)
21 |
22 | csvLoaded.then((csvData) => {
23 | for (let i = 0; i < csvData.length; i++) {
24 | const record = []
25 | const keys = Object.keys(csvData[i])
26 | for (const key of keys) {
27 | const numValue = +csvData[i][key]
28 | if (csvData[i][key] !== '' && !isNaN(numValue)) {
29 | record.push(numValue)
30 | } else {
31 | record.push(csvData[i][key])
32 | detectedDimensions[key] = true
33 | }
34 | }
35 | data.records.push(record)
36 | }
37 | for (let i = 0; i < csvData.columns.length; i++) {
38 | const key = csvData.columns[i]
39 | const series = {
40 | name: key,
41 | type:
42 | options.dimensions.includes(key) ||
43 | (detectedDimensions[key] && !options.measures.includes(key))
44 | ? 'dimension'
45 | : 'measure'
46 | }
47 | if (options.units[key]) {
48 | series.unit = options.units[key]
49 | }
50 | data.series.push(series)
51 | }
52 | return resolve(data)
53 | })
54 | })
55 | }
56 | }
57 |
58 | export default Csv2Js
59 |
--------------------------------------------------------------------------------
/docs/assets/javascripts/extlinks.js:
--------------------------------------------------------------------------------
1 | function changeTarget(links, target) {
2 | for (let i = 0; i < links.length; i++) {
3 | if (links[i].hostname !== window.location.hostname || links[i].href.includes('/assets/')) {
4 | links[i].target = target
5 | }
6 | }
7 | }
8 |
9 | document.addEventListener('DOMContentLoaded', (event) => {
10 | const target = '_blank'
11 | changeTarget(document.links, target)
12 |
13 | const iframe = document.getElementById('coviframe')
14 | if (iframe) {
15 | iframe.addEventListener('load', (event) => {
16 | changeTarget(iframe.contentWindow.document.getElementsByTagName('a'), target)
17 | })
18 | }
19 | })
20 |
--------------------------------------------------------------------------------
/docs/assets/javascripts/highlight.js:
--------------------------------------------------------------------------------
1 | document.addEventListener('DOMContentLoaded', (event) => {
2 | document.querySelectorAll('pre code').forEach((el) => {
3 | if (window.location.href.includes('LICENSE')) {
4 | return
5 | }
6 |
7 | hljs.highlightElement(el) // eslint-disable-line no-undef
8 | if (window.location.href.includes('/examples/')) {
9 | hljs.lineNumbersBlock(el) // eslint-disable-line no-undef
10 | }
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/docs/assets/javascripts/thumbs.js:
--------------------------------------------------------------------------------
1 | function setupVideos() {
2 | const videos = document.querySelectorAll('video.image-gallery')
3 | videos.forEach((video) => {
4 | const playPromise = video.play()
5 | if (playPromise !== undefined) {
6 | playPromise.then((_) => {
7 | const observer = new IntersectionObserver(
8 | (entries) => {
9 | entries.forEach((entry) => {
10 | if (entry.intersectionRatio !== 1 && !video.paused) {
11 | video.pause()
12 | } else if (video.paused) {
13 | video.play()
14 | }
15 | })
16 | },
17 | { threshold: 0.2 }
18 | )
19 | observer.observe(video)
20 | })
21 | }
22 | })
23 | }
24 |
25 | const currentScript = document.currentScript
26 | document.addEventListener('DOMContentLoaded', (event) => {
27 | const parentContainer = currentScript.nextElementSibling
28 | parentContainer.style.display = 'flex'
29 | parentContainer.style['flex-wrap'] = 'wrap'
30 | parentContainer.style.justifyContent = 'center'
31 | setupVideos()
32 | })
33 |
--------------------------------------------------------------------------------
/docs/assets/logo-white.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/assets/readme-example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vizzuhq/vizzu-story-js/d6c2c87f608015a66272b8d6c1056d403a4ccf86/docs/assets/readme-example.gif
--------------------------------------------------------------------------------
/docs/assets/stylesheets/gallery.css:
--------------------------------------------------------------------------------
1 | .image-gallery {
2 | max-width: 320px;
3 | border: 1px solid #ddd;
4 | margin: 20px;
5 | }
6 |
7 | .image-gallery-w-caption {
8 | max-width: 320px;
9 | border: 1px solid #ddd;
10 | margin: 20px;
11 | margin-bottom: 0px !important;
12 | }
13 |
14 | .image-figure {
15 | margin: 0px !important;
16 | }
17 |
18 | .image-caption {
19 | margin: 0px;
20 | margin-bottom: 20px !important;
21 | font-family: 'Roboto' !important;
22 | font-style: normal !important;
23 | }
24 |
25 | .image-center {
26 | display: block;
27 | margin-left: auto;
28 | margin-right: auto;
29 | }
30 |
--------------------------------------------------------------------------------
/docs/assets/stylesheets/highlight.css:
--------------------------------------------------------------------------------
1 | .highlight {
2 | background-color: #f8f9fa !important;
3 | }
4 |
5 | .hljs {
6 | background-color: #f9f9fb00 !important;
7 | }
8 |
9 | .hljs-title {
10 | color: #f4941b !important;
11 | }
12 |
13 | .hljs-property,
14 | .hljs-attr {
15 | color: #4171cd !important;
16 | }
17 |
18 | .hljs-literal {
19 | color: #03ae71 !important;
20 | }
21 |
22 | .hljs-string {
23 | color: #f25456 !important;
24 | }
25 |
26 | .hljs-number {
27 | color: #f25456 !important;
28 | }
29 |
30 | .hljs-built_in,
31 | .hljs-keyword {
32 | color: #03ae71 !important;
33 | }
34 |
35 | .hljs-keyword {
36 | color: #03ae71 !important;
37 | }
38 |
39 | .hljs-meta {
40 | color: #d49664ff !important;
41 | }
42 |
43 | .hljs-params,
44 | .hljs-comment {
45 | color: #4c7450ff !important;
46 | }
47 |
48 | .hljs-ln-numbers {
49 | -webkit-touch-callout: none;
50 | -webkit-user-select: none;
51 | -khtml-user-select: none;
52 | -moz-user-select: none;
53 | -ms-user-select: none;
54 | user-select: none;
55 |
56 | text-align: center;
57 | color: #ccc;
58 | border-right: 1px solid #ccc;
59 | vertical-align: top;
60 | padding-right: 5px !important;
61 | }
62 |
63 | .hljs-ln-code {
64 | padding-left: 10px !important;
65 | }
66 |
67 | .snippet:focus {
68 | color: #93a49a70 !important;
69 | transition: 200ms;
70 | }
71 |
--------------------------------------------------------------------------------
/docs/assets/stylesheets/vizzu.css:
--------------------------------------------------------------------------------
1 | [data-md-color-scheme='vizzu'] {
2 | --md-primary-fg-color: #333;
3 | --md-primary-fg-color--dark: #666;
4 | --md-default-fg-color: #666;
5 | --md-typeset-color: #666;
6 | --md-typeset-a-color: #4171cd;
7 | }
8 |
9 | .md-typeset details.tip,
10 | .md-typeset .admonition.tip,
11 | .md-typeset details.note,
12 | .md-typeset .admonition.note,
13 | .md-typeset details.info,
14 | .md-typeset .admonition.info,
15 | .md-typeset details.example,
16 | .md-typeset .admonition.example {
17 | color: #666 !important;
18 | border-color: #ccc !important;
19 | }
20 |
21 | .md-typeset .tip > .admonition-title,
22 | .md-typeset .tip > summary {
23 | color: #666 !important;
24 | border-color: #ccc !important;
25 | background-color: #f8f9fa !important;
26 | }
27 | .md-typeset .tip > .admonition-title::before,
28 | .md-typeset .tip > summary::before {
29 | background-color: #ccc !important;
30 | -webkit-mask-image: var(--md-admonition-icon--tip);
31 | mask-image: var(--md-admonition-icon--tip);
32 | }
33 | .md-typeset .tip > summary::after {
34 | background-color: #ccc !important;
35 | -webkit-mask-image: var(--md-details-icon);
36 | mask-image: var(--md-details-icon);
37 | }
38 |
39 | .md-typeset .note > .admonition-title,
40 | .md-typeset .note > summary {
41 | color: #666 !important;
42 | border-color: #ccc !important;
43 | background-color: #f8f9fa !important;
44 | }
45 | .md-typeset .note > .admonition-title::before,
46 | .md-typeset .note > summary::before {
47 | background-color: #ccc !important;
48 | -webkit-mask-image: var(--md-admonition-icon--note);
49 | mask-image: var(--md-admonition-icon--note);
50 | }
51 | .md-typeset .note > summary::after {
52 | background-color: #ccc !important;
53 | -webkit-mask-image: var(--md-details-icon);
54 | mask-image: var(--md-details-icon);
55 | }
56 |
57 | .md-typeset .info > .admonition-title,
58 | .md-typeset .info > summary {
59 | color: #666 !important;
60 | border-color: #ccc !important;
61 | background-color: #f8f9fa !important;
62 | }
63 | .md-typeset .info > .admonition-title::before,
64 | .md-typeset .info > summary::before {
65 | background-color: #ccc !important;
66 | -webkit-mask-image: var(--md-admonition-icon--info);
67 | mask-image: var(--md-admonition-icon--info);
68 | }
69 | .md-typeset .info > summary::after {
70 | background-color: #ccc !important;
71 | -webkit-mask-image: var(--md-details-icon);
72 | mask-image: var(--md-details-icon);
73 | }
74 |
75 | .md-typeset .example > .admonition-title,
76 | .md-typeset .example > summary {
77 | color: #666 !important;
78 | border-color: #ccc !important;
79 | background-color: #f8f9fa !important;
80 | }
81 | .md-typeset .example > .admonition-title::before,
82 | .md-typeset .example > summary::before {
83 | background-color: #ccc !important;
84 | -webkit-mask-image: var(--md-admonition-icon--example);
85 | mask-image: var(--md-admonition-icon--example);
86 | }
87 | .md-typeset .example > summary::after {
88 | background-color: #ccc !important;
89 | -webkit-mask-image: var(--md-details-icon);
90 | mask-image: var(--md-details-icon);
91 | }
92 |
--------------------------------------------------------------------------------
/docs/assets/vizzu-story.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vizzuhq/vizzu-story-js/d6c2c87f608015a66272b8d6c1056d403a4ccf86/docs/assets/vizzu-story.gif
--------------------------------------------------------------------------------
/docs/dev/index.md:
--------------------------------------------------------------------------------
1 | We have compiled some information on the development of `Vizzu-Story` here. If
2 | you're interested in contributing to our open-source tool (which we highly
3 | encourage), please refer to the Contributing chapter. Our community upholds a
4 | strict Code of Conduct that we expect all members to follow.
5 |
--------------------------------------------------------------------------------
/docs/examples/_basic.md:
--------------------------------------------------------------------------------
1 | # Basic example
2 |
3 | The below story shows a basic use case for `Vizzu-Story`.
4 |
5 |
6 |
7 |
8 |
9 | Create a `vizzu-player` element that will contain the rendered story.
10 |
11 | ```
12 |
13 | ```
14 |
15 | In a script module element:
16 |
17 | ```javascript
18 | // {% include "./_basic/main.js" %}
19 | ```
20 |
--------------------------------------------------------------------------------
/docs/examples/_basic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vizzuhq/vizzu-story-js/d6c2c87f608015a66272b8d6c1056d403a4ccf86/docs/examples/_basic.png
--------------------------------------------------------------------------------
/docs/examples/_basic/main.js:
--------------------------------------------------------------------------------
1 | // Import VizzuPlayer
2 | // eslint-disable-next-line no-unused-vars
3 | import VizzuPlayer from 'https://cdn.jsdelivr.net/npm/vizzu-story@latest/dist/vizzu-story.min.js'
4 |
5 | // Get the created element
6 | const vp = document.querySelector('vizzu-player')
7 |
8 | // Create data object
9 | const data = {
10 | series: [
11 | {
12 | name: 'Foo',
13 | values: ['Alice', 'Bob', 'Ted']
14 | },
15 | {
16 | name: 'Bar',
17 | values: [15, 32, 12]
18 | },
19 | {
20 | name: 'Baz',
21 | values: [5, 3, 2]
22 | }
23 | ]
24 | }
25 |
26 | // Each slide here is a page in the final interactive story
27 | const slides = [
28 | {
29 | config: {
30 | x: 'Foo',
31 | y: 'Bar'
32 | }
33 | },
34 | {
35 | config: {
36 | color: 'Foo',
37 | x: 'Baz',
38 | geometry: 'circle'
39 | }
40 | }
41 | ]
42 |
43 | // Create story configuration object, add data and slides to it
44 | const story = {
45 | data,
46 | slides
47 | }
48 |
49 | // Set up the created element with the configuration object
50 | vp.slides = story
51 |
--------------------------------------------------------------------------------
/docs/examples/linkedinpoll.md:
--------------------------------------------------------------------------------
1 | # Presentation Poll Results
2 |
3 | In August, 2022, we asked data scientists in 5 LinkedIn groups about how often
4 | they have to present the results of their analysis to business stakeholders.
5 | This is a data story about the results of that poll.
6 |
7 |
8 |
9 |
10 |
11 | Create a `vizzu-player` element that will contain the rendered story.
12 |
13 | ```
14 |
15 | ```
16 |
17 | In a script module element:
18 |
19 | ```javascript
20 | // {% include "./linkedinpoll/main.js" %}
21 | ```
22 |
23 | - [Group 1](https://www.linkedin.com/groups/1859449/): AI & ML - Analytics ,
24 | Data Science . SAP BI/ Analytics Cloud /Tableau /Power BI /Birst
25 |
26 | - [Group 2](https://www.linkedin.com/groups/4376214/): Artificial Intelligence,
27 | Digital Transformation Data Science, Automation, Machine Learning Analytics
28 |
29 | - [Group 3](https://www.linkedin.com/groups/6773411/): Data Scientist, Data
30 | Analyst and Data Engineer
31 |
32 | - [Group 4](https://www.linkedin.com/groups/25827/): Python Developers Community
33 | (moderated)
34 |
35 | - [Group 5](https://www.linkedin.com/groups/2064830/): Data Analytics, Data
36 | Science, Business Analytics, Business Intelligence, Data Scientist & Analyst
37 |
--------------------------------------------------------------------------------
/docs/examples/linkedinpoll.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vizzuhq/vizzu-story-js/d6c2c87f608015a66272b8d6c1056d403a4ccf86/docs/examples/linkedinpoll.png
--------------------------------------------------------------------------------
/docs/examples/linkedinpoll/linkedinpoll.csv:
--------------------------------------------------------------------------------
1 | Group,Group number,Answer,Vote percentage,Vote count,GroupVote count,GroupVote percentage,Answer percentage
2 | "Data Analytics, Data Science, Business Analytics...",Group 5,2 or more per month,39,27,70,39.0,3.8
3 | Python Developers Community (moderated),Group 4,2 or more per month,44,191,433,44.0,27
4 | "Data Scientist, Data Analyst and Data Engineer",Group 3,2 or more per month,47,66,141,47.0,9.3
5 | "Artificial Intelligence, Digital Transformation...",Group 2,2 or more per month,55,6,11,55.0,0.8
6 | "AI & ML - Analytics , Data Science...",Group 1,2 or more per month,56,29,52,56.0,4.1
7 | "Data Analytics, Data Science, Business Analytics...",Group 5,1 per month,31,22,70,31.0,3.1
8 | Python Developers Community (moderated),Group 4,1 per month,24,104,433,24.0,14.7
9 | "Data Scientist, Data Analyst and Data Engineer",Group 3,1 per month,20,28,141,20.0,4
10 | "Artificial Intelligence, Digital Transformation...",Group 2,1 per month,18,2,11,18.0,0.3
11 | "AI & ML - Analytics , Data Science...",Group 1,1 per month,12,6,52,12.0,0.8
12 | "Data Analytics, Data Science, Business Analytics...",Group 5,1-2 per quarter,14,10,70,14.0,1.4
13 | Python Developers Community (moderated),Group 4,1-2 per quarter,13,56,433,13.0,7.9
14 | "Data Scientist, Data Analyst and Data Engineer",Group 3,1-2 per quarter,16,23,141,16.0,3.3
15 | "Artificial Intelligence, Digital Transformation...",Group 2,1-2 per quarter,18,2,11,18.0,0.3
16 | "AI & ML - Analytics , Data Science...",Group 1,1-2 per quarter,17,9,52,17.0,1.3
17 | "Data Analytics, Data Science, Business Analytics...",Group 5,Never,16,11,70,16.0,1.6
18 | Python Developers Community (moderated),Group 4,Never,19,82,433,19.0,11.6
19 | "Data Scientist, Data Analyst and Data Engineer",Group 3,Never,17,24,141,17.0,3.4
20 | "Artificial Intelligence, Digital Transformation...",Group 2,Never,9,1,11,9.0,0.1
21 | "AI & ML - Analytics , Data Science...",Group 1,Never,15,8,52,15.0,1.1
--------------------------------------------------------------------------------
/docs/examples/linkedinpoll/main.js:
--------------------------------------------------------------------------------
1 | // Import VizzuPlayer
2 | // eslint-disable-next-line no-unused-vars
3 | import VizzuPlayer from 'https://cdn.jsdelivr.net/npm/vizzu-story@latest/dist/vizzu-story.min.js'
4 | import Csv2Js from '../../assets/javascripts/csv2js.js'
5 |
6 | // Get the created element
7 | const vp = document.querySelector('vizzu-player')
8 |
9 | // Create data object
10 | const dataLoaded = Csv2Js.csv('./linkedinpoll.csv', {
11 | units: { 'Vote percentage': '%', 'GroupVote percentage': '%', 'Answer percentage': '%' }
12 | })
13 | dataLoaded.then((data) => {
14 | // Each slide here is a page in the final interactive story
15 | const slides = [
16 | {
17 | style: {
18 | legend: {
19 | label: { fontSize: '1.1em' },
20 | paddingRight: '-1em'
21 | },
22 | plot: {
23 | marker: { label: { fontSize: '1.1em' } },
24 | paddingLeft: '10em',
25 | xAxis: {
26 | title: { color: '#00000000' },
27 | label: { fontSize: '1.1em' }
28 | },
29 | yAxis: { label: { fontSize: '1.1em' } }
30 | },
31 | logo: { width: '6em' },
32 | fontSize: '0.8em'
33 | },
34 | config: {
35 | x: { set: ['Vote percentage', 'Answer'] },
36 | y: 'Group number',
37 | color: 'Answer',
38 | label: 'Vote percentage',
39 | title: 'How often do you present your findings to business stakeholders?'
40 | }
41 | },
42 | {
43 | style: { plot: { xAxis: { label: { color: '#00000000' } } } },
44 | config: {
45 | split: true,
46 | title: '2 or more is the most popular answer in every group'
47 | }
48 | },
49 | {
50 | style: {
51 | plot: {
52 | marker: { label: { fontSize: '0.916667em' } }
53 | }
54 | },
55 | config: {
56 | x: { set: ['Vote count', 'Answer'] },
57 | label: 'Vote count',
58 | title: '61% of the votes came from one group'
59 | }
60 | },
61 | [
62 | {
63 | style: { plot: { yAxis: { title: { color: '#00000000' } } } },
64 | config: {
65 | x: 'Answer',
66 | y: ['Group number', 'Vote count'],
67 | split: false,
68 | legend: 'color'
69 | }
70 | },
71 | {
72 | style: { plot: { marker: { label: { fontSize: '1.1em' } } } },
73 | config: { y: 'Vote count', title: 'More than 700 people voted' }
74 | }
75 | ],
76 | [
77 | {
78 | config: {
79 | x: ['Answer percentage', 'Answer'],
80 | y: null,
81 | label: 'Answer percentage'
82 | }
83 | },
84 | {
85 | style: { plot: { xAxis: { label: { color: '#00000000' } } } },
86 | config: {
87 | coordSystem: 'polar',
88 | title: 'More than two-third of respondents present at least once per month'
89 | }
90 | }
91 | ]
92 | ]
93 |
94 | // Create story configuration object, add data and slides to it
95 | const story = {
96 | data,
97 | slides
98 | }
99 |
100 | // Set the size of the HTML element
101 | vp.style.cssText = 'width: 100%; height: 450px;'
102 |
103 | // Set up the created element with the configuration object
104 | vp.slides = story
105 | })
106 |
--------------------------------------------------------------------------------
/docs/examples/population.md:
--------------------------------------------------------------------------------
1 | # UN Population Forecast
2 |
3 | In this example, we explore the population of Africa between 1953-2098. On top
4 | of that, this story shows how to use the chart configuration presets. Check
5 | [Vizzu - Chart presets chapter](https://lib.vizzuhq.com/latest/tutorial/chart_presets/)
6 | and
7 | [Vizzu - Preset charts gallery](https://lib.vizzuhq.com/latest/examples/presets/)
8 | for more details on the available chart presets.
9 |
10 |
11 |
12 |
13 |
14 | Create a `vizzu-player` element that will contain the rendered story.
15 |
16 | ```
17 |
18 | ```
19 |
20 | In a script module element:
21 |
22 | ```javascript
23 | // {% include "./population/main.js" %}
24 | ```
25 |
--------------------------------------------------------------------------------
/docs/examples/population.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vizzuhq/vizzu-story-js/d6c2c87f608015a66272b8d6c1056d403a4ccf86/docs/examples/population.png
--------------------------------------------------------------------------------
/docs/examples/population/main.js:
--------------------------------------------------------------------------------
1 | // Import VizzuPlayer
2 | // eslint-disable-next-line no-unused-vars
3 | import VizzuPlayer from 'https://cdn.jsdelivr.net/npm/vizzu-story@latest/dist/vizzu-story.min.js'
4 | import Csv2Js from '../../assets/javascripts/csv2js.js'
5 |
6 | // Get the created element
7 | const vp = document.querySelector('vizzu-player')
8 | // Set the size of the HTML element
9 | vp.style.cssText = 'width: 100%; height: 400px;'
10 | // Initializing Vizzu Player
11 | const vpInitialized = vp.initializing
12 |
13 | // Create data object
14 | const dataLoaded = Csv2Js.csv('./population.csv', { dimensions: ['Year'] })
15 |
16 | // Because using presets here, you need to
17 | // create the configuration object after initialization
18 | // (then presets are accessible via vp.Vizzu.presets)
19 | Promise.all([dataLoaded, vpInitialized]).then((results) => {
20 | const data = results[0]
21 | const chart = results[1]
22 |
23 | // Switch on the tooltip that appears
24 | // when the user hovers the mouse over a chart element
25 | chart.feature('tooltip', true)
26 |
27 | // Each slide here is a page in the final interactive story
28 | const slides = [
29 | {
30 | filter: (record) => record.Continent === 'Africa',
31 | style: { plot: { xAxis: { label: { angle: 2.0 } } } },
32 | config: vp.Vizzu.presets.stackedArea({
33 | x: 'Year',
34 | y: 'Medium',
35 | stackedBy: 'Subregion',
36 | title: 'Population of Africa 1953-2098'
37 | })
38 | },
39 | {
40 | config: vp.Vizzu.presets.percentageArea({
41 | x: 'Year',
42 | y: 'Medium',
43 | stackedBy: 'Subregion'
44 | })
45 | },
46 | {
47 | config: vp.Vizzu.presets.stream({
48 | x: 'Year',
49 | y: 'Medium',
50 | stackedBy: 'Subregion'
51 | })
52 | },
53 | {
54 | config: vp.Vizzu.presets.violin({
55 | x: 'Year',
56 | y: 'Medium',
57 | splittedBy: 'Subregion'
58 | })
59 | }
60 | ]
61 |
62 | // Create story configuration object, add data and slides to it
63 | const story = {
64 | data,
65 | slides
66 | }
67 |
68 | // Set up the created element with the configuration object
69 | vp.slides = story
70 | })
71 |
--------------------------------------------------------------------------------
/docs/examples/proglangs.md:
--------------------------------------------------------------------------------
1 | # Popularity of Programming Languages
2 |
3 | What programming languages do data scientists use?
4 |
5 | This was one of the questions in the
6 | [State of Data Science Reports](https://www.anaconda.com/state-of-data-science-report-2022)
7 | published by Anaconda between 2020 and 2022. This data story shows the answers
8 | to this question.
9 |
10 |
11 |
12 |
13 |
14 | Create a `vizzu-player` element that will contain the rendered story.
15 |
16 | ```
17 |
18 | ```
19 |
20 | In a script module element:
21 |
22 | ```javascript
23 | // {% include "./proglangs/main.js" %}
24 | ```
25 |
--------------------------------------------------------------------------------
/docs/examples/proglangs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vizzuhq/vizzu-story-js/d6c2c87f608015a66272b8d6c1056d403a4ccf86/docs/examples/proglangs.png
--------------------------------------------------------------------------------
/docs/examples/proglangs/main.js:
--------------------------------------------------------------------------------
1 | // Import VizzuPlayer
2 | // eslint-disable-next-line no-unused-vars
3 | import VizzuPlayer from 'https://cdn.jsdelivr.net/npm/vizzu-story@latest/dist/vizzu-story.min.js'
4 | import Csv2Js from '../../assets/javascripts/csv2js.js'
5 |
6 | // Get the created element
7 | const vp = document.querySelector('vizzu-player')
8 |
9 | // Create data object
10 | const dataLoaded = Csv2Js.csv('./proglangs.csv', { dimensions: ['Year'], units: { Value: '%' } })
11 |
12 | dataLoaded.then((data) => {
13 | // Each slide here is a page in the final interactive story
14 | const slides = [
15 | {
16 | filter: (record) => record.Year === '2022',
17 | style: {
18 | logo: { width: '5em' },
19 | plot: {
20 | xAxis: { title: { color: '#00000000' } },
21 | paddingLeft: '2.5em',
22 | marker: {
23 | colorPalette:
24 | '#3DAE2BFF ' + '#00833EFF ' + '#00A19BFF ' + '#0075A9FF ' + '#003764FF',
25 | minLightness: 0,
26 | maxLightness: 0.4
27 | }
28 | },
29 | fontSize: '0.8em'
30 | },
31 | config: {
32 | x: ['Popularity', 'Value'],
33 | y: ['Language', 'Year', 'Lang_year'],
34 | color: 'Popularity',
35 | label: 'Value',
36 | align: 'stretch',
37 | title: 'Use of programming languages by data scientists in 2022',
38 | lightness: 'Year',
39 | legend: 'color'
40 | }
41 | },
42 | {
43 | style: { plot: { xAxis: { label: { color: '#00000000' } } } },
44 | config: {
45 | split: true,
46 | align: 'none',
47 | title: 'Python is always or frequently used by 58%'
48 | }
49 | },
50 |
51 | [
52 | {
53 | style: { plot: { xAxis: { label: { color: '#999999FF' } } } },
54 | config: { split: false, align: 'stretch' }
55 | },
56 | {
57 | filter: (record) =>
58 | (record.Popularity === 'Always' || record.Popularity === 'Frequently') &&
59 | record.Year === '2022',
60 | config: { x: { range: { max: 100 } }, align: 'none' }
61 | },
62 | {
63 | config: {
64 | sort: 'byValue',
65 | title: 'Python & SQL are the most popular by a huge margin'
66 | }
67 | }
68 | ],
69 | [
70 | {
71 | config: {
72 | sort: 'none',
73 | title: "Let's focus on the six languages with data since 2020"
74 | }
75 | },
76 | {
77 | filter: (record) =>
78 | (record.Popularity === 'Always' || record.Popularity === 'Frequently') &&
79 | (record.Language === 'R' ||
80 | record.Language === 'Python' ||
81 | record.Language === 'JavaScript' ||
82 | record.Language === 'Java' ||
83 | record.Language === 'C#' ||
84 | record.Language === 'C/C++') &&
85 | record.Year === '2022'
86 | },
87 | {
88 | config: {
89 | y: ['Lang_year', 'Year'],
90 | x: ['Popularity', 'Language', 'Value']
91 | }
92 | }
93 | ],
94 | [
95 | {
96 | filter: (record) =>
97 | (record.Popularity === 'Always' || record.Popularity === 'Frequently') &&
98 | (record.Language === 'R' ||
99 | record.Language === 'Python' ||
100 | record.Language === 'JavaScript' ||
101 | record.Language === 'Java' ||
102 | record.Language === 'C#' ||
103 | record.Language === 'C/C++') &&
104 | record.Year !== '2020'
105 | },
106 | {
107 | filter: (record) =>
108 | (record.Popularity === 'Always' || record.Popularity === 'Frequently') &&
109 | (record.Language === 'R' ||
110 | record.Language === 'Python' ||
111 | record.Language === 'JavaScript' ||
112 | record.Language === 'Java' ||
113 | record.Language === 'C#' ||
114 | record.Language === 'C/C++'),
115 | config: {
116 | title: 'C/C++, C#, Java and Javascript are gaining popularity'
117 | }
118 | }
119 | ]
120 | ]
121 |
122 | // Create story configuration object, add data and slides to it
123 | const story = {
124 | data,
125 | slides
126 | }
127 |
128 | // Set the size of the HTML element
129 | vp.style.cssText = 'width: 100%; height: 600px;'
130 |
131 | // Set up the created element with the configuration object
132 | vp.slides = story
133 | vp.initializing.then((chart) => {
134 | // Switch on the tooltip that appears
135 | // when the user hovers the mouse over a chart element
136 | chart.feature('tooltip', true)
137 |
138 | // Set a handler that prevents showing specific elements
139 | chart.on('plot-marker-label-draw', (event) => {
140 | if (event.detail.text.split('%')[0] < 5) event.preventDefault()
141 | })
142 | })
143 | })
144 |
--------------------------------------------------------------------------------
/docs/examples/proglangs/proglangs.csv:
--------------------------------------------------------------------------------
1 | Language,Popularity,Year,Lang_year,Value
2 | TypeScript,Always,2022,TypeScript '22,6
3 | TypeScript,Frequently,2022,TypeScript '22,13
4 | TypeScript,Sometimes,2022,TypeScript '22,17
5 | TypeScript,Rarely,2022,TypeScript '22,14
6 | TypeScript,Never,2022,TypeScript '22,50
7 | SQL,Always,2022,SQL '22,16
8 | SQL,Frequently,2022,SQL '22,26
9 | SQL,Sometimes,2022,SQL '22,29
10 | SQL,Rarely,2022,SQL '22,16
11 | SQL,Never,2022,SQL '22,13
12 | Rust,Always,2022,Rust '22,2
13 | Rust,Frequently,2022,Rust '22,12
14 | Rust,Sometimes,2022,Rust '22,16
15 | Rust,Rarely,2022,Rust '22,14
16 | Rust,Never,2022,Rust '22,56
17 | R,Always,2022,R '22,7
18 | R,Frequently,2022,R '22,19
19 | R,Sometimes,2022,R '22,28
20 | R,Rarely,2022,R '22,23
21 | R,Never,2022,R '22,24
22 | R,Always,2021,R '21,10
23 | R,Frequently,2021,R '21,17
24 | R,Sometimes,2021,R '21,25
25 | R,Rarely,2021,R '21,18
26 | R,Never,2021,R '21,30
27 | R,Always,2020,R '20,10
28 | R,Frequently,2020,R '20,17
29 | R,Sometimes,2020,R '20,20
30 | R,Rarely,2020,R '20,20
31 | R,Never,2020,R '20,34
32 | Python,Always,2022,Python '22,31
33 | Python,Frequently,2022,Python '22,27
34 | Python,Sometimes,2022,Python '22,22
35 | Python,Rarely,2022,Python '22,14
36 | Python,Never,2022,Python '22,6
37 | Python,Always,2021,Python '21,34
38 | Python,Frequently,2021,Python '21,29
39 | Python,Sometimes,2021,Python '21,22
40 | Python,Rarely,2021,Python '21,11
41 | Python,Never,2021,Python '21,4
42 | Python,Always,2020,Python '20,47
43 | Python,Frequently,2020,Python '20,28
44 | Python,Sometimes,2020,Python '20,16
45 | Python,Rarely,2020,Python '20,6
46 | Python,Never,2020,Python '20,4
47 | PHP,Always,2022,PHP '22,5
48 | PHP,Frequently,2022,PHP '22,13
49 | PHP,Sometimes,2022,PHP '22,20
50 | PHP,Rarely,2022,PHP '22,17
51 | PHP,Never,2022,PHP '22,45
52 | Julia,Always,2022,Julia '22,3
53 | Julia,Frequently,2022,Julia '22,12
54 | Julia,Sometimes,2022,Julia '22,17
55 | Julia,Rarely,2022,Julia '22,17
56 | Julia,Never,2022,Julia '22,51
57 | JavaScript,Always,2022,JavaScript '22,6
58 | JavaScript,Frequently,2022,JavaScript '22,20
59 | JavaScript,Sometimes,2022,JavaScript '22,26
60 | JavaScript,Rarely,2022,JavaScript '22,19
61 | JavaScript,Never,2022,JavaScript '22,28
62 | JavaScript,Always,2021,JavaScript '21,8
63 | JavaScript,Frequently,2021,JavaScript '21,16
64 | JavaScript,Sometimes,2021,JavaScript '21,24
65 | JavaScript,Rarely,2021,JavaScript '21,19
66 | JavaScript,Never,2021,JavaScript '21,33
67 | JavaScript,Always,2020,JavaScript '20,3
68 | JavaScript,Frequently,2020,JavaScript '20,12
69 | JavaScript,Sometimes,2020,JavaScript '20,20
70 | JavaScript,Rarely,2020,JavaScript '20,23
71 | JavaScript,Never,2020,JavaScript '20,42
72 | Java,Always,2022,Java '22,7
73 | Java,Frequently,2022,Java '22,18
74 | Java,Sometimes,2022,Java '22,21
75 | Java,Rarely,2022,Java '22,21
76 | Java,Never,2022,Java '22,33
77 | Java,Always,2021,Java '21,6
78 | Java,Frequently,2021,Java '21,15
79 | Java,Sometimes,2021,Java '21,22
80 | Java,Rarely,2021,Java '21,19
81 | Java,Never,2021,Java '21,38
82 | Java,Always,2020,Java '20,3
83 | Java,Frequently,2020,Java '20,7
84 | Java,Sometimes,2020,Java '20,13
85 | Java,Rarely,2020,Java '20,22
86 | Java,Never,2020,Java '20,55
87 | HTML/CSS,Always,2022,HTML/CSS '22,8
88 | HTML/CSS,Frequently,2022,HTML/CSS '22,18
89 | HTML/CSS,Sometimes,2022,HTML/CSS '22,31
90 | HTML/CSS,Rarely,2022,HTML/CSS '22,22
91 | HTML/CSS,Never,2022,HTML/CSS '22,20
92 | Go,Always,2022,Go '22,3
93 | Go,Frequently,2022,Go '22,12
94 | Go,Sometimes,2022,Go '22,18
95 | Go,Rarely,2022,Go '22,14
96 | Go,Never,2022,Go '22,53
97 | C#,Always,2022,C# '22,5
98 | C#,Frequently,2022,C# '22,14
99 | C#,Sometimes,2022,C# '22,19
100 | C#,Rarely,2022,C# '22,19
101 | C#,Never,2022,C# '22,42
102 | C#,Always,2021,C# '21,4
103 | C#,Frequently,2021,C# '21,10
104 | C#,Sometimes,2021,C# '21,19
105 | C#,Rarely,2021,C# '21,18
106 | C#,Never,2021,C# '21,49
107 | C#,Always,2020,C# '20,1
108 | C#,Frequently,2020,C# '20,3
109 | C#,Sometimes,2020,C# '20,8
110 | C#,Rarely,2020,C# '20,18
111 | C#,Never,2020,C# '20,70
112 | C/C++,Always,2022,C/C++ '22,8
113 | C/C++,Frequently,2022,C/C++ '22,18
114 | C/C++,Sometimes,2022,C/C++ '22,23
115 | C/C++,Rarely,2022,C/C++ '22,22
116 | C/C++,Never,2022,C/C++ '22,30
117 | C/C++,Always,2021,C/C++ '21,5
118 | C/C++,Frequently,2021,C/C++ '21,13
119 | C/C++,Sometimes,2021,C/C++ '21,23
120 | C/C++,Rarely,2021,C/C++ '21,25
121 | C/C++,Never,2021,C/C++ '21,34
122 | C/C++,Always,2020,C/C++ '20,2
123 | C/C++,Frequently,2020,C/C++ '20,7
124 | C/C++,Sometimes,2020,C/C++ '20,14
125 | C/C++,Rarely,2020,C/C++ '20,26
126 | C/C++,Never,2020,C/C++ '20,50
127 | Bash/Shell,Always,2022,Bash/Shell '22,9
128 | Bash/Shell,Frequently,2022,Bash/Shell '22,22
129 | Bash/Shell,Sometimes,2022,Bash/Shell '22,24
130 | Bash/Shell,Rarely,2022,Bash/Shell '22,19
131 | Bash/Shell,Never,2022,Bash/Shell '22,26
--------------------------------------------------------------------------------
/docs/examples/titanic.md:
--------------------------------------------------------------------------------
1 | # Passengers of the Titanic
2 |
3 |
4 |
5 |
6 |
7 | Create a `vizzu-player` element that will contain the rendered story.
8 |
9 | ```
10 |
11 | ```
12 |
13 | In a script module element:
14 |
15 | ```javascript
16 | // {% include "./titanic/main.js" %}
17 | ```
18 |
--------------------------------------------------------------------------------
/docs/examples/titanic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vizzuhq/vizzu-story-js/d6c2c87f608015a66272b8d6c1056d403a4ccf86/docs/examples/titanic.png
--------------------------------------------------------------------------------
/docs/examples/titanic/main.js:
--------------------------------------------------------------------------------
1 | // Import VizzuPlayer
2 | // eslint-disable-next-line no-unused-vars
3 | import VizzuPlayer from 'https://cdn.jsdelivr.net/npm/vizzu-story@latest/dist/vizzu-story.min.js'
4 | import Csv2Js from '../../assets/javascripts/csv2js.js'
5 |
6 | // Get the created element
7 | const vp = document.querySelector('vizzu-player')
8 | // Set the size of the HTML element
9 | vp.style.cssText = 'width: 100%; height: 400px;'
10 | // Initializing Vizzu Player
11 | const vpInitialized = vp.initializing
12 |
13 | // Create data object
14 | const dataLoaded = Csv2Js.csv('./titanic.csv', { dimensions: ['Pclass'] })
15 |
16 | // Because using presets here, you need to
17 | // create the configuration object after initialization
18 | // (then presets are accessible via vp.Vizzu.presets)
19 | Promise.all([dataLoaded, vpInitialized]).then((results) => {
20 | const data = results[0]
21 | const chart = results[1]
22 |
23 | // Switch on the tooltip that appears
24 | // when the user hovers the mouse over a chart element
25 | chart.feature('tooltip', true)
26 |
27 | // Set the style of the charts in the story
28 | const style = {
29 | plot: {
30 | yAxis: {
31 | label: { fontSize: '1em', paddingRight: '1.2em' },
32 | title: { color: '#ffffff00' }
33 | },
34 | xAxis: {
35 | label: {
36 | angle: '2.5',
37 | fontSize: '1.1em',
38 | paddingRight: '0em',
39 | paddingTop: '1em'
40 | },
41 | title: { fontSize: '1em', paddingTop: '2.5em' }
42 | }
43 | },
44 | logo: { width: '5em' }
45 | }
46 |
47 | // Each slide here is a page in the final interactive story
48 | const slides = [
49 | [
50 | {
51 | config: vp.Vizzu.presets.bar({
52 | x: 'Count',
53 | title: 'Passengers of the Titanic'
54 | })
55 | }
56 | ],
57 | [
58 | { config: vp.Vizzu.presets.stackedBar({ x: 'Count', stackedBy: 'Sex' }) },
59 | {
60 | config: vp.Vizzu.presets.groupedBar({
61 | x: 'Count',
62 | y: 'Sex',
63 | legend: 'color',
64 | title: 'Rougly one-third of the passengers were ladies'
65 | })
66 | }
67 | ],
68 | [
69 | {
70 | config: {
71 | x: ['Count', 'Survived'],
72 | y: 'Sex',
73 | color: 'Sex',
74 | lightness: 'Survived',
75 | label: ['Survived', 'Count']
76 | }
77 | },
78 | {
79 | config: {
80 | align: 'stretch',
81 | title: 'Much more women survived than men'
82 | }
83 | }
84 | ],
85 | [
86 | {
87 | config: {
88 | x: 'Count',
89 | align: 'none',
90 | label: null,
91 | lightness: null,
92 | title: "Let's add the age of the passengers to the mix"
93 | }
94 | },
95 | { config: { y: ['Count', 'Sex'], x: 'Age_group', label: 'Count' } }
96 | ],
97 | [
98 | {
99 | config: {
100 | label: null,
101 | title: "Let's see how many people survived/died in these age groups"
102 | }
103 | },
104 | {
105 | config: {
106 | y: ['Count', 'Sex', 'Survived'],
107 | lightness: 'Survived',
108 | legend: 'lightness'
109 | }
110 | },
111 | { config: { y: ['Count', 'Survived', 'Sex'] } }
112 | ],
113 | [
114 | {
115 | config: {
116 | align: 'stretch',
117 | title: 'Survival rate varies a bit between age groups'
118 | }
119 | }
120 | ],
121 | [
122 | {
123 | filter: (record) => record.Sex === 'male',
124 | config: {
125 | title: 'But again shows a very different picture for men...'
126 | }
127 | }
128 | ],
129 | [
130 | { filter: null },
131 | {
132 | filter: (record) => record.Sex === 'female',
133 | config: { title: '...and women' }
134 | }
135 | ]
136 | ]
137 |
138 | // Create story configuration object, add data and slides to it
139 | const story = {
140 | data,
141 | style,
142 | slides
143 | }
144 |
145 | // Set up the created element with the configuration object
146 | vp.slides = story
147 | })
148 |
--------------------------------------------------------------------------------
/docs/examples/trumptwitter.md:
--------------------------------------------------------------------------------
1 | # Trump Twitter Tirade
2 |
3 |
4 |
5 |
6 |
7 | Create a `vizzu-player` element that will contain the rendered story.
8 |
9 | ```
10 |
11 | ```
12 |
13 | In a script module element:
14 |
15 | ```javascript
16 | // {% include "./trumptwitter/main.js" %}
17 | ```
18 |
--------------------------------------------------------------------------------
/docs/examples/trumptwitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vizzuhq/vizzu-story-js/d6c2c87f608015a66272b8d6c1056d403a4ccf86/docs/examples/trumptwitter.png
--------------------------------------------------------------------------------
/docs/examples/trumptwitter/main.js:
--------------------------------------------------------------------------------
1 | // Import VizzuPlayer
2 | // eslint-disable-next-line no-unused-vars
3 | import VizzuPlayer from 'https://cdn.jsdelivr.net/npm/vizzu-story@latest/dist/vizzu-story.min.js'
4 | import Csv2Js from '../../assets/javascripts/csv2js.js'
5 |
6 | // Get the created element
7 | const vp = document.querySelector('vizzu-player')
8 |
9 | // Create data object
10 | const dataLoaded = Csv2Js.csv('./trumptwitter.csv', {
11 | measures: [
12 | 'NewtoTwitter',
13 | 'Businessman',
14 | 'Nominee',
15 | 'President',
16 | 'RT_New to Twitter',
17 | 'RT_Businessman',
18 | 'RT_Nominee',
19 | 'RT_President',
20 | 'Obama'
21 | ]
22 | })
23 |
24 | dataLoaded.then((data) => {
25 | // Set the style of the charts in the story
26 | const style = {
27 | tooltip: { fontSize: '22px' },
28 | title: { paddingTop: '1.2em', fontSize: '2.5em' },
29 | legend: { label: { fontSize: '1.8em' }, width: '16em' },
30 | logo: { width: '6em' },
31 | plot: {
32 | marker: { label: { fontSize: '1.5em' } },
33 | yAxis: {
34 | label: { fontSize: '1.5em' },
35 | title: { color: '#ffffff00' },
36 | interlacing: { color: '#ffffff00' }
37 | },
38 | xAxis: {
39 | label: { fontSize: '1.6em', paddingTop: '1em' },
40 | title: { fontSize: '1.4em', paddingTop: '2.5em' }
41 | }
42 | }
43 | }
44 |
45 | // Each slide here is a page in the final interactive story
46 | const slides = [
47 | [
48 | {
49 | filter: (record) => record.Firsttweet === 'Yes' && record.Dummy === 'No',
50 | config: {
51 | channels: {
52 | y: { set: ['tweets'] },
53 | x: { set: ['Period', 'year', 'month'] },
54 | color: 'Period'
55 | },
56 | title: "Trump started tweeting in May '09"
57 | }
58 | }
59 | ],
60 | [
61 | {
62 | filter: (record) => record.Period === 'New to Twitter' && record.Dummy === 'No',
63 | config: { title: "In the first two years he wasn't very active" }
64 | }
65 | ],
66 | [
67 | {
68 | filter: (record) =>
69 | (record.Period === 'New to Twitter' || record.Period === 'Businessman') &&
70 | record.Dummy === 'No',
71 | config: { title: 'Then he got hooked on' }
72 | }
73 | ],
74 | [
75 | {
76 | filter: (record) =>
77 | (record.Period === 'New to Twitter' ||
78 | record.Period === 'Businessman' ||
79 | record.Period === 'Nominee') &&
80 | record.Dummy === 'No',
81 | config: {
82 | title: 'Interesting trend after becoming a presidential nominee'
83 | }
84 | }
85 | ],
86 | [
87 | {
88 | filter: (record) => record.Dummy === 'No',
89 | config: { title: 'And after he became President' }
90 | }
91 | ],
92 | [
93 | { config: { geometry: 'area', align: 'center' } },
94 | { config: { title: "All of Trump's tweets until May 2020" } }
95 | ],
96 | [
97 | {
98 | config: {
99 | y: 'retweetcount',
100 | title: 'And the number of times these were retweeted'
101 | }
102 | }
103 | ],
104 | [
105 | {
106 | config: {
107 | y: 'tweets',
108 | title: "Let's focus on the number of tweets for now"
109 | }
110 | },
111 | { config: { x: { set: ['year', 'month'] }, color: null } }
112 | ],
113 | [
114 | {
115 | config: {
116 | y: ['tweets', 'Type'],
117 | color: 'Type',
118 | title: 'Original tweets, retweets & replies sent'
119 | },
120 | style: {
121 | plot: { marker: { colorPalette: '#A0CDEBFF #60C0E6FF #1DA1F3FF' } }
122 | }
123 | }
124 | ],
125 | [
126 | {
127 | config: { split: true, align: 'none' },
128 | style: { plot: { yAxis: { label: { color: '#ffffff00' } } } }
129 | }
130 | ],
131 | [
132 | {
133 | config: {
134 | split: false,
135 | align: 'stretch',
136 | title: 'Original tweets, retweets & replies sent (%)'
137 | },
138 | style: { plot: { yAxis: { label: { color: '#999999ff' } } } }
139 | }
140 | ],
141 | [
142 | { config: { align: 'center', title: '' } },
143 | {
144 | config: { y: 'tweets', color: null, legend: 'lightness' },
145 | style: { plot: { marker: { colorPalette: 'null' } } }
146 | },
147 | {
148 | config: {
149 | y: ['tweets', 'Tool'],
150 | color: 'Tool',
151 | title: 'Tools Trump Used to Tweet',
152 | legend: 'color'
153 | },
154 | style: {
155 | plot: {
156 | marker: {
157 | colorPalette: '#597696FF #ED2828FF #26EC87FF #29B9BFFF '
158 | }
159 | }
160 | }
161 | }
162 | ],
163 | [
164 | {
165 | config: { split: true, align: 'none' },
166 | style: { plot: { yAxis: { label: { color: '#ffffff00' } } } }
167 | }
168 | ],
169 | [
170 | { config: { geometry: 'rectangle' } },
171 | {
172 | config: {
173 | x: ['tweets', 'year', 'month'],
174 | y: 'Tool',
175 | geometry: 'rectangle',
176 | split: false,
177 | align: 'none'
178 | },
179 | style: {
180 | plot: {
181 | xAxis: { title: { color: '#ffffff00' } },
182 | yAxis: { label: { color: '#999999ff' } }
183 | }
184 | }
185 | },
186 | { config: { x: 'tweets', label: 'tweets' } }
187 | ],
188 | [
189 | { config: { x: ['tweets', 'AMPM', 'hour12'], label: null } },
190 | {
191 | config: {
192 | y: { set: ['tweets', 'Tool'], range: { min: '110%', max: '0%' } },
193 | x: ['AMPM', 'hour12']
194 | }
195 | },
196 | { config: { geometry: 'area' } },
197 | {
198 | config: {
199 | coordSystem: 'polar',
200 | angle: Math.PI,
201 | title: 'Time of Day When Trump Tweeted'
202 | },
203 | style: {
204 | plot: {
205 | yAxis: { label: { color: '#ffffff00' } },
206 | xAxis: {
207 | label: {
208 | fontSize: '2em',
209 | paddingBottom: '2.5em',
210 | paddingTop: '2.5em',
211 | paddingLeft: '2.5em',
212 | paddingRight: '2.5em'
213 | }
214 | }
215 | }
216 | }
217 | }
218 | ],
219 | [
220 | {
221 | config: {
222 | y: ['Businessman', 'Tool'],
223 | title: 'Times Trump Tweeted When Being a Businessman'
224 | }
225 | }
226 | ],
227 | [
228 | {
229 | config: {
230 | y: ['President', 'Tool'],
231 | title: 'Times Trump Tweeted When Being President'
232 | }
233 | }
234 | ]
235 | ]
236 |
237 | // Create story configuration object, add data and slides to it
238 | const story = {
239 | data,
240 | style,
241 | slides
242 | }
243 |
244 | // Set the size of the HTML element
245 | vp.style.cssText = 'width: 100%; height: 400px;'
246 |
247 | // Set up the created element with the configuration object
248 | vp.slides = story
249 | vp.initializing.then((chart) => {
250 | // Switch on the tooltip that appears
251 | // when the user hovers the mouse over a chart element
252 | chart.feature('tooltip', true)
253 | })
254 | })
255 |
--------------------------------------------------------------------------------
/docs/examples/usbudget.md:
--------------------------------------------------------------------------------
1 | # US Federal R&D budget
2 |
3 | US Federal R&D budget In this more involved example, we explore the history of
4 | the US Federal R&D budget between 1955-2020. On top of the base functionality,
5 | this story showcases:
6 |
7 | - Styling the overall story
8 | - Setting the size of the story
9 | - Slides with multiple steps
10 |
11 |
12 |
13 |
14 |
15 | Create a `vizzu-player` element that will contain the rendered story.
16 |
17 | ```
18 |
19 | ```
20 |
21 | In a script module element:
22 |
23 | ```javascript
24 | // {% include "./usbudget/main.js" %}
25 | ```
26 |
--------------------------------------------------------------------------------
/docs/examples/usbudget.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vizzuhq/vizzu-story-js/d6c2c87f608015a66272b8d6c1056d403a4ccf86/docs/examples/usbudget.png
--------------------------------------------------------------------------------
/docs/examples/usbudget/main.js:
--------------------------------------------------------------------------------
1 | // Import VizzuPlayer
2 | // eslint-disable-next-line no-unused-vars
3 | import VizzuPlayer from 'https://cdn.jsdelivr.net/npm/vizzu-story@latest/dist/vizzu-story.min.js'
4 | import Csv2Js from '../../assets/javascripts/csv2js.js'
5 |
6 | // Get the created element
7 | const vp = document.querySelector('vizzu-player')
8 |
9 | // Create data object
10 | const dataLoaded = Csv2Js.csv('./usbudget.csv', { dimensions: ['Year'], units: { Amount: 'B$' } })
11 |
12 | dataLoaded.then((data) => {
13 | // Set the style of the charts in the story
14 |
15 | const style = {
16 | plot: {
17 | yAxis: {
18 | label: {
19 | fontSize: '1em',
20 | paddingRight: '1.2em'
21 | },
22 | title: { color: '#ffffff00' }
23 | },
24 | xAxis: {
25 | label: {
26 | angle: '2.5',
27 | fontSize: '1.1em',
28 | paddingRight: '0em',
29 | paddingTop: '1em'
30 | },
31 | title: { fontSize: '0.8em', paddingTop: '2.5em' }
32 | }
33 | },
34 | logo: { width: '5em' }
35 | }
36 |
37 | // Each slide here is a page in the final interactive story
38 | const slides = [
39 | // Add the first slide,
40 | // containing a single animation step that sets the initial chart
41 | {
42 | // Only include rows where the Function value != Defense
43 | filter: (record) => record.Function !== 'Defense',
44 | config: {
45 | channels: {
46 | y: {
47 | set: ['Amount', 'Function'],
48 | // Set the range of the y-axis
49 | // to the min and max of the data being shown
50 | // default value is 110% of the maximum value
51 | range: { min: '0%', max: '100%' }
52 | },
53 | x: { set: ['Year'] },
54 | color: 'Function'
55 | },
56 | title: 'Stacked Area Chart - U.S. R&D Budget in 1955-2020',
57 | geometry: 'area'
58 | }
59 | },
60 | // Show components side-by-side
61 | {
62 | config: {
63 | split: true,
64 | title: 'Show Components Side by Side'
65 | }
66 | },
67 | // This slide contains multiple steps
68 | [
69 | // Step 1 - let's get back to the previous view
70 | { config: { split: false } },
71 | // Step 2 - Add the defense function to the chart by removing it from the filter
72 | {
73 | filter: null,
74 | config: { title: 'Add New Category While Keeping the Context' }
75 | }
76 | ],
77 | // Show share of components
78 | {
79 | config: {
80 | align: 'stretch',
81 | title: 'Show Share of Components (%)'
82 | }
83 | },
84 | // Compare data from 1955 and 2020
85 | [
86 | // Step 1 - switch back to value instead of percentage
87 | { config: { align: 'none' } },
88 | // Step 2 - switch to a stacked column chart by changing the geometry
89 | {
90 | config: {
91 | geometry: 'rectangle'
92 | }
93 | },
94 | // Step 3 - zoom to data from the first and last years
95 | {
96 | filter: (record) => record.Year === '1955' || record.Year === '2020',
97 | config: {
98 | title: 'Zoom to Specific Elements'
99 | }
100 | }
101 | ],
102 | // Group & rearrange elements for comparison
103 | [
104 | {
105 | config: {
106 | x: ['Year', 'Function'],
107 | y: 'Amount',
108 | label: 'Amount',
109 | title: 'Group & Rearrange for Better Comparison'
110 | }
111 | },
112 | {
113 | config: { x: ['Function', 'Year'] }
114 | }
115 | ]
116 | ]
117 |
118 | // Create story configuration object, add data, style and slides to it
119 | const story = {
120 | data,
121 | style,
122 | slides
123 | }
124 |
125 | // Set the size of the HTML element
126 | vp.style.cssText = 'width: 100%; height: 400px;'
127 |
128 | // Set up the created element with the configuration object
129 | vp.slides = story
130 | vp.initializing.then((chart) => {
131 | // Switch on the tooltip that appears
132 | // when the user hovers the mouse over a chart element
133 | chart.feature('tooltip', true)
134 |
135 | // Set a handler that prevents showing the year values that are not divisible by 5
136 | chart.on('plot-axis-label-draw', (event) => {
137 | const Year = parseFloat(event.detail.text)
138 | if (!isNaN(Year) && Year > 1950 && Year < 2020 && Year % 5 !== 0) {
139 | event.preventDefault()
140 | }
141 | })
142 | })
143 | })
144 |
--------------------------------------------------------------------------------
/docs/examples/usbudget/usbudget.csv:
--------------------------------------------------------------------------------
1 | Year,Function,Amount
2 | 1955,Health,0.54
3 | 1955,Space,0.34
4 | 1955,Science,0.63
5 | 1955,Energy,0.21
6 | 1955,Nat. Res.,0.25
7 | 1955,Other,0.72
8 | 1955,Defense,12.08
9 | 1956,Health,0.62
10 | 1956,Space,0.38
11 | 1956,Science,0.71
12 | 1956,Energy,0.32
13 | 1956,Nat. Res.,0.29
14 | 1956,Other,0.94
15 | 1956,Defense,14.41
16 | 1957,Health,0.98
17 | 1957,Space,0.4
18 | 1957,Science,0.87
19 | 1957,Energy,0.56
20 | 1957,Nat. Res.,0.33
21 | 1957,Other,1.08
22 | 1957,Defense,15.63
23 | 1958,Health,1.12
24 | 1958,Space,0.51
25 | 1958,Science,1.12
26 | 1958,Energy,0.77
27 | 1958,Nat. Res.,0.38
28 | 1958,Other,1.28
29 | 1958,Defense,17.48
30 | 1959,Health,1.51
31 | 1959,Space,0.81
32 | 1959,Science,1.43
33 | 1959,Energy,0.9
34 | 1959,Nat. Res.,0.62
35 | 1959,Other,1.88
36 | 1959,Defense,37.55
37 | 1960,Health,1.91
38 | 1960,Space,2.28
39 | 1960,Science,1.66
40 | 1960,Energy,1.1
41 | 1960,Nat. Res.,0.47
42 | 1960,Other,2.15
43 | 1960,Defense,40.99
44 | 1961,Health,2.25
45 | 1961,Space,4.4
46 | 1961,Science,1.94
47 | 1961,Energy,1.18
48 | 1961,Nat. Res.,0.72
49 | 1961,Other,2.21
50 | 1961,Defense,47.17
51 | 1962,Health,2.93
52 | 1962,Space,7.5
53 | 1962,Science,2.12
54 | 1962,Energy,2.68
55 | 1962,Nat. Res.,0.67
56 | 1962,Other,2.62
57 | 1962,Defense,47.83
58 | 1963,Health,3.67
59 | 1963,Space,15.28
60 | 1963,Science,2.43
61 | 1963,Energy,3.08
62 | 1963,Nat. Res.,0.79
63 | 1963,Other,2.9
64 | 1963,Defense,47.63
65 | 1964,Health,4.63
66 | 1964,Space,24.31
67 | 1964,Science,2.86
68 | 1964,Energy,3.3
69 | 1964,Nat. Res.,0.79
70 | 1964,Other,3.15
71 | 1964,Defense,51.78
72 | 1965,Health,3.82
73 | 1965,Space,29.1
74 | 1965,Science,2.95
75 | 1965,Energy,3.09
76 | 1965,Nat. Res.,0.91
77 | 1965,Other,3.9
78 | 1965,Defense,45.8
79 | 1966,Health,4.68
80 | 1966,Space,33.42
81 | 1966,Science,3.26
82 | 1966,Energy,2.86
83 | 1966,Nat. Res.,1.01
84 | 1966,Other,4.31
85 | 1966,Defense,44.97
86 | 1967,Health,5.56
87 | 1967,Space,31
88 | 1967,Science,3.49
89 | 1967,Energy,2.94
90 | 1967,Nat. Res.,1.11
91 | 1967,Other,4.43
92 | 1967,Defense,49.61
93 | 1968,Health,5.84
94 | 1968,Space,26.51
95 | 1968,Science,3.14
96 | 1968,Energy,3.06
97 | 1968,Nat. Res.,1.23
98 | 1968,Other,5.54
99 | 1968,Defense,50.77
100 | 1969,Health,6.1
101 | 1969,Space,22.87
102 | 1969,Science,3.78
103 | 1969,Energy,2.67
104 | 1969,Nat. Res.,1.32
105 | 1969,Other,5.01
106 | 1969,Defense,47.25
107 | 1970,Health,5.79
108 | 1970,Space,18.98
109 | 1970,Science,3.7
110 | 1970,Energy,2.43
111 | 1970,Nat. Res.,1.62
112 | 1970,Other,5.96
113 | 1970,Defense,43.27
114 | 1971,Health,5.76
115 | 1971,Space,16.13
116 | 1971,Science,3.67
117 | 1971,Energy,2.33
118 | 1971,Nat. Res.,1.8
119 | 1971,Other,7.78
120 | 1971,Defense,41.62
121 | 1972,Health,6.74
122 | 1972,Space,14.99
123 | 1972,Science,3.81
124 | 1972,Energy,1.61
125 | 1972,Nat. Res.,2.48
126 | 1972,Other,6.95
127 | 1972,Defense,43.31
128 | 1973,Health,7.79
129 | 1973,Space,14.3
130 | 1973,Science,3.78
131 | 1973,Energy,1.78
132 | 1973,Nat. Res.,2.53
133 | 1973,Other,6.9
134 | 1973,Defense,42.92
135 | 1974,Health,7.27
136 | 1974,Space,12.71
137 | 1974,Science,3.64
138 | 1974,Energy,2.3
139 | 1974,Nat. Res.,2.24
140 | 1974,Other,7.03
141 | 1974,Defense,41.23
142 | 1975,Health,7.51
143 | 1975,Space,11.43
144 | 1975,Science,3.5
145 | 1975,Energy,3.71
146 | 1975,Nat. Res.,2.57
147 | 1975,Other,6.34
148 | 1975,Defense,38.6
149 | 1976,Health,8.51
150 | 1976,Space,11.99
151 | 1976,Science,3.27
152 | 1976,Energy,5.29
153 | 1976,Nat. Res.,2.4
154 | 1976,Other,6.32
155 | 1976,Defense,36.48
156 | 1977,Health,5.71
157 | 1977,Space,12.15
158 | 1977,Science,3.24
159 | 1977,Energy,7.61
160 | 1977,Nat. Res.,2.03
161 | 1977,Other,5.88
162 | 1977,Defense,37.67
163 | 1978,Health,8.97
164 | 1978,Space,11.21
165 | 1978,Science,3.16
166 | 1978,Energy,8.25
167 | 1978,Nat. Res.,2.19
168 | 1978,Other,6.64
169 | 1978,Defense,39.2
170 | 1979,Health,9.73
171 | 1979,Space,10.98
172 | 1979,Science,3.24
173 | 1979,Energy,9.92
174 | 1979,Nat. Res.,2.69
175 | 1979,Other,6.07
176 | 1979,Defense,36.43
177 | 1980,Health,10.17
178 | 1980,Space,11.77
179 | 1980,Science,3.27
180 | 1980,Energy,9.09
181 | 1980,Nat. Res.,2.63
182 | 1980,Other,6.15
183 | 1980,Defense,40.45
184 | 1981,Health,10.27
185 | 1981,Space,12.07
186 | 1981,Science,3.52
187 | 1981,Energy,9.26
188 | 1981,Nat. Res.,2.29
189 | 1981,Other,5.92
190 | 1981,Defense,42.6
191 | 1982,Health,10.21
192 | 1982,Space,6.34
193 | 1982,Science,3.34
194 | 1982,Energy,7.83
195 | 1982,Nat. Res.,1.97
196 | 1982,Other,5.23
197 | 1982,Defense,46.59
198 | 1983,Health,10.05
199 | 1983,Space,4.5
200 | 1983,Science,3.29
201 | 1983,Energy,6.15
202 | 1983,Nat. Res.,1.93
203 | 1983,Other,4.72
204 | 1983,Defense,50.25
205 | 1984,Health,9.92
206 | 1984,Space,6.58
207 | 1984,Science,3.55
208 | 1984,Energy,6.01
209 | 1984,Nat. Res.,1.84
210 | 1984,Other,5.22
211 | 1984,Defense,56.06
212 | 1985,Health,10.8
213 | 1985,Space,5.07
214 | 1985,Science,3.56
215 | 1985,Energy,8.95
216 | 1985,Nat. Res.,1.86
217 | 1985,Other,5.24
218 | 1985,Defense,63.92
219 | 1986,Health,11.48
220 | 1986,Space,5.9
221 | 1986,Science,3.93
222 | 1986,Energy,5.4
223 | 1986,Nat. Res.,1.9
224 | 1986,Other,5.34
225 | 1986,Defense,73.42
226 | 1987,Health,11.7
227 | 1987,Space,5.42
228 | 1987,Science,4.03
229 | 1987,Energy,4.67
230 | 1987,Nat. Res.,1.78
231 | 1987,Other,4.93
232 | 1987,Defense,74.72
233 | 1988,Health,13.41
234 | 1988,Space,6.34
235 | 1988,Science,4.16
236 | 1988,Energy,4.46
237 | 1988,Nat. Res.,2.05
238 | 1988,Other,4.83
239 | 1988,Defense,74.2
240 | 1989,Health,14.15
241 | 1989,Space,7.91
242 | 1989,Science,4.22
243 | 1989,Energy,4.6
244 | 1989,Nat. Res.,1.98
245 | 1989,Other,5.38
246 | 1989,Defense,75.69
247 | 1990,Health,14.93
248 | 1990,Space,10.17
249 | 1990,Science,4.17
250 | 1990,Energy,4.24
251 | 1990,Nat. Res.,2.21
252 | 1990,Other,5.41
253 | 1990,Defense,74.3
254 | 1991,Health,14.89
255 | 1991,Space,10.96
256 | 1991,Science,4.31
257 | 1991,Energy,4.37
258 | 1991,Nat. Res.,2.31
259 | 1991,Other,5.59
260 | 1991,Defense,66.17
261 | 1992,Health,16.45
262 | 1992,Space,11.32
263 | 1992,Science,4.29
264 | 1992,Energy,4.42
265 | 1992,Nat. Res.,2.71
266 | 1992,Other,6.06
267 | 1992,Defense,65.04
268 | 1993,Health,17.27
269 | 1993,Space,11.66
270 | 1993,Science,4.23
271 | 1993,Energy,4.19
272 | 1993,Nat. Res.,2.93
273 | 1993,Other,6.3
274 | 1993,Defense,67.26
275 | 1994,Health,17.32
276 | 1994,Space,10.86
277 | 1994,Science,4.14
278 | 1994,Energy,4.32
279 | 1994,Nat. Res.,2.85
280 | 1994,Other,6.78
281 | 1994,Defense,62.01
282 | 1995,Health,17.96
283 | 1995,Space,13.15
284 | 1995,Science,4.14
285 | 1995,Energy,5.03
286 | 1995,Nat. Res.,2.65
287 | 1995,Other,6.1
288 | 1995,Defense,60.15
289 | 1996,Health,16.44
290 | 1996,Space,10.9
291 | 1996,Science,4.36
292 | 1996,Energy,4.6
293 | 1996,Nat. Res.,2.52
294 | 1996,Other,6.61
295 | 1996,Defense,61.75
296 | 1997,Health,17.63
297 | 1997,Space,12.52
298 | 1997,Science,4.18
299 | 1997,Energy,4.06
300 | 1997,Nat. Res.,2.45
301 | 1997,Other,6.7
302 | 1997,Defense,61.82
303 | 1998,Health,19.33
304 | 1998,Space,13.12
305 | 1998,Science,5.88
306 | 1998,Energy,2.32
307 | 1998,Nat. Res.,2.51
308 | 1998,Other,6.48
309 | 1998,Defense,61
310 | 1999,Health,21.51
311 | 1999,Space,12.48
312 | 1999,Science,6.35
313 | 1999,Energy,1.93
314 | 1999,Nat. Res.,2.6
315 | 1999,Other,5.95
316 | 1999,Defense,60.43
317 | 2000,Health,23.34
318 | 2000,Space,7.96
319 | 2000,Science,6.9
320 | 2000,Energy,1.86
321 | 2000,Nat. Res.,2.48
322 | 2000,Other,5.83
323 | 2000,Defense,60.35
324 | 2001,Health,26.13
325 | 2001,Space,8.06
326 | 2001,Science,6.97
327 | 2001,Energy,1.92
328 | 2001,Nat. Res.,2.51
329 | 2001,Other,6.04
330 | 2001,Defense,63.41
331 | 2002,Health,29.94
332 | 2002,Space,8.22
333 | 2002,Science,7.21
334 | 2002,Energy,1.69
335 | 2002,Nat. Res.,2.62
336 | 2002,Other,6.4
337 | 2002,Defense,68.19
338 | 2003,Health,32.84
339 | 2003,Space,8.33
340 | 2003,Science,7.93
341 | 2003,Energy,1.84
342 | 2003,Nat. Res.,2.66
343 | 2003,Other,7.63
344 | 2003,Defense,79.59
345 | 2004,Health,35.56
346 | 2004,Space,10.9
347 | 2004,Science,8.33
348 | 2004,Energy,1.88
349 | 2004,Nat. Res.,2.19
350 | 2004,Other,6.28
351 | 2004,Defense,88.6
352 | 2005,Health,36.29
353 | 2005,Space,9.05
354 | 2005,Science,8.48
355 | 2005,Energy,1.67
356 | 2005,Nat. Res.,2.47
357 | 2005,Other,6.77
358 | 2005,Defense,92.95
359 | 2006,Health,36.04
360 | 2006,Space,8.68
361 | 2006,Science,8.51
362 | 2006,Energy,1.47
363 | 2006,Nat. Res.,1.95
364 | 2006,Other,6.79
365 | 2006,Defense,93.12
366 | 2007,Health,35.81
367 | 2007,Space,10.56
368 | 2007,Science,8.29
369 | 2007,Energy,1.55
370 | 2007,Nat. Res.,2.06
371 | 2007,Other,7.02
372 | 2007,Defense,95.66
373 | 2008,Health,36.07
374 | 2008,Space,12.46
375 | 2008,Science,8.25
376 | 2008,Energy,1.48
377 | 2008,Nat. Res.,2.31
378 | 2008,Other,6.75
379 | 2008,Defense,96.79
380 | 2009,Health,37.05
381 | 2009,Space,11.01
382 | 2009,Science,8.75
383 | 2009,Energy,2.22
384 | 2009,Nat. Res.,2.32
385 | 2009,Other,7.06
386 | 2009,Defense,99.68
387 | 2010,Health,41.09
388 | 2010,Space,9.49
389 | 2010,Science,9.78
390 | 2010,Energy,2.34
391 | 2010,Nat. Res.,2.21
392 | 2010,Other,6.4
393 | 2010,Defense,96.65
394 | 2011,Health,41.67
395 | 2011,Space,9.14
396 | 2011,Science,10.84
397 | 2011,Energy,4.3
398 | 2011,Nat. Res.,2.19
399 | 2011,Other,6.59
400 | 2011,Defense,93.1
401 | 2012,Health,39.03
402 | 2012,Space,11.5
403 | 2012,Science,10.48
404 | 2012,Energy,3.46
405 | 2012,Nat. Res.,2.36
406 | 2012,Other,6.17
407 | 2012,Defense,86.15
408 | 2013,Health,36.45
409 | 2013,Space,11.96
410 | 2013,Science,10.4
411 | 2013,Energy,2.29
412 | 2013,Nat. Res.,2.14
413 | 2013,Other,5.87
414 | 2013,Defense,80.08
415 | 2014,Health,34.01
416 | 2014,Space,11.91
417 | 2014,Science,10.28
418 | 2014,Energy,2.61
419 | 2014,Nat. Res.,2.14
420 | 2014,Other,6.14
421 | 2014,Defense,77.97
422 | 2015,Health,33.77
423 | 2015,Space,12.2
424 | 2015,Science,10.07
425 | 2015,Energy,3.04
426 | 2015,Nat. Res.,2.27
427 | 2015,Other,5.64
428 | 2015,Defense,77.25
429 | 2016,Health,33.63
430 | 2016,Space,12.36
431 | 2016,Science,10.25
432 | 2016,Energy,3.01
433 | 2016,Nat. Res.,2.41
434 | 2016,Other,5.91
435 | 2016,Defense,78.66
436 | 2017,Health,36
437 | 2017,Space,10.71
438 | 2017,Science,10.23
439 | 2017,Energy,3.1
440 | 2017,Nat. Res.,2.54
441 | 2017,Other,5.83
442 | 2017,Defense,54.62
443 | 2018,Health,35.8
444 | 2018,Space,10.52
445 | 2018,Science,9.93
446 | 2018,Energy,3.13
447 | 2018,Nat. Res.,2.41
448 | 2018,Other,5.98
449 | 2018,Defense,51.09
450 | 2019,Health,37.29
451 | 2019,Space,9.99
452 | 2019,Science,9.97
453 | 2019,Energy,3.56
454 | 2019,Nat. Res.,2.4
455 | 2019,Other,5.79
456 | 2019,Defense,57.06
457 | 2020,Health,38.61
458 | 2020,Space,12.27
459 | 2020,Science,10.52
460 | 2020,Energy,3.46
461 | 2020,Nat. Res.,2.48
462 | 2020,Other,6.49
463 | 2020,Defense,65.81
464 |
--------------------------------------------------------------------------------
/docs/installation.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | !!! info
4 |
5 | `Vizzu-Story` requires and downloads the
6 | [Vizzu](https://github.com/vizzuhq/vizzu-lib) `JavaScript`/`C++`
7 | [library](https://www.jsdelivr.com/package/npm/vizzu) from `jsDelivr CDN`, but
8 | you can also use a different or self-hosted version of it. Check
9 | [Initialization chapter](./tutorial/initialization.md#vizzu-url) for more
10 | details.
11 |
12 | Install via [npm](https://www.npmjs.com/package/vizzu-story):
13 |
14 | ```sh
15 | npm install vizzu-story
16 | ```
17 |
18 | Or use it from [CDN](https://www.jsdelivr.com/package/npm/vizzu-story):
19 |
20 | ```javascript
21 | import VizzuPlayer from 'https://cdn.jsdelivr.net/npm/vizzu-story@latest/dist/vizzu-story.min.js'
22 | ```
23 |
--------------------------------------------------------------------------------
/docs/reference/README.md:
--------------------------------------------------------------------------------
1 | # Vizzu-Story JS Library Reference
2 |
3 | This is the API reference document of the `Vizzu-Story` `JS` library. It
4 | provides information about every detail of the API. This works best for users
5 | who already have a basic understanding of the `Vizzu-Story` library and its
6 | logic.
7 |
8 | In case you're just getting started with `Vizzu-Story`, we strongly recommend
9 | visiting our [Tutorial](../tutorial/index.md) first.
10 |
11 | ## Library Overview
12 |
13 | The main entry point of the library is the
14 | [VizzuPlayer](./classes/VizzuPlayer.md) class, and its most important component
15 | is the [slides](./classes/VizzuPlayer.md#slides) accessor.
16 |
17 | - [VizzuPlayer](./classes/VizzuPlayer.md) class
18 | - [constructor()](./classes/VizzuPlayer.md#constructors)
19 | - [slides](./classes/VizzuPlayer.md#slides) ([Story](./interfaces/Story.md)) :
20 | `void`
21 |
22 | ## Details
23 |
24 | You can find all interface declarations and types under these namespaces.
25 |
26 | - [Story](./interfaces/Story.md) - Contains slides, underlying data and chart's
27 | style settings
28 | - [Phase](./interfaces/Phase.md) - A single step of a slide
29 |
--------------------------------------------------------------------------------
/docs/tutorial/building_blocks.md:
--------------------------------------------------------------------------------
1 | # Building blocks
2 |
3 | In `Vizzu-Story`, you can build and show a `story` object that contains all of
4 | the data being shown throughout the story and the charts created based on that
5 | data, arranged into `slides` and `steps`.
6 |
7 | ## Slides and steps
8 |
9 | Create the data story by defining a sequence of slides. A slide can be a single
10 | chart corresponding to an [animate](https://lib.vizzuhq.com/latest/tutorial/)
11 | call from `Vizzu`. Or a slide can be a sequence of animation calls, in which
12 | case all of these animations will be played until the last one in the sequence,
13 | allowing for more complex transitions between slides.
14 |
15 | ```javascript
16 | const slides = [
17 | // This slide contains a single animation step
18 | {
19 | config: {
20 | x: 'Foo',
21 | y: 'Bar'
22 | }
23 | },
24 | // This slide contains multiple steps
25 | [{
26 | config: {
27 | color: 'Foo',
28 | x: 'Baz',
29 | geometry: 'circle'
30 | }
31 | }, {
32 | config: {
33 | color: 'Foo',
34 | x: 'Baz',
35 | geometry: 'rectangle'
36 | }
37 | }],
38 | ]
39 | ```
40 |
41 | Navigation controls beneath the chart will navigate between the slides. You can
42 | use the `PgUp` and `PgDn` buttons, left and right arrows to navigate between
43 | slides, and the `Home` and `End` buttons to jump to the first or last slide.
44 |
45 | On each chart, you can define the chart configuration and style with the same
46 | objects as in `Vizzu`. However, you can not modify the underlying data between
47 | the slides, only the data filter used.
48 |
49 | ```typescript
50 | interface Chart {
51 | config?: Vizzu.Config.Chart
52 | filter?: Vizzu.Data.FilterCallback | null
53 | style?: Vizzu.Styles.Chart
54 | animOptions?: Vizzu.Anim.Options
55 | }
56 | ```
57 |
58 | ```javascript
59 | const slides = [
60 | // This slide sets config, filter, style and animOptions
61 | {
62 | config: {
63 | x: 'Foo',
64 | y: 'Bar'
65 | },
66 | filter: record => record['Foo'] === 'Bob',
67 | style: {
68 | plot: {
69 | marker: {
70 | colorPalette: '#FF0000'
71 | }
72 | }
73 | },
74 | animOptions: {
75 | duration: 1
76 | }
77 | }
78 | ]
79 | ```
80 |
81 | !!! tip
82 |
83 | Check
84 | [Vizzu - Filtering & adding new records chapter](https://lib.vizzuhq.com/latest/tutorial/filter_add_new_records/)
85 | and [Vizzu - Style chapter](https://lib.vizzuhq.com/latest/tutorial/style/) for
86 | more details on data filtering and style options.
87 |
88 | ## Story
89 |
90 | Put the `data` object (described in the [Data chapter](./data.md)) and the slide
91 | list into the `story` descriptor object.
92 |
93 | ```javascript
94 | const story = {
95 | data: data,
96 | slides: slides
97 | }
98 | ```
99 |
100 | Here you can also set the `story` `style` property to set the chart style used
101 | for the whole `story`.
102 |
103 | ```javascript
104 | const style = {
105 | title: {
106 | fontSize: 50
107 | }
108 | }
109 |
110 | const story = {
111 | data: data,
112 | style: style,
113 | slides: slides
114 | }
115 | ```
116 |
117 | Then set up the created element with the configuration object:
118 |
119 | ```javascript
120 | const vp = document.querySelector('vizzu-player')
121 | vp.slides = story
122 | ```
123 |
124 | ## Chart features
125 |
126 | You can enable or disable chart features, such as the `Tooltip` that appears if
127 | the viewer hovers their mouse over a specific element of the chart.
128 |
129 | ```javascript
130 | vp.initializing.then((chart) => {
131 | chart.feature("tooltip", true)
132 | })
133 | ```
134 |
135 | !!! tip
136 |
137 | See
138 | [Vizzu - Axes, title, tooltip chapter](https://lib.vizzuhq.com/latest/tutorial/axes_title_tooltip/)
139 | for more details on chart features.
140 |
141 | ## Chart events
142 |
143 | You have many more options to change the look and feel of the `story` by using
144 | events.
145 |
146 | ```javascript
147 | vp.initializing.then((chart) => {
148 | chart.on("click", (event) => {
149 | alert(JSON.stringify(event.detail))
150 | })
151 | })
152 | ```
153 |
154 | !!! tip
155 |
156 | See [Vizzu - Events chapter](https://lib.vizzuhq.com/latest/tutorial/events/)
157 | for more details on events.
158 |
--------------------------------------------------------------------------------
/docs/tutorial/data.md:
--------------------------------------------------------------------------------
1 | # Data
2 |
3 | You can use the same data definition formats as in the `Vizzu` library.
4 | Similarly, there are two types of data series: dimensions and measures.
5 |
6 | !!! note
7 |
8 | Please note, that all of the data used throughout your data story has to be
9 | added to the story at initialization. The data being shown can be filtered at
10 | each step.
11 |
12 | !!! tip
13 |
14 | See [Vizzu - Data chapter](https://lib.vizzuhq.com/latest/tutorial/data/) for
15 | more details about data.
16 |
17 | Here's some sample code for common use cases.
18 |
19 | ## Specify data by series
20 |
21 | ```javascript
22 | const data = {
23 | series: [{
24 | name: 'Foo',
25 | values: ['Alice', 'Bob', 'Ted']
26 | }, {
27 | name: 'Bar',
28 | values: [15, 32, 12]
29 | }, {
30 | name: 'Baz',
31 | values: [5, 3, 2]
32 | }]
33 | }
34 | ```
35 |
36 | ## Specify data by records
37 |
38 | ```javascript
39 | const data = {
40 | series: [{
41 | name: 'Foo',
42 | type: 'dimension',
43 | }, {
44 | name: 'Bar',
45 | type: 'measure',
46 | }, {
47 | name: 'Baz',
48 | type: 'measure',
49 | }],
50 | records: [
51 | ['Alice', 15, 5],
52 | ['Bob', 32, 3],
53 | ['Ted', 12, 2],
54 | ],
55 | }
56 | ```
57 |
--------------------------------------------------------------------------------
/docs/tutorial/index.md:
--------------------------------------------------------------------------------
1 | # Tutorial
2 |
3 | This is an excellent starting point to get acquainted with `Vizzu-Story`, as it
4 | walks you through the installation and initialization of the extension,
5 | introduces the logic it employs and the different settings to control how your
6 | animated data stories look and behave.
7 |
8 | The tutorial is organized into chapters that introduce the concept and the
9 | details of `Vizzu-Story` step-by-step. You can find the list of chapters at the
10 | end of this page and in the menu.
11 |
12 | Since `Vizzu-Story` is built on top of
13 | [Vizzu](https://github.com/vizzuhq/vizzu), it's recommended that you learn and
14 | understand `Vizzu` first. The tutorial for `Vizzu` can be found
15 | [here](https://lib.vizzuhq.com/latest/tutorial/).
16 |
17 | ## Basic logic of Vizzu-Story
18 |
19 | { class='image-center' }
20 |
21 | With `Vizzu-Story`, you can build and show a `story` object that contains all of
22 | the data being shown throughout the story and the charts created based on that
23 | data, arranged into `slides` and `steps`. When played,`Vizzu-Story`
24 | automatically adds a set of buttons underneath the chart, via which the users
25 | can navigate between the `slides` within the story.
26 |
27 | `slides` can contain one or more `steps`.
28 |
29 | A `step` (and a single-step `slide`) is basically the same as a single chart
30 | corresponding to an [animate](https://lib.vizzuhq.com/latest/tutorial/) call
31 | from `Vizzu`, with a minor, but important difference:
32 |
33 | - all of the data has to be added to the story at initialization, and it can be
34 | filtered at every `step` throughout the `story`.
35 |
36 | In case of a `slide` with multiple `steps`, all, but the last `steps` are
37 | interim charts that connect a `slide` with a previous `slide` but the animation
38 | will not stop at these `steps` when the `story` is being played.
39 |
40 | ## Installation
41 |
42 | Install via [npm](https://www.npmjs.com/package/vizzu-story):
43 |
44 | ```sh
45 | npm install vizzu-story
46 | ```
47 |
48 | Or use it from [CDN](https://www.jsdelivr.com/package/npm/vizzu-story):
49 |
50 | ```javascript
51 | import VizzuPlayer from 'https://cdn.jsdelivr.net/npm/vizzu-story@latest/dist/vizzu-story.min.js'
52 | ```
53 |
54 | Visit [Installation chapter](../installation.md) for more options and details.
55 |
56 | ## Usage
57 |
--------------------------------------------------------------------------------
/docs/tutorial/initialization.md:
--------------------------------------------------------------------------------
1 | # Initialization
2 |
3 | ## Import
4 |
5 | In a script module element import the extension from `CDN` or local install:
6 |
7 | ```
8 |
12 | ```
13 |
14 | ## Constructor
15 |
16 | In order to initialize a `VizzuPlayer` with a `VizzuController` that will
17 | contain the rendered `story`, create a `vizzu-player` `HTML` element:
18 |
19 | ```
20 |
21 | ```
22 |
23 | ## Size
24 |
25 | `Vizzu-Story` tries to apply the ideal size for the story, but you can also set
26 | them manually via the `width` and `height` style properties of the
27 | `vizzu-player` `HTML` element:
28 |
29 | Set size in `HTML`
30 |
31 | ```
32 |
33 |
39 |
40 |
41 |
42 |
43 | ```
44 |
45 | or in `JavaScript`:
46 |
47 | ```javascript
48 | const vp = document.querySelector('vizzu-player')
49 | vp.style.cssText = 'width: 100%;height: 400px;'
50 | ```
51 |
52 | ## HTML attributes
53 |
54 | ### vizzu-url
55 |
56 | `Vizzu-Story` requires and downloads the
57 | [Vizzu](https://github.com/vizzuhq/vizzu-lib) `JavaScript`/`C++`
58 | [library](https://www.jsdelivr.com/package/npm/vizzu) from `jsDelivr CDN`, but
59 | you can also use a different or self-hosted version of it.
60 |
61 | Set `Vizzu` via the `vizzu-url` `HTML` attribute
62 |
63 | ```
64 |
65 | ```
66 |
67 | or add it to `window`:
68 |
69 | ```javascript
70 | import Vizzu from '/vizzu.min.js'
71 |
72 |
73 | window.Vizzu = Vizzu
74 | ```
75 |
76 | !!! info
77 |
78 | After initialization, you can access the imported `Vizzu` library via the
79 | `Vizzu` getter.
80 |
81 | ```javascript
82 | const vp = document.querySelector('vizzu-player')
83 |
84 | vp.initializing.then((chart) => {
85 | const Vizzu = vp.Vizzu
86 | })
87 | ```
88 |
89 | ### hash-navigation
90 |
91 | If you add `hash-navigation` attribute, slides can be selected using the `URL`
92 | hash (`#` part), for example `presentation.html#3` selects slide `3`. You can
93 | also use negative numbers, where `-1` means the last slide.
94 |
95 | ```
96 |
97 | ```
98 |
99 | ### start-slide
100 |
101 | You can start the story on a specific slide via the `start-slide` `HTML`
102 | attribute. You can also use negative numbers, where `-1` means the last slide.
103 |
104 | ```
105 |
106 | ```
107 |
108 | ### custom-spinner
109 |
110 | You can use a custom loading animation. Set spinner via the `custom-spinner`
111 | `HTML` attribute:
112 |
113 | ```
114 |
115 | ```
116 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import { EslintJavaScriptConfig, EslintTypeScriptConfig } from '@vizzu/eslint-config'
2 |
3 | export default [
4 | ...EslintTypeScriptConfig,
5 | ...EslintJavaScriptConfig,
6 | {
7 | ignores: [
8 | '**/node_modules/**',
9 | '**/build/**',
10 | '**/dist/**',
11 | '**/.vscode/**',
12 | '**/*.d.ts'
13 | ]
14 | }
15 | ]
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vizzu-story",
3 | "version": "0.8.0",
4 | "description": "Build and present animated data stories",
5 | "main": "dist/vizzu-story.min.js",
6 | "types": "dist/vizzu-story.d.ts",
7 | "type": "module",
8 | "files": [
9 | "dist/vizzu-story.d.ts"
10 | ],
11 | "scripts": {
12 | "lock": "npm-run-all lock:*",
13 | "lock:js": "npm update",
14 | "lock:py": "pdm lock --no-default -d",
15 | "format": "npm-run-all format:*",
16 | "format:src": "npm-run-all format-src:*",
17 | "format-src:js": "npx prettier -c src tests package.json",
18 | "format:docs": "npm-run-all format-docs:*",
19 | "format-docs:js": "npx prettier -c docs",
20 | "format-docs:py": "python ./tools/ci/std_check.py mdformat --wrap 80 --end-of-line keep --line-length 70 --check docs README.md CONTRIBUTING.md CODE_OF_CONDUCT.md",
21 | "format:tools": "npm-run-all format-tools:*",
22 | "format-tools:js": "npx prettier -c tools .github",
23 | "format-tools:py": "black --diff --check tools",
24 | "lint": "npm-run-all lint:*",
25 | "lint:src": "npm-run-all lint-src:*",
26 | "lint-src:js": "npx eslint src tests",
27 | "lint:docs": "npm-run-all lint-docs:*",
28 | "lint-docs:js": "npx eslint docs",
29 | "lint:tools": "npm-run-all lint-tools:*",
30 | "lint-tools:js": "npx eslint tools",
31 | "lint-tools:py": "pylint tools",
32 | "type": "npm-run-all type:*",
33 | "type:src": "npm-run-all type-src:*",
34 | "type-src:js": "npx check-dts src/vizzu-story.d.ts",
35 | "type:tools": "npm-run-all type-tools:*",
36 | "type-tools:py": "mypy tools",
37 | "test": "npm-run-all test:*",
38 | "test:unit": "NODE_OPTIONS='--no-warnings --experimental-vm-modules' npx jest --config tests/vizzu-player/unit/jest.config.js --verbose",
39 | "test:e2e": "NODE_OPTIONS='--no-warnings --experimental-vm-modules' npx jest --config tests/vizzu-player/e2e/jest.config.cjs --verbose",
40 | "ci": "npm-run-all ci:*",
41 | "ci:src": "npm-run-all ci-src:*",
42 | "ci-src:js": "npm-run-all format-src:js lint-src:js type-src:js test",
43 | "ci:docs": "npm-run-all ci-docs:*",
44 | "ci-docs:js": "npm-run-all format-docs:js lint-docs:js",
45 | "ci-docs:py": "npm-run-all format-docs:py",
46 | "ci:tools": "npm-run-all ci-tools:*",
47 | "ci-tools:js": "npm-run-all format-tools:js lint-tools:js",
48 | "ci-tools:py": "npm-run-all format-tools:py lint-tools:py type-tools:py",
49 | "fix": "npm-run-all fix-format fix-lint",
50 | "fix-format": "npm-run-all fix-format:*",
51 | "fix-format:src": "npm-run-all fix-format-src:*",
52 | "fix-format-src:js": "npx prettier -w src tests package.json",
53 | "fix-format:docs": "npm-run-all fix-format-docs:*",
54 | "fix-format-docs:js": "npx prettier -w docs",
55 | "fix-format-docs:py": "python ./tools/ci/std_check.py mdformat --wrap 80 --end-of-line keep --line-length 70 docs README.md CONTRIBUTING.md CODE_OF_CONDUCT.md",
56 | "fix-format:tools": "npm-run-all fix-format-tools:*",
57 | "fix-format-tools:js": "npx prettier -w tools .github",
58 | "fix-format-tools:py": "black tools",
59 | "fix-lint": "npm-run-all fix-lint:*",
60 | "fix-lint:src": "npm-run-all fix-lint-src:*",
61 | "fix-lint-src:js": "npx eslint --fix src tests",
62 | "fix-lint:docs": "npm-run-all fix-lint-docs:*",
63 | "fix-lint-docs:js": "npx eslint --fix docs",
64 | "fix-lint:tools": "npm-run-all fix-lint-tools:*",
65 | "fix-lint-tools:js": "npx eslint --fix tools",
66 | "build-docs": "mkdocs build -f ./tools/docs/mkdocs.yml",
67 | "deploy-docs": "python ./tools/docs/deploy.py",
68 | "build": "python ./tools/ci/version.py False && rm -rf dist build && rollup -c && mkdir build && npm pack --pack-destination build && tar -ztvf build/*.tgz"
69 | },
70 | "repository": {
71 | "type": "git",
72 | "url": "https://github.com/vizzuhq/vizzu-story-js.git"
73 | },
74 | "keywords": [
75 | "template",
76 | "interactive",
77 | "presentation",
78 | "data-visualization",
79 | "charting",
80 | "vizzu"
81 | ],
82 | "author": "Vizzu Inc.",
83 | "license": "Apache-2.0",
84 | "homepage": "https://vizzu-story.vizzuhq.com/",
85 | "url": "https://github.com/vizzuhq/vizzu-story-js/issues",
86 | "devDependencies": {
87 | "@rollup/plugin-terser": "^0.4.4",
88 | "@vizzu/eslint-config": "^1.0.0",
89 | "@vizzu/prettier-config": "^0.1.0",
90 | "check-dts": "^0.8.2",
91 | "eslint": "^9.19.0",
92 | "express": "^4.21.2",
93 | "husky": "^9.1.7",
94 | "jest": "^29.7.0",
95 | "jest-environment-jsdom": "^29.7.0",
96 | "lodash.clonedeep": "^4.5.0",
97 | "npm-run-all": "^4.1.5",
98 | "prettier": "^3.4.2",
99 | "puppeteer": "^24.2.0",
100 | "rollup": "^4.34.4",
101 | "rollup-plugin-copy": "^3.5.0",
102 | "typedoc": "^0.27.6",
103 | "typedoc-plugin-markdown": "^4.4.1",
104 | "typedoc-plugin-rename-defaults": "^0.7.2",
105 | "typescript": "^5.7.3"
106 | },
107 | "dependencies": {
108 | "vizzu": "~0.16.1"
109 | },
110 | "prettier": "@vizzu/prettier-config"
111 | }
112 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "vizzu-story-dev"
3 | version = "0.1.0"
4 | description = ""
5 | authors = [
6 | {name = "Vizzu Inc.", email = "hello@vizzu.io"},
7 | ]
8 | dependencies = []
9 | requires-python = ">=3.13"
10 | readme = "README.md"
11 | license = {text = "Apache-2.0"}
12 |
13 | [tool.pdm.dev-dependencies]
14 | codequality = [
15 | "black",
16 | "pylint",
17 | "mypy",
18 | ]
19 | docs = [
20 | "setuptools",
21 | "mdformat",
22 | "mdformat-beautysh",
23 | "mdformat-black",
24 | "mdformat-configurable-black",
25 | "mdformat-config",
26 | "mdformat-web",
27 | "mdformat-gfm",
28 | "mdformat-tables",
29 | "mdformat-footnote",
30 | "mdformat-frontmatter",
31 | "mdformat-mkdocs",
32 | "mkdocs",
33 | "mkdocs-material",
34 | "mkdocs-section-index",
35 | "mkdocs-literate-nav",
36 | "mkdocs-autorefs",
37 | "mkdocs-gen-files",
38 | "mkdocs-exclude",
39 | "mike",
40 | "pyyaml",
41 | "types-pyyaml",
42 | ]
43 |
44 | [tool.pylint.messages-control]
45 | disable = ["fixme"]
46 | good-names= ["i", "df"]
--------------------------------------------------------------------------------
/rollup.config.cjs:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const terser = require('@rollup/plugin-terser')
3 | const copy = require('rollup-plugin-copy')
4 |
5 | module.exports = [
6 | {
7 | input: path.resolve(__dirname, "./src/vizzu-player.js"),
8 | output: {
9 | file: path.resolve(__dirname, "./dist/vizzu-story.min.js"),
10 | format: "es",
11 | name: "bundle",
12 | },
13 | plugins: [
14 | terser(),
15 | copy({
16 | targets: [
17 | { src: 'src/vizzu-story.d.ts', dest: 'dist', flatten: true },
18 | ],
19 | })
20 | ],
21 | },
22 | ]
23 |
--------------------------------------------------------------------------------
/src/AnimationQueue.js:
--------------------------------------------------------------------------------
1 | class AnimationNode {
2 | constructor(configObject, animOptions, parameters) {
3 | this.configObject = configObject
4 | this.animOptions = animOptions
5 | this.parameters = parameters
6 | this.next = null
7 | }
8 | }
9 |
10 | class AnimationQueue {
11 | constructor(vizzu) {
12 | this.head = null
13 | this.tail = null
14 | this.vizzu = vizzu
15 | this.playing = false
16 | this.paused = false
17 | this.controller = null
18 | this.direction = 'normal'
19 | this.lastAnimation = null
20 | this._lastParameters = null
21 | this.seekerEnabled = false
22 |
23 | this.vizzu.on('animation-complete', () => {
24 | this.playing = false
25 | this.next()
26 | })
27 | }
28 |
29 | enqueue(configObject, animOptions, parameters = null) {
30 | if (
31 | this.tail &&
32 | this.tail.configObject === configObject &&
33 | this.tail.animOptions === animOptions &&
34 | this.tail.parameters === parameters
35 | )
36 | return
37 |
38 | const newNode = new AnimationNode(configObject, animOptions, parameters)
39 | if (!this.head) {
40 | this.head = newNode
41 | this.tail = newNode
42 | } else {
43 | this.tail.next = newNode
44 | this.tail = newNode
45 | }
46 |
47 | if (!this.playing) {
48 | this.play()
49 | }
50 | }
51 |
52 | dequeue() {
53 | if (!this.head) return
54 |
55 | const removedNode = this.head
56 | this.head = this.head.next
57 | return removedNode
58 | }
59 |
60 | insertqueue(configObject, animOptions) {
61 | if (!this.head) return
62 | const firstAnimation = this.head
63 | const newAnimation = new AnimationNode(configObject, animOptions)
64 |
65 | if (!firstAnimation.next) {
66 | // There's no animation after the first one
67 | firstAnimation.next = newAnimation
68 | // The new animation becomes the last in the queue
69 | this.tail = newAnimation
70 | } else {
71 | // There's already an animation after the first one
72 | newAnimation.next = firstAnimation.next
73 | firstAnimation.next = newAnimation
74 | }
75 | }
76 |
77 | isLast(node) {
78 | if (!node || !node.next) return true
79 | return node.next === null
80 | }
81 |
82 | isEmpty() {
83 | return this.head === null
84 | }
85 |
86 | clear() {
87 | this.head = null
88 | this.tail = null
89 | }
90 |
91 | peek() {
92 | return this.head
93 | }
94 |
95 | play() {
96 | this.playing = false
97 | if (!this.head) return
98 |
99 | const firstAnimation = this.head
100 | if (firstAnimation.animOptions.playState === 'paused') {
101 | this.paused = true
102 | firstAnimation.animOptions.playState = 'running'
103 | }
104 |
105 | if (firstAnimation.animOptions.direction === 'reverse') {
106 | this.direction = 'reverse'
107 | } else {
108 | this.direction = 'normal'
109 | }
110 | this.seekerEnabled = true
111 | if (firstAnimation.parameters.steppType !== 'normal') {
112 | this.seekerEnabled = false
113 | }
114 |
115 | // change speed when the current animate is not a last
116 | let configObject = firstAnimation.configObject
117 |
118 | if (!this.isLast(firstAnimation)) {
119 | configObject = this._speedUp(firstAnimation.configObject)
120 | }
121 |
122 | let startSlideConfig = null
123 | if (configObject.length > 1) {
124 | startSlideConfig = configObject[0]
125 | this.vizzu.feature('rendering', false)
126 | this.vizzu.animate(startSlideConfig.target, 0).catch(this.vizzuCatch)
127 | }
128 | const vizzuAnimate = this.vizzu.animate(configObject, firstAnimation.animOptions)
129 |
130 | vizzuAnimate.activated.then((control) => {
131 | this.playing = true
132 | this._lastParameters = firstAnimation.parameters || null
133 | this.vizzu.feature('rendering', true)
134 | this.controller = control
135 |
136 | if (this.paused) {
137 | control.pause()
138 | }
139 | })
140 |
141 | vizzuAnimate.catch((message) => {
142 | if (message.name !== 'CancelError') {
143 | console.error(message)
144 | }
145 | })
146 |
147 | this.lastAnimation = {
148 | configObject,
149 | animOptions: firstAnimation.animOptions
150 | }
151 | if (!this.paused && this.direction === 'reverse' && startSlideConfig !== null) {
152 | this.vizzu.animate(startSlideConfig.target, 0).catch(this.vizzuCatch)
153 | }
154 | }
155 |
156 | next() {
157 | this.dequeue()
158 |
159 | if (!this.head) {
160 | this.playing = false
161 | return
162 | }
163 |
164 | this.play()
165 | }
166 |
167 | pause() {
168 | if (!this.controller) return
169 |
170 | this.playing = false
171 | this.paused = true
172 | this.controller.pause()
173 | }
174 |
175 | reverse() {
176 | if (!this.controller) return
177 | this.playing = true
178 | this.direction = 'reverse'
179 | this.controller.reverse()
180 | this.controller.play()
181 | }
182 |
183 | seekStart(percent) {
184 | this.playing = false
185 | this.controller.cancel()
186 | this.vizzu.feature('rendering', false)
187 | if (this.lastAnimation.configObject.length > 1) {
188 | this.vizzu
189 | .animate(this.lastAnimation.configObject[0].target, {
190 | position: 1,
191 | duration: '0s'
192 | })
193 | .catch(this.vizzuCatch)
194 | }
195 | const vizzuAnimate = this.vizzu.animate(
196 | this._speedUp(this.lastAnimation.configObject),
197 | this.lastAnimation.animOptions
198 | )
199 |
200 | vizzuAnimate.activated.then((control) => {
201 | this.controller = control
202 | control.pause()
203 | this.pushed = true
204 | control.seek((this._currentSeek || percent) + '%')
205 | this.vizzu.feature('rendering', true)
206 | })
207 |
208 | vizzuAnimate.catch(this.vizzuCatch)
209 | }
210 |
211 | seek(percent) {
212 | if (!this.controller) return
213 | this._currentSeek = percent
214 | this.controller.seek(percent + '%')
215 | }
216 |
217 | vizzuCatch(message) {
218 | if (message.name !== 'CancelError') {
219 | console.error(message)
220 | }
221 | }
222 |
223 | getParameter(key) {
224 | if (this._lastParameters && key in this._lastParameters) {
225 | return this._lastParameters[key]
226 | }
227 | return null
228 | }
229 |
230 | manualUpdate(configObject, animOptions, slideNumber) {
231 | if (this.controller) {
232 | this.controller.play()
233 | this.controller.stop()
234 | }
235 |
236 | if (!this.head) {
237 | this.enqueue(configObject, animOptions, slideNumber)
238 | return
239 | }
240 |
241 | // Override the configuration and options of the first animation
242 | this.head.configObject = configObject
243 | this.head.animOptions = animOptions
244 | this.play()
245 | }
246 |
247 | abort() {
248 | if (!this.controller) return
249 |
250 | this.playing = false
251 | this.paused = false
252 | this.controller.stop()
253 | this.controller = null
254 | this.dequeue()
255 | this.next()
256 | }
257 |
258 | isPaused() {
259 | return this.paused
260 | }
261 |
262 | isPlaying() {
263 | return this.playing
264 | }
265 |
266 | hasNext() {
267 | return !!this.head && !!this.head.next
268 | }
269 |
270 | continue() {
271 | if (!this.controller) return
272 |
273 | this.paused = false
274 | this.playing = true
275 |
276 | this.controller.play()
277 | }
278 |
279 | _speedUp(configObject, duration = '500ms') {
280 | if (configObject instanceof Array) {
281 | return configObject.map((elem) => {
282 | return { target: elem.target, options: { duration } }
283 | })
284 | }
285 |
286 | if (configObject instanceof Object && configObject.target) {
287 | return {
288 | target: configObject.target,
289 | options: { duration }
290 | }
291 | }
292 | return {
293 | target: configObject,
294 | options: { duration }
295 | }
296 | }
297 | }
298 |
299 | export default AnimationQueue
300 |
--------------------------------------------------------------------------------
/src/controllers/slider.js:
--------------------------------------------------------------------------------
1 | const LOG_PREFIX = [
2 | '%cVIZZU%cSLIDER',
3 | 'background: #e2ae30; color: #3a60bf; font-weight: bold',
4 | 'background: #000000; color: #fafafa;'
5 | ]
6 |
7 | const TEXT = {
8 | SEEK: 'Seek animation'
9 | }
10 |
11 | class Slider extends HTMLElement {
12 | constructor() {
13 | super()
14 |
15 | this.attachShadow({ mode: 'open' })
16 | this.shadowRoot.innerHTML = this._render()
17 |
18 | let isPointerDown = false
19 |
20 | this.slider = this.shadowRoot.getElementById('slider')
21 |
22 | // Set up slider event listener
23 | this.slider.addEventListener('input', (event) => {
24 | if (this.isDisabled()) {
25 | return
26 | }
27 |
28 | this.seek(event.target.value / 10)
29 | })
30 |
31 | window.addEventListener('pointermove', async (e) => {
32 | if (this.isDisabled() || !isPointerDown) {
33 | return
34 | }
35 | e.preventDefault()
36 | const currentPoition = Math.min(
37 | Math.max(0, e.offsetX - this.slider.offsetLeft),
38 | this.slider.offsetWidth
39 | )
40 | const currentPoistionInPercent = currentPoition / this.slider.offsetWidth
41 |
42 | this.seek(currentPoistionInPercent * 100)
43 | this.slider.value = currentPoistionInPercent * 1000
44 | })
45 |
46 | this.slider.addEventListener('pointerdown', async (e) => {
47 | if (this.isDisabled()) {
48 | return
49 | }
50 | isPointerDown = true
51 | const currentSlide = this.player.animationQueue.getParameter('currentSlide')
52 | this.player._currentSlide = currentSlide
53 | this.player.animationQueue.clear()
54 | this.player.animationQueue.seekStart(this.slider.value / 10)
55 | })
56 |
57 | this.slider.addEventListener('pointerup', async (e) => {
58 | if (this.isDisabled()) {
59 | return
60 | }
61 | isPointerDown = false
62 | this.player.animationQueue.continue()
63 | })
64 | }
65 |
66 | async connectedCallback() {
67 | await Promise.resolve()
68 | if (!this.controller) {
69 | const parent = this.getRootNode()?.host
70 | if (parent.nodeName === 'VIZZU-CONTROLLER') {
71 | this.controller = parent
72 | await parent.initializing
73 | this.player = parent.player
74 |
75 | const updateSlider = (event) => {
76 | if (this.player.animationQueue.playing) {
77 | const process = event.detail.progress * 1000
78 | if (this.player.direction === this.player.animationQueue.direction) {
79 | this._updateSlider(process)
80 | } else {
81 | this._updateSlider(1000 - process)
82 | }
83 |
84 | if (!this.player.animationQueue.seekerEnabled) {
85 | this.slider.setAttribute('readonly', true)
86 | }
87 | }
88 | }
89 | this.player.vizzu.on('update', updateSlider)
90 |
91 | const _checkDisabeld = () => {
92 | this.slider.removeAttribute('readonly')
93 | if (
94 | !this.player.animationQueue.playing &&
95 | !this.player.animationQueue.seekerEnabled
96 | ) {
97 | this.slider.setAttribute('disabled', true)
98 | }
99 | }
100 | this.player.vizzu.on('animation-complete', _checkDisabeld)
101 | }
102 | }
103 | }
104 |
105 | seek(percent) {
106 | this.player.animationQueue.seek(percent)
107 | }
108 |
109 | isDisabled() {
110 | return this.slider.hasAttribute('disabled') || this.slider.hasAttribute('readonly')
111 | }
112 |
113 | log(...msg) {
114 | if (this.player.debug) {
115 | console.log(...LOG_PREFIX, ...msg)
116 | }
117 | }
118 |
119 | _update(state) {
120 | this.log('update', state)
121 | const e = new CustomEvent('update', { detail: state })
122 | this.dispatchEvent(e)
123 | }
124 |
125 | _updateSlider(value) {
126 | if (!this.slider) {
127 | return null
128 | }
129 | if (this.player.direction === 'normal' && this.player.currentSlide === 0) {
130 | this.slider.setAttribute('disabled', true)
131 | this.slider.value = 0
132 | } else {
133 | this.slider.removeAttribute('disabled')
134 | this.slider.removeAttribute('readonly')
135 | this.slider.value = value
136 | }
137 | }
138 |
139 | _render() {
140 | return `
141 |
220 |
221 |
222 |
`
223 | }
224 | }
225 |
226 | try {
227 | if (!customElements.get('vizzu-controller-slider')) {
228 | customElements.define('vizzu-controller-slider', Slider)
229 | } else {
230 | console.warn('Slider already defined')
231 | }
232 | } catch (e) {
233 | console.error('Failed to register custom element: ', e)
234 | }
235 |
236 | export default Slider
237 |
--------------------------------------------------------------------------------
/src/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vizzu - Presentation sample
8 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/example/index.js:
--------------------------------------------------------------------------------
1 | // import VizzuPlayer from "https://cdn.jsdelivr.net/npm/vizzu-story@latest/dist/vizzu-story.min.js"
2 | import VizzuPlayer from '../vizzu-player.js' // eslint-disable-line no-unused-vars
3 |
4 | import data from './data.js'
5 | import style from './style.js'
6 |
7 | function labelHandler(event) {
8 | const Year = parseFloat(event.detail.text)
9 | if (!isNaN(Year) && Year > 1950 && Year < 2020 && Year % 5 !== 0) {
10 | event.preventDefault()
11 | }
12 | }
13 |
14 | // eslint-disable-next-line no-unused-vars
15 | const vizzuPlayerData = {
16 | data, // data, copied into the initializer slide (if not present)
17 | style, // style, copied into the initializer slide (if not present)
18 | slides: [
19 | // slide
20 | {
21 | config: {}, // Config.Chart
22 | filter: () => true, // data.filter TODO: not declarative, cannot be
23 | // serialized ??? string => Function
24 | style: {}, // Styles.Chart
25 | animOptions: {} // Anim.Options
26 | },
27 | // ... or list of slides
28 | [
29 | {}, // phase1,
30 | {} // phase2, ...
31 | ]
32 | ]
33 | }
34 |
35 | const vpd = {
36 | data,
37 | style,
38 | slides: [
39 | {
40 | // slide 1
41 | filter: (record) => record.Function !== 'Defense',
42 | config: {
43 | channels: {
44 | y: {
45 | set: ['Amount', 'Function'],
46 | range: {
47 | min: '0%',
48 | max: '100%'
49 | }
50 | },
51 | x: {
52 | set: ['Year']
53 | },
54 | color: 'Function'
55 | },
56 | title: 'U.S. Non-Defense R&D Budget by Functions',
57 | geometry: 'area'
58 | }
59 | },
60 | {
61 | // slide 2
62 | config: {
63 | title: 'Share of Total Expenditures %',
64 | align: 'stretch'
65 | }
66 | },
67 | [
68 | // slide 3
69 | {
70 | // slide 3.1
71 | filter: (record) => record.Function === 'Health' || record.Function === 'Space',
72 | config: {
73 | title: 'Compare Space & Health',
74 | align: 'none',
75 | split: true
76 | }
77 | },
78 | {
79 | // slide 3.2
80 | filter: (record) => record.Function !== 'Defense',
81 | config: {
82 | title: 'All Non-defense Functions Side-by-Side',
83 | align: 'none',
84 | split: true
85 | }
86 | }
87 | ],
88 | [
89 | // slide 4
90 | {
91 | // slide 4.1
92 | filter: null,
93 | config: {
94 | title: 'Show Defense Expenditures',
95 | split: false
96 | }
97 | },
98 | {
99 | // slide 4.2
100 | filter: (record) => record.Function === 'Defense',
101 | config: {
102 | title: 'Defense Expenditures',
103 | align: 'none'
104 | }
105 | }
106 | ],
107 | {
108 | // slide 5
109 | filter: null,
110 | config: {
111 | title: 'Total U.S. R&D Budget'
112 | }
113 | },
114 | {
115 | // slide 6
116 | config: {
117 | title: 'Total U.S. R&D Budget - Components Side by Side',
118 | x: 'Year',
119 | y: 'Amount',
120 | noop: 'Function',
121 | align: 'none',
122 | geometry: 'line'
123 | }
124 | }
125 | ]
126 | }
127 |
128 | const vp = document.querySelector('vizzu-player')
129 | vp.initializing.then((chart) => {
130 | vp.slides = vpd // init slides
131 | chart.on('plot-axis-label-draw', labelHandler)
132 | chart.feature('tooltip', true)
133 | })
134 |
135 | /*
136 | TODO:
137 | - vizzu spinner
138 | - `animate` with seek
139 | - `reverse` animation
140 | - !SEEK!
141 | - animOptions check
142 | - styling with CSS variables
143 | */
144 |
--------------------------------------------------------------------------------
/src/example/loadinganim.svg:
--------------------------------------------------------------------------------
1 |
28 |
--------------------------------------------------------------------------------
/src/example/style.js:
--------------------------------------------------------------------------------
1 | const style = {
2 | plot: {
3 | paddingLeft: '0em',
4 | yAxis: {
5 | label: {
6 | fontSize: '1em',
7 | paddingRight: '1.2em'
8 | },
9 | title: {
10 | color: '#ffffff00'
11 | }
12 | },
13 | xAxis: {
14 | label: {
15 | angle: '2.5',
16 | fontSize: '1.1em',
17 | paddingRight: '0em',
18 | paddingTop: '1em'
19 | },
20 | title: {
21 | fontSize: '1em',
22 | paddingTop: '2.5em'
23 | }
24 | }
25 | }
26 | }
27 |
28 | export default style
29 |
--------------------------------------------------------------------------------
/src/vizzu-controller.js:
--------------------------------------------------------------------------------
1 | import Slider from './controllers/slider.js'
2 |
3 | const LOG_PREFIX = [
4 | '%cVIZZU%cCONTROLLER',
5 | 'background: #e2ae30; color: #3a60bf; font-weight: bold',
6 | 'background: #3a60bf; color: #e2ae30;'
7 | ]
8 |
9 | const TEXT = {
10 | PLAY: 'Play',
11 | PAUSE: 'Pause',
12 | NEXT: 'Next',
13 | PREVIOUS: 'Previous',
14 | FIRST: 'First',
15 | LAST: 'Last',
16 | FULLSCREEN: 'Fullscreen',
17 | EXIT_FULLSCREEN: 'Exit fullscreen',
18 | FULLSCREEN_NOT_ALLOWED: 'Fullscreen not allowed'
19 | }
20 |
21 | class VizzuController extends HTMLElement {
22 | constructor() {
23 | super()
24 |
25 | this.attachShadow({ mode: 'open' })
26 | this.shadowRoot.innerHTML = this._render()
27 |
28 | this._update = this.update.bind(this)
29 | this._keyHandler = this._handleKey.bind(this)
30 |
31 | this.shadowRoot.addEventListener('click', (e) => {
32 | const btn = e.target.closest('button')
33 |
34 | if (!btn) return
35 | switch (btn.id) {
36 | case 'start':
37 | this.toStart()
38 | break
39 | case 'end':
40 | this.toEnd()
41 | break
42 | case 'previous':
43 | this.previous()
44 | break
45 | case 'next':
46 | this.next()
47 | break
48 | case 'fullscreen':
49 | this.fullscreen()
50 | break
51 | }
52 | })
53 |
54 | this._resolvePlayer = null
55 | this.initializing = new Promise((resolve) => {
56 | this._resolvePlayer = resolve
57 | })
58 | }
59 |
60 | update(e) {
61 | const data = e.detail
62 | this.log('update', data)
63 | this._state = data
64 |
65 | if (data.currentSlide === 0) {
66 | this.setAttribute('first', '')
67 | } else {
68 | this.removeAttribute('first')
69 | }
70 |
71 | if (data.currentSlide === data.length - 1) {
72 | this.setAttribute('last', '')
73 | } else {
74 | this.removeAttribute('last')
75 | }
76 |
77 | this.shadowRoot.getElementById('status').innerHTML = this._html_status
78 | }
79 |
80 | get currentSlide() {
81 | if (!this.player || !this.player.animationQueue) return null
82 | return this?.player?.animationQueue.getParameter('currentSlide')
83 | }
84 |
85 | get _html_status() {
86 | return `${(this.currentSlide || 0) + 1}/${
87 | this._state?.length || '?'
88 | }`
89 | }
90 |
91 | _unsubscribe(player) {
92 | player?.removeEventListener('update', this._update)
93 | this._player = null
94 | }
95 |
96 | _subscribe(player) {
97 | player?.addEventListener('update', this._update)
98 | this._player = player
99 | }
100 |
101 | _handleKey(e) {
102 | const kbmode = this._player?.getAttribute('keyboard') || 'focus'
103 | this.log(`key[${kbmode}]: ${e.key}`)
104 |
105 | const usedControllKeys = [
106 | 'PageUp',
107 | 'PageDown',
108 | 'ArrowRight',
109 | 'ArrowLeft',
110 | 'Home',
111 | 'End',
112 | 'f',
113 | 'F',
114 | 'Escape',
115 | 'Enter'
116 | ]
117 |
118 | if (
119 | usedControllKeys.includes(e.key) &&
120 | (kbmode === 'focus' || (kbmode === 'fullscreen' && document.fullscreenElement))
121 | ) {
122 | e.preventDefault()
123 | switch (e.key) {
124 | case 'ArrowRight':
125 | case 'PageDown':
126 | this.next()
127 | break
128 |
129 | case 'ArrowLeft':
130 | case 'PageUp':
131 | this.previous()
132 | break
133 |
134 | case 'Home':
135 | this.toStart()
136 | break
137 |
138 | case 'End':
139 | this.toEnd()
140 | break
141 |
142 | case 'f':
143 | case 'F':
144 | this.fullscreen()
145 | break
146 | case 'Escape':
147 | if (document.fullscreenElement) {
148 | document.exitFullscreen()
149 | }
150 | break
151 | }
152 | }
153 | }
154 |
155 | async connectedCallback() {
156 | if (!this._player) {
157 | const p = this.getRootNode()?.host
158 | if (p.nodeName === 'VIZZU-PLAYER') {
159 | const player = this.getRootNode()?.host
160 |
161 | await player.initializing
162 | this._resolvePlayer(player.initializing)
163 | player.controller = this
164 | this._player = player
165 | this._subscribe(this._player)
166 | }
167 | }
168 |
169 | if (this._player) {
170 | this._player.addEventListener('keydown', this._keyHandler)
171 | }
172 | }
173 |
174 | disconnectedCallback() {
175 | if (this._player) {
176 | this._player.removeEventListener('keydown', this._keyHandler)
177 | this._unsubscribe(this._player)
178 | }
179 | }
180 |
181 | get player() {
182 | return this._player
183 | }
184 |
185 | set player(player) {
186 | this._player = player
187 | }
188 |
189 | get debug() {
190 | try {
191 | const debugCookie = document.cookie.split(';').some((c) => c.startsWith('vizzu-debug'))
192 | return debugCookie || this.hasAttribute('debug')
193 | } catch (e) {
194 | return this.hasAttribute('debug')
195 | }
196 | }
197 |
198 | set debug(debug) {
199 | document.cookie = `vizzu-debug=${debug ? 'true' : ''}; path=/`
200 | }
201 |
202 | log(...msg) {
203 | if (this.debug) {
204 | console.log(...LOG_PREFIX, ...msg)
205 | }
206 | }
207 |
208 | seek(percent) {
209 | this.log('seek', percent)
210 | this._player?._update(this?._player._state)
211 | this.player?.seek(percent)
212 | }
213 |
214 | toStart() {
215 | this.log('toStart')
216 | this.player?.toStart()
217 | }
218 |
219 | toEnd() {
220 | this.log('toEnd')
221 | this.player?.toEnd()
222 | }
223 |
224 | previous() {
225 | this.log('previous')
226 | this.player?.previous()
227 | }
228 |
229 | next() {
230 | this.log('next')
231 | this.player?.next()
232 | }
233 |
234 | get fullscreenTarget() {
235 | return this._fullscreenTarget || this.getRootNode()?.host || this.parentElement
236 | }
237 |
238 | fullscreen() {
239 | if (!this.shadowRoot.ownerDocument.fullscreenEnabled) {
240 | console.warn(TEXT.FULLSCREEN_NOT_ALLOWED)
241 | return
242 | }
243 |
244 | if (document.fullscreenElement) {
245 | document.exitFullscreen()
246 | } else {
247 | this.fullscreenTarget?.requestFullscreen()
248 | }
249 | }
250 |
251 | _render() {
252 | return `
253 |
330 | ${this._html_status}
331 |
332 |
337 |
342 |
343 |
348 |
353 |
354 |
363 | `
364 | }
365 | }
366 |
367 | try {
368 | if (!customElements.get('vizzu-controller')) {
369 | customElements.define('vizzu-controller', VizzuController)
370 | } else {
371 | console.warn('VizzuController already defined')
372 | }
373 | } catch (e) {
374 | console.error('Failed to register custom element: ', e)
375 | }
376 |
377 | export default VizzuController
378 | export { Slider }
379 |
--------------------------------------------------------------------------------
/src/vizzu-story.d.ts:
--------------------------------------------------------------------------------
1 | /** Vizzu library types */
2 | import * as vizzu from 'vizzu'
3 |
4 | type Vizzu = typeof vizzu
5 |
6 | declare namespace Vizzu {
7 | export import Config = vizzu.Config
8 | export import Data = vizzu.Data
9 | export import Styles = vizzu.Styles
10 | export import Anim = vizzu.Anim
11 | }
12 |
13 | /** Atomic phase of a slide coressponding to one Vizzu.animate() call. */
14 | interface Phase {
15 | config?: Vizzu.Config.Chart
16 | filter?: Vizzu.Data.FilterCallback | null
17 | style?: Vizzu.Styles.Chart
18 | animOptions?: Vizzu.Anim.Options
19 | }
20 |
21 | /** Slide consists of a single or multiple phase. Controls will navigate
22 | * between slides. */
23 | type Slide = Phase | Phase[]
24 |
25 | /** Story configuration object represents the whole presentation containing
26 | * the underlying data and the slides. */
27 | interface Story {
28 | /** Data, copied into the initializer slide (if not present). */
29 | data?: Vizzu.Data.Set
30 | /** Initial style, copied into the initializer slide (if not present). */
31 | style?: Vizzu.Styles.Chart
32 | /** The sequence of the presentation's slides. */
33 | slides: Slide[]
34 | }
35 |
36 | export default class VizzuPlayer extends HTMLElement {
37 | /** Setter for story object. */
38 | set slides(slides: Story)
39 | }
40 |
--------------------------------------------------------------------------------
/tests/assets/chart-params/animOptions.js:
--------------------------------------------------------------------------------
1 | const animOptionsAssets = {}
2 |
3 | animOptionsAssets.animOptions1 = {
4 | duration: 1
5 | }
6 |
7 | animOptionsAssets.animOptions2 = {
8 | delay: 5
9 | }
10 |
11 | animOptionsAssets.animOptions3 = {
12 | regroupStrategy: 'drilldown'
13 | }
14 |
15 | export { animOptionsAssets }
16 |
--------------------------------------------------------------------------------
/tests/assets/chart-params/config.js:
--------------------------------------------------------------------------------
1 | const configAssets = {}
2 |
3 | configAssets.config1 = {
4 | x: 'Foo',
5 | y: 'Bar'
6 | }
7 |
8 | configAssets.config2 = {
9 | geometry: 'circle'
10 | }
11 |
12 | configAssets.config3 = {
13 | coordSystem: 'polar'
14 | }
15 |
16 | export { configAssets }
17 |
--------------------------------------------------------------------------------
/tests/assets/chart-params/data.js:
--------------------------------------------------------------------------------
1 | const dataAssets = {}
2 |
3 | dataAssets.dataInit = {
4 | series: [
5 | { name: 'Foo', values: ['Alice', 'Bob', 'Ted'] },
6 | { name: 'Bar', values: [15, 32, 12] },
7 | { name: 'Baz', values: [5, 3, 2] }
8 | ]
9 | }
10 |
11 | export { dataAssets }
12 |
--------------------------------------------------------------------------------
/tests/assets/chart-params/filter.js:
--------------------------------------------------------------------------------
1 | const filterAssets = {}
2 |
3 | filterAssets.filter1 = () => {}
4 |
5 | filterAssets.filter2 = () => true
6 |
7 | filterAssets.filter3 = () => {
8 | return []
9 | }
10 |
11 | export { filterAssets }
12 |
--------------------------------------------------------------------------------
/tests/assets/chart-params/style.js:
--------------------------------------------------------------------------------
1 | const styleAssets = {}
2 |
3 | styleAssets.styleInit = { backgroundColor: '#FFFFFFFF' }
4 |
5 | styleAssets.style1 = { title: { fontSize: 50 } }
6 |
7 | styleAssets.style2 = { borderWidth: 5 }
8 |
9 | styleAssets.style3 = { tooltip: { arrowSize: 10 } }
10 |
11 | export { styleAssets }
12 |
--------------------------------------------------------------------------------
/tests/assets/data-sets/basic.mjs:
--------------------------------------------------------------------------------
1 | const vizzuPlayerData = {
2 | data: {
3 | series: [
4 | {
5 | name: 'Foo',
6 | values: ['Alice', 'Bob', 'Ted']
7 | },
8 | {
9 | name: 'Bar',
10 | values: [15, 32, 12]
11 | },
12 | {
13 | name: 'Baz',
14 | values: [5, 3, 2]
15 | }
16 | ]
17 | },
18 | slides: [
19 | [
20 | {
21 | config: {
22 | x: 'Foo',
23 | y: 'Bar',
24 | label: 'Bar'
25 | }
26 | }
27 | ],
28 | [
29 | {
30 | filter: (record) => record.Foo === 'Bob',
31 | config: {
32 | color: 'Foo',
33 | x: 'Baz',
34 | label: 'Baz',
35 | geometry: 'circle'
36 | }
37 | }
38 | ]
39 | ]
40 | }
41 |
42 | export default vizzuPlayerData
43 |
--------------------------------------------------------------------------------
/tests/assets/data-sets/nolabel.mjs:
--------------------------------------------------------------------------------
1 | const vizzuPlayerData = {
2 | data: {
3 | series: [
4 | {
5 | name: 'Foo',
6 | values: ['Alice', 'Bob', 'Ted']
7 | },
8 | {
9 | name: 'Bar',
10 | values: [15, 32, 12]
11 | },
12 | {
13 | name: 'Baz',
14 | values: [5, 3, 2]
15 | }
16 | ]
17 | },
18 | slides: [
19 | [
20 | {
21 | config: {
22 | x: 'Foo',
23 | y: 'Bar'
24 | }
25 | }
26 | ],
27 | [
28 | {
29 | filter: (record) => record.Foo === 'Bob',
30 | config: {
31 | color: 'Foo',
32 | x: 'Baz',
33 | geometry: 'circle'
34 | }
35 | }
36 | ]
37 | ]
38 | }
39 |
40 | export default vizzuPlayerData
41 |
--------------------------------------------------------------------------------
/tests/assets/data-tests/label/basic.cjs:
--------------------------------------------------------------------------------
1 | const first = ['15', '32', '12']
2 |
3 | const expected = {
4 | 0: first,
5 | 1: ['3'],
6 | 2: first
7 | }
8 |
9 | module.exports = expected
10 |
--------------------------------------------------------------------------------
/tests/assets/data-tests/label/index.cjs:
--------------------------------------------------------------------------------
1 | const nolabel = require('./nolabel')
2 | const basic = require('./basic')
3 | const olympics = require('./olympics.cjs')
4 |
5 | const testCases = [
6 | {
7 | name: 'nolabel',
8 | expected: nolabel
9 | },
10 | {
11 | name: 'basic',
12 | expected: basic
13 | },
14 | {
15 | name: 'olympics',
16 | expected: olympics
17 | }
18 | ]
19 |
20 | for (const testCase of testCases) {
21 | for (const key in testCase.expected) {
22 | if (Array.isArray(testCase.expected[key])) {
23 | testCase.expected[key] = testCase.expected[key].sort()
24 | }
25 | }
26 | }
27 |
28 | module.exports = testCases
29 |
--------------------------------------------------------------------------------
/tests/assets/data-tests/label/nolabel.cjs:
--------------------------------------------------------------------------------
1 | const expected = {}
2 |
3 | module.exports = expected
4 |
--------------------------------------------------------------------------------
/tests/assets/data-tests/label/olympics.cjs:
--------------------------------------------------------------------------------
1 | const first = ['20 217']
2 |
3 | const expected = {
4 | 0: first,
5 | 1: [
6 | '3 080',
7 | '862',
8 | '25',
9 | '228',
10 | '515',
11 | '808',
12 | '555',
13 | '201',
14 | '103',
15 | '1 021',
16 | '1 763',
17 | '103',
18 | '3',
19 | '1 356',
20 | '996',
21 | '668',
22 | '388',
23 | '13',
24 | '5',
25 | '679',
26 | '3',
27 | '425',
28 | '201',
29 | '286',
30 | '16',
31 | '96',
32 | '10',
33 | '162',
34 | '611',
35 | '154',
36 | '42',
37 | '106',
38 | '126',
39 | '96',
40 | '494',
41 | '568',
42 | '24',
43 | '607',
44 | '108',
45 | '544',
46 | '90',
47 | '57',
48 | '195',
49 | '171',
50 | '42',
51 | '15',
52 | '18',
53 | '42',
54 | '153',
55 | '153',
56 | '176',
57 | '39',
58 | '51',
59 | '24',
60 | '120',
61 | '24',
62 | '6',
63 | '6',
64 | '6',
65 | '12',
66 | '32',
67 | '6',
68 | '78',
69 | '286',
70 | '51',
71 | '115',
72 | '36',
73 | '2',
74 | '7',
75 | '3',
76 | '121',
77 | '12',
78 | '7',
79 | '6',
80 | '3',
81 | '1'
82 | ],
83 | 2: [
84 | 'Athletics',
85 | 'Shooting',
86 | 'Rowing',
87 | 'Art. Gymn.',
88 | 'Swimming',
89 | 'Wrestling',
90 | 'Boxing',
91 | 'Fencing',
92 | 'Weightl.',
93 | 'Judo'
94 | ],
95 | 3: [
96 | 'Athletics',
97 | 'Shooting',
98 | 'Golf',
99 | 'Tennis',
100 | 'Cycling Track',
101 | 'Rowing',
102 | 'Sailing',
103 | 'Archery',
104 | 'Football',
105 | 'Art. Gymn.',
106 | 'Swimming',
107 | 'Water Polo',
108 | 'Roque',
109 | 'Wrestling',
110 | 'Boxing',
111 | 'Fencing',
112 | 'Diving',
113 | 'Tug of War',
114 | 'Lacrosse',
115 | 'Weightl.',
116 | 'Jeu de Paume',
117 | 'Equestrian',
118 | 'Cycling Road',
119 | 'Figure skating',
120 | 'Polo',
121 | 'Ice Hockey',
122 | 'Rugby',
123 | 'Ski Jumping',
124 | 'Speed skating',
125 | 'Bobsleigh',
126 | 'Skeleton',
127 | 'Hockey',
128 | 'Modern Pentathlon',
129 | 'Basketball',
130 | 'Alpine Skiing',
131 | 'Canoe Sprint',
132 | 'Canoe Marathon',
133 | 'Judo',
134 | 'Canoe Slalom',
135 | 'Cross Country Skiing',
136 | 'Volleyball',
137 | 'Artistic Swimming',
138 | 'Short Track',
139 | 'Freestyle Skiing',
140 | 'Cycling Mountain Bike',
141 | 'Softball',
142 | 'Baseball',
143 | 'Beach Volleyball',
144 | 'Snowboard',
145 | 'Luge',
146 | 'Taekwondo',
147 | 'Triathlon',
148 | 'Curling',
149 | 'Cycling BMX Racing',
150 | 'Nordic Combined',
151 | 'Marathon Swimming',
152 | 'Cycling BMX Freestyle',
153 | '3x3 Basketball',
154 | 'Surfing',
155 | 'Skateboarding',
156 | 'Karate',
157 | 'Sport Climbing',
158 | 'Handball',
159 | 'Biathlon',
160 | 'Rhythmic Gymnastics',
161 | 'Table Tennis',
162 | 'Trampoline Gymnastics',
163 | 'Cricket',
164 | 'Rackets',
165 | 'Water Motorsports',
166 | 'Badminton',
167 | 'Rugby Sevens',
168 | 'Croquet',
169 | 'Equestrian Vaulting',
170 | 'Military Patrol',
171 | 'Basque Pelota'
172 | ],
173 | 4: ['20 217'],
174 | 5: ['20 217'],
175 | 6: [
176 | '122',
177 | '278',
178 | '280',
179 | '324',
180 | '311',
181 | '438',
182 | '427',
183 | '365',
184 | '388',
185 | '421',
186 | '479',
187 | '514',
188 | '523',
189 | '542',
190 | '608',
191 | '633',
192 | '705',
193 | '724',
194 | '746',
195 | '805',
196 | '877',
197 | '986',
198 | '183',
199 | '842',
200 | '205',
201 | '927',
202 | '234',
203 | '926',
204 | '252',
205 | '957',
206 | '258',
207 | '957',
208 | '293',
209 | '973',
210 | '307',
211 | '1 080',
212 | '327'
213 | ],
214 | 7: [
215 | '122',
216 | '278',
217 | '280',
218 | '324',
219 | '311',
220 | '438',
221 | '427',
222 | '365',
223 | '388',
224 | '421',
225 | '479',
226 | '514',
227 | '523',
228 | '542',
229 | '608',
230 | '633',
231 | '705',
232 | '724',
233 | '746',
234 | '805',
235 | '877',
236 | '986',
237 | '183',
238 | '842',
239 | '205',
240 | '927',
241 | '234',
242 | '926',
243 | '252',
244 | '957',
245 | '258',
246 | '957',
247 | '293',
248 | '973',
249 | '307',
250 | '1 080',
251 | '327'
252 | ],
253 | 15: ['105', '354', '294', '3', '7'],
254 | 20: [
255 | '43',
256 | '94',
257 | '97',
258 | '110',
259 | '103',
260 | '155',
261 | '126',
262 | '109',
263 | '116',
264 | '124',
265 | '138',
266 | '145',
267 | '147',
268 | '152',
269 | '163',
270 | '174',
271 | '195',
272 | '198',
273 | '204',
274 | '226',
275 | '241',
276 | '260',
277 | '271',
278 | '300',
279 | '301',
280 | '302',
281 | '302',
282 | '307',
283 | '340'
284 | ],
285 | 21: ['43'],
286 | 22: ['11', '6', '2', '5', '2', '2', '10', '1', '1', '2', '1'],
287 | 23: ['30', '10', '17', '31', '3', '4', '10', '9', '7', '5'],
288 | 24: ['106', '14', '18', '31', '5', '11', '11', '8', '5', '5'],
289 | 25: ['106', '14', '18', '31', '5', '11', '11', '8', '5', '5'],
290 | 26: ['129', '17', '74', '36', '8', '11', '11', '8', '8', '8'],
291 | 27: ['154', '22', '84', '43', '11', '12', '11', '32', '11', '10'],
292 | 28: ['154', '22', '84', '43', '11', '12', '11', '32', '11', '10'],
293 | 29: ['195', '22', '98', '52', '21', '21', '22', '51', '14', '25'],
294 | 30: ['240', '22', '107', '65', '29', '17', '26', '25', '55', '39'],
295 | 31: ['262', '32', '110', '71', '36', '24', '27', '25', '61', '47'],
296 | 32: ['303', '35', '114', '81', '48', '24', '27', '25', '70', '52'],
297 | 33: ['327', '66', '118', '88', '56', '33', '28', '25', '75', '59'],
298 | 34: ['327', '66', '118', '88', '56', '33', '28', '25', '75', '59'],
299 | 35: ['327', '66', '118', '88', '56', '33', '28', '25', '75', '59'],
300 | 36: ['365', '66', '121', '98', '64', '43', '30', '29', '91', '67'],
301 | 37: ['404', '66', '122', '103', '72', '59', '32', '32', '103', '71'],
302 | 38: ['436', '70', '59', '127', '107', '80', '68', '33', '108', '74'],
303 | 39: ['470', '82', '102', '129', '107', '93', '40', '74', '109', '75'],
304 | 40: ['506', '92', '132', '133', '108', '103', '46', '84', '111', '78'],
305 | 41: ['551', '106', '161', '138', '115', '106', '51', '94', '113', '79'],
306 | 42: ['584', '139', '211', '142', '117', '111', '100', '64', '117', '82'],
307 | 43: ['618', '189', '260', '145', '119', '113', '104', '73', '121', '86'],
308 | 44: ['618', '236', '340', '150', '125', '121', '111', '73', '124', '89'],
309 | 45: ['701', '253', '340', '155', '130', '135', '111', '83', '126', '93'],
310 | 46: ['737', '301', '395', '160', '136', '141', '122', '87', '126', '94'],
311 | 47: ['774', '334', '395', '165', '144', '147', '133', '90', '127', '95'],
312 | 48: ['818', '354', '395', '166', '159', '160', '140', '93', '129', '96'],
313 | 49: ['855', '367', '395', '177', '172', '173', '100', '148', '98', '133'],
314 | 50: ['891', '380', '395', '186', '183', '183', '117', '156', '114', '137'],
315 | 51: ['927', '396', '395', '205', '190', '160', '191', '131', '159', '137'],
316 | 52: ['974', '407', '395', '234', '201', '198', '199', '139', '167', '138'],
317 | 53: ['1 020', '424', '395', '261', '211', '224', '207', '147', '175', '150'],
318 | 54: ['1 059', '434', '395', '283', '221', '262', '217', '164', '181', '169'],
319 | 56: [
320 | 'USA',
321 | 'Germany',
322 | 'Soviet Union',
323 | 'Great Britain',
324 | 'France',
325 | 'China',
326 | 'Italy',
327 | 'Australia',
328 | 'Hungary',
329 | 'Japan',
330 | 'Other'
331 | ],
332 | 57: first
333 | }
334 |
335 | module.exports = expected
336 |
--------------------------------------------------------------------------------
/tests/assets/mocks/vizzu-attribute.js:
--------------------------------------------------------------------------------
1 | import VizzuMock from './vizzu.js'
2 |
3 | class Vizzu extends VizzuMock {
4 | get instanceMockType() {
5 | return 'attributeInstance'
6 | }
7 |
8 | static get classMockType() {
9 | return 'attributeClass'
10 | }
11 | }
12 |
13 | export default Vizzu
14 |
--------------------------------------------------------------------------------
/tests/assets/mocks/vizzu-cdn.js:
--------------------------------------------------------------------------------
1 | import VizzuMock from './vizzu.js'
2 |
3 | class Vizzu extends VizzuMock {
4 | get instanceMockType() {
5 | return 'cdnInstance'
6 | }
7 |
8 | static get classMockType() {
9 | return 'cdnClass'
10 | }
11 | }
12 |
13 | export default Vizzu
14 |
--------------------------------------------------------------------------------
/tests/assets/mocks/vizzu-window.js:
--------------------------------------------------------------------------------
1 | import VizzuMock from './vizzu.js'
2 |
3 | class Vizzu extends VizzuMock {
4 | get instanceMockType() {
5 | return 'windowInstance'
6 | }
7 |
8 | static get classMockType() {
9 | return 'windowClass'
10 | }
11 | }
12 |
13 | export default Vizzu
14 |
--------------------------------------------------------------------------------
/tests/assets/mocks/vizzu.js:
--------------------------------------------------------------------------------
1 | class Presets {
2 | stream(config) {
3 | return {
4 | channels: {
5 | x: config.x,
6 | y: [config.y, config.stackedBy],
7 | color: config.stackedBy
8 | }
9 | }
10 | }
11 | }
12 |
13 | class Control {
14 | constructor(animation) {
15 | this._animation = animation
16 | }
17 |
18 | stop() {}
19 |
20 | pause() {}
21 | }
22 |
23 | class Vizzu {
24 | constructor() {
25 | this._snapshot = {}
26 | }
27 |
28 | animate() {
29 | this._snapshot = [...arguments][0]
30 | const anim = Promise.resolve('test1')
31 | anim.activated = Promise.resolve(new Control([...arguments][0]))
32 | return anim
33 | }
34 |
35 | get config() {
36 | return this._snapshot.config || {}
37 | }
38 |
39 | getComputedStyle() {
40 | return this._snapshot?.style || {}
41 | }
42 |
43 | feature() {}
44 |
45 | on() {}
46 |
47 | off() {}
48 |
49 | static get presets() {
50 | return new Presets()
51 | }
52 |
53 | async initializing() {
54 | return Promise.resolve()
55 | }
56 | }
57 |
58 | export default Vizzu
59 |
--------------------------------------------------------------------------------
/tests/assets/slides/asset-functions.js:
--------------------------------------------------------------------------------
1 | function waitForSlidesToBeSet(vp, timeout) {
2 | return new Promise((resolve, reject) => {
3 | const startTime = Date.now()
4 |
5 | const checkSlides = () => {
6 | if (vp.slides !== undefined) {
7 | resolve(vp.slides)
8 | } else if (Date.now() - startTime >= timeout) {
9 | reject(new Error('Timeout: slides were not set within the specified time.'))
10 | } else {
11 | setTimeout(checkSlides, 100)
12 | }
13 | }
14 |
15 | checkSlides()
16 | })
17 | }
18 |
19 | function waitForAnimationEnd(vp, timeout) {
20 | return new Promise((resolve, reject) => {
21 | const startTime = Date.now()
22 |
23 | const checkAnimation = () => {
24 | if (!vp.animationQueue.isPlaying()) {
25 | resolve()
26 | } else if (Date.now() - startTime >= timeout) {
27 | reject(new Error('Timeout: animation was not finished within the specified time.'))
28 | } else {
29 | setTimeout(checkAnimation, 100)
30 | }
31 | }
32 |
33 | checkAnimation()
34 | })
35 | }
36 |
37 | export { waitForSlidesToBeSet, waitForAnimationEnd }
38 |
--------------------------------------------------------------------------------
/tests/assets/slides/more-slides.js:
--------------------------------------------------------------------------------
1 | import { slidesWithOneSlideWithOneStep } from './one-slide-one-step.js'
2 | import { slideWithMoreSteps } from './one-slide-more-steps.js'
3 |
4 | import lodashClonedeep from 'lodash.clonedeep'
5 |
6 | const slidesWithMoreSlides = generateSlides()
7 |
8 | function addPreviousSlideLastKeyframeToExpected(expected) {
9 | return expected.map((keyFrame, key) => {
10 | if (key === 0) return keyFrame
11 |
12 | return [expected[key - 1].at(-1), ...keyFrame]
13 | })
14 | }
15 |
16 | function addLastFilterToExpected(expected) {
17 | let lastFilter
18 | return expected.map((keyFrame, key) => {
19 | return keyFrame.map((keyFrameItem) => {
20 | if (
21 | keyFrameItem.target.data &&
22 | 'filter' in keyFrameItem.target.data &&
23 | keyFrameItem.target.data.filter !== null
24 | ) {
25 | lastFilter = keyFrameItem.target.data.filter
26 | } else if (lastFilter) {
27 | keyFrameItem.target.data = { filter: lastFilter }
28 | }
29 | return keyFrameItem
30 | })
31 | })
32 | }
33 |
34 | function generateSlides() {
35 | const slidesWithMoreSlides = []
36 | slidesWithOneSlideWithOneStep.forEach((slide) => {
37 | const oneStepAsObjectSlidePlusMoreStepsSlide =
38 | generateOneStepAsObjectSlidePlusMoreStepsSlide(slide)
39 | oneStepAsObjectSlidePlusMoreStepsSlide.expected = addPreviousSlideLastKeyframeToExpected(
40 | oneStepAsObjectSlidePlusMoreStepsSlide.expected
41 | )
42 | slidesWithMoreSlides.push(oneStepAsObjectSlidePlusMoreStepsSlide)
43 |
44 | const oneStepAsListSlidePlusMoreStepsSlide =
45 | generateOneStepAsListSlidePlusMoreStepsSlide(slide)
46 | oneStepAsListSlidePlusMoreStepsSlide.expected = addPreviousSlideLastKeyframeToExpected(
47 | oneStepAsListSlidePlusMoreStepsSlide.expected
48 | )
49 | slidesWithMoreSlides.push(oneStepAsListSlidePlusMoreStepsSlide)
50 |
51 | const moreStepsSlidePlusOneStepAsObjectSlide =
52 | generateMoreStepsSlidePlusOneStepAsObjectSlide(slide)
53 | moreStepsSlidePlusOneStepAsObjectSlide.expected = addLastFilterToExpected(
54 | moreStepsSlidePlusOneStepAsObjectSlide.expected
55 | )
56 | moreStepsSlidePlusOneStepAsObjectSlide.expected = addPreviousSlideLastKeyframeToExpected(
57 | moreStepsSlidePlusOneStepAsObjectSlide.expected
58 | )
59 | slidesWithMoreSlides.push(moreStepsSlidePlusOneStepAsObjectSlide)
60 |
61 | const moreStepsSlidePlusOneStepAsListSlide =
62 | generateMoreStepsSlidePlusOneStepAsListSlide(slide)
63 | moreStepsSlidePlusOneStepAsListSlide.expected = addLastFilterToExpected(
64 | moreStepsSlidePlusOneStepAsListSlide.expected
65 | )
66 | moreStepsSlidePlusOneStepAsListSlide.expected = addPreviousSlideLastKeyframeToExpected(
67 | moreStepsSlidePlusOneStepAsListSlide.expected
68 | )
69 | slidesWithMoreSlides.push(moreStepsSlidePlusOneStepAsListSlide)
70 | })
71 | return slidesWithMoreSlides
72 | }
73 |
74 | function generateOneStepAsObjectSlidePlusMoreStepsSlide(slide) {
75 | const slides = lodashClonedeep(slide)
76 | slides.description = `slide1(object step with ${slides.description}), slide2(more steps)`
77 | slides.input.slides.push(slideWithMoreSteps.input.slides[0])
78 | slides.expected.push(slideWithMoreSteps.expected[0])
79 | return slides
80 | }
81 |
82 | function generateOneStepAsListSlidePlusMoreStepsSlide(slide) {
83 | const slides = lodashClonedeep(slide)
84 | slides.description = `slide1(list step with ${slides.description}), slide2(more steps)`
85 | slides.input.slides[0] = [slides.input.slides[0]]
86 | slides.input.slides.push(slideWithMoreSteps.input.slides[0])
87 | slides.expected.push(slideWithMoreSteps.expected[0])
88 | return slides
89 | }
90 |
91 | function generateMoreStepsSlidePlusOneStepAsObjectSlide(slide) {
92 | const slides = lodashClonedeep(slideWithMoreSteps)
93 | slides.description = `slide1(more steps), slide2(object step with ${slide.description})`
94 | slides.input.slides.push(slide.input.slides[0])
95 | const expected = lodashClonedeep(slide.expected[0])
96 | slides.expected.push(expected)
97 | return slides
98 | }
99 |
100 | function generateMoreStepsSlidePlusOneStepAsListSlide(slide) {
101 | const slides = lodashClonedeep(slideWithMoreSteps)
102 | slides.description = `slide1(more steps), slide2(list step with ${slide.description})`
103 | slides.input.slides.push([slide.input.slides[0]])
104 | const expected = lodashClonedeep(slide.expected[0])
105 | slides.expected.push(expected)
106 | return slides
107 | }
108 |
109 | const filterNull = {
110 | target: {
111 | data: { filter: null },
112 | config: {},
113 | style: {}
114 | }
115 | }
116 | const filterTrue = {
117 | target: {
118 | data: { filter: true },
119 | config: {},
120 | style: {}
121 | }
122 | }
123 | const filterFalse = {
124 | target: {
125 | data: { filter: false },
126 | config: {},
127 | style: {}
128 | }
129 | }
130 |
131 | const slidesWithMoreSlidesWithMoreFilters = {
132 | input: {
133 | slides: [
134 | {},
135 | [{}, { filter: true }, {}, { filter: false }, {}, { filter: null }, {}],
136 | {},
137 | { filter: true },
138 | {},
139 | { filter: false },
140 | {},
141 | { filter: null },
142 | {}
143 | ]
144 | },
145 | expected: [
146 | [filterNull],
147 | [
148 | filterNull,
149 | filterNull,
150 | filterTrue,
151 | filterTrue,
152 | filterFalse,
153 | filterFalse,
154 | filterNull,
155 | filterNull
156 | ],
157 | [filterNull, filterNull],
158 | [filterNull, filterTrue],
159 | [filterTrue, filterTrue],
160 | [filterTrue, filterFalse],
161 | [filterFalse, filterFalse],
162 | [filterFalse, filterNull],
163 | [filterNull, filterNull]
164 | ]
165 | }
166 |
167 | export { slidesWithMoreSlides, slidesWithMoreSlidesWithMoreFilters }
168 |
--------------------------------------------------------------------------------
/tests/assets/slides/one-slide-more-steps.js:
--------------------------------------------------------------------------------
1 | import { filterAssets } from '../chart-params/filter.js'
2 | import { configAssets } from '../chart-params/config.js'
3 | import { styleAssets } from '../chart-params/style.js'
4 | import { animOptionsAssets } from '../chart-params/animOptions.js'
5 |
6 | const slideWithMoreSteps = {
7 | input: {
8 | slides: [
9 | [
10 | {
11 | filter: filterAssets.filter2,
12 | config: configAssets.config2,
13 | style: styleAssets.style2,
14 | animOptions: animOptionsAssets.animOptions2
15 | },
16 | {
17 | filter: filterAssets.filter3,
18 | config: configAssets.config3,
19 | style: styleAssets.style3,
20 | animOptions: animOptionsAssets.animOptions3
21 | }
22 | ]
23 | ]
24 | },
25 | expected: [
26 | [
27 | {
28 | target: {
29 | data: { filter: filterAssets.filter2 },
30 | config: configAssets.config2,
31 | style: styleAssets.style2
32 | },
33 | options: animOptionsAssets.animOptions2
34 | },
35 | {
36 | target: {
37 | data: { filter: filterAssets.filter3 },
38 | config: configAssets.config3,
39 | style: styleAssets.style3
40 | },
41 | options: animOptionsAssets.animOptions3
42 | }
43 | ]
44 | ]
45 | }
46 |
47 | export { slideWithMoreSteps }
48 |
--------------------------------------------------------------------------------
/tests/assets/slides/one-slide-one-empty-step.js:
--------------------------------------------------------------------------------
1 | const slidesWithOneSlideWithOneEmptyStep = []
2 |
3 | slidesWithOneSlideWithOneEmptyStep.push({
4 | description: 'object step',
5 | input: {
6 | slides: [{}]
7 | },
8 | expected: [[{ target: { data: { filter: null }, config: {}, style: {} } }]]
9 | })
10 |
11 | slidesWithOneSlideWithOneEmptyStep.push({
12 | description: 'list step',
13 | input: {
14 | slides: [[{}]]
15 | },
16 | expected: [[{ target: { data: { filter: null }, config: {}, style: {} } }]]
17 | })
18 |
19 | export { slidesWithOneSlideWithOneEmptyStep }
20 |
--------------------------------------------------------------------------------
/tests/assets/slides/one-slide-one-step.js:
--------------------------------------------------------------------------------
1 | import { filterAssets } from '../chart-params/filter.js'
2 | import { configAssets } from '../chart-params/config.js'
3 | import { styleAssets } from '../chart-params/style.js'
4 | import { animOptionsAssets } from '../chart-params/animOptions.js'
5 |
6 | const slidesWithOneSlideWithOneStep = []
7 |
8 | slidesWithOneSlideWithOneStep.push(
9 | {
10 | description: 'filter',
11 | input: {
12 | slides: [{ filter: filterAssets.filter1 }]
13 | },
14 | expected: [
15 | [
16 | {
17 | target: {
18 | config: {},
19 | style: {},
20 | data: { filter: filterAssets.filter1 }
21 | }
22 | }
23 | ]
24 | ]
25 | },
26 | {
27 | description: 'config',
28 | input: {
29 | slides: [{ config: configAssets.config1 }]
30 | },
31 | expected: [
32 | [{ target: { data: { filter: null }, config: configAssets.config1, style: {} } }]
33 | ]
34 | },
35 | {
36 | description: 'style',
37 | input: {
38 | slides: [{ style: styleAssets.style1 }]
39 | },
40 | expected: [[{ target: { data: { filter: null }, config: {}, style: styleAssets.style1 } }]]
41 | },
42 | {
43 | description: 'animOptions',
44 | input: {
45 | slides: [{ animOptions: animOptionsAssets.animOptions1 }]
46 | },
47 | expected: [
48 | [
49 | {
50 | target: { data: { filter: null }, config: {}, style: {} },
51 | options: animOptionsAssets.animOptions1
52 | }
53 | ]
54 | ]
55 | }
56 | )
57 |
58 | slidesWithOneSlideWithOneStep.push(
59 | {
60 | description: 'filter, animOptions',
61 | input: {
62 | slides: [
63 | {
64 | filter: filterAssets.filter1,
65 | animOptions: animOptionsAssets.animOptions1
66 | }
67 | ]
68 | },
69 | expected: [
70 | [
71 | {
72 | target: {
73 | data: { filter: filterAssets.filter1 },
74 | config: {},
75 | style: {}
76 | },
77 | options: animOptionsAssets.animOptions1
78 | }
79 | ]
80 | ]
81 | },
82 | {
83 | description: 'config, animOptions',
84 | input: {
85 | slides: [
86 | {
87 | config: configAssets.config1,
88 | animOptions: animOptionsAssets.animOptions1
89 | }
90 | ]
91 | },
92 | expected: [
93 | [
94 | {
95 | target: { data: { filter: null }, style: {}, config: configAssets.config1 },
96 | options: animOptionsAssets.animOptions1
97 | }
98 | ]
99 | ]
100 | },
101 | {
102 | description: 'style, animOptions',
103 | input: {
104 | slides: [
105 | {
106 | style: styleAssets.style1,
107 | animOptions: animOptionsAssets.animOptions1
108 | }
109 | ]
110 | },
111 | expected: [
112 | [
113 | {
114 | target: { data: { filter: null }, config: {}, style: styleAssets.style1 },
115 | options: animOptionsAssets.animOptions1
116 | }
117 | ]
118 | ]
119 | }
120 | )
121 |
122 | slidesWithOneSlideWithOneStep.push({
123 | description: 'filter, config, style, animOptions',
124 | input: {
125 | slides: [
126 | {
127 | filter: filterAssets.filter1,
128 | config: configAssets.config1,
129 | style: styleAssets.style1,
130 | animOptions: animOptionsAssets.animOptions1
131 | }
132 | ]
133 | },
134 | expected: [
135 | [
136 | {
137 | target: {
138 | data: { filter: filterAssets.filter1 },
139 | config: configAssets.config1,
140 | style: styleAssets.style1
141 | },
142 | options: animOptionsAssets.animOptions1
143 | }
144 | ]
145 | ]
146 | })
147 |
148 | export { slidesWithOneSlideWithOneStep }
149 |
--------------------------------------------------------------------------------
/tests/assets/slides/zero-slide.js:
--------------------------------------------------------------------------------
1 | const zeroSlide = {
2 | input: {
3 | slides: []
4 | },
5 | expected: []
6 | }
7 |
8 | export { zeroSlide }
9 |
--------------------------------------------------------------------------------
/tests/vizzu-player/e2e/jest.config.cjs:
--------------------------------------------------------------------------------
1 | const config = {
2 | rootDir: '../../../',
3 | testEnvironment: 'node',
4 | testMatch: ['**/vizzu-player/e2e/*.test.cjs'],
5 | transform: {},
6 | testTimeout: 25000
7 | }
8 |
9 | module.exports = config
10 |
--------------------------------------------------------------------------------
/tests/vizzu-player/e2e/vp.data.label.test.cjs:
--------------------------------------------------------------------------------
1 | const puppeteer = require('puppeteer')
2 | const path = require('path')
3 | const express = require('express')
4 |
5 | const testCases = require('../../assets/data-tests/label/index')
6 | const testDataSetPromises = []
7 | testCases.forEach((testCase) => {
8 | testDataSetPromises.push(import(`../../assets/data-sets/${testCase.name}.mjs`))
9 | })
10 |
11 | describe('if slides set', () => {
12 | let testDataSets
13 |
14 | let server
15 | let browser
16 |
17 | beforeAll(async () => {
18 | testDataSets = await Promise.all(testDataSetPromises)
19 |
20 | const app = express()
21 | app.use('/', express.static(path.join(__dirname, '../../../')))
22 | server = app.listen(0, () => {})
23 |
24 | browser = await puppeteer.launch({
25 | headless: 'new',
26 | args: ['--disable-web-security', '--no-sandbox']
27 | })
28 | })
29 |
30 | afterAll(async () => {
31 | await browser.close()
32 | server.close()
33 | })
34 |
35 | // TODO: recheck all slides, not just the first one
36 | testCases.forEach((testCase, index) => {
37 | test(`labels should should display the expected values - ${testCase.name}`, async () => {
38 | const page = await browser.newPage()
39 |
40 | const slides = testDataSets[index].default
41 |
42 | const keyFramesLengths = slides.slides.map((slide) => slide.length)
43 | const allKeyFrames = keyFramesLengths.reduce((accumulator, currentValue) => {
44 | return accumulator + currentValue
45 | }, 0)
46 |
47 | const data = await page.evaluate(
48 | async (serverPort, name, allKeyFrames) => {
49 | document.body.innerHTML = ''
50 |
51 | let resolveComplete = null
52 | const completed = new Promise((resolve) => {
53 | resolveComplete = resolve
54 | })
55 |
56 | let completeCounter = 0
57 | const datas = {}
58 |
59 | function labelDrawHandler(event) {
60 | // check all slides, recheck first slide
61 | if (completeCounter < allKeyFrames + 1) {
62 | if (!datas[completeCounter]) {
63 | datas[completeCounter] = []
64 | }
65 | datas[completeCounter].push(event.detail.text)
66 | }
67 | }
68 |
69 | const complete = () => {
70 | // check all slides, recheck first slide
71 | if (completeCounter === allKeyFrames + 1) {
72 | resolveComplete(true)
73 | }
74 | if (datas[completeCounter]) datas[completeCounter].sort()
75 | completeCounter++
76 | }
77 |
78 | await import(`http://127.0.0.1:${serverPort}/src/vizzu-player.js`)
79 |
80 | const slidesModule = await import(
81 | `http://127.0.0.1:${serverPort}/tests/assets/data-sets/${name}.mjs`
82 | )
83 | const slides = slidesModule.default
84 |
85 | const vp = document.getElementById('story')
86 | window.vp = vp
87 | vp.initializing.then(async (chart) => {
88 | vp.style.cssText = 'width: 100%;height: 100%;'
89 | chart.feature('tooltip', true)
90 | chart.on('plot-marker-label-draw', labelDrawHandler)
91 | chart.on('animation-complete', complete)
92 | vp.slides = slides
93 | })
94 |
95 | await completed
96 |
97 | return datas
98 | },
99 | server.address().port,
100 | testCase.name,
101 | allKeyFrames
102 | )
103 |
104 | expect(data).toStrictEqual(testCase.expected)
105 | })
106 | })
107 | })
108 |
--------------------------------------------------------------------------------
/tests/vizzu-player/unit/jest.config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | rootDir: '../../../',
3 | collectCoverage: true,
4 | coverageProvider: 'v8',
5 | coverageDirectory: '.coverage',
6 | coverageThreshold: {
7 | global: {
8 | branches: 0,
9 | functions: 0,
10 | lines: 0,
11 | statements: 0
12 | }
13 | },
14 | coveragePathIgnorePatterns: ['assets', 'node_modules'],
15 | testEnvironment: 'jsdom',
16 | testMatch: ['**/vizzu-player/unit/*.test.js'],
17 | transform: {},
18 | moduleNameMapper: {
19 | 'https://cdn.jsdelivr.net/npm/vizzu@0.16/dist/vizzu.min.js':
20 | '../tests/assets/mocks/vizzu-cdn.js'
21 | }
22 | }
23 |
24 | export default config
25 |
--------------------------------------------------------------------------------
/tests/vizzu-player/unit/vp.animationqueue.test.js:
--------------------------------------------------------------------------------
1 | import { zeroSlide } from '../../assets/slides/zero-slide.js'
2 | import { slideWithMoreSteps } from '../../assets/slides/one-slide-more-steps.js'
3 |
4 | import { waitForSlidesToBeSet, waitForAnimationEnd } from '../../assets/slides/asset-functions.js'
5 |
6 | import VizzuPlayer from '../../../src/vizzu-player.js'
7 |
8 | describe('if animationQueue is called', () => {
9 | const shouldBeEmpty = 'getter should return an empty the queue'
10 | const shouldBe = ' should return'
11 |
12 | let slides
13 | let vp
14 |
15 | beforeEach(() => {
16 | vp = new VizzuPlayer()
17 | slides = { slides: [] }
18 | for (let i = 0; i < 6; i++) {
19 | slides.slides.push(...slideWithMoreSteps.input.slides)
20 | }
21 | })
22 |
23 | describe('queue is empty with zero slide', () => {
24 | test(`${shouldBeEmpty}`, () => {
25 | const vp = new VizzuPlayer()
26 | vp.slides = zeroSlide.input
27 | return vp.connectedCallback().then(() => {
28 | return waitForSlidesToBeSet(vp, 5000).then(() => {
29 | const isEmpty = vp.animationQueue.isEmpty()
30 | expect(isEmpty).toStrictEqual(true)
31 | })
32 | })
33 | })
34 | })
35 |
36 | describe('queue the slide counter after the next button', () => {
37 | test(`just one, ${shouldBe} 1`, () => {
38 | vp.slides = slides
39 | return vp.connectedCallback().then(() => {
40 | return waitForSlidesToBeSet(vp, 5000).then(() => {
41 | vp.animationQueue.abort()
42 | vp.next()
43 | return waitForAnimationEnd(vp, 5000).then(async () => {
44 | const currentSlide = vp.animationQueue.getParameter('currentSlide')
45 | expect(currentSlide).toStrictEqual(1)
46 | })
47 | })
48 | })
49 | })
50 |
51 | test(`jump to third slide, ${shouldBe} 2`, () => {
52 | vp.slides = slides
53 | return vp.connectedCallback().then(() => {
54 | return waitForSlidesToBeSet(vp, 5000).then(() => {
55 | vp.animationQueue.abort()
56 | vp.setSlide(2)
57 | return waitForAnimationEnd(vp, 5000).then(async () => {
58 | const currentSlide = vp.animationQueue.getParameter('currentSlide')
59 | expect(currentSlide).toStrictEqual(2)
60 | })
61 | })
62 | })
63 | })
64 | })
65 | describe('queue abort', () => {
66 | test(`after is empty`, () => {
67 | const vp = new VizzuPlayer()
68 | vp.slides = slides
69 | return vp.connectedCallback().then(() => {
70 | return waitForSlidesToBeSet(vp, 5000).then(() => {
71 | vp.next()
72 | vp.animationQueue.abort()
73 | const isEmpty = vp.animationQueue.isEmpty()
74 | expect(isEmpty).toStrictEqual(true)
75 | })
76 | })
77 | })
78 | })
79 |
80 | describe('queue is paused', () => {
81 | it(`after is paused and not empty`, () => {
82 | const vp = new VizzuPlayer()
83 | vp.slides = slides
84 | return vp.connectedCallback().then(() => {
85 | return waitForSlidesToBeSet(vp, 5000).then(() => {
86 | vp.next()
87 | vp.animationQueue.pause()
88 | const isEmpty = vp.animationQueue.isEmpty()
89 | expect(vp.animationQueue.isPaused()).toStrictEqual(true)
90 | expect(isEmpty).toStrictEqual(false)
91 | })
92 | })
93 | })
94 | })
95 | })
96 |
--------------------------------------------------------------------------------
/tools/ci/markdown_format.py:
--------------------------------------------------------------------------------
1 | # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
2 |
3 | import mdformat
4 |
5 |
6 | class Markdown:
7 | # pylint: disable=too-few-public-methods
8 |
9 | @staticmethod
10 | def format(content: str) -> str:
11 | return mdformat.text( # type: ignore
12 | content,
13 | options={"wrap": 80, "end-of-line": "keep", "line-length": 70},
14 | extensions={
15 | "gfm",
16 | "tables",
17 | "footnote",
18 | "frontmatter",
19 | "configblack",
20 | "mkdocs",
21 | },
22 | codeformatters={
23 | "python",
24 | "bash",
25 | "sh",
26 | "json",
27 | "toml",
28 | "yaml",
29 | "javascript",
30 | "js",
31 | "css",
32 | "html",
33 | "xml",
34 | },
35 | )
36 |
--------------------------------------------------------------------------------
/tools/ci/std_check.py:
--------------------------------------------------------------------------------
1 | # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
2 |
3 | import sys
4 | import subprocess
5 |
6 |
7 | def main() -> None:
8 | with subprocess.Popen(
9 | sys.argv[1:], stdout=subprocess.PIPE, stderr=subprocess.PIPE
10 | ) as process:
11 | out, err = process.communicate()
12 | if out or err or process.returncode:
13 | if out:
14 | print(out.decode())
15 | if err:
16 | print(err.decode())
17 | raise RuntimeError(f"failed to run {sys.argv[1]}")
18 |
19 |
20 | main()
21 |
--------------------------------------------------------------------------------
/tools/ci/version.py:
--------------------------------------------------------------------------------
1 | # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
2 |
3 | from contextlib import chdir
4 | import json
5 | from pathlib import Path
6 | import sys
7 |
8 |
9 | REPO_PATH = Path(__file__).parent / ".." / ".."
10 | TOOLS_PATH = REPO_PATH / "tools"
11 |
12 | sys.path.insert(0, str(TOOLS_PATH / "modules"))
13 |
14 | from vizzu import ( # pylint: disable=import-error, wrong-import-position, wrong-import-order
15 | Vizzu,
16 | )
17 |
18 |
19 | class Version:
20 | # pylint: disable=too-few-public-methods
21 |
22 | @staticmethod
23 | def set_readme_version(restore: bool) -> None:
24 | with open("README.md", "r", encoding="utf8") as fh_readme:
25 | content = fh_readme.read()
26 |
27 | content = Vizzu.set_version(content, restore)
28 |
29 | with open("README.md", "w", encoding="utf8") as fh_readme:
30 | fh_readme.write(content)
31 |
32 |
33 | def main() -> None:
34 | with chdir(REPO_PATH):
35 | restore = json.loads(sys.argv[1].lower())
36 | Version.set_readme_version(restore)
37 |
38 |
39 | main()
40 |
--------------------------------------------------------------------------------
/tools/docs/config.py:
--------------------------------------------------------------------------------
1 | # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
2 |
3 | from pathlib import Path
4 | from typing import Optional
5 |
6 | import yaml
7 |
8 |
9 | class MkdocsConfig:
10 | # pylint: disable=too-few-public-methods
11 |
12 | @staticmethod
13 | def _format_url(url: Optional[str]) -> Optional[str]:
14 | if url and url.endswith("/"):
15 | return url[:-1]
16 | return url
17 |
18 | @staticmethod
19 | def _format(config: dict) -> dict:
20 | if "site_url" in config:
21 | config["site_url"] = MkdocsConfig._format_url(config["site_url"])
22 | return config
23 |
24 | @staticmethod
25 | def load(config: Path) -> dict:
26 | with open(config, "rt", encoding="utf8") as f_yml:
27 | return MkdocsConfig._format(yaml.load(f_yml, Loader=yaml.FullLoader))
28 |
--------------------------------------------------------------------------------
/tools/docs/deploy.py:
--------------------------------------------------------------------------------
1 | # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
2 |
3 | from contextlib import chdir
4 | from pathlib import Path
5 | from subprocess import Popen
6 | import sys
7 |
8 |
9 | REPO_PATH = Path(__file__).parent / ".." / ".."
10 | TOOLS_PATH = REPO_PATH / "tools"
11 | MKDOCS_PATH = TOOLS_PATH / "docs"
12 |
13 | sys.path.insert(0, str(TOOLS_PATH / "modules"))
14 |
15 | from vizzu import ( # pylint: disable=import-error, wrong-import-position, wrong-import-order
16 | Vizzu,
17 | )
18 |
19 |
20 | class Deploy:
21 | latest: bool = True
22 |
23 | @staticmethod
24 | def mike() -> None:
25 | version = Vizzu.get_vizzustory_version()
26 |
27 | params = [
28 | "mike",
29 | "deploy",
30 | ]
31 | if Deploy.latest:
32 | params.append("-u")
33 | params.append(version)
34 | if Deploy.latest:
35 | params.append("latest")
36 | params.append("-F")
37 | params.append("tools/docs/mkdocs.yml")
38 |
39 | with Popen(
40 | params,
41 | ) as process:
42 | process.communicate()
43 |
44 | if process.returncode:
45 | raise RuntimeError("failed to run mike")
46 |
47 | @staticmethod
48 | def set_config(restore: bool) -> None:
49 | with open(MKDOCS_PATH / "mkdocs.yml", "r", encoding="utf8") as fh_readme:
50 | content = fh_readme.read()
51 |
52 | if not restore:
53 | if not Deploy.latest:
54 | content = content.replace(
55 | "- content.action.edit",
56 | "# - content.action.edit",
57 | )
58 | else:
59 | if not Deploy.latest:
60 | content = content.replace(
61 | "# - content.action.edit",
62 | "- content.action.edit",
63 | )
64 |
65 | with open(MKDOCS_PATH / "mkdocs.yml", "w", encoding="utf8") as fh_readme:
66 | fh_readme.write(content)
67 |
68 |
69 | def main() -> None:
70 | with chdir(REPO_PATH):
71 | Deploy.set_config(restore=False)
72 | Deploy.mike()
73 | Deploy.set_config(restore=True)
74 |
75 |
76 | main()
77 |
--------------------------------------------------------------------------------
/tools/docs/examples/gen_examples.py:
--------------------------------------------------------------------------------
1 | # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
2 |
3 | from contextlib import chdir
4 | from pathlib import Path
5 | import sys
6 | import re
7 |
8 | import mkdocs_gen_files # type: ignore
9 |
10 |
11 | REPO_PATH = Path(__file__).parent / ".." / ".." / ".."
12 | TOOLS_PATH = REPO_PATH / "tools"
13 | MKDOCS_PATH = TOOLS_PATH / "docs"
14 | JS_ASSETS_PATH = "assets/javascripts"
15 |
16 | sys.path.insert(0, str(TOOLS_PATH / "modules"))
17 |
18 | from vizzu import ( # pylint: disable=import-error, wrong-import-position, wrong-import-order
19 | Vizzu,
20 | VIZZUSTORY_SITE_URL,
21 | )
22 |
23 |
24 | class GenExamples:
25 | # pylint: disable=too-few-public-methods
26 |
27 | @staticmethod
28 | def _create_index() -> None:
29 | with mkdocs_gen_files.open("examples/index.md", "a") as fh_index:
30 | meta = """---\nhide:\n - toc\n---"""
31 | fh_index.write(f"{meta}\n\n")
32 | fh_index.write("# Examples\n")
33 | fh_index.write(f'\n')
34 |
35 | @staticmethod
36 | def _add_image(example: Path) -> None:
37 | with mkdocs_gen_files.open("examples/index.md", "a") as fh_index:
38 | fh_index.write(
39 | "["
40 | + f"![{example.stem}]"
41 | + f"(./{example.stem}.png)"
42 | + "{ class='image-gallery' }"
43 | + "]"
44 | + f"(./{example.name})\n"
45 | )
46 |
47 | @staticmethod
48 | def _remove_eslint_comments(content: str) -> str:
49 | regex = r"^\s*//\s*eslint.*?\n"
50 | return re.sub(regex, "", content, flags=re.MULTILINE)
51 |
52 | @staticmethod
53 | def _set_csv2js_url(content: str) -> str:
54 | return content.replace(
55 | "../../assets/javascripts/csv2js.js",
56 | f"{VIZZUSTORY_SITE_URL}/latest/assets/javascripts/csv2js.js",
57 | )
58 |
59 | @staticmethod
60 | def _set_csv_url(example: Path, content: str) -> str:
61 | return content.replace(
62 | 'Csv2Js.csv("./',
63 | f'Csv2Js.csv("{VIZZUSTORY_SITE_URL}/latest/examples/{example.stem}/',
64 | )
65 |
66 | @staticmethod
67 | def _get_js(example: Path) -> str:
68 | with open(
69 | example.parent / example.stem / "main.js", "r", encoding="utf8"
70 | ) as fh_js:
71 | return fh_js.read()
72 |
73 | @staticmethod
74 | def _generate_example_js(example: Path, js_content: str) -> None:
75 | content = Vizzu.set_vizzustory_backend_url(js_content)
76 | with mkdocs_gen_files.open(f"examples/{example.stem}/main.js", "w") as fh_js:
77 | fh_js.write(content)
78 |
79 | @staticmethod
80 | def _generate_example_md(example: Path, js_content: str) -> None:
81 | regex = r"// {% include \"(\.\/.*\.js)\" %}"
82 | with open(example.parent / example.name, "r", encoding="utf8") as fh_md:
83 | content = fh_md.read()
84 | content = re.sub(regex, js_content, content)
85 | content = GenExamples._remove_eslint_comments(content)
86 | content = GenExamples._set_csv2js_url(content)
87 | content = GenExamples._set_csv_url(example, content)
88 | content = Vizzu.set_version(content)
89 | mkdocs_gen_files.set_edit_path(
90 | f"examples/{example.name}", f"examples/{example.name}"
91 | )
92 | with mkdocs_gen_files.open(f"examples/{example.name}", "w") as fh_md:
93 | fh_md.write(content)
94 |
95 | @staticmethod
96 | def _generate_example(example: Path) -> None:
97 | js_content = GenExamples._get_js(example)
98 | GenExamples._generate_example_js(example, js_content)
99 | GenExamples._generate_example_md(example, js_content)
100 | GenExamples._add_image(example)
101 |
102 | @staticmethod
103 | def generate() -> None:
104 | GenExamples._create_index()
105 | example_path = REPO_PATH / "docs" / "examples"
106 | for example in sorted(example_path.glob("*.md")):
107 | GenExamples._generate_example(example)
108 |
109 |
110 | def main() -> None:
111 | with chdir(REPO_PATH):
112 | GenExamples.generate()
113 |
114 |
115 | main()
116 |
--------------------------------------------------------------------------------
/tools/docs/mkdocs.yml:
--------------------------------------------------------------------------------
1 | strict: true
2 |
3 | site_name: vizzu-story
4 | site_url: https://vizzu-story.vizzuhq.com
5 | copyright: Copyright © 2022-2025 Vizzu Inc.
6 |
7 | docs_dir: ../../docs
8 | site_dir: ../../site
9 |
10 | repo_url: https://github.com/vizzuhq/vizzu-story-js
11 | edit_uri: https://github.com/vizzuhq/vizzu-story-js/edit/main/docs
12 | use_directory_urls: true
13 |
14 | theme:
15 | name: material
16 | palette:
17 | scheme: vizzu
18 | font:
19 | text: Roboto
20 | code: Roboto Mono
21 | logo: assets/logo-white.svg
22 | favicon: assets/favicon.svg
23 | custom_dir: ./overrides
24 | features:
25 | - toc.follow
26 | - search.suggest
27 | - search.highlight
28 | - navigation.top
29 | - navigation.footer
30 | - content.code.copy
31 | - content.action.edit
32 |
33 | extra_css:
34 | - assets/stylesheets/vizzu.css
35 | - assets/stylesheets/gallery.css
36 | - assets/stylesheets/highlight.css
37 |
38 | extra_javascript:
39 | - assets/javascripts/extlinks.js
40 | - //cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.7/build/highlight.min.js
41 | - //cdn.jsdelivr.net/npm/highlightjs-line-numbers.js@2.8.0/dist/highlightjs-line-numbers.min.js
42 | - assets/javascripts/highlight.js
43 |
44 | extra:
45 | version:
46 | provider: mike
47 | default: latest
48 | social:
49 | - icon: fontawesome/brands/slack
50 | name: Vizzu on Slack
51 | link: https://join.slack.com/t/vizzu-community/shared_invite/zt-w2nqhq44-2CCWL4o7qn2Ns1EFSf9kEg
52 | - icon: fontawesome/brands/twitter
53 | name: Vizzu on Twitter
54 | link: https://twitter.com/VizzuHQ
55 | - icon: fontawesome/brands/reddit
56 | name: Vizzu on Reddit
57 | link: https://www.reddit.com/user/VizzuHQ/?sort=top
58 | - icon: fontawesome/brands/github
59 | name: Vizzu on GitHub
60 | link: https://github.com/vizzuhq/
61 |
62 | markdown_extensions:
63 | - pymdownx.tasklist:
64 | custom_checkbox: true
65 | - attr_list
66 | - md_in_html
67 | - admonition
68 | - pymdownx.highlight:
69 | use_pygments: false
70 | - pymdownx.details
71 | - pymdownx.superfences
72 |
73 | plugins:
74 | - mike:
75 | version_selector: true
76 | alias_type: symlink
77 | canonical_version: latest
78 | redirect_template: ./tools/mkdocs/overrides/mike/redirect.html
79 | - search
80 | - section-index
81 | - literate-nav:
82 | implicit_index: true
83 | - autorefs
84 | - gen-files:
85 | scripts:
86 | - pages/gen_pages.py
87 | - examples/gen_examples.py
88 | - reference/gen_reference.py
89 | - exclude:
90 | glob:
91 | - reference.md
92 |
93 | nav:
94 | - Home: index.md
95 | - installation.md
96 | - Tutorial:
97 | - tutorial/index.md
98 | - tutorial/data.md
99 | - tutorial/initialization.md
100 | - Building blocks: tutorial/building_blocks.md
101 | - Examples: examples/
102 | - Code reference: reference/README.md
103 | - Development:
104 | - dev/index.md
105 | - Contributing: CONTRIBUTING.md
106 | - Code of Conduct: CODE_OF_CONDUCT.md
107 | - License: LICENSE.md
108 |
--------------------------------------------------------------------------------
/tools/docs/overrides/main.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %} {% block extrahead %}
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | {% endblock %} {% block outdated %} You're not viewing the latest version.
20 |
21 | Click here to go to latest.
22 |
23 | {% endblock %} {% block content %} {% if page.meta.data_url %}
24 |
25 | {% include ".icons/material/database.svg" %}
26 |
27 | {% endif %} {% if page.meta.csv_url %}
28 |
29 | {% include ".icons/fontawesome/solid/file-csv.svg" %}
30 |
31 | {% endif %}{{ super() }} {% endblock content %}
32 |
--------------------------------------------------------------------------------
/tools/docs/overrides/mike/redirect.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
24 |
25 |
26 |
27 | Redirecting
28 |
31 |
34 |
35 |
36 | Redirecting to {{href}}...
37 |
38 |
39 |
--------------------------------------------------------------------------------
/tools/docs/pages/gen_pages.py:
--------------------------------------------------------------------------------
1 | # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
2 |
3 | from contextlib import chdir
4 | import os
5 | from pathlib import Path
6 | from typing import Union, Optional, List
7 | import sys
8 |
9 | import mkdocs_gen_files # type: ignore
10 |
11 |
12 | REPO_PATH = Path(__file__).parent / ".." / ".." / ".."
13 | TOOLS_PATH = REPO_PATH / "tools"
14 | MKDOCS_PATH = TOOLS_PATH / "docs"
15 |
16 | sys.path.insert(0, str(TOOLS_PATH / "modules"))
17 | sys.path.insert(0, str(TOOLS_PATH / "ci"))
18 | sys.path.insert(0, str(MKDOCS_PATH))
19 |
20 | from vizzu import ( # pylint: disable=import-error, wrong-import-position, wrong-import-order
21 | Vizzu,
22 | )
23 | from config import ( # pylint: disable=import-error, wrong-import-position, wrong-import-order
24 | MkdocsConfig,
25 | )
26 |
27 |
28 | class IndexPages:
29 | # pylint: disable=too-few-public-methods
30 |
31 | @staticmethod
32 | def _write_index_file(file: str, toc: list) -> None:
33 | for item in toc:
34 | if isinstance(item, str):
35 | IndexPages._write_str_index(file, item)
36 | elif isinstance(item, dict):
37 | IndexPages._write_dict_index(file, item)
38 | else:
39 | raise NotImplementedError(f"{item}")
40 |
41 | @staticmethod
42 | def _write_str_index(file: str, item: str) -> None:
43 | with mkdocs_gen_files.open(file, "a") as f_index:
44 | parts = item.split("/")
45 | part = parts[-1].replace(".md", "").capitalize()
46 | link = Path(os.path.relpath(item, Path(file).parent))
47 | f_index.write(f"* [{part}]({link})\n")
48 |
49 | @staticmethod
50 | def _write_dict_index(file: str, item: dict) -> None:
51 | with mkdocs_gen_files.open(file, "a") as f_index:
52 | for key in item:
53 | if isinstance(item[key], str):
54 | link = Path(os.path.relpath(item[key], Path(file).parent))
55 | f_index.write(f"* [{key}]({link})\n")
56 | continue
57 | if item[key] and isinstance(item[key], list):
58 | if isinstance(item[key][0], str):
59 | if item[key][0].endswith("index.md"):
60 | link = Path(
61 | os.path.relpath(item[key][0], Path(file).parent)
62 | )
63 | f_index.write(f"* [{key}]({link})\n")
64 | continue
65 | raise NotImplementedError(f"{item}")
66 |
67 | @staticmethod
68 | def generate(
69 | nav_item: Union[list, dict, str], skip: Optional[List[str]] = None
70 | ) -> None:
71 | if isinstance(nav_item, list):
72 | if (
73 | nav_item
74 | and isinstance(nav_item[0], str)
75 | and nav_item[0].endswith("index.md")
76 | ):
77 | if not skip or nav_item[0] not in skip:
78 | original = Path("docs", nav_item[0])
79 | if original.exists():
80 | mkdocs_gen_files.set_edit_path(nav_item[0], nav_item[0])
81 | with mkdocs_gen_files.open(nav_item[0], "a") as f_index:
82 | f_index.write("\n")
83 | IndexPages._write_index_file(file=nav_item[0], toc=nav_item[1:])
84 | for item in nav_item:
85 | IndexPages.generate(nav_item=item, skip=skip)
86 | elif isinstance(nav_item, dict):
87 | for key in nav_item:
88 | IndexPages.generate(nav_item=nav_item[key], skip=skip)
89 |
90 |
91 | class Page:
92 | # pylint: disable=too-few-public-methods
93 |
94 | @staticmethod
95 | def generate(src: Path, dst: str, pos: str, site: str, keep: bool = False) -> None:
96 | with open(src, "rt", encoding="utf8") as f_src:
97 | content = f_src.read()
98 |
99 | content = content.replace(f"{site}/latest/", pos).replace(f"{site}/latest", pos)
100 | content = Vizzu.set_version(content)
101 | if keep:
102 | content = f"{content}
"
103 |
104 | mkdocs_gen_files.set_edit_path(dst, ".." / Path(dst).parent / Path(src).name)
105 | with mkdocs_gen_files.open(dst, "w") as f_dst:
106 | f_dst.write(content)
107 |
108 |
109 | class Docs:
110 | # pylint: disable=too-few-public-methods
111 |
112 | @staticmethod
113 | def generate(skip: Optional[List[str]] = None) -> None:
114 | docs_path = REPO_PATH / "docs"
115 | for path in list(docs_path.rglob("*.md")) + list(docs_path.rglob("*.js")):
116 | if skip and path.name in skip:
117 | continue
118 | with open(path, "rt", encoding="utf8") as f_src:
119 | dst = path.relative_to(docs_path)
120 | content = f_src.read()
121 | if path.suffix == ".md":
122 | content = Vizzu.set_version(content)
123 | mkdocs_gen_files.set_edit_path(dst, dst)
124 | with mkdocs_gen_files.open(dst, "w") as f_dst:
125 | f_dst.write(content)
126 |
127 |
128 | def main() -> None:
129 | with chdir(REPO_PATH):
130 | config = MkdocsConfig.load(MKDOCS_PATH / "mkdocs.yml")
131 |
132 | Docs.generate(skip=["reference.md"])
133 |
134 | IndexPages.generate(nav_item=config["nav"])
135 |
136 | Page.generate(
137 | src=REPO_PATH / "README.md",
138 | dst="index.md",
139 | pos="./",
140 | site=config["site_url"],
141 | )
142 |
143 | Page.generate(
144 | src=REPO_PATH / "CONTRIBUTING.md",
145 | dst="CONTRIBUTING.md",
146 | pos="../",
147 | site=config["site_url"],
148 | )
149 |
150 | Page.generate(
151 | src=REPO_PATH / "CODE_OF_CONDUCT.md",
152 | dst="CODE_OF_CONDUCT.md",
153 | pos="../",
154 | site=config["site_url"],
155 | )
156 |
157 | Page.generate(
158 | src=REPO_PATH / "LICENSE",
159 | dst="LICENSE.md",
160 | pos="../",
161 | site=config["site_url"],
162 | keep=True,
163 | )
164 |
165 |
166 | main()
167 |
--------------------------------------------------------------------------------
/tools/docs/reference/gen_reference.cjs:
--------------------------------------------------------------------------------
1 | const path = require('node:path')
2 |
3 | const repoPath = path.join(__dirname, '..', '..', '..')
4 | const mkdocsPath = path.join(repoPath, 'tools', 'docs')
5 | const genPath = path.join(mkdocsPath, 'reference')
6 | const srcPath = path.join(repoPath, 'src')
7 |
8 | async function reference() {
9 | const TypeDoc = await import('typedoc')
10 | const app = await TypeDoc.Application.bootstrapWithPlugins(
11 | {
12 | plugin: ['typedoc-plugin-markdown', 'typedoc-plugin-rename-defaults'],
13 | entryPoints: [path.join(srcPath, 'vizzu-story.d.ts')],
14 | entryPointStrategy: 'expand',
15 | tsconfig: path.join(genPath, 'tsconfig.json'),
16 | name: 'Vizzu-Story',
17 | disableSources: true,
18 | excludePrivate: true,
19 | theme: 'markdown'
20 | },
21 | [new TypeDoc.TSConfigReader()]
22 | )
23 |
24 | const project = await app.convert()
25 |
26 | if (project) {
27 | const outputDir = path.join(genPath, 'tmp')
28 | return app.generateDocs(project, outputDir)
29 | }
30 | }
31 |
32 | reference()
33 |
--------------------------------------------------------------------------------
/tools/docs/reference/gen_reference.py:
--------------------------------------------------------------------------------
1 | # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
2 |
3 | from contextlib import chdir
4 | import os
5 | from pathlib import Path
6 | import shutil
7 | import sys
8 |
9 | import mkdocs_gen_files
10 |
11 |
12 | REPO_PATH = Path(__file__).parent / ".." / ".." / ".."
13 | TOOLS_PATH = REPO_PATH / "tools"
14 | MKDOCS_PATH = TOOLS_PATH / "docs"
15 | GEN_PATH = MKDOCS_PATH / "reference"
16 | SRC_PATH = REPO_PATH / "src"
17 |
18 | sys.path.insert(0, str(TOOLS_PATH / "modules"))
19 | sys.path.insert(0, str(TOOLS_PATH / "ci"))
20 |
21 | from node import ( # pylint: disable=import-error, wrong-import-position, wrong-import-order
22 | Node,
23 | )
24 | from vizzu import ( # pylint: disable=import-error, wrong-import-position, wrong-import-order
25 | Vizzu,
26 | )
27 | from markdown_format import ( # pylint: disable=import-error, wrong-import-position, wrong-import-order
28 | Markdown,
29 | )
30 |
31 |
32 | class Reference:
33 | # pylint: disable=too-few-public-methods
34 |
35 | @staticmethod
36 | def generate(folder: str) -> None:
37 | Reference._gen_reference(folder)
38 |
39 | @staticmethod
40 | def _gen_reference(folder: str) -> None:
41 | tmp_dir = GEN_PATH / "tmp"
42 | if os.path.exists(tmp_dir):
43 | shutil.rmtree(tmp_dir)
44 |
45 | Node.node(False, GEN_PATH / "gen_reference.cjs")
46 |
47 | for path in Path(tmp_dir).rglob("*.md"):
48 | with open(path, "rt", encoding="utf8") as f_src:
49 | if (Path(tmp_dir) / "README.md").resolve() == path.resolve():
50 | continue
51 | content = f_src.read()
52 | content = content.replace("##### ", "").replace("#### ", "")
53 | content = Vizzu.set_version(content)
54 | content = Markdown.format(content)
55 | with mkdocs_gen_files.open(
56 | f"{folder}/{path.relative_to(tmp_dir)}", "w"
57 | ) as f_dst:
58 | f_dst.write(content)
59 |
60 |
61 | def main() -> None:
62 | with chdir(REPO_PATH):
63 | Reference.generate("reference")
64 |
65 |
66 | main()
67 |
--------------------------------------------------------------------------------
/tools/docs/reference/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "skipLibCheck": true
4 | },
5 | "files": ["../../../src/vizzu-story.d.ts"]
6 | }
7 |
--------------------------------------------------------------------------------
/tools/modules/node.py:
--------------------------------------------------------------------------------
1 | # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
2 |
3 | from pathlib import Path
4 | from subprocess import PIPE, Popen
5 | from typing import Union
6 |
7 |
8 | class Node:
9 | @staticmethod
10 | def node(strict: bool, script: Union[str, Path], *params: str) -> str:
11 | return Node.run(strict, "node", script, *params)
12 |
13 | @staticmethod
14 | def npx(strict: bool, script: Union[str, Path], *params: str) -> str:
15 | return Node.run(strict, "npx", script, *params)
16 |
17 | @staticmethod
18 | def run(strict: bool, exe: str, script: Union[str, Path], *params: str) -> str:
19 | with Popen(
20 | [exe, script, *params],
21 | stdin=PIPE,
22 | stdout=PIPE,
23 | stderr=PIPE,
24 | ) as node:
25 | outs, errs = node.communicate()
26 |
27 | if errs:
28 | print(errs.decode())
29 |
30 | if node.returncode or (strict and errs):
31 | raise RuntimeError(f"failed to run {Path(script).stem}")
32 |
33 | return outs.decode()
34 |
--------------------------------------------------------------------------------
/tools/modules/vizzu.py:
--------------------------------------------------------------------------------
1 | # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
2 |
3 | from pathlib import Path
4 | import re
5 |
6 |
7 | REPO_PATH = Path(__file__).parent / ".." / ".."
8 |
9 | VIZZUSTORY_BACKEND_URL = ""
10 |
11 | VIZZUSTORY_VERSION = ""
12 | VIZZU_VERSION = ""
13 |
14 | VIZZUSTORY_SITE_URL = "https://vizzu-story.vizzuhq.com"
15 | VIZZUSTORY_CDN_URL = "https://cdn.jsdelivr.net/npm/vizzu-story"
16 | VIZZU_SITE_URL = "https://lib.vizzuhq.com"
17 | VIZZU_CDN_URL = "https://cdn.jsdelivr.net/npm/vizzu"
18 |
19 |
20 | class Vizzu:
21 | _vizzustory_version = ""
22 | _vizzu_version = ""
23 |
24 | @staticmethod
25 | def get_vizzustory_backend_url() -> str:
26 | if VIZZUSTORY_BACKEND_URL:
27 | return VIZZUSTORY_BACKEND_URL
28 | version = Vizzu.get_vizzustory_version()
29 | return f"{VIZZUSTORY_CDN_URL}@{version}/dist/vizzu-story.min.js"
30 |
31 | @staticmethod
32 | def set_vizzustory_backend_url(content: str, restore: bool = False) -> str:
33 | url = Vizzu.get_vizzustory_backend_url()
34 | if not restore:
35 | content = content.replace(
36 | f"{VIZZUSTORY_CDN_URL}@latest/dist/vizzu-story.min.js",
37 | url,
38 | )
39 | else:
40 | content = content.replace(
41 | url,
42 | f"{VIZZUSTORY_CDN_URL}@latest/dist/vizzu-story.min.js",
43 | )
44 | return content
45 |
46 | @staticmethod
47 | def get_vizzustory_full_version() -> list:
48 | with open(
49 | REPO_PATH / "package.json",
50 | "r",
51 | encoding="utf8",
52 | ) as f_version:
53 | content = f_version.read()
54 | version = re.search(r"\"version\":\s\"(\d+).(\d+).(\d+)\"", content)
55 | return [
56 | version.group(1), # type: ignore
57 | version.group(2), # type: ignore
58 | version.group(3), # type: ignore
59 | ]
60 |
61 | @staticmethod
62 | def get_vizzustory_version() -> str:
63 | if VIZZUSTORY_VERSION:
64 | return VIZZUSTORY_VERSION
65 | if not Vizzu._vizzustory_version:
66 | version_parts = Vizzu.get_vizzustory_full_version()
67 | Vizzu._vizzustory_version = f"{version_parts[0]}.{version_parts[1]}"
68 | return Vizzu._vizzustory_version
69 |
70 | @staticmethod
71 | def get_vizzu_version() -> str:
72 | if VIZZU_VERSION:
73 | return VIZZU_VERSION
74 | if not Vizzu._vizzu_version:
75 | with open(
76 | REPO_PATH / "package.json",
77 | "r",
78 | encoding="utf8",
79 | ) as f_version:
80 | content = f_version.read()
81 | version = re.search(r"\"vizzu\":\s\"~(\d+).(\d+).(\d+)\"", content)
82 | Vizzu._vizzu_version = f"{version.group(1)}.{version.group(2)}" # type: ignore
83 | return Vizzu._vizzu_version
84 |
85 | @staticmethod
86 | def set_version(content: str, restore: bool = False) -> str:
87 | vizzu_version = Vizzu.get_vizzu_version()
88 | vizzustory_version = Vizzu.get_vizzustory_version()
89 | if not restore:
90 | content = content.replace(
91 | f"{VIZZUSTORY_SITE_URL}/latest/",
92 | f"{VIZZUSTORY_SITE_URL}/{vizzustory_version}/",
93 | )
94 | content = content.replace(
95 | f"{VIZZU_SITE_URL}/latest/",
96 | f"{VIZZU_SITE_URL}/{vizzu_version}/",
97 | )
98 | content = content.replace(
99 | f"{VIZZUSTORY_CDN_URL}@latest/dist/vizzu-story.min.js",
100 | f"{VIZZUSTORY_CDN_URL}@{vizzustory_version}/dist/vizzu-story.min.js",
101 | )
102 | content = content.replace(
103 | f"{VIZZU_CDN_URL}@latest/dist/vizzu.min.js",
104 | f"{VIZZU_CDN_URL}@{vizzu_version}/dist/vizzu.min.js",
105 | )
106 | else:
107 | content = content.replace(
108 | f"{VIZZUSTORY_SITE_URL}/{vizzustory_version}/",
109 | f"{VIZZUSTORY_SITE_URL}/latest/",
110 | )
111 | content = content.replace(
112 | f"{VIZZU_SITE_URL}/{vizzu_version}/",
113 | f"{VIZZU_SITE_URL}/latest/",
114 | )
115 | content = content.replace(
116 | f"{VIZZUSTORY_CDN_URL}@{vizzustory_version}/dist/vizzu-story.min.js",
117 | f"{VIZZUSTORY_CDN_URL}@latest/dist/vizzu-story.min.js",
118 | )
119 | content = content.replace(
120 | f"{VIZZU_CDN_URL}@{vizzu_version}/dist/vizzu.min.js",
121 | f"{VIZZU_CDN_URL}@latest/dist/vizzu.min.js",
122 | )
123 |
124 | return content
125 |
--------------------------------------------------------------------------------