├── .all-contributorsrc ├── .browserslistrc ├── .editorconfig ├── .eslintrc.js ├── .github └── workflows │ └── deploy-docs.yml ├── .gitignore ├── .markdownlint.json ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── babel.config.js ├── docs ├── docs │ ├── .vuepress │ │ ├── config.js │ │ ├── config.js.2f8f54db.mjs │ │ ├── config.js.d04c39a0.mjs │ │ ├── configs │ │ │ ├── head.js │ │ │ ├── index.js │ │ │ ├── navbar │ │ │ │ ├── en.js │ │ │ │ ├── index.js │ │ │ │ └── ko.js │ │ │ └── sidebar │ │ │ │ ├── en.js │ │ │ │ ├── index.js │ │ │ │ └── ko.js │ │ └── public │ │ │ ├── google603171b62dec4aac.html │ │ │ └── images │ │ │ ├── logo.png │ │ │ └── vue-pivottable-demo.gif │ ├── README.md │ ├── guide │ │ ├── getting-started.md │ │ ├── introduction.md │ │ ├── locale.md │ │ ├── props.md │ │ ├── renderer.md │ │ ├── scoped-slot.md │ │ ├── slot.md │ │ ├── styling.md │ │ └── utilities.md │ └── ko │ │ ├── README.md │ │ └── guide │ │ ├── getting-started.md │ │ ├── introduction.md │ │ ├── locale.md │ │ ├── props.md │ │ ├── renderer.md │ │ ├── scoped-slot.md │ │ ├── slot.md │ │ ├── styling.md │ │ └── utilities.md └── package.json ├── package.json ├── packages ├── plotly-renderer │ ├── LICENSE │ ├── README.md │ ├── index.html │ ├── package.json │ ├── src │ │ ├── common.js │ │ └── index.js │ └── vite.config.js └── scroll-renderer │ ├── LICENSE │ ├── README.md │ ├── btc-daily.js │ ├── package.json │ ├── src │ ├── common.js │ └── index.js │ └── vite.config.js ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── postcss.config.js ├── setting.json ├── src ├── DraggableAttribute.js ├── Dropdown.js ├── Pivottable.js ├── PivottableUi.js ├── TableRenderer.js ├── assets │ └── vue-pivottable.css ├── helper │ ├── common.js │ └── utils.js └── index.js └── vite.config.js /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "commitConvention": "angular", 8 | "contributors": [ 9 | { 10 | "login": "Seungwoo321", 11 | "name": "Seungwoo321", 12 | "avatar_url": "https://avatars.githubusercontent.com/u/13829929?v=4", 13 | "profile": "https://seungwoo321.github.io/", 14 | "contributions": [ 15 | "code" 16 | ] 17 | }, 18 | { 19 | "login": "rosairekota", 20 | "name": "rkota", 21 | "avatar_url": "https://avatars.githubusercontent.com/u/48548004?v=4", 22 | "profile": "https://kinshasadigital.com/", 23 | "contributions": [ 24 | "maintenance" 25 | ] 26 | }, 27 | { 28 | "login": "jbrathwa", 29 | "name": "Jayraj Rathwa", 30 | "avatar_url": "https://avatars.githubusercontent.com/u/24322038?v=4", 31 | "profile": "https://github.com/jbrathwa", 32 | "contributions": [ 33 | "bug" 34 | ] 35 | }, 36 | { 37 | "login": "cbbdev", 38 | "name": "cbbdev", 39 | "avatar_url": "https://avatars.githubusercontent.com/u/55853311?v=4", 40 | "profile": "https://github.com/cbbdev", 41 | "contributions": [ 42 | "maintenance" 43 | ] 44 | }, 45 | { 46 | "login": "hyemyn2", 47 | "name": "hyemyn2", 48 | "avatar_url": "https://avatars.githubusercontent.com/u/67949202?v=4", 49 | "profile": "https://github.com/hyemyn2", 50 | "contributions": [ 51 | "code" 52 | ] 53 | }, 54 | { 55 | "login": "raisercostin", 56 | "name": "Costin Grigore", 57 | "avatar_url": "https://avatars.githubusercontent.com/u/1624467?v=4", 58 | "profile": "http://raisercostin.org", 59 | "contributions": [ 60 | "code" 61 | ] 62 | }, 63 | { 64 | "login": "apeschar", 65 | "name": "Albert Peschar", 66 | "avatar_url": "https://avatars.githubusercontent.com/u/122977?v=4", 67 | "profile": "https://github.com/apeschar", 68 | "contributions": [ 69 | "code" 70 | ] 71 | } 72 | ], 73 | "contributorsPerLine": 7, 74 | "skipCi": true, 75 | "repoType": "github", 76 | "repoHost": "https://github.com", 77 | "projectName": "vue-pivottable", 78 | "projectOwner": "Seungwoo321", 79 | "commitType": "docs" 80 | } 81 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 1 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | '@vue/standard' 9 | ], 10 | rules: { 11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 13 | }, 14 | parserOptions: { 15 | parser: 'babel-eslint' 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Docs 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | paths: 7 | - 'docs/**' 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build-and-deploy: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | 17 | - name: Setup Node.js 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: '18' 21 | 22 | - name: Install pnpm 23 | uses: pnpm/action-setup@v2 24 | with: 25 | version: 9 26 | run_install: true 27 | - name: Build docs 28 | run: pnpm docs:build 29 | 30 | - name: Deploy to GitHub Pages 31 | uses: JamesIves/github-pages-deploy-action@v4 32 | with: 33 | folder: docs/docs/.vuepress/dist 34 | branch: gh-pages 35 | clean: true 36 | token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Etc 2 | package-lock.json 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | .DS_Store 29 | 30 | # Bower dependency directory (https://bower.io/) 31 | bower_components 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (https://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # TypeScript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | 64 | # next.js build output 65 | .next 66 | 67 | # vscode 68 | .history 69 | 70 | #vuepress 71 | .temp 72 | packages/docs/.vuepress/dist 73 | 74 | .cache 75 | .temp 76 | dist -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD033": false, 3 | "MD013": false 4 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Seungwoo321 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue Pivottable 2 | 3 | > ⚠️ **This package supports Vue 2 only.** 4 | > 👉 Looking for Vue 3? Use: [vue3-pivottable GitHub repo](https://github.com/vue-pivottable/vue3-pivottable) 5 | 6 | It is a Vue port of the jQuery-based [PivotTable.js](https://pivottable.js.org/) 7 | 8 | [![npm](https://flat.badgen.net/npm/v/vue-pivottable)](https://npmjs.com/package/vue-pivottable) 9 | [![npm](https://flat.badgen.net/npm/dt/vue-pivottable)](https://npmjs.com/package/vue-pivottable) 10 | [![npm](https://flat.badgen.net/npm/license/vue-pivottable)](https://flat.badgen.net/npm/license/vue-pivottable) 11 | [![jsdelivr](https://data.jsdelivr.com/v1/package/npm/vue-pivottable/badge)](https://www.jsdelivr.com/package/npm/vue-pivottable) 12 | 13 | [![All Contributors](https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square)](#contributors-) 14 | 15 | 16 | ## Documentation 17 | 18 | You can view the documentation at . 19 | It's also lighter by removing `vue-plotly` from the dependencies. 20 | 21 | ## Live Demo 22 | 23 | [link](https://jsfiddle.net/seungwoo321/repqmz3f/) 24 | 25 | ## Example Code 26 | 27 | ```bash 28 | # Clone the project 29 | git clone https://github.com/Seungwoo321/vue-pivottable.git 30 | 31 | # Go into the cloned directory 32 | cd vue-pivottable/example/ 33 | 34 | # npm install 35 | npm install 36 | 37 | # npm run serve 38 | npm run serve 39 | ``` 40 | 41 | Open browser to 42 | 43 | ![vue-pivottable-demo.gif](https://seungwoo321.github.io/vue-pivottable-demo.gif) 44 | 45 | ## Installation 46 | 47 | ```shall 48 | npm i vue-pivottable@0.4.68 49 | ``` 50 | 51 | ## Usage 52 | 53 | * Vue Pivottable 54 | 55 | ```html 56 | 64 | 65 | 74 | ``` 75 | 76 | * Vue Pivottable Ui 77 | 78 | ```html 79 | 87 | 88 | 97 | ``` 98 | 99 | * Plotly renderer 100 | 101 | [See docs](https://seungwoo321.github.io/vue-pivottable/guide/renderer.html#plotly-renderer-v0-4-6) 102 | 103 | ```html 104 | 114 | 115 | 140 | ``` 141 | 142 | ## Contributors 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 |
Seungwoo321
Seungwoo321

💻
rkota
rkota

🚧
Jayraj Rathwa
Jayraj Rathwa

🐛
cbbdev
cbbdev

🚧
hyemyn2
hyemyn2

💻
Costin Grigore
Costin Grigore

💻
Albert Peschar
Albert Peschar

💻
160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | ## Inspired 174 | 175 | * [plotly/react-pivottable](https://github.com/plotly/react-pivottable) - React-based pivot table library 176 | * [David-Desmaisons/vue-plotly](https://github.com/David-Desmaisons/vue-plotly) - vue wrapper for plotly.js 177 | 178 | ## License 179 | 180 | MIT 181 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /docs/docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | import { defaultTheme } from '@vuepress/theme-default' 2 | import { defineUserConfig } from 'vuepress' 3 | import { viteBundler } from '@vuepress/bundler-vite' 4 | import { 5 | head, 6 | navbarEn, 7 | navbarKo, 8 | sidebarEn, 9 | sidebarKo, 10 | } from './configs/index.js' 11 | 12 | export default defineUserConfig({ 13 | lang: 'en-US', 14 | base: '/vue-pivottable/', 15 | 16 | head, 17 | 18 | locales: { 19 | '/': { 20 | lang: 'en-US', 21 | title: 'Vue Pivottable (Vue2)', 22 | description: 'It is a Vue port of the jQuery-based PivotTable.js', 23 | }, 24 | '/ko/': { 25 | lang: 'ko-Kr', 26 | title: 'Vue Pivottable (Vue2)', 27 | description: 'jQuery 기반의 PivotTable.js를 Vue로 포팅한 것입니다', 28 | }, 29 | }, 30 | 31 | theme: defaultTheme({ 32 | logo: '/images/logo.png', 33 | repo: 'seungwoo321/vue-pivottable', 34 | docsDir: 'packages/docs', 35 | docsBranch: 'master', 36 | externalLinkIcon: true, 37 | editLinks: true, 38 | smoothScroll: true, 39 | locales: { 40 | '/': { 41 | navbar: navbarEn, 42 | sidebar: sidebarEn, 43 | }, 44 | '/ko/': { 45 | navbar: navbarKo, 46 | selectLanguageName: '한국어', 47 | selectLanguageText: '언어 선택', 48 | selectLanguageAriaLabel: '언어 선택', 49 | sidebar: sidebarKo, 50 | editLinkText: 'GitHub에서 이 페이지 편집하기', 51 | lastUpdatedText: '마지막 업데이트', 52 | contributorsText: '기여자', 53 | tip: '팁', 54 | warning: '주의', 55 | danger: '경고', 56 | notFound: [ 57 | '여기엔 아무것도 없어요', 58 | '우리가 어떻게 여기에 왔죠?', 59 | '404 페이지입니다', 60 | '잘못된 링크로 들어온 것 같아요', 61 | ], 62 | backToHome: '홈으로 돌아가기', 63 | openInNewWindow: '새 창에서 열기', 64 | toggleColorMode: '색상 모드 전환', 65 | toggleSidebar: '사이드바 전환', 66 | } 67 | } 68 | }), 69 | contributors: true, 70 | lastUpdated: true, 71 | bundler: viteBundler(), 72 | 73 | plugins: [ 74 | // googleAnalytics({ 75 | // id: 'G-G8TZ5WYL3Z' 76 | // }) 77 | ] 78 | }) 79 | 80 | -------------------------------------------------------------------------------- /docs/docs/.vuepress/config.js.2f8f54db.mjs: -------------------------------------------------------------------------------- 1 | // docs/.vuepress/config.js 2 | import { defaultTheme } from "@vuepress/theme-default"; 3 | import { defineUserConfig } from "vuepress"; 4 | import { viteBundler } from "@vuepress/bundler-vite"; 5 | import googleAnalytics from "@vuepress/plugin-google-analytics"; 6 | 7 | // docs/.vuepress/configs/head.js 8 | var head = [ 9 | ["meta", { name: "application-name", content: "VuePivottable" }], 10 | ["meta", { name: "apple-mobile-web-app-title", content: "VuePivottable" }], 11 | ["script", { async: true, src: "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-7701657858733816", crossorigin: "anonymous" }] 12 | ]; 13 | 14 | // docs/.vuepress/configs/navbar/en.js 15 | var navbarEn = [ 16 | { 17 | text: "Guide", 18 | children: [ 19 | "/guide/introduction.md", 20 | "/guide/getting-started.md", 21 | "/guide/locale.md", 22 | "/guide/slot.md", 23 | "/guide/scoped-slot.md", 24 | "/guide/utilities.md", 25 | "/guide/renderer.md", 26 | "/guide/props.md", 27 | "/guide/styling.md" 28 | ] 29 | }, 30 | { 31 | text: `v0.4.68 (Vue 2)`, 32 | children: [ 33 | { 34 | text: "v1.x (Vue 3)", 35 | link: "https://vue-pivottable.vercel.app/" 36 | } 37 | ] 38 | }, 39 | { 40 | text: "Contribute \u{1F49A}", 41 | link: "https://github.com/sponsors/Seungwoo321" 42 | } 43 | ]; 44 | 45 | // docs/.vuepress/configs/navbar/ko.js 46 | var navbarKo = [ 47 | { 48 | text: "\uAC00\uC774\uB4DC", 49 | children: [ 50 | "/ko/guide/introduction.md", 51 | "/ko/guide/getting-started.md", 52 | "/ko/guide/locale.md", 53 | "/ko/guide/slot.md", 54 | "/ko/guide/scoped-slot.md", 55 | "/ko/guide/utilities.md", 56 | "/ko/guide/renderer.md", 57 | "/ko/guide/props.md", 58 | "/ko/guide/styling.md" 59 | ] 60 | }, 61 | { 62 | text: `v0.4.68 (Vue 2)`, 63 | children: [ 64 | { 65 | text: "v1.x (Vue 3)", 66 | link: "https://vue-pivottable.vercel.app/" 67 | } 68 | ] 69 | }, 70 | { 71 | text: "\uAE30\uC5EC\uD558\uAE30 \u{1F49A}", 72 | link: "https://github.com/sponsors/Seungwoo321" 73 | } 74 | ]; 75 | 76 | // docs/.vuepress/configs/sidebar/en.js 77 | var sidebarEn = { 78 | "/guide/": [ 79 | { 80 | text: "Guide", 81 | children: [ 82 | "introduction", 83 | "getting-started" 84 | ], 85 | collapsible: false 86 | }, 87 | { 88 | text: "Advanced", 89 | children: [ 90 | "locale", 91 | "slot", 92 | "scoped-slot", 93 | "utilities", 94 | "renderer" 95 | ], 96 | collapsible: false 97 | }, 98 | { 99 | text: "Props", 100 | link: "props", 101 | collapsible: false 102 | }, 103 | { 104 | text: "Styling", 105 | link: "styling", 106 | collapsible: false 107 | } 108 | ] 109 | }; 110 | 111 | // docs/.vuepress/configs/sidebar/ko.js 112 | var sidebarKo = { 113 | "/ko/guide/": [ 114 | { 115 | text: "\uAC00\uC774\uB4DC", 116 | children: [ 117 | "introduction", 118 | "getting-started" 119 | ], 120 | collapsible: false 121 | }, 122 | { 123 | text: "\uC2EC\uD654", 124 | children: [ 125 | "locale", 126 | "slot", 127 | "scoped-slot", 128 | "utilities", 129 | "renderer" 130 | ], 131 | collapsible: false 132 | }, 133 | { 134 | text: "Props", 135 | link: "props", 136 | collapsible: false 137 | }, 138 | { 139 | text: "\uC2A4\uD0C0\uC77C\uB9C1", 140 | link: "styling", 141 | collapsible: false 142 | } 143 | ] 144 | }; 145 | 146 | // docs/.vuepress/config.js 147 | var config_default = defineUserConfig({ 148 | lang: "en-US", 149 | base: "/vue-pivottable/", 150 | head, 151 | locales: { 152 | "/": { 153 | lang: "en-US", 154 | title: "Vue Pivottable (Vue2)", 155 | description: "It is a Vue port of the jQuery-based PivotTable.js" 156 | }, 157 | "/ko/": { 158 | lang: "ko-Kr", 159 | title: "Vue Pivottable (Vue2)", 160 | description: "jQuery \uAE30\uBC18\uC758 PivotTable.js\uB97C Vue\uB85C \uD3EC\uD305\uD55C \uAC83\uC785\uB2C8\uB2E4" 161 | } 162 | }, 163 | theme: defaultTheme({ 164 | logo: "/images/logo.png", 165 | repo: "seungwoo321/vue-pivottable", 166 | docsDir: "packages/docs", 167 | docsBranch: "master", 168 | externalLinkIcon: true, 169 | editLinks: true, 170 | smoothScroll: true, 171 | locales: { 172 | "/": { 173 | navbar: navbarEn, 174 | sidebar: sidebarEn 175 | }, 176 | "/ko/": { 177 | navbar: navbarKo, 178 | selectLanguageName: "\uD55C\uAD6D\uC5B4", 179 | selectLanguageText: "\uC5B8\uC5B4 \uC120\uD0DD", 180 | selectLanguageAriaLabel: "\uC5B8\uC5B4 \uC120\uD0DD", 181 | sidebar: sidebarKo, 182 | editLinkText: "GitHub\uC5D0\uC11C \uC774 \uD398\uC774\uC9C0 \uD3B8\uC9D1\uD558\uAE30", 183 | lastUpdatedText: "\uB9C8\uC9C0\uB9C9 \uC5C5\uB370\uC774\uD2B8", 184 | contributorsText: "\uAE30\uC5EC\uC790", 185 | tip: "\uD301", 186 | warning: "\uC8FC\uC758", 187 | danger: "\uACBD\uACE0", 188 | notFound: [ 189 | "\uC5EC\uAE30\uC5D4 \uC544\uBB34\uAC83\uB3C4 \uC5C6\uC5B4\uC694", 190 | "\uC6B0\uB9AC\uAC00 \uC5B4\uB5BB\uAC8C \uC5EC\uAE30\uC5D0 \uC654\uC8E0?", 191 | "404 \uD398\uC774\uC9C0\uC785\uB2C8\uB2E4", 192 | "\uC798\uBABB\uB41C \uB9C1\uD06C\uB85C \uB4E4\uC5B4\uC628 \uAC83 \uAC19\uC544\uC694" 193 | ], 194 | backToHome: "\uD648\uC73C\uB85C \uB3CC\uC544\uAC00\uAE30", 195 | openInNewWindow: "\uC0C8 \uCC3D\uC5D0\uC11C \uC5F4\uAE30", 196 | toggleColorMode: "\uC0C9\uC0C1 \uBAA8\uB4DC \uC804\uD658", 197 | toggleSidebar: "\uC0AC\uC774\uB4DC\uBC14 \uC804\uD658" 198 | } 199 | } 200 | }), 201 | contributors: true, 202 | lastUpdated: true, 203 | bundler: viteBundler(), 204 | plugins: [ 205 | googleAnalytics({ 206 | id: "G-G8TZ5WYL3Z" 207 | }) 208 | ] 209 | }); 210 | export { 211 | config_default as default 212 | }; 213 | //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiZG9jcy8udnVlcHJlc3MvY29uZmlnLmpzIiwgImRvY3MvLnZ1ZXByZXNzL2NvbmZpZ3MvaGVhZC5qcyIsICJkb2NzLy52dWVwcmVzcy9jb25maWdzL25hdmJhci9lbi5qcyIsICJkb2NzLy52dWVwcmVzcy9jb25maWdzL25hdmJhci9rby5qcyIsICJkb2NzLy52dWVwcmVzcy9jb25maWdzL3NpZGViYXIvZW4uanMiLCAiZG9jcy8udnVlcHJlc3MvY29uZmlncy9zaWRlYmFyL2tvLmpzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZGlybmFtZSA9IFwiL1VzZXJzL216YzAxLXN3bGVlL2Rldi9yZXBvc2l0b3J5L2dpdGh1Yi9kb2NzL2RvY3MvLnZ1ZXByZXNzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCIvVXNlcnMvbXpjMDEtc3dsZWUvZGV2L3JlcG9zaXRvcnkvZ2l0aHViL2RvY3MvZG9jcy8udnVlcHJlc3MvY29uZmlnLmpzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9Vc2Vycy9temMwMS1zd2xlZS9kZXYvcmVwb3NpdG9yeS9naXRodWIvZG9jcy9kb2NzLy52dWVwcmVzcy9jb25maWcuanNcIjtpbXBvcnQgeyBkZWZhdWx0VGhlbWUgfSBmcm9tICdAdnVlcHJlc3MvdGhlbWUtZGVmYXVsdCdcbmltcG9ydCB7IGRlZmluZVVzZXJDb25maWcgfSBmcm9tICd2dWVwcmVzcydcbmltcG9ydCB7IHZpdGVCdW5kbGVyIH0gZnJvbSAnQHZ1ZXByZXNzL2J1bmRsZXItdml0ZSdcbmltcG9ydCBnb29nbGVBbmFseXRpY3MgZnJvbSBcIkB2dWVwcmVzcy9wbHVnaW4tZ29vZ2xlLWFuYWx5dGljc1wiO1xuaW1wb3J0IHtcbiAgaGVhZCxcbiAgbmF2YmFyRW4sXG4gIG5hdmJhcktvLFxuICBzaWRlYmFyRW4sXG4gIHNpZGViYXJLbyxcbn0gZnJvbSAnLi9jb25maWdzL2luZGV4LmpzJ1xuXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVVc2VyQ29uZmlnKHtcbiAgbGFuZzogJ2VuLVVTJyxcbiAgYmFzZTogJy92dWUtcGl2b3R0YWJsZS8nLFxuXG4gIGhlYWQsXG5cbiAgbG9jYWxlczoge1xuICAgICcvJzoge1xuICAgICAgbGFuZzogJ2VuLVVTJyxcbiAgICAgIHRpdGxlOiAnVnVlIFBpdm90dGFibGUgKFZ1ZTIpJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnSXQgaXMgYSBWdWUgcG9ydCBvZiB0aGUgalF1ZXJ5LWJhc2VkIFBpdm90VGFibGUuanMnLFxuICAgIH0sXG4gICAgJy9rby8nOiB7XG4gICAgICBsYW5nOiAna28tS3InLFxuICAgICAgdGl0bGU6ICdWdWUgUGl2b3R0YWJsZSAoVnVlMiknLFxuICAgICAgZGVzY3JpcHRpb246ICdqUXVlcnkgXHVBRTMwXHVCQzE4XHVDNzU4IFBpdm90VGFibGUuanNcdUI5N0MgVnVlXHVCODVDIFx1RDNFQ1x1RDMwNVx1RDU1QyBcdUFDODNcdUM3ODVcdUIyQzhcdUIyRTQnLFxuICAgIH0sXG4gIH0sXG5cbiAgdGhlbWU6IGRlZmF1bHRUaGVtZSh7XG4gICAgbG9nbzogJy9pbWFnZXMvbG9nby5wbmcnLFxuICAgIHJlcG86ICdzZXVuZ3dvbzMyMS92dWUtcGl2b3R0YWJsZScsXG4gICAgZG9jc0RpcjogJ3BhY2thZ2VzL2RvY3MnLFxuICAgIGRvY3NCcmFuY2g6ICdtYXN0ZXInLFxuICAgIGV4dGVybmFsTGlua0ljb246IHRydWUsXG4gICAgZWRpdExpbmtzOiB0cnVlLFxuICAgIHNtb290aFNjcm9sbDogdHJ1ZSxcbiAgICBsb2NhbGVzOiB7XG4gICAgICAnLyc6IHtcbiAgICAgICAgbmF2YmFyOiBuYXZiYXJFbixcbiAgICAgICAgc2lkZWJhcjogc2lkZWJhckVuLFxuICAgICAgfSxcbiAgICAgICcva28vJzoge1xuICAgICAgICBuYXZiYXI6IG5hdmJhcktvLFxuICAgICAgICBzZWxlY3RMYW5ndWFnZU5hbWU6ICdcdUQ1NUNcdUFENkRcdUM1QjQnLFxuICAgICAgICBzZWxlY3RMYW5ndWFnZVRleHQ6ICdcdUM1QjhcdUM1QjQgXHVDMTIwXHVEMEREJyxcbiAgICAgICAgc2VsZWN0TGFuZ3VhZ2VBcmlhTGFiZWw6ICdcdUM1QjhcdUM1QjQgXHVDMTIwXHVEMEREJyxcbiAgICAgICAgc2lkZWJhcjogc2lkZWJhcktvLFxuICAgICAgICBlZGl0TGlua1RleHQ6ICdHaXRIdWJcdUM1RDBcdUMxMUMgXHVDNzc0IFx1RDM5OFx1Qzc3NFx1QzlDMCBcdUQzQjhcdUM5RDFcdUQ1NThcdUFFMzAnLFxuICAgICAgICBsYXN0VXBkYXRlZFRleHQ6ICdcdUI5QzhcdUM5QzBcdUI5QzkgXHVDNUM1XHVCMzcwXHVDNzc0XHVEMkI4JyxcbiAgICAgICAgY29udHJpYnV0b3JzVGV4dDogJ1x1QUUzMFx1QzVFQ1x1Qzc5MCcsXG4gICAgICAgIHRpcDogJ1x1RDMwMScsXG4gICAgICAgIHdhcm5pbmc6ICdcdUM4RkNcdUM3NTgnLFxuICAgICAgICBkYW5nZXI6ICdcdUFDQkRcdUFDRTAnLFxuICAgICAgICBub3RGb3VuZDogW1xuICAgICAgICAgICdcdUM1RUNcdUFFMzBcdUM1RDQgXHVDNTQ0XHVCQjM0XHVBQzgzXHVCM0M0IFx1QzVDNlx1QzVCNFx1QzY5NCcsXG4gICAgICAgICAgJ1x1QzZCMFx1QjlBQ1x1QUMwMCBcdUM1QjRcdUI1QkJcdUFDOEMgXHVDNUVDXHVBRTMwXHVDNUQwIFx1QzY1NFx1QzhFMD8nLFxuICAgICAgICAgICc0MDQgXHVEMzk4XHVDNzc0XHVDOUMwXHVDNzg1XHVCMkM4XHVCMkU0JyxcbiAgICAgICAgICAnXHVDNzk4XHVCQUJCXHVCNDFDIFx1QjlDMVx1RDA2Q1x1Qjg1QyBcdUI0RTRcdUM1QjRcdUM2MjggXHVBQzgzIFx1QUMxOVx1QzU0NFx1QzY5NCcsXG4gICAgICAgIF0sXG4gICAgICAgIGJhY2tUb0hvbWU6ICdcdUQ2NDhcdUM3M0NcdUI4NUMgXHVCM0NDXHVDNTQ0XHVBQzAwXHVBRTMwJyxcbiAgICAgICAgb3BlbkluTmV3V2luZG93OiAnXHVDMEM4IFx1Q0MzRFx1QzVEMFx1QzExQyBcdUM1RjRcdUFFMzAnLFxuICAgICAgICB0b2dnbGVDb2xvck1vZGU6ICdcdUMwQzlcdUMwQzEgXHVCQUE4XHVCNERDIFx1QzgwNFx1RDY1OCcsXG4gICAgICAgIHRvZ2dsZVNpZGViYXI6ICdcdUMwQUNcdUM3NzRcdUI0RENcdUJDMTQgXHVDODA0XHVENjU4JyxcbiAgICAgIH1cbiAgICB9XG4gIH0pLFxuICBjb250cmlidXRvcnM6IHRydWUsXG4gIGxhc3RVcGRhdGVkOiB0cnVlLFxuICBidW5kbGVyOiB2aXRlQnVuZGxlcigpLFxuXG4gIHBsdWdpbnM6IFtcbiAgICBnb29nbGVBbmFseXRpY3Moe1xuICAgICAgaWQ6ICdHLUc4VFo1V1lMM1onXG4gICAgfSlcbiAgXVxufSlcblxuIiwgImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvVXNlcnMvbXpjMDEtc3dsZWUvZGV2L3JlcG9zaXRvcnkvZ2l0aHViL2RvY3MvZG9jcy8udnVlcHJlc3MvY29uZmlnc1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL1VzZXJzL216YzAxLXN3bGVlL2Rldi9yZXBvc2l0b3J5L2dpdGh1Yi9kb2NzL2RvY3MvLnZ1ZXByZXNzL2NvbmZpZ3MvaGVhZC5qc1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vVXNlcnMvbXpjMDEtc3dsZWUvZGV2L3JlcG9zaXRvcnkvZ2l0aHViL2RvY3MvZG9jcy8udnVlcHJlc3MvY29uZmlncy9oZWFkLmpzXCI7XG5leHBvcnQgY29uc3QgaGVhZCA9IFtcbiAgWydtZXRhJywgeyBuYW1lOiAnYXBwbGljYXRpb24tbmFtZScsIGNvbnRlbnQ6ICdWdWVQaXZvdHRhYmxlJyB9XSxcbiAgWydtZXRhJywgeyBuYW1lOiAnYXBwbGUtbW9iaWxlLXdlYi1hcHAtdGl0bGUnLCBjb250ZW50OiAnVnVlUGl2b3R0YWJsZScgfV0sXG4gIFsnc2NyaXB0JywgeyBhc3luYzogdHJ1ZSwgc3JjOiAnaHR0cHM6Ly9wYWdlYWQyLmdvb2dsZXN5bmRpY2F0aW9uLmNvbS9wYWdlYWQvanMvYWRzYnlnb29nbGUuanM/Y2xpZW50PWNhLXB1Yi03NzAxNjU3ODU4NzMzODE2JywgY3Jvc3NvcmlnaW46ICdhbm9ueW1vdXMnfV1cbl0iLCAiY29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2Rpcm5hbWUgPSBcIi9Vc2Vycy9temMwMS1zd2xlZS9kZXYvcmVwb3NpdG9yeS9naXRodWIvZG9jcy9kb2NzLy52dWVwcmVzcy9jb25maWdzL25hdmJhclwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL1VzZXJzL216YzAxLXN3bGVlL2Rldi9yZXBvc2l0b3J5L2dpdGh1Yi9kb2NzL2RvY3MvLnZ1ZXByZXNzL2NvbmZpZ3MvbmF2YmFyL2VuLmpzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9Vc2Vycy9temMwMS1zd2xlZS9kZXYvcmVwb3NpdG9yeS9naXRodWIvZG9jcy9kb2NzLy52dWVwcmVzcy9jb25maWdzL25hdmJhci9lbi5qc1wiO2V4cG9ydCBjb25zdCBuYXZiYXJFbiA9IFtcbiAge1xuICAgIHRleHQ6ICdHdWlkZScsXG4gICAgY2hpbGRyZW46IFtcbiAgICAgICcvZ3VpZGUvaW50cm9kdWN0aW9uLm1kJyxcbiAgICAgICcvZ3VpZGUvZ2V0dGluZy1zdGFydGVkLm1kJyxcbiAgICAgICcvZ3VpZGUvbG9jYWxlLm1kJyxcbiAgICAgICcvZ3VpZGUvc2xvdC5tZCcsXG4gICAgICAnL2d1aWRlL3Njb3BlZC1zbG90Lm1kJyxcbiAgICAgICcvZ3VpZGUvdXRpbGl0aWVzLm1kJyxcbiAgICAgICcvZ3VpZGUvcmVuZGVyZXIubWQnLFxuICAgICAgJy9ndWlkZS9wcm9wcy5tZCcsXG4gICAgICAnL2d1aWRlL3N0eWxpbmcubWQnLFxuICAgIF0gXG4gIH0sXG4gIHtcbiAgICB0ZXh0OiBgdjAuNC42OCAoVnVlIDIpYCxcbiAgICBjaGlsZHJlbjogW1xuICAgICAge1xuICAgICAgICB0ZXh0OiAndjEueCAoVnVlIDMpJyxcbiAgICAgICAgbGluazogJ2h0dHBzOi8vdnVlLXBpdm90dGFibGUudmVyY2VsLmFwcC8nXG4gICAgICB9XG4gICAgXVxuICB9LFxuICB7XG4gICAgdGV4dDogJ0NvbnRyaWJ1dGUgXHVEODNEXHVEQzlBJyxcbiAgICBsaW5rOiAnaHR0cHM6Ly9naXRodWIuY29tL3Nwb25zb3JzL1NldW5nd29vMzIxJ1xuICB9LFxuXSIsICJjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZGlybmFtZSA9IFwiL1VzZXJzL216YzAxLXN3bGVlL2Rldi9yZXBvc2l0b3J5L2dpdGh1Yi9kb2NzL2RvY3MvLnZ1ZXByZXNzL2NvbmZpZ3MvbmF2YmFyXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCIvVXNlcnMvbXpjMDEtc3dsZWUvZGV2L3JlcG9zaXRvcnkvZ2l0aHViL2RvY3MvZG9jcy8udnVlcHJlc3MvY29uZmlncy9uYXZiYXIva28uanNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL1VzZXJzL216YzAxLXN3bGVlL2Rldi9yZXBvc2l0b3J5L2dpdGh1Yi9kb2NzL2RvY3MvLnZ1ZXByZXNzL2NvbmZpZ3MvbmF2YmFyL2tvLmpzXCI7ZXhwb3J0IGNvbnN0IG5hdmJhcktvID0gW1xuICB7XG4gICAgdGV4dDogJ1x1QUMwMFx1Qzc3NFx1QjREQycsXG4gICAgY2hpbGRyZW46IFtcbiAgICAgICcva28vZ3VpZGUvaW50cm9kdWN0aW9uLm1kJyxcbiAgICAgICcva28vZ3VpZGUvZ2V0dGluZy1zdGFydGVkLm1kJyxcbiAgICAgICcva28vZ3VpZGUvbG9jYWxlLm1kJyxcbiAgICAgICcva28vZ3VpZGUvc2xvdC5tZCcsXG4gICAgICAnL2tvL2d1aWRlL3Njb3BlZC1zbG90Lm1kJyxcbiAgICAgICcva28vZ3VpZGUvdXRpbGl0aWVzLm1kJyxcbiAgICAgICcva28vZ3VpZGUvcmVuZGVyZXIubWQnLFxuICAgICAgJy9rby9ndWlkZS9wcm9wcy5tZCcsXG4gICAgICAnL2tvL2d1aWRlL3N0eWxpbmcubWQnLFxuICAgIF0gXG4gIH0sXG4gIHtcbiAgICB0ZXh0OiBgdjAuNC42OCAoVnVlIDIpYCxcbiAgICBjaGlsZHJlbjogW1xuICAgICAge1xuICAgICAgICB0ZXh0OiAndjEueCAoVnVlIDMpJyxcbiAgICAgICAgbGluazogJ2h0dHBzOi8vdnVlLXBpdm90dGFibGUudmVyY2VsLmFwcC8nXG4gICAgICB9XG4gICAgXVxuICB9LFxuICB7XG4gICAgdGV4dDogJ1x1QUUzMFx1QzVFQ1x1RDU1OFx1QUUzMCBcdUQ4M0RcdURDOUEnLFxuICAgIGxpbms6ICdodHRwczovL2dpdGh1Yi5jb20vc3BvbnNvcnMvU2V1bmd3b28zMjEnXG4gIH1cbl0iLCAiY29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2Rpcm5hbWUgPSBcIi9Vc2Vycy9temMwMS1zd2xlZS9kZXYvcmVwb3NpdG9yeS9naXRodWIvZG9jcy9kb2NzLy52dWVwcmVzcy9jb25maWdzL3NpZGViYXJcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIi9Vc2Vycy9temMwMS1zd2xlZS9kZXYvcmVwb3NpdG9yeS9naXRodWIvZG9jcy9kb2NzLy52dWVwcmVzcy9jb25maWdzL3NpZGViYXIvZW4uanNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL1VzZXJzL216YzAxLXN3bGVlL2Rldi9yZXBvc2l0b3J5L2dpdGh1Yi9kb2NzL2RvY3MvLnZ1ZXByZXNzL2NvbmZpZ3Mvc2lkZWJhci9lbi5qc1wiO2V4cG9ydCBjb25zdCBzaWRlYmFyRW4gPSB7XG4gICcvZ3VpZGUvJzogW1xuICAgIHtcbiAgICAgIHRleHQ6ICdHdWlkZScsXG4gICAgICBjaGlsZHJlbjogW1xuICAgICAgICAnaW50cm9kdWN0aW9uJyxcbiAgICAgICAgJ2dldHRpbmctc3RhcnRlZCcsXG4gICAgICBdLFxuICAgICAgY29sbGFwc2libGU6IGZhbHNlXG4gICAgfSxcbiAgICB7XG4gICAgICB0ZXh0OiAnQWR2YW5jZWQnLFxuICAgICAgY2hpbGRyZW46IFtcbiAgICAgICAgJ2xvY2FsZScsXG4gICAgICAgICdzbG90JyxcbiAgICAgICAgJ3Njb3BlZC1zbG90JyxcbiAgICAgICAgJ3V0aWxpdGllcycsXG4gICAgICAgICdyZW5kZXJlcidcbiAgICAgIF0sXG4gICAgICBjb2xsYXBzaWJsZTogZmFsc2VcbiAgICB9LFxuICAgIHtcbiAgICAgIHRleHQ6ICdQcm9wcycsXG4gICAgICBsaW5rOiAncHJvcHMnLFxuICAgICAgY29sbGFwc2libGU6IGZhbHNlXG4gICAgfSxcbiAgICB7XG4gICAgICB0ZXh0OiAnU3R5bGluZycsXG4gICAgICBsaW5rOiAnc3R5bGluZycsXG4gICAgICBjb2xsYXBzaWJsZTogZmFsc2VcbiAgICB9XG4gIF1cbn0iLCAiY29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2Rpcm5hbWUgPSBcIi9Vc2Vycy9temMwMS1zd2xlZS9kZXYvcmVwb3NpdG9yeS9naXRodWIvZG9jcy9kb2NzLy52dWVwcmVzcy9jb25maWdzL3NpZGViYXJcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIi9Vc2Vycy9temMwMS1zd2xlZS9kZXYvcmVwb3NpdG9yeS9naXRodWIvZG9jcy9kb2NzLy52dWVwcmVzcy9jb25maWdzL3NpZGViYXIva28uanNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL1VzZXJzL216YzAxLXN3bGVlL2Rldi9yZXBvc2l0b3J5L2dpdGh1Yi9kb2NzL2RvY3MvLnZ1ZXByZXNzL2NvbmZpZ3Mvc2lkZWJhci9rby5qc1wiO2V4cG9ydCBjb25zdCBzaWRlYmFyS28gPSB7XG4gICcva28vZ3VpZGUvJzogW1xuICAgIHtcbiAgICAgIHRleHQ6ICdcdUFDMDBcdUM3NzRcdUI0REMnLFxuICAgICAgY2hpbGRyZW46IFtcbiAgICAgICAgJ2ludHJvZHVjdGlvbicsXG4gICAgICAgICdnZXR0aW5nLXN0YXJ0ZWQnLFxuICAgICAgXSxcbiAgICAgIGNvbGxhcHNpYmxlOiBmYWxzZVxuICAgIH0sXG4gICAge1xuICAgICAgdGV4dDogJ1x1QzJFQ1x1RDY1NCcsXG4gICAgICBjaGlsZHJlbjogW1xuICAgICAgICAnbG9jYWxlJyxcbiAgICAgICAgJ3Nsb3QnLFxuICAgICAgICAnc2NvcGVkLXNsb3QnLFxuICAgICAgICAndXRpbGl0aWVzJyxcbiAgICAgICAgJ3JlbmRlcmVyJ1xuICAgICAgXSxcbiAgICAgIGNvbGxhcHNpYmxlOiBmYWxzZVxuICAgIH0sXG4gICAge1xuICAgICAgdGV4dDogJ1Byb3BzJyxcbiAgICAgIGxpbms6ICdwcm9wcycsXG4gICAgICBjb2xsYXBzaWJsZTogZmFsc2VcbiAgICB9LFxuICAgIHtcbiAgICAgIHRleHQ6ICdcdUMyQTRcdUQwQzBcdUM3N0NcdUI5QzEnLFxuICAgICAgbGluazogJ3N0eWxpbmcnLFxuICAgICAgY29sbGFwc2libGU6IGZhbHNlXG4gICAgfVxuICBdXG59Il0sCiAgIm1hcHBpbmdzIjogIjtBQUE0VixTQUFTLG9CQUFvQjtBQUN6WCxTQUFTLHdCQUF3QjtBQUNqQyxTQUFTLG1CQUFtQjtBQUM1QixPQUFPLHFCQUFxQjs7O0FDRnJCLElBQU0sT0FBTztBQUFBLEVBQ2xCLENBQUMsUUFBUSxFQUFFLE1BQU0sb0JBQW9CLFNBQVMsZ0JBQWdCLENBQUM7QUFBQSxFQUMvRCxDQUFDLFFBQVEsRUFBRSxNQUFNLDhCQUE4QixTQUFTLGdCQUFnQixDQUFDO0FBQUEsRUFDekUsQ0FBQyxVQUFVLEVBQUUsT0FBTyxNQUFNLEtBQUssaUdBQWlHLGFBQWEsWUFBVyxDQUFDO0FBQzNKOzs7QUNMd1ksSUFBTSxXQUFXO0FBQUEsRUFDdlo7QUFBQSxJQUNFLE1BQU07QUFBQSxJQUNOLFVBQVU7QUFBQSxNQUNSO0FBQUEsTUFDQTtBQUFBLE1BQ0E7QUFBQSxNQUNBO0FBQUEsTUFDQTtBQUFBLE1BQ0E7QUFBQSxNQUNBO0FBQUEsTUFDQTtBQUFBLE1BQ0E7QUFBQSxJQUNGO0FBQUEsRUFDRjtBQUFBLEVBQ0E7QUFBQSxJQUNFLE1BQU07QUFBQSxJQUNOLFVBQVU7QUFBQSxNQUNSO0FBQUEsUUFDRSxNQUFNO0FBQUEsUUFDTixNQUFNO0FBQUEsTUFDUjtBQUFBLElBQ0Y7QUFBQSxFQUNGO0FBQUEsRUFDQTtBQUFBLElBQ0UsTUFBTTtBQUFBLElBQ04sTUFBTTtBQUFBLEVBQ1I7QUFDRjs7O0FDNUJ3WSxJQUFNLFdBQVc7QUFBQSxFQUN2WjtBQUFBLElBQ0UsTUFBTTtBQUFBLElBQ04sVUFBVTtBQUFBLE1BQ1I7QUFBQSxNQUNBO0FBQUEsTUFDQTtBQUFBLE1BQ0E7QUFBQSxNQUNBO0FBQUEsTUFDQTtBQUFBLE1BQ0E7QUFBQSxNQUNBO0FBQUEsTUFDQTtBQUFBLElBQ0Y7QUFBQSxFQUNGO0FBQUEsRUFDQTtBQUFBLElBQ0UsTUFBTTtBQUFBLElBQ04sVUFBVTtBQUFBLE1BQ1I7QUFBQSxRQUNFLE1BQU07QUFBQSxRQUNOLE1BQU07QUFBQSxNQUNSO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFBQSxFQUNBO0FBQUEsSUFDRSxNQUFNO0FBQUEsSUFDTixNQUFNO0FBQUEsRUFDUjtBQUNGOzs7QUM1QjJZLElBQU0sWUFBWTtBQUFBLEVBQzNaLFdBQVc7QUFBQSxJQUNUO0FBQUEsTUFDRSxNQUFNO0FBQUEsTUFDTixVQUFVO0FBQUEsUUFDUjtBQUFBLFFBQ0E7QUFBQSxNQUNGO0FBQUEsTUFDQSxhQUFhO0FBQUEsSUFDZjtBQUFBLElBQ0E7QUFBQSxNQUNFLE1BQU07QUFBQSxNQUNOLFVBQVU7QUFBQSxRQUNSO0FBQUEsUUFDQTtBQUFBLFFBQ0E7QUFBQSxRQUNBO0FBQUEsUUFDQTtBQUFBLE1BQ0Y7QUFBQSxNQUNBLGFBQWE7QUFBQSxJQUNmO0FBQUEsSUFDQTtBQUFBLE1BQ0UsTUFBTTtBQUFBLE1BQ04sTUFBTTtBQUFBLE1BQ04sYUFBYTtBQUFBLElBQ2Y7QUFBQSxJQUNBO0FBQUEsTUFDRSxNQUFNO0FBQUEsTUFDTixNQUFNO0FBQUEsTUFDTixhQUFhO0FBQUEsSUFDZjtBQUFBLEVBQ0Y7QUFDRjs7O0FDaEMyWSxJQUFNLFlBQVk7QUFBQSxFQUMzWixjQUFjO0FBQUEsSUFDWjtBQUFBLE1BQ0UsTUFBTTtBQUFBLE1BQ04sVUFBVTtBQUFBLFFBQ1I7QUFBQSxRQUNBO0FBQUEsTUFDRjtBQUFBLE1BQ0EsYUFBYTtBQUFBLElBQ2Y7QUFBQSxJQUNBO0FBQUEsTUFDRSxNQUFNO0FBQUEsTUFDTixVQUFVO0FBQUEsUUFDUjtBQUFBLFFBQ0E7QUFBQSxRQUNBO0FBQUEsUUFDQTtBQUFBLFFBQ0E7QUFBQSxNQUNGO0FBQUEsTUFDQSxhQUFhO0FBQUEsSUFDZjtBQUFBLElBQ0E7QUFBQSxNQUNFLE1BQU07QUFBQSxNQUNOLE1BQU07QUFBQSxNQUNOLGFBQWE7QUFBQSxJQUNmO0FBQUEsSUFDQTtBQUFBLE1BQ0UsTUFBTTtBQUFBLE1BQ04sTUFBTTtBQUFBLE1BQ04sYUFBYTtBQUFBLElBQ2Y7QUFBQSxFQUNGO0FBQ0Y7OztBTHBCQSxJQUFPLGlCQUFRLGlCQUFpQjtBQUFBLEVBQzlCLE1BQU07QUFBQSxFQUNOLE1BQU07QUFBQSxFQUVOO0FBQUEsRUFFQSxTQUFTO0FBQUEsSUFDUCxLQUFLO0FBQUEsTUFDSCxNQUFNO0FBQUEsTUFDTixPQUFPO0FBQUEsTUFDUCxhQUFhO0FBQUEsSUFDZjtBQUFBLElBQ0EsUUFBUTtBQUFBLE1BQ04sTUFBTTtBQUFBLE1BQ04sT0FBTztBQUFBLE1BQ1AsYUFBYTtBQUFBLElBQ2Y7QUFBQSxFQUNGO0FBQUEsRUFFQSxPQUFPLGFBQWE7QUFBQSxJQUNsQixNQUFNO0FBQUEsSUFDTixNQUFNO0FBQUEsSUFDTixTQUFTO0FBQUEsSUFDVCxZQUFZO0FBQUEsSUFDWixrQkFBa0I7QUFBQSxJQUNsQixXQUFXO0FBQUEsSUFDWCxjQUFjO0FBQUEsSUFDZCxTQUFTO0FBQUEsTUFDUCxLQUFLO0FBQUEsUUFDSCxRQUFRO0FBQUEsUUFDUixTQUFTO0FBQUEsTUFDWDtBQUFBLE1BQ0EsUUFBUTtBQUFBLFFBQ04sUUFBUTtBQUFBLFFBQ1Isb0JBQW9CO0FBQUEsUUFDcEIsb0JBQW9CO0FBQUEsUUFDcEIseUJBQXlCO0FBQUEsUUFDekIsU0FBUztBQUFBLFFBQ1QsY0FBYztBQUFBLFFBQ2QsaUJBQWlCO0FBQUEsUUFDakIsa0JBQWtCO0FBQUEsUUFDbEIsS0FBSztBQUFBLFFBQ0wsU0FBUztBQUFBLFFBQ1QsUUFBUTtBQUFBLFFBQ1IsVUFBVTtBQUFBLFVBQ1I7QUFBQSxVQUNBO0FBQUEsVUFDQTtBQUFBLFVBQ0E7QUFBQSxRQUNGO0FBQUEsUUFDQSxZQUFZO0FBQUEsUUFDWixpQkFBaUI7QUFBQSxRQUNqQixpQkFBaUI7QUFBQSxRQUNqQixlQUFlO0FBQUEsTUFDakI7QUFBQSxJQUNGO0FBQUEsRUFDRixDQUFDO0FBQUEsRUFDRCxjQUFjO0FBQUEsRUFDZCxhQUFhO0FBQUEsRUFDYixTQUFTLFlBQVk7QUFBQSxFQUVyQixTQUFTO0FBQUEsSUFDUCxnQkFBZ0I7QUFBQSxNQUNkLElBQUk7QUFBQSxJQUNOLENBQUM7QUFBQSxFQUNIO0FBQ0YsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K 214 | -------------------------------------------------------------------------------- /docs/docs/.vuepress/configs/head.js: -------------------------------------------------------------------------------- 1 | 2 | export const head = [ 3 | ['meta', { name: 'application-name', content: 'VuePivottable' }], 4 | ['meta', { name: 'apple-mobile-web-app-title', content: 'VuePivottable' }], 5 | ['script', { async: true, src: 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-7701657858733816', crossorigin: 'anonymous'}] 6 | ] -------------------------------------------------------------------------------- /docs/docs/.vuepress/configs/index.js: -------------------------------------------------------------------------------- 1 | export * from './head.js' 2 | export * from './navbar/index.js' 3 | export * from './sidebar/index.js' -------------------------------------------------------------------------------- /docs/docs/.vuepress/configs/navbar/en.js: -------------------------------------------------------------------------------- 1 | export const navbarEn = [ 2 | { 3 | text: 'Guide', 4 | children: [ 5 | '/guide/introduction.md', 6 | '/guide/getting-started.md', 7 | '/guide/locale.md', 8 | '/guide/slot.md', 9 | '/guide/scoped-slot.md', 10 | '/guide/utilities.md', 11 | '/guide/renderer.md', 12 | '/guide/props.md', 13 | '/guide/styling.md', 14 | ] 15 | }, 16 | { 17 | text: `v0.4.68 (Vue 2)`, 18 | children: [ 19 | { 20 | text: 'v1.x (Vue 3)', 21 | link: 'https://vue-pivottable.vercel.app/' 22 | } 23 | ] 24 | }, 25 | { 26 | text: 'Contribute 💚', 27 | link: 'https://github.com/sponsors/Seungwoo321' 28 | }, 29 | ] -------------------------------------------------------------------------------- /docs/docs/.vuepress/configs/navbar/index.js: -------------------------------------------------------------------------------- 1 | export * from './en.js' 2 | export * from './ko.js' -------------------------------------------------------------------------------- /docs/docs/.vuepress/configs/navbar/ko.js: -------------------------------------------------------------------------------- 1 | export const navbarKo = [ 2 | { 3 | text: '가이드', 4 | children: [ 5 | '/ko/guide/introduction.md', 6 | '/ko/guide/getting-started.md', 7 | '/ko/guide/locale.md', 8 | '/ko/guide/slot.md', 9 | '/ko/guide/scoped-slot.md', 10 | '/ko/guide/utilities.md', 11 | '/ko/guide/renderer.md', 12 | '/ko/guide/props.md', 13 | '/ko/guide/styling.md', 14 | ] 15 | }, 16 | { 17 | text: `v0.4.68 (Vue 2)`, 18 | children: [ 19 | { 20 | text: 'v1.x (Vue 3)', 21 | link: 'https://vue-pivottable.vercel.app/' 22 | } 23 | ] 24 | }, 25 | { 26 | text: '기여하기 💚', 27 | link: 'https://github.com/sponsors/Seungwoo321' 28 | } 29 | ] -------------------------------------------------------------------------------- /docs/docs/.vuepress/configs/sidebar/en.js: -------------------------------------------------------------------------------- 1 | export const sidebarEn = { 2 | '/guide/': [ 3 | { 4 | text: 'Guide', 5 | children: [ 6 | 'introduction', 7 | 'getting-started', 8 | ], 9 | collapsible: false 10 | }, 11 | { 12 | text: 'Advanced', 13 | children: [ 14 | 'locale', 15 | 'slot', 16 | 'scoped-slot', 17 | 'utilities', 18 | 'renderer' 19 | ], 20 | collapsible: false 21 | }, 22 | { 23 | text: 'Props', 24 | link: 'props', 25 | collapsible: false 26 | }, 27 | { 28 | text: 'Styling', 29 | link: 'styling', 30 | collapsible: false 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /docs/docs/.vuepress/configs/sidebar/index.js: -------------------------------------------------------------------------------- 1 | export * from './en.js' 2 | export * from './ko.js' -------------------------------------------------------------------------------- /docs/docs/.vuepress/configs/sidebar/ko.js: -------------------------------------------------------------------------------- 1 | export const sidebarKo = { 2 | '/ko/guide/': [ 3 | { 4 | text: '가이드', 5 | children: [ 6 | 'introduction', 7 | 'getting-started', 8 | ], 9 | collapsible: false 10 | }, 11 | { 12 | text: '심화', 13 | children: [ 14 | 'locale', 15 | 'slot', 16 | 'scoped-slot', 17 | 'utilities', 18 | 'renderer' 19 | ], 20 | collapsible: false 21 | }, 22 | { 23 | text: 'Props', 24 | link: 'props', 25 | collapsible: false 26 | }, 27 | { 28 | text: '스타일링', 29 | link: 'styling', 30 | collapsible: false 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /docs/docs/.vuepress/public/google603171b62dec4aac.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google603171b62dec4aac.html -------------------------------------------------------------------------------- /docs/docs/.vuepress/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seungwoo321/vue-pivottable/84c279186215f37d3402f7cd5a161c59e17f4853/docs/docs/.vuepress/public/images/logo.png -------------------------------------------------------------------------------- /docs/docs/.vuepress/public/images/vue-pivottable-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seungwoo321/vue-pivottable/84c279186215f37d3402f7cd5a161c59e17f4853/docs/docs/.vuepress/public/images/vue-pivottable-demo.gif -------------------------------------------------------------------------------- /docs/docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | title: Home 4 | actions: 5 | - text: Get Started 6 | link: /guide/introduction.md 7 | type: primary 8 | 9 | - text: New version (Vue3) 10 | link: https://vue-pivottable.vercel.app/ 11 | type: secondary 12 | 13 | footer: MIT Licensed | Copyright © 2018-present Seungwoo Lee 14 | --- 15 | 16 | ```vue 17 | 25 | 26 | 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/docs/guide/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebarDepth: 1 3 | --- 4 | 5 | # Getting Started 6 | 7 | ## Installation 8 | 9 | > ⚠️ **Latest version** of vue-pivottable supports **Vue 3** only. If you're using Vue 3, please refer to the [vue3-pivottable](https://github.com/vue-pivottable/vue3-pivottable) repository. 10 | 11 | ### Direct Download / CDN 12 | 13 | * unpkg: 14 | * jsdelivr: 15 | 16 | Include vue-pivottable after Vue: 17 | 18 | ```html 19 | 20 | 21 | ``` 22 | 23 | ### NPM 24 | 25 | ```bash 26 | # install in your project 27 | npm install vue-pivottable@0.4.68 28 | ``` 29 | 30 | ## Usage 31 | 32 | Import and use a component. 33 | 34 | ### Vue Pivottable 35 | 36 | ```vue 37 | 45 | 46 | 55 | ``` 56 | 57 | 63 | 64 | ### Vue Pivottable UI 65 | 66 | ```vue 67 | 75 | 76 | 85 | ``` 86 | 87 | 93 | -------------------------------------------------------------------------------- /docs/docs/guide/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebarDepth: 1 3 | --- 4 | 5 | # Introduction 6 | 7 | ## What is VuePivottable? 8 | 9 | It is a Vue port of the jQuery-based [PivotTable.js](https://pivottable.js.org/) 10 | 11 |
12 | Pivottable UI demo 13 |
14 | 15 | 21 | 22 | ## Inspired 23 | 24 | * [plotly/react-pivottable](https://github.com/plotly/react-pivottable) - React-based pivot table library 25 | * [David-Desmaisons/vue-plotly](https://github.com/David-Desmaisons/vue-plotly) - vue wrapper for plotly.js 26 | 27 | ## License 28 | 29 | VuePivottable is open-sourced software licensed under the MIT license. 30 | -------------------------------------------------------------------------------- /docs/docs/guide/locale.md: -------------------------------------------------------------------------------- 1 | # Locale 2 | 3 | ## How to use? 4 | 5 | The default `locales` is defined in /src/helper/utils.js . 6 | 7 | ```js 8 | const locales = { 9 | en: { 10 | aggregators, 11 | localeStrings: { 12 | renderError: 'An error occurred rendering the PivotTable results.', 13 | computeError: 'An error occurred computing the PivotTable results.', 14 | uiRenderError: 'An error occurred rendering the PivotTable UI.', 15 | selectAll: 'Select All', 16 | selectNone: 'Select None', 17 | tooMany: '(too many to list)', 18 | filterResults: 'Filter values', 19 | totals: 'Totals', 20 | vs: 'vs', 21 | by: 'by' 22 | } 23 | } 24 | } 25 | export { 26 | locales 27 | } 28 | ``` 29 | 30 | If you want to add a new language add both `locale` and `locales` props. 31 | 32 | ```js 33 | import { PivotUtilities } from "vue-pivottable"; 34 | 35 | const locales = { 36 | en: PivotUtilities.locales.en, 37 | ko: { 38 | aggregators: PivotUtilities.aggregators, 39 | localeStrings: { 40 | renderError: 41 | "피벗 테이블 결과를 렌더링하는 동안 오류가 발생 했습니다.", 42 | computeError: 43 | "피벗 테이블 결과를 계산하는 동안 오류가 발생 했습니다.", 44 | uiRenderError: 45 | "피벗 테이블 UI를 렌더링하는 동안 오류가 발생 했습니다.", 46 | selectAll: "모두 선택", 47 | selectNone: "선택 안함", 48 | tooMany: "표시 할 값이 너무 많습니다.", 49 | filterResults: "값 필터링", 50 | totals: "합계", 51 | only: "단독", 52 | vs: "vs", 53 | by: "by", 54 | }, 55 | }, 56 | } 57 | 58 | const locale = 'en' // or 'ko' 59 | 60 | ``` 61 | 62 | ## Example 63 | 64 | 70 | -------------------------------------------------------------------------------- /docs/docs/guide/props.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebarDepth: 3 3 | --- 4 | 5 | # Props Reference 6 | 7 | ## Basic Props 8 | 9 | Props that support both `vue-pivottable` and `vue-pivottable-ui`. 10 | 11 | ### data 12 | 13 | * Type: `Array` || `Object` 14 | * Default: `undefined` 15 | * **Required** 16 | 17 | data to be summarized 18 | 19 | [live](https://codesandbox.io/s/vue-pivottable-base-qhqoz) 20 | 21 | 27 | 28 | ### vals 29 | 30 | * Type: `Array` 31 | * Default: `[]` 32 | 33 | attribute names used as arguments to aggregator (gets passed to aggregator generating function) 34 | 35 | [live](https://codesandbox.io/embed/vue-pivottable-props-vals-vjqfh) 36 | 37 | 43 | 44 | ### cols 45 | 46 | * Type: `Array` 47 | * Default: `[]` 48 | 49 | attribute names to prepopulate in cols area 50 | 51 | ### rows 52 | 53 | * Type: `Array` 54 | * Default: `[]` 55 | 56 | attribute names to prepopulate in row area 57 | 58 | ### rowTotal 59 | 60 | * Type: `Boolean` 61 | * Default: `true` 62 | 63 | show total of rows 64 | 65 | ### colTotal 66 | 67 | * Type: `Boolean` 68 | * Default: `true` 69 | 70 | show total of cols 71 | 72 | ### aggregators 73 | 74 | * Type: `Object` 75 | * Default: `aggregators` from `Utilities` 76 | 77 | dictionary of generators for aggregation functions in dropdown (see [original PivotTable.js documentation](https://github.com/nicolaskruchten/pivottable/wiki/Aggregators)) 78 | 79 | ### aggregatorName 80 | 81 | * Type: `String` 82 | * Default: `Count` 83 | 84 | key to `aggregators` object specifying the aggregator to use for computations 85 | 86 | ### rendererName 87 | 88 | * Type: `String` 89 | * Default : `Table` 90 | 91 | key to renderers object specifying the renderer to use 92 | 93 | ### valueFilter 94 | 95 | * Type: `Object` 96 | * Default: `{}` 97 | 98 | object whose keys are attribute names and values are objects of attribute value-boolean pairs which denote records to include or exclude from computation and rendering; used to prepopulate the filter menus that appear on double-click 99 | 100 | ### sorters 101 | 102 | * Type: `Function or Object` 103 | * Default: `{}` 104 | 105 | accessed or called with an attribute name and can return a function which can be used as an argument to array.sort for output purposes. 106 | 107 | ### derivedAttributes 108 | 109 | * Type: `Object` 110 | * Default: `{}` 111 | 112 | defines derived attributes 113 | 114 | ### rowOrder 115 | 116 | * Type: `String` 117 | * Default: `key_a_to_z` 118 | 119 | the order in which row data is provided to the renderer, must be one of `"key_a_to_z"`, `"value_a_to_z"`, `"value_z_to_a"`, ordering by value orders by row total 120 | 121 | ### colOrder 122 | 123 | * Type: `String` 124 | * Default: `key_a_to_z` 125 | 126 | the order in which column data is provided to the renderer, must be one of `"key_a_to_z"`, `"value_a_to_z"`, `"value_z_to_a"`, ordering by value orders by column total 127 | 128 | ### tableMaxWidth 129 | 130 | * Type: `Number` 131 | * Default: `0` 132 | 133 | value of max-width in table style 134 | 135 | ### tableColorScaleGenerator 136 | 137 | * Type: `Function` 138 | * Default: `undefined` 139 | 140 | generate custom color render for `Table Heatmap` 141 | 142 | ### locales deprecated 143 | 144 | * Type: `Object` 145 | * Default: [see utilities](/guide/utilities.html#locales) 146 | 147 | localeStrings is deprecated, replace in locales. 148 | 149 | ## Pivottable UI Props 150 | 151 | Props that only support `vue-pivottable-ui`. 152 | 153 | ### hiddenAttributes 154 | 155 | * Type: `Array` 156 | * Default: `[]` 157 | 158 | contains attribute names to omit from the UI 159 | 160 | ### hiddenFromAggregators 161 | 162 | * Type: `Array` 163 | * Default: `[]` 164 | 165 | contains attribute names to omit from the aggregator arguments dropdowns 166 | 167 | ### hiddenFromDragDrop 168 | 169 | * Type: `Array` 170 | * Default: `[]` 171 | 172 | contains attribute names to omit from the aggregator arguments dropdowns 173 | 174 | ### sortonlyFromDragDrop 175 | 176 | * Type: `Array` 177 | * Default: `[]` 178 | 179 | contains attribute names to sort from the drag'n'drop of the UI 180 | 181 | ### disabledFromDragDrop 182 | 183 | * Type: `Array` 184 | * Default: `[]` 185 | contains attribute names to disable from the drag'n'drop portion of the UI 186 | 187 | ### menuLimit 188 | 189 | * Type: `Number` 190 | * Default: `500` 191 | 192 | maximum number of values to list in the double-click menu 193 | 194 | ### rowLimit 195 | 196 | * Type: `Number` 197 | * Default: `0` 198 | 199 | :::danger 200 | rowLimit is deprecated. 201 | ::: 202 | 203 | ### colLimit 204 | 205 | * Type: `Number` 206 | * Default: `0` 207 | 208 | :::danger 209 | colLimit is deprecated. 210 | ::: 211 | -------------------------------------------------------------------------------- /docs/docs/guide/renderer.md: -------------------------------------------------------------------------------- 1 | # Renderer 2 | 3 | ## Usage 4 | 5 | Just define a custom `renderer` function and pass it as props. 6 | 7 | ## plotly renderer New in 0.4.6+ 8 | 9 | No longer include plotly-renderer in vue-pivottable, but you can use it like this: 10 | 11 | ### Install 12 | 13 | ```bash 14 | npm install @vue-pivottable/plotly-renderer 15 | ``` 16 | 17 | ### SFC 18 | 19 | ```js 20 | import PlotlyRenderer from '@vue-pivottable/plotly-renderer' 21 | 22 | const renderer = (() => ({ 23 | 'Grouped Column Chart': PlotlyRenderer['Grouped Column Chart'], 24 | 'Stacked Column Chart': PlotlyRenderer['Stacked Column Chart'], 25 | 'Grouped Bar Chart': PlotlyRenderer['Grouped Bar Chart'], 26 | 'Stacked Bar Chart': PlotlyRenderer['Stacked Bar Chart'], 27 | 'Line Chart': PlotlyRenderer['Line Chart'], 28 | 'Dot Chart': PlotlyRenderer['Dot Chart'], 29 | 'Area Chart': PlotlyRenderer['Area Chart'], 30 | 'Scatter Chart': PlotlyRenderer['Scatter Chart'], 31 | 'Multiple Pie Chart': PlotlyRenderer['Multiple Pie Chart'] 32 | }))() 33 | ``` 34 | 35 | ### browser 36 | 37 | ```html 38 | 39 | 40 | 41 | 42 | 43 | 44 | Plotly Renderer 45 | 46 | 47 | 48 | 49 | 50 | 51 |
52 | 59 | 60 |
61 | 82 | 83 | 84 | 85 | ``` 86 | 87 | ## scroll table renderer New in 0.4.64+ 88 | 89 | ### Install 90 | 91 | ```bash 92 | npm install @vue-pivottable/scroll-renderer 93 | ``` 94 | 95 | ### SFC 96 | 97 | ```js 98 | import ScrollRenderer from '@vue-pivottable/scroll-renderer' 99 | 100 | const renderer = (() => ({ 101 | Table : ScrollRenderer.Table, 102 | 'Table Heatmap': ScrollRenderer['Table Heatmap'], 103 | 'Table Col Heatmap': ScrollRenderer['Table Col Heatmap'], 104 | 'Table Row Heatmap': ScrollRenderer['Table Row Heatmap'] 105 | }))() 106 | ``` 107 | 108 | ### Live Demo 109 | 110 | [link](https://jsfiddle.net/seungwoo321/nopkdha6/) 111 | -------------------------------------------------------------------------------- /docs/docs/guide/scoped-slot.md: -------------------------------------------------------------------------------- 1 | 2 | # Scoped Slot 3 | 4 | For more information on ScopedSlot, see the [official Vue documentation](https://vuejs.org/v2/guide/components-slots.html#Scoped-Slots). 5 | 6 | :::warning 7 | Both slot and scoped slots are supported, but the use of the v-slot directive is recommended. 8 | ::: 9 | 10 | ## pvtAttr 11 | 12 | ```vue{10-13} 13 | 28 | 29 | 38 | ``` 39 | 40 | 46 | 47 | ## output 48 | 49 | ```vue{11-36} 50 | 97 | 98 | 130 | ``` 131 | 132 | 138 | -------------------------------------------------------------------------------- /docs/docs/guide/slot.md: -------------------------------------------------------------------------------- 1 | # Slot 2 | 3 | Options for customizing vue-pivottable-ui. 4 | 5 | :::warning 6 | Both slot and scoped slots are supported, but the use of the v-slot directive is recommended. 7 | ::: 8 | 9 | ## rendererCell 10 | 11 | If you want to replace the selection UI that selects the renderer, use the `rendererCell` slot. 12 | 13 | ```vue{10-12} 14 | 28 | 29 | 38 | ``` 39 | 40 | 46 | 47 | ## aggregatorCell 48 | 49 | If you want to replace the select UI that selects the aggregator you can use it. 50 | 51 | ```vue{10-12} 52 | 66 | 67 | 76 | ``` 77 | 78 | 84 | 85 | ## colGroup 86 | 87 | You can use this slot if you want the width of `td.pvtAxisContainer` to be fixed, or if you want not the drag field to overflow `td.pvtAxisContainer` . 88 | 89 | ::: tip 90 | `td.pvtAxisContainer` has **overflow-x:auto;** property. 91 | ::: 92 | 93 | ```vue{13-16} 94 | 117 | 118 | 132 | ``` 133 | 134 | 140 | 141 | ## output 142 | 143 | This is a slot that replaces the area in `td.pvtOutput`. 144 | 145 | ```vue{11-13} 146 | 170 | 171 | 196 | ``` 197 | 198 | 204 | -------------------------------------------------------------------------------- /docs/docs/guide/styling.md: -------------------------------------------------------------------------------- 1 | 2 | # Styling 3 | 4 | ## How to Styling? 5 | 6 | This is very simple. Import the style from the dist and then define a new style. 7 | 8 | ## Example 9 | 10 | ### Bootstrap v5.2.2 11 | 12 | 20 | 21 | ::: details 22 | 23 | ```css 24 | 25 | * { 26 | box-sizing: border-box 27 | } 28 | 29 | /* pvtUi */ 30 | table.pvtUi { 31 | table-layout: fixed; 32 | width: 100%; 33 | border-collapse: collapse; 34 | text-align: left; 35 | font-size: 16px; 36 | font-weight: 400; 37 | line-height: 24px; 38 | vertical-align: top; 39 | border: 1px solid #dee2e6; 40 | } 41 | table.pvtUi > :not(caption)> * > * { 42 | border: 1px solid #dee2e6; 43 | } 44 | table.pvtUi td { 45 | padding: 0.5rem 0.5rem; 46 | overflow-x: auto; 47 | } 48 | table.pvtUi > tbody > tr:nth-child(2) > :nth-child(2), 49 | table.pvtUi > tbody > tr:nth-child(3) > :nth-child(1) { 50 | background-color: rgba(0, 0, 0, 0.05); 51 | } 52 | table.pvtUi > tbody td:nth-child(1) { 53 | width: 25%; 54 | } 55 | 56 | /* pvtUi selector */ 57 | .pvtUi .pvtDropdown { 58 | display: block; 59 | width: 100%; 60 | padding: 0.375rem 2.25rem 0.375rem 0.75rem; 61 | font-size: 1rem; 62 | font-weight: 400; 63 | line-height: 1.5; 64 | color: #212529; 65 | background-color: #fff; 66 | background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); 67 | background-repeat: no-repeat; 68 | background-position: right 0.75rem center; 69 | background-size: 16px 12px; 70 | border: 1px solid #ced4da; 71 | transition: 72 | border-color .15s ease-in-out, 73 | box-shadow .15s ease-in-out; 74 | appearance: none; 75 | outline: none; 76 | } 77 | 78 | /* pvtUi selector */ 79 | .pvtUi .pvtVals :first-child { 80 | display: flex; 81 | } 82 | .pvtUi .pvtVals .pvtDropdown:not(:first-child) { 83 | margin-top: 0.5rem; 84 | } 85 | .pvtUi .pvtVals .pvtRowOrder, 86 | .pvtUi .pvtVals .pvtColOrder { 87 | cursor: pointer; 88 | width: 15px; 89 | margin-left: 5px; 90 | user-select: none; 91 | text-decoration: none; 92 | display: flex; 93 | justify-content: center; 94 | align-items: center; 95 | } 96 | 97 | /* pivot Axis Container */ 98 | .pvtUi .pvtAxisContainer.pvtHorizList li { 99 | display: inline-flex; 100 | margin-left: .125rem; 101 | flex-wrap: wrap; 102 | } 103 | .pvtUi .pvtAxisContainer.pvtVertList.pvtRows { 104 | vertical-align: top; 105 | } 106 | .pvtUi .pvtAxisContainer.pvtVertList.pvtRows li { 107 | display: flex; 108 | margin-bottom: .125rem; 109 | } 110 | .pvtUi .pvtAxisContainer li { 111 | align-items: stretch; 112 | list-style-type: none; 113 | cursor: move; 114 | } 115 | .pvtUi .pvtAxisContainer li .pvtAttr { 116 | background-color: #0d6efd; 117 | color: #fff; 118 | padding: 0.35em 0.65em; 119 | white-space: nowrap; 120 | border-radius: 0.375rem; 121 | user-select: none; 122 | } 123 | .pvtUi .pvtAxisContainer li .pvtAttr:hover:not(.disabled, .sortonly) { 124 | border-color: #0a58ca; 125 | background-color: #0b5ed7; 126 | } 127 | .pvtUi .pvtAxisContainer li .pvtAttr.disabled { 128 | opacity: 0.65; 129 | cursor: default; 130 | } 131 | .pvtUi .pvtAxisContainer li .pvtAttr.sortonly { 132 | border: 1px solid #0d6efd; 133 | color: #0d6efd; 134 | background-color: #fff; 135 | } 136 | .pvtUi .pvtAxisContainer li .pvtAttr .pvtFilterBox { 137 | position: absolute; 138 | display: flex; 139 | flex-wrap: wrap; 140 | align-items: stretch; 141 | z-index: 1; 142 | background-color: #fff; 143 | user-select: none; 144 | border: 1px solid #dee2e6; 145 | max-width: 600px; 146 | min-width: 210px; 147 | min-height: 100px;; 148 | color: #000; 149 | padding: 12px 8px; 150 | } 151 | 152 | /* pvtFilterBox */ 153 | .sortable-chosen .pvtFilterBox { 154 | display: none !important; 155 | } 156 | .pvtFilterBox .pvtSearchContainer .pvtSearch { 157 | border-top-right-radius: 0; 158 | border-bottom-right-radius: 0; 159 | border-right-width: 0; 160 | } 161 | .pvtFilterBox .pvtSearchContainer .pvtButton:not(:last-child) { 162 | border-radius: 0; 163 | } 164 | .pvtFilterBox .pvtSearchContainer .pvtButton:last-child { 165 | border-top-left-radius: 0; 166 | border-bottom-left-radius: 0; 167 | border-left-width: 0; 168 | } 169 | 170 | /* pvtTable */ 171 | table.pvtTable { 172 | width: 100%; 173 | color: #212529; 174 | border-color: #dee2e6; 175 | border-collapse: collapse; 176 | } 177 | table.pvtTable thead, 178 | table.pvtTable tbody, 179 | table.pvtTable tr, 180 | table.pvtTable td, 181 | table.pvtTable th { 182 | border-color: inherit; 183 | border-style: solid; 184 | border-width: 0; 185 | } 186 | table.pvtTable > :not(caption)> * > * { 187 | padding: 0.5rem 0.5rem; 188 | background-color: transparent; 189 | border-bottom-width: 1px; 190 | box-shadow: inset 0 0 0 9999px transparent; 191 | } 192 | table.pvtTable thead { 193 | vertical-align: bottom; 194 | } 195 | table.pvtTable tbody { 196 | vertical-align: inherit; 197 | } 198 | table.pvtTable th { 199 | font-weight: bold; 200 | } 201 | 202 | /* pvtCheckContainer */ 203 | .pvtCheckContainer p { 204 | display: block; 205 | min-height: 1.5rem; 206 | padding-left: 1.5em; 207 | margin-bottom: .125rem; 208 | } 209 | .pvtCheckContainer p:not(:first-child) { 210 | margin-top: .125rem; 211 | } 212 | .pvtCheckContainer p:last-child { 213 | margin-bottom: 0; 214 | } 215 | .pvtCheckContainer p input[type="checkbox"] { 216 | border-radius: .25em; 217 | float: left; 218 | margin-left: -1.5em; 219 | width: 1em; 220 | height: 1em; 221 | vertical-align: top; 222 | border: 1px solid rgba(0, 0, 0, .25); 223 | background-color: #fff; 224 | background-position: center; 225 | background-size: contain; 226 | appearance: none; 227 | } 228 | .pvtCheckContainer p.selected input[type="checkbox"] { 229 | background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"); 230 | background-color: #0d6efd; 231 | border-color: #0d6efd; 232 | } 233 | .pvtCheckContainer p .pvtOnly { 234 | cursor: pointer; 235 | position: absolute; 236 | right: .75rem; 237 | color: #0d6efd; 238 | font-size: 1rem; 239 | font-weight: 400; 240 | background-color: transparent; 241 | line-height: 1.5; 242 | text-align: center; 243 | text-decoration: underline; 244 | vertical-align: middle; 245 | user-select: none; 246 | cursor: pointer; 247 | border: 1px solid transparent; 248 | border-radius: 0.375rem; 249 | transition: color .15s ease-in-out, 250 | background-color .15s ease-in-out, 251 | border-color .15s ease-in-out, 252 | box-shadow .15s ease-in-out; 253 | } 254 | .pvtCheckContainer .pvtOnly:hover { 255 | color: #0a58ca; 256 | } 257 | 258 | /* pvtSearch */ 259 | .pvtSearch { 260 | padding: .375rem .75rem; 261 | color: #6c757d; 262 | font-size: 1rem; 263 | font-weight: 400; 264 | background-color: #fff; 265 | border: 1px solid #ced4da; 266 | border-radius: 0.375rem; 267 | outline: none; 268 | transition: color .15s ease-in-out, 269 | background-color .15s ease-in-out, 270 | border-color .15s ease-in-out, 271 | box-shadow .15s ease-in-out; 272 | } 273 | 274 | /* pvtButton */ 275 | .pvtButton { 276 | padding: .375rem .75rem; 277 | color: #6c757d; 278 | font-size: 1rem; 279 | font-weight: 400; 280 | background-color: transparent; 281 | line-height: 1.5; 282 | text-align: center; 283 | text-decoration: none; 284 | vertical-align: middle; 285 | user-select: none; 286 | cursor: pointer; 287 | border: 1px solid #6c757d; 288 | border-radius: 0.375rem; 289 | transition: color .15s ease-in-out, 290 | background-color .15s ease-in-out, 291 | border-color .15s ease-in-out, 292 | box-shadow .15s ease-in-out; 293 | } 294 | .pvtButton:hover, 295 | .pvtButton:active { 296 | color: #fff; 297 | background-color: #6c757d; 298 | border-color: #6c757d; 299 | } 300 | 301 | /* pvtTriangle */ 302 | .pvtTriangle { 303 | cursor: pointer; 304 | color: #fff; 305 | } 306 | 307 | /* pvtFilteredAttribute */ 308 | .pvtFilteredAttribute { 309 | font-style: italic; 310 | } 311 | 312 | /* pvtFilterTextClear */ 313 | .pvtFilterTextClear { 314 | background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e"); 315 | position: absolute; 316 | right: 5px; 317 | top: 5px; 318 | font-size: 18px; 319 | cursor: pointer; 320 | text-decoration: none; 321 | } 322 | /* media css */ 323 | @media screen and (max-width:576px) { 324 | .pvtRenderers { 325 | width: 100%; 326 | } 327 | 328 | .pvtAxisContainer { 329 | display: none; 330 | } 331 | } 332 | @media screen and (width: 768px) { 333 | .pvtRenderers { 334 | width: 40%; 335 | } 336 | } 337 | ``` 338 | 339 | ::: 340 | 341 | ### Vuexy UI Template (bootstrap based) 342 | 343 | - 344 | 345 | 352 | 353 | ::: details 354 | 355 | ```css 356 | * { 357 | box-sizing: border-box; 358 | line-height: 1.15; 359 | } 360 | body { 361 | font-size: 1rem; 362 | line-height: 1.45; 363 | } 364 | /* pvtUi */ 365 | table.pvtUi { 366 | table-layout: fixed; 367 | width: 100%; 368 | border-collapse: collapse; 369 | text-align: left; 370 | font-weight: 400; 371 | vertical-align: top; 372 | border: 1px solid #6e6b7b; 373 | color: #6e6b7b; 374 | } 375 | table.pvtUi td { 376 | padding: 0.75rem; 377 | overflow-x: auto; 378 | } 379 | table.pvtUi>tbody td:nth-child(1) { 380 | width: 25%; 381 | } 382 | 383 | /* pvtUi selector */ 384 | .pvtUi .pvtDropdown { 385 | user-select: none; 386 | margin: 0; 387 | border: 1px solid #d8d6de; 388 | border-radius: .357rem; 389 | background-color: inherit; 390 | background-color: inherit; 391 | background-position: calc(100% - 12px) 13px, calc(100% - 20px) 13px, 100% 0; 392 | background-size: 18px 14px, 18px 14px; 393 | background-repeat: no-repeat; 394 | background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23d8d6de' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-chevron-down'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); 395 | appearance: none; 396 | outline: none; 397 | font-size: 1rem; 398 | font-weight: 400; 399 | height: 2.714rem; 400 | vertical-align: middle; 401 | color: #6e6b7b; 402 | width: 100%; 403 | padding: 0.438rem 2rem 0.438rem 1rem; 404 | } 405 | .pvtUi .pvtVals :first-child { 406 | display: flex; 407 | } 408 | .pvtAxisContainer, 409 | .pvtVals, 410 | td.pvtOutput { 411 | border: 1px solid #ebe9f1; 412 | } 413 | 414 | .pvtFilteredAttribute { 415 | font-style: italic; 416 | } 417 | 418 | .pvtFilterTextClear { 419 | position: absolute; 420 | right: 5px; 421 | top: 5px; 422 | font-size: 18px; 423 | cursor: pointer; 424 | text-decoration: none; 425 | } 426 | 427 | .pvtTriangle { 428 | cursor: pointer; 429 | font-size: 18px; 430 | color: #7367f0; 431 | } 432 | 433 | .pvtOnly { 434 | display: block; 435 | width: 35px; 436 | float: right; 437 | font-size: 12px; 438 | padding-left: 5px; 439 | cursor: pointer; 440 | } 441 | 442 | .pvtOnlySpacer { 443 | display: block; 444 | width: 35px; 445 | float: right; 446 | } 447 | 448 | /* pvtTable */ 449 | table.pvtTable { 450 | background-color: transparent; 451 | width: 100%; 452 | text-align: left; 453 | border-collapse: collapse; 454 | font-size: .857rem; 455 | } 456 | table.pvtTable thead tr th { 457 | vertical-align: top; 458 | text-transform: uppercase; 459 | font-size: 0.857rem; 460 | letter-spacing: 0.5px; 461 | background-color: #f3f2f7; 462 | } 463 | table.pvtTable thead tr th, 464 | table.pvtTable tbody tr th, 465 | table.pvtTable tbody tr td { 466 | border: 1px solid #ebe9f1; 467 | padding: .3rem .5rem; 468 | } 469 | 470 | table.pvtTable tbody tr td { 471 | vertical-align: top; 472 | text-align: right; 473 | } 474 | table.pvtTable .pvtColLabel { 475 | text-align: center; 476 | } 477 | table.pvtTable .pvtTotalLabel { 478 | text-align: right; 479 | } 480 | .pvtTotal, 481 | .pvtGrandTotal { 482 | font-weight: bold; 483 | } 484 | .pvtRowOrder, 485 | .pvtColOrder { 486 | cursor: pointer; 487 | width: 15px; 488 | margin-left: 5px; 489 | display: inline-block; 490 | user-select: none; 491 | text-decoration: none !important; 492 | } 493 | .pvtAxisContainer, 494 | .pvtVals { 495 | padding: 5px; 496 | min-width: 20px; 497 | min-height: 20px; 498 | } 499 | .pvtVals { 500 | white-space: nowrap; 501 | vertical-align: top; 502 | } 503 | .pvtVals div { 504 | padding-bottom: 5px; 505 | } 506 | .pvtVals.pvtText { 507 | vertical-align: middle; 508 | font-size: 15px; 509 | font-weight: bold; 510 | user-select: none; 511 | text-align: start; 512 | } 513 | .pvtRenderers { 514 | border: 1px solid #ebe9f1; 515 | user-select: none; 516 | } 517 | 518 | /* pvtAxisContainer */ 519 | .pvtAxisContainer li { 520 | margin-bottom: 6px; 521 | padding: 8px 3px; 522 | list-style-type: none; 523 | cursor: move; 524 | } 525 | .pvtAxisContainer li.pvtPlaceholder { 526 | border-radius: 5px; 527 | border: 1px dashed #a2b1c6; 528 | } 529 | .pvtAxisContainer li.pvtPlaceholder span.pvtAttr { 530 | display: none; 531 | } 532 | .pvtAxisContainer li span.pvtAttr { 533 | box-shadow: none; 534 | font-weight: 500; 535 | padding: 0.786rem 1.5rem; 536 | white-space: nowrap; 537 | border-radius: 0.25rem; 538 | user-select: none; 539 | text-align: center; 540 | vertical-align: middle; 541 | background-color: transparent; 542 | border: 1px dashed #7367f0; 543 | border-radius: 0.25rem; 544 | color: #7367f0; 545 | } 546 | .pvtAxisContainer li span.pvtAttr.disabled { 547 | cursor: not-allowed; 548 | } 549 | .pvtHorizList li { 550 | display: inline-block; 551 | } 552 | .pvtVertList { 553 | vertical-align: top; 554 | } 555 | 556 | /* pvtFilterBox */ 557 | .sortable-chosen .pvtFilterBox { 558 | display: none; 559 | } 560 | .pvtFilterBox input { 561 | border: 1px solid #ebe9f1; 562 | border-radius: 5px; 563 | color: #506784; 564 | padding: 0 3px; 565 | font-size: 14px; 566 | } 567 | .pvtFilterBox input:focus { 568 | border-color: #7367f0; 569 | outline: none; 570 | } 571 | .pvtFilterBox { 572 | background-color: #f8f8f8; 573 | z-index: 100; 574 | color: #6e6b7b; 575 | border: 1px solid #ebe9f1; 576 | max-width: 600px; 577 | min-width: 210px; 578 | position: absolute; 579 | padding: 6px; 580 | user-select: none; 581 | min-height: 100px; 582 | } 583 | .pvtFilterBox .pvtButton { 584 | display: inline-block; 585 | user-select: none; 586 | padding: 0.8rem 2rem; 587 | padding-right: calc(2rem / 1.75); 588 | padding-left: calc(2rem / 1.75); 589 | border-radius: 0.358rem; 590 | text-decoration: none; 591 | border: 1px solid #ebe9f1; 592 | } 593 | .pvtFilterBox input[type='text'] { 594 | outline: none; 595 | display: block; 596 | width: 100%; 597 | border-radius: .375rem; 598 | border-color: #ebe9f1; 599 | margin-bottom: 5px; 600 | padding: .438rem 1rem 601 | } 602 | 603 | /* pvtSearchContainer */ 604 | .pvtSearchContainer a:nth-child(3) { 605 | border-top-right-radius: 0; 606 | border-bottom-right-radius: 0; 607 | } 608 | 609 | .pvtSearchContainer a:nth-child(4) { 610 | border-top-left-radius: 0; 611 | border-bottom-left-radius: 0; 612 | border-left-width: 0; 613 | } 614 | 615 | /* pvtCheckContainer */ 616 | .pvtCheckContainer { 617 | text-align: left; 618 | font-size: 14px; 619 | white-space: nowrap; 620 | overflow: scroll; 621 | width: 100%; 622 | max-height: 30vh; 623 | } 624 | .pvtCheckContainer p { 625 | margin: 0 0 1px 0; 626 | padding: 3px; 627 | cursor: default; 628 | } 629 | 630 | .pvtCheckContainer p input[type="checkbox"] { 631 | margin-right: 0.5rem; 632 | } 633 | 634 | .pvtCheckContainer p.selected { 635 | min-width: 100%; 636 | width: max-content; 637 | } 638 | 639 | .pvtCheckContainer p:hover.pvtOnly { 640 | display: block; 641 | } 642 | 643 | .pvtCheckContainer p:hover.pvtOnlySpacer { 644 | display: none; 645 | } 646 | ``` 647 | 648 | ::: 649 | -------------------------------------------------------------------------------- /docs/docs/guide/utilities.md: -------------------------------------------------------------------------------- 1 | # Utilities 2 | 3 | ```js 4 | import { PivotUtilities } from 'vue-pivottable' 5 | ``` 6 | 7 | ## numberFormat 8 | 9 | ```js 10 | const usFmt = PivotUtilities.numberFormat() 11 | const usFmtInt = PivotUtilities.numberFormat({ digitsAfterDecimal: 0 }) 12 | const usFmtPct = PivotUtilities.numberFormat({ 13 | digitsAfterDecimal: 1, 14 | scaler: 100, 15 | suffix: '%' 16 | }) 17 | ``` 18 | 19 | ## sortAs 20 | 21 | ```js 22 | const sorters = function () { 23 | return { 24 | 'Day of Week': PivotUtilities.sortAs(['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']) 25 | } 26 | } 27 | ``` 28 | 29 | ## aggregatorTemplates 30 | 31 | Built-in Aggregators and Aggregator Templates 32 | 33 | ```js 34 | const aggregators = ((tpl) => ({ 35 | 'Count': tpl.count(usFmtInt), 36 | 'Count Unique Values': tpl.countUnique(usFmtInt), 37 | 'List Unique Values': tpl.listUnique(', '), 38 | Sum: tpl.sum(usFmt), 39 | 'Integer Sum': tpl.sum(usFmtInt), 40 | 'Average': tpl.average(usFmt), 41 | 'Median': tpl.median(usFmt), 42 | 'Sample Variance': tpl.var(1, usFmt), 43 | 'Sample Standard Deviation': tpl.stdev(1, usFmt), 44 | 'Minimum': tpl.min(usFmt), 45 | 'Maximum': tpl.max(usFmt), 46 | 'First': tpl.first(usFmt), 47 | 'Last': tpl.last(usFmt), 48 | 'Sum over Sum': tpl.sumOverSum(usFmt), 49 | 'Sum as Fraction of Total': tpl.fractionOf(tpl.sum(), 'total', usFmtPct), 50 | 'Sum as Fraction of Rows': tpl.fractionOf(tpl.sum(), 'row', usFmtPct), 51 | 'Sum as Fraction of Columns': tpl.fractionOf(tpl.sum(), 'col', usFmtPct), 52 | 'Count as Fraction of Total': tpl.fractionOf(tpl.count(), 'total', usFmtPct), 53 | 'Count as Fraction of Rows': tpl.fractionOf(tpl.count(), 'row', usFmtPct), 54 | 'Count as Fraction of Columns': tpl.fractionOf(tpl.count(), 'col', usFmtPct) 55 | }) 56 | )(PivotUtilities.aggregatorTemplates) 57 | ``` 58 | 59 | ## locales 60 | 61 | ```js 62 | const locales = { 63 | en: { 64 | aggregators, 65 | localeStrings: { 66 | renderError: 'An error occurred rendering the PivotTable results.', 67 | computeError: 'An error occurred computing the PivotTable results.', 68 | uiRenderError: 'An error occurred rendering the PivotTable UI.', 69 | selectAll: 'Select All', 70 | selectNone: 'Select None', 71 | tooMany: '(too many to list)', 72 | filterResults: 'Filter values', 73 | totals: 'Totals', 74 | vs: 'vs', 75 | by: 'by' 76 | } 77 | }, 78 | fr: { 79 | frAggregators, 80 | localeStrings: { 81 | renderError: 'Une erreur est survenue en dessinant le tableau croisé.', 82 | computeError: 'Une erreur est survenue en calculant le tableau croisé.', 83 | uiRenderError: "Une erreur est survenue en dessinant l'interface du tableau croisé dynamique.", 84 | selectAll: 'Sélectionner tout', 85 | selectNone: 'Ne rien sélectionner', 86 | tooMany: '(trop de valeurs à afficher)', 87 | filterResults: 'Filtrer les valeurs', 88 | totals: 'Totaux', 89 | vs: 'sur', 90 | by: 'par', 91 | apply: 'Appliquer', 92 | cancel: 'Annuler' 93 | } 94 | } 95 | } 96 | ``` 97 | -------------------------------------------------------------------------------- /docs/docs/ko/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | title: Home 4 | heroImage: /images/logo.png 5 | actions: 6 | - text: 시작하기 7 | link: /guide/introduction.md 8 | type: primary 9 | 10 | - text: 새 버전 (Vue3) 11 | link: https://vue-pivottable.vercel.app/ 12 | type: secondary 13 | 14 | footer: MIT Licensed | Copyright © 2018-present Seungwoo Lee 15 | --- 16 | 17 | ```vue 18 | 26 | 27 | 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/docs/ko/guide/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebarDepth: 1 3 | --- 4 | 5 | # 시작하기 6 | 7 | ## 설치 8 | 9 | > ⚠️ **최신 버전**의 vue-pivottable은 **Vue 3**만 지원합니다. Vue 3을 사용 중이라면 [vue3-pivottable](https://github.com/vue-pivottable/vue3-pivottable) 저장소를 참고하세요. 10 | 11 | ### 직접 다운로드 / CDN 12 | 13 | * unpkg: 14 | * jsdelivr: 15 | 16 | Vue 다음에 vue-pivottable을 포함하세요: 17 | 18 | ```html 19 | 20 | 21 | ``` 22 | 23 | ### NPM 설치 24 | 25 | ```bash 26 | # install in your project 27 | npm install vue-pivottable@0.4.68 28 | ``` 29 | 30 | ## 사용 방법 31 | 32 | 컴포넌트를 import 하고 사용하세요. 33 | 34 | ### Vue Pivottable 사용 예시 35 | 36 | ```vue 37 | 45 | 46 | 55 | ``` 56 | 57 | 63 | 64 | ### Vue Pivottable UI 사용 예시 65 | 66 | ```vue 67 | 75 | 76 | 85 | ``` 86 | 87 | 93 | -------------------------------------------------------------------------------- /docs/docs/ko/guide/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebarDepth: 1 3 | --- 4 | 5 | # 소개 6 | 7 | ## VuePivottable이란? 8 | 9 | VuePivottable은 jQuery 기반의 [PivotTable.js](https://pivottable.js.org/)를 Vue로 포팅한 라이브러리입니다. 10 | 11 |
12 | Pivottable UI demo 13 |
14 | 15 | 21 | 22 | ## 영감 받은 프로젝트 23 | 24 | * [plotly/react-pivottable](https://github.com/plotly/react-pivottable) - React 기반의 피벗 테이블 라이브러리 25 | * [David-Desmaisons/vue-plotly](https://github.com/David-Desmaisons/vue-plotly) - plotly.js를 위한 Vue 래퍼 26 | 27 | ## 라이선스 28 | 29 | VuePivottable은 MIT 라이선스 하에 공개된 오픈소스 소프트웨어입니다. 30 | -------------------------------------------------------------------------------- /docs/docs/ko/guide/locale.md: -------------------------------------------------------------------------------- 1 | # 언어 설정 2 | 3 | ## 사용 방법 4 | 5 | 기본 `locales`는 /src/helper/utils.js 파일에 정의되어 있습니다. 6 | 7 | ```js 8 | const locales = { 9 | en: { 10 | aggregators, 11 | localeStrings: { 12 | renderError: 'An error occurred rendering the PivotTable results.', 13 | computeError: 'An error occurred computing the PivotTable results.', 14 | uiRenderError: 'An error occurred rendering the PivotTable UI.', 15 | selectAll: 'Select All', 16 | selectNone: 'Select None', 17 | tooMany: '(too many to list)', 18 | filterResults: 'Filter values', 19 | totals: 'Totals', 20 | vs: 'vs', 21 | by: 'by' 22 | } 23 | } 24 | } 25 | export { 26 | locales 27 | } 28 | ``` 29 | 30 | 새로운 언어를 추가하려면 `locale`과 `locales` 속성을 모두 설정해야 합니다. 31 | 32 | ```js 33 | import { PivotUtilities } from "vue-pivottable"; 34 | 35 | const locales = { 36 | en: PivotUtilities.locales.en, 37 | ko: { 38 | aggregators: PivotUtilities.aggregators, 39 | localeStrings: { 40 | renderError: 41 | "피벗 테이블 결과를 렌더링하는 동안 오류가 발생 했습니다.", 42 | computeError: 43 | "피벗 테이블 결과를 계산하는 동안 오류가 발생 했습니다.", 44 | uiRenderError: 45 | "피벗 테이블 UI를 렌더링하는 동안 오류가 발생 했습니다.", 46 | selectAll: "모두 선택", 47 | selectNone: "선택 안함", 48 | tooMany: "표시 할 값이 너무 많습니다.", 49 | filterResults: "값 필터링", 50 | totals: "합계", 51 | only: "단독", 52 | vs: "vs", 53 | by: "by", 54 | }, 55 | }, 56 | } 57 | 58 | const locale = 'en' // or 'ko' 59 | 60 | ``` 61 | 62 | ## 예제 63 | 64 | 70 | -------------------------------------------------------------------------------- /docs/docs/ko/guide/props.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebarDepth: 3 3 | --- 4 | 5 | # Props 참조 6 | 7 | ## 기본 Props 8 | 9 | `vue-pivottable`와 `vue-pivottable-ui` 모두를 지원하는 Props입니다. 10 | 11 | ### data 12 | 13 | * 타입: `Array` || `Object` 14 | * 기본값: `undefined` 15 | * **필수** 16 | 17 | 요약할 데이터 18 | 19 | [라이브 예제](https://codesandbox.io/s/vue-pivottable-base-qhqoz) 20 | 21 | ### vals 22 | 23 | * 타입: `Array` 24 | * 기본값: `[]` 25 | 26 | 집계기의 인수로 사용되는 속성 이름(집계 함수 생성에 전달됨) 27 | 28 | [라이브 예제](https://codesandbox.io/embed/vue-pivottable-props-vals-vjqfh) 29 | 30 | ### cols 31 | 32 | * 타입: `Array` 33 | * 기본값: `[]` 34 | 35 | 열 영역에 미리 채울 속성 이름 36 | 37 | ### rows 38 | 39 | * 타입: `Array` 40 | * 기본값: `[]` 41 | 42 | 행 영역에 미리 채울 속성 이름 43 | 44 | ### rowTotal 45 | 46 | * 타입: `Boolean` 47 | * 기본값: `true` 48 | 49 | 행의 합계 표시 여부 50 | 51 | ### colTotal 52 | 53 | * 타입: `Boolean` 54 | * 기본값: `true` 55 | 56 | 열의 합계 표시 여부 57 | 58 | ### aggregators 59 | 60 | * 타입: `Object` 61 | * 기본값: `Utilities`의 `aggregators` 62 | 63 | 드롭다운에서 집계 함수의 생성기 사전 ([원본 PivotTable.js 문서](https://github.com/nicolaskruchten/pivottable/wiki/Aggregators) 참조) 64 | 65 | ### aggregatorName 66 | 67 | * 타입: `String` 68 | * 기본값: `Count` 69 | 70 | 계산에 사용할 집계기를 지정하는 `aggregators` 객체의 키 71 | 72 | ### rendererName 73 | 74 | * 타입: `String` 75 | * 기본값: `Table` 76 | 77 | 사용할 렌더러를 지정하는 렌더러 객체의 키 78 | 79 | ### valueFilter 80 | 81 | * 타입: `Object` 82 | * 기본값: `{}` 83 | 84 | 키가 속성 이름이고 값이 계산 및 렌더링에서 포함하거나 제외할 레코드를 나타내는 속성값-불리언 쌍의 객체인 객체; 더블 클릭시 나타나는 필터 메뉴를 미리 채우는 데 사용됨 85 | 86 | ### sorters 87 | 88 | * 타입: `Function or Object` 89 | * 기본값: `{}` 90 | 91 | 속성 이름으로 접근하거나 호출되며 출력 목적으로 array.sort의 인수로 사용할 수 있는 함수를 반환할 수 있음 92 | 93 | ### derivedAttributes 94 | 95 | * 타입: `Object` 96 | * 기본값: `{}` 97 | 98 | 파생 속성을 정의함 99 | 100 | ### rowOrder 101 | 102 | * 타입: `String` 103 | * 기본값: `key_a_to_z` 104 | 105 | 행 데이터가 렌더러에 제공되는 순서, `"key_a_to_z"`, `"value_a_to_z"`, `"value_z_to_a"` 중 하나여야 함, 값으로 정렬하면 행 합계로 정렬됨 106 | 107 | ### colOrder 108 | 109 | * 타입: `String` 110 | * 기본값: `key_a_to_z` 111 | 112 | 열 데이터가 렌더러에 제공되는 순서, `"key_a_to_z"`, `"value_a_to_z"`, `"value_z_to_a"` 중 하나여야 함, 값으로 정렬하면 열 합계로 정렬됨 113 | 114 | ### tableMaxWidth 115 | 116 | * 타입: `Number` 117 | * 기본값: `0` 118 | 119 | 테이블 스타일의 max-width 값 120 | 121 | ### tableColorScaleGenerator 122 | 123 | * 타입: `Function` 124 | * 기본값: `undefined` 125 | 126 | `Table Heatmap`을 위한 사용자 정의 색상 렌더 생성 127 | 128 | ### locales deprecated 129 | 130 | * 타입: `Object` 131 | * 기본값: [utilities 참조](/guide/utilities.html#locales) 132 | 133 | localeStrings는 더 이상 사용되지 않으며, locales로 대체되었습니다. 134 | 135 | ## Pivottable UI Props 136 | 137 | `vue-pivottable-ui`만 지원하는 Props입니다. 138 | 139 | ### hiddenAttributes 140 | 141 | * 타입: `Array` 142 | * 기본값: `[]` 143 | 144 | UI에서 생략할 속성 이름을 포함 145 | 146 | ### hiddenFromAggregators 147 | 148 | * 타입: `Array` 149 | * 기본값: `[]` 150 | 151 | 집계기 인수 드롭다운에서 생략할 속성 이름을 포함 152 | 153 | ### hiddenFromDragDrop 154 | 155 | * 타입: `Array` 156 | * 기본값: `[]` 157 | 158 | 집계기 인수 드롭다운에서 생략할 속성 이름을 포함 159 | 160 | ### sortonlyFromDragDrop 161 | 162 | * 타입: `Array` 163 | * 기본값: `[]` 164 | 165 | UI의 드래그 앤 드롭에서 정렬만 가능한 속성 이름을 포함 166 | 167 | ### disabledFromDragDrop 168 | 169 | * 타입: `Array` 170 | * 기본값: `[]` 171 | 172 | UI의 드래그 앤 드롭 부분에서 비활성화할 속성 이름을 포함 173 | 174 | ### menuLimit 175 | 176 | * 타입: `Number` 177 | * 기본값: `500` 178 | 179 | 더블 클릭 메뉴에 나열할 최대 값 수 180 | 181 | ### rowLimit 182 | 183 | * 타입: `Number` 184 | * 기본값: `0` 185 | 186 | :::danger 187 | rowLimit은 더 이상 사용되지 않습니다. 188 | ::: 189 | 190 | ### colLimit 191 | 192 | * 타입: `Number` 193 | * 기본값: `0` 194 | 195 | :::danger 196 | colLimit은 더 이상 사용되지 않습니다. 197 | ::: 198 | -------------------------------------------------------------------------------- /docs/docs/ko/guide/renderer.md: -------------------------------------------------------------------------------- 1 | # 렌더러 2 | 3 | ## 사용법 4 | 5 | 사용자 정의 `renderer` 함수를 정의하고 props로 전달하기만 하면 됩니다. 6 | 7 | ## plotly 렌더러 0.4.6+에서 새로 추가 8 | 9 | vue-pivottable에 plotly-renderer가 더 이상 포함되어 있지 않지만, 다음과 같이 사용할 수 있습니다: 10 | 11 | ### 설치 12 | 13 | ```bash 14 | npm install @vue-pivottable/plotly-renderer 15 | ``` 16 | 17 | ### SFC 18 | 19 | ```js 20 | import PlotlyRenderer from '@vue-pivottable/plotly-renderer' 21 | 22 | const renderer = (() => ({ 23 | 'Grouped Column Chart': PlotlyRenderer['Grouped Column Chart'], 24 | 'Stacked Column Chart': PlotlyRenderer['Stacked Column Chart'], 25 | 'Grouped Bar Chart': PlotlyRenderer['Grouped Bar Chart'], 26 | 'Stacked Bar Chart': PlotlyRenderer['Stacked Bar Chart'], 27 | 'Line Chart': PlotlyRenderer['Line Chart'], 28 | 'Dot Chart': PlotlyRenderer['Dot Chart'], 29 | 'Area Chart': PlotlyRenderer['Area Chart'], 30 | 'Scatter Chart': PlotlyRenderer['Scatter Chart'], 31 | 'Multiple Pie Chart': PlotlyRenderer['Multiple Pie Chart'] 32 | }))() 33 | ``` 34 | 35 | ### 브라우저 36 | 37 | ```html 38 | 39 | 40 | 41 | 42 | 43 | 44 | Plotly 렌더러 45 | 46 | 47 | 48 | 49 | 50 | 51 |
52 | 59 | 60 |
61 | 82 | 83 | 84 | 85 | ``` 86 | 87 | ## 스크롤 테이블 렌더러 0.4.64+에서 새로 추가 88 | 89 | ### 설치 90 | 91 | ```bash 92 | npm install @vue-pivottable/scroll-renderer 93 | ``` 94 | 95 | ### SFC 96 | 97 | ```js 98 | import ScrollRenderer from '@vue-pivottable/scroll-renderer' 99 | 100 | const renderer = (() => ({ 101 | Table : ScrollRenderer.Table, 102 | 'Table Heatmap': ScrollRenderer['Table Heatmap'], 103 | 'Table Col Heatmap': ScrollRenderer['Table Col Heatmap'], 104 | 'Table Row Heatmap': ScrollRenderer['Table Row Heatmap'] 105 | }))() 106 | ``` 107 | 108 | ### 라이브 데모 109 | 110 | [링크](https://jsfiddle.net/seungwoo321/nopkdha6/) -------------------------------------------------------------------------------- /docs/docs/ko/guide/scoped-slot.md: -------------------------------------------------------------------------------- 1 | # 범위가 지정된 슬롯 2 | 3 | 범위가 지정된 슬롯에 대한 자세한 내용은 [Vue 공식 문서](https://vuejs.org/v2/guide/components-slots.html#Scoped-Slots)를 참조하세요. 4 | 5 | :::warning 6 | 슬롯과 범위가 지정된 슬롯 모두 지원되지만, v-slot 지시문 사용을 권장합니다. 7 | ::: 8 | 9 | ## pvtAttr 10 | 11 | ```vue{10-13} 12 | 27 | 28 | 37 | ``` 38 | 39 | 45 | 46 | ## output 47 | 48 | ```vue{11-36} 49 | 96 | 97 | 129 | ``` 130 | 131 | -------------------------------------------------------------------------------- /docs/docs/ko/guide/slot.md: -------------------------------------------------------------------------------- 1 | # 슬롯 2 | 3 | vue-pivottable-ui를 사용자 정의하기 위한 옵션입니다. 4 | 5 | :::warning 6 | 슬롯과 범위가 지정된 슬롯(scoped slots) 모두 지원되지만, v-slot 지시문 사용을 권장합니다. 7 | ::: 8 | 9 | ## rendererCell 10 | 11 | 렌더러를 선택하는 선택 UI를 교체하려면 `rendererCell` 슬롯을 사용하세요. 12 | 13 | ```vue{10-12} 14 | 28 | 29 | 38 | ``` 39 | 40 | 46 | 47 | ## aggregatorCell 48 | 49 | 집계기를 선택하는 선택 UI를 교체하려면 이것을 사용할 수 있습니다. 50 | 51 | ```vue{10-12} 52 | 66 | 67 | 76 | ``` 77 | 78 | 84 | 85 | ## colGroup 86 | 87 | `td.pvtAxisContainer`의 너비를 고정하거나, 드래그 필드가 `td.pvtAxisContainer`를 오버플로우하지 않도록 하려면 이 슬롯을 사용할 수 있습니다. 88 | 89 | ::: tip 90 | `td.pvtAxisContainer`는 **overflow-x:auto;** 속성을 가지고 있습니다. 91 | ::: 92 | 93 | ```vue{13-16} 94 | 117 | 118 | 132 | ``` 133 | 134 | 140 | 141 | ## output 142 | 143 | 이것은 `td.pvtOutput` 영역을 대체하는 슬롯입니다. 144 | 145 | ```vue{11-13} 146 | 170 | 171 | 196 | ``` 197 | 198 | 204 | -------------------------------------------------------------------------------- /docs/docs/ko/guide/styling.md: -------------------------------------------------------------------------------- 1 | # 스타일링 2 | 3 | ## 스타일링 방법 4 | 5 | 매우 간단합니다. dist에서 스타일을 가져온 다음 새 스타일을 정의하세요. 6 | 7 | ## 예제 8 | 9 | ### Bootstrap v5.2.2 10 | 11 | 19 | 20 | ::: details 21 | 22 | ```css 23 | 24 | * { 25 | box-sizing: border-box 26 | } 27 | 28 | /* pvtUi */ 29 | table.pvtUi { 30 | table-layout: fixed; 31 | width: 100%; 32 | border-collapse: collapse; 33 | text-align: left; 34 | font-size: 16px; 35 | font-weight: 400; 36 | line-height: 24px; 37 | vertical-align: top; 38 | border: 1px solid #dee2e6; 39 | } 40 | table.pvtUi > :not(caption)> * > * { 41 | border: 1px solid #dee2e6; 42 | } 43 | table.pvtUi td { 44 | padding: 0.5rem 0.5rem; 45 | overflow-x: auto; 46 | } 47 | table.pvtUi > tbody > tr:nth-child(2) > :nth-child(2), 48 | table.pvtUi > tbody > tr:nth-child(3) > :nth-child(1) { 49 | background-color: rgba(0, 0, 0, 0.05); 50 | } 51 | table.pvtUi > tbody td:nth-child(1) { 52 | width: 25%; 53 | } 54 | 55 | /* pvtUi selector */ 56 | .pvtUi .pvtDropdown { 57 | display: block; 58 | width: 100%; 59 | padding: 0.375rem 2.25rem 0.375rem 0.75rem; 60 | font-size: 1rem; 61 | font-weight: 400; 62 | line-height: 1.5; 63 | color: #212529; 64 | background-color: #fff; 65 | background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); 66 | background-repeat: no-repeat; 67 | background-position: right 0.75rem center; 68 | background-size: 16px 12px; 69 | border: 1px solid #ced4da; 70 | transition: 71 | border-color .15s ease-in-out, 72 | box-shadow .15s ease-in-out; 73 | appearance: none; 74 | outline: none; 75 | } 76 | 77 | /* pvtUi selector */ 78 | .pvtUi .pvtVals :first-child { 79 | display: flex; 80 | } 81 | .pvtUi .pvtVals .pvtDropdown:not(:first-child) { 82 | margin-top: 0.5rem; 83 | } 84 | .pvtUi .pvtVals .pvtRowOrder, 85 | .pvtUi .pvtVals .pvtColOrder { 86 | cursor: pointer; 87 | width: 15px; 88 | margin-left: 5px; 89 | user-select: none; 90 | text-decoration: none; 91 | display: flex; 92 | justify-content: center; 93 | align-items: center; 94 | } 95 | 96 | /* pivot Axis Container */ 97 | .pvtUi .pvtAxisContainer.pvtHorizList li { 98 | display: inline-flex; 99 | margin-left: .125rem; 100 | flex-wrap: wrap; 101 | } 102 | .pvtUi .pvtAxisContainer.pvtVertList.pvtRows { 103 | vertical-align: top; 104 | } 105 | .pvtUi .pvtAxisContainer.pvtVertList.pvtRows li { 106 | display: flex; 107 | margin-bottom: .125rem; 108 | } 109 | .pvtUi .pvtAxisContainer li { 110 | align-items: stretch; 111 | list-style-type: none; 112 | cursor: move; 113 | } 114 | .pvtUi .pvtAxisContainer li .pvtAttr { 115 | background-color: #0d6efd; 116 | color: #fff; 117 | padding: 0.35em 0.65em; 118 | white-space: nowrap; 119 | border-radius: 0.375rem; 120 | user-select: none; 121 | } 122 | .pvtUi .pvtAxisContainer li .pvtAttr:hover:not(.disabled, .sortonly) { 123 | border-color: #0a58ca; 124 | background-color: #0b5ed7; 125 | } 126 | .pvtUi .pvtAxisContainer li .pvtAttr.disabled { 127 | opacity: 0.65; 128 | cursor: default; 129 | } 130 | .pvtUi .pvtAxisContainer li .pvtAttr.sortonly { 131 | border: 1px solid #0d6efd; 132 | color: #0d6efd; 133 | background-color: #fff; 134 | } 135 | .pvtUi .pvtAxisContainer li .pvtAttr .pvtFilterBox { 136 | position: absolute; 137 | display: flex; 138 | flex-wrap: wrap; 139 | align-items: stretch; 140 | z-index: 1; 141 | background-color: #fff; 142 | user-select: none; 143 | border: 1px solid #dee2e6; 144 | max-width: 600px; 145 | min-width: 210px; 146 | min-height: 100px;; 147 | color: #000; 148 | padding: 12px 8px; 149 | } 150 | 151 | /* pvtFilterBox */ 152 | .sortable-chosen .pvtFilterBox { 153 | display: none !important; 154 | } 155 | .pvtFilterBox .pvtSearchContainer .pvtSearch { 156 | border-top-right-radius: 0; 157 | border-bottom-right-radius: 0; 158 | border-right-width: 0; 159 | } 160 | .pvtFilterBox .pvtSearchContainer .pvtButton:not(:last-child) { 161 | border-radius: 0; 162 | } 163 | .pvtFilterBox .pvtSearchContainer .pvtButton:last-child { 164 | border-top-left-radius: 0; 165 | border-bottom-left-radius: 0; 166 | border-left-width: 0; 167 | } 168 | 169 | /* pvtTable */ 170 | table.pvtTable { 171 | width: 100%; 172 | color: #212529; 173 | border-color: #dee2e6; 174 | border-collapse: collapse; 175 | } 176 | table.pvtTable thead, 177 | table.pvtTable tbody, 178 | table.pvtTable tr, 179 | table.pvtTable td, 180 | table.pvtTable th { 181 | border-color: inherit; 182 | border-style: solid; 183 | border-width: 0; 184 | } 185 | table.pvtTable > :not(caption)> * > * { 186 | padding: 0.5rem 0.5rem; 187 | background-color: transparent; 188 | border-bottom-width: 1px; 189 | box-shadow: inset 0 0 0 9999px transparent; 190 | } 191 | table.pvtTable thead { 192 | vertical-align: bottom; 193 | } 194 | table.pvtTable tbody { 195 | vertical-align: inherit; 196 | } 197 | table.pvtTable th { 198 | font-weight: bold; 199 | } 200 | 201 | /* pvtCheckContainer */ 202 | .pvtCheckContainer p { 203 | display: block; 204 | min-height: 1.5rem; 205 | padding-left: 1.5em; 206 | margin-bottom: .125rem; 207 | } 208 | .pvtCheckContainer p:not(:first-child) { 209 | margin-top: .125rem; 210 | } 211 | .pvtCheckContainer p:last-child { 212 | margin-bottom: 0; 213 | } 214 | .pvtCheckContainer p input[type="checkbox"] { 215 | border-radius: .25em; 216 | float: left; 217 | margin-left: -1.5em; 218 | width: 1em; 219 | height: 1em; 220 | vertical-align: top; 221 | border: 1px solid rgba(0, 0, 0, .25); 222 | background-color: #fff; 223 | background-position: center; 224 | background-size: contain; 225 | appearance: none; 226 | } 227 | .pvtCheckContainer p.selected input[type="checkbox"] { 228 | background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"); 229 | background-color: #0d6efd; 230 | border-color: #0d6efd; 231 | } 232 | .pvtCheckContainer p .pvtOnly { 233 | cursor: pointer; 234 | position: absolute; 235 | right: .75rem; 236 | color: #0d6efd; 237 | font-size: 1rem; 238 | font-weight: 400; 239 | background-color: transparent; 240 | line-height: 1.5; 241 | text-align: center; 242 | text-decoration: underline; 243 | vertical-align: middle; 244 | user-select: none; 245 | cursor: pointer; 246 | border: 1px solid transparent; 247 | border-radius: 0.375rem; 248 | transition: color .15s ease-in-out, 249 | background-color .15s ease-in-out, 250 | border-color .15s ease-in-out, 251 | box-shadow .15s ease-in-out; 252 | } 253 | .pvtCheckContainer .pvtOnly:hover { 254 | color: #0a58ca; 255 | } 256 | 257 | /* pvtSearch */ 258 | .pvtSearch { 259 | padding: .375rem .75rem; 260 | color: #6c757d; 261 | font-size: 1rem; 262 | font-weight: 400; 263 | background-color: #fff; 264 | border: 1px solid #ced4da; 265 | border-radius: 0.375rem; 266 | outline: none; 267 | transition: color .15s ease-in-out, 268 | background-color .15s ease-in-out, 269 | border-color .15s ease-in-out, 270 | box-shadow .15s ease-in-out; 271 | } 272 | 273 | /* pvtButton */ 274 | .pvtButton { 275 | padding: .375rem .75rem; 276 | color: #6c757d; 277 | font-size: 1rem; 278 | font-weight: 400; 279 | background-color: transparent; 280 | line-height: 1.5; 281 | text-align: center; 282 | text-decoration: none; 283 | vertical-align: middle; 284 | user-select: none; 285 | cursor: pointer; 286 | border: 1px solid #6c757d; 287 | border-radius: 0.375rem; 288 | transition: color .15s ease-in-out, 289 | background-color .15s ease-in-out, 290 | border-color .15s ease-in-out, 291 | box-shadow .15s ease-in-out; 292 | } 293 | .pvtButton:hover, 294 | .pvtButton:active { 295 | color: #fff; 296 | background-color: #6c757d; 297 | border-color: #6c757d; 298 | } 299 | 300 | /* pvtTriangle */ 301 | .pvtTriangle { 302 | cursor: pointer; 303 | color: #fff; 304 | } 305 | 306 | /* pvtFilteredAttribute */ 307 | .pvtFilteredAttribute { 308 | font-style: italic; 309 | } 310 | 311 | /* pvtFilterTextClear */ 312 | .pvtFilterTextClear { 313 | background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e"); 314 | position: absolute; 315 | right: 5px; 316 | top: 5px; 317 | font-size: 18px; 318 | cursor: pointer; 319 | text-decoration: none; 320 | } 321 | /* media css */ 322 | @media screen and (max-width:576px) { 323 | .pvtRenderers { 324 | width: 100%; 325 | } 326 | 327 | .pvtAxisContainer { 328 | display: none; 329 | } 330 | } 331 | @media screen and (width: 768px) { 332 | .pvtRenderers { 333 | width: 40%; 334 | } 335 | } 336 | ``` 337 | 338 | ::: 339 | 340 | ### Vuexy UI 템플릿 (부트스트랩 기반) 341 | 342 | - 343 | 344 | 351 | 352 | ::: details 353 | 354 | ```css 355 | * { 356 | box-sizing: border-box; 357 | line-height: 1.15; 358 | } 359 | body { 360 | font-size: 1rem; 361 | line-height: 1.45; 362 | } 363 | /* pvtUi */ 364 | table.pvtUi { 365 | table-layout: fixed; 366 | width: 100%; 367 | border-collapse: collapse; 368 | text-align: left; 369 | font-weight: 400; 370 | vertical-align: top; 371 | border: 1px solid #6e6b7b; 372 | color: #6e6b7b; 373 | } 374 | table.pvtUi td { 375 | padding: 0.75rem; 376 | overflow-x: auto; 377 | } 378 | table.pvtUi>tbody td:nth-child(1) { 379 | width: 25%; 380 | } 381 | 382 | /* pvtUi selector */ 383 | .pvtUi .pvtDropdown { 384 | user-select: none; 385 | margin: 0; 386 | border: 1px solid #d8d6de; 387 | border-radius: .357rem; 388 | background-color: inherit; 389 | background-color: inherit; 390 | background-position: calc(100% - 12px) 13px, calc(100% - 20px) 13px, 100% 0; 391 | background-size: 18px 14px, 18px 14px; 392 | background-repeat: no-repeat; 393 | background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23d8d6de' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-chevron-down'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); 394 | appearance: none; 395 | outline: none; 396 | font-size: 1rem; 397 | font-weight: 400; 398 | height: 2.714rem; 399 | vertical-align: middle; 400 | color: #6e6b7b; 401 | width: 100%; 402 | padding: 0.438rem 2rem 0.438rem 1rem; 403 | } 404 | .pvtUi .pvtVals :first-child { 405 | display: flex; 406 | } 407 | .pvtAxisContainer, 408 | .pvtVals, 409 | td.pvtOutput { 410 | border: 1px solid #ebe9f1; 411 | } 412 | 413 | .pvtFilteredAttribute { 414 | font-style: italic; 415 | } 416 | 417 | .pvtFilterTextClear { 418 | position: absolute; 419 | right: 5px; 420 | top: 5px; 421 | font-size: 18px; 422 | cursor: pointer; 423 | text-decoration: none; 424 | } 425 | 426 | .pvtTriangle { 427 | cursor: pointer; 428 | font-size: 18px; 429 | color: #7367f0; 430 | } 431 | 432 | .pvtOnly { 433 | display: block; 434 | width: 35px; 435 | float: right; 436 | font-size: 12px; 437 | padding-left: 5px; 438 | cursor: pointer; 439 | } 440 | 441 | .pvtOnlySpacer { 442 | display: block; 443 | width: 35px; 444 | float: right; 445 | } 446 | 447 | /* pvtTable */ 448 | table.pvtTable { 449 | background-color: transparent; 450 | width: 100%; 451 | text-align: left; 452 | border-collapse: collapse; 453 | font-size: .857rem; 454 | } 455 | table.pvtTable thead tr th { 456 | vertical-align: top; 457 | text-transform: uppercase; 458 | font-size: 0.857rem; 459 | letter-spacing: 0.5px; 460 | background-color: #f3f2f7; 461 | } 462 | table.pvtTable thead tr th, 463 | table.pvtTable tbody tr th, 464 | table.pvtTable tbody tr td { 465 | border: 1px solid #ebe9f1; 466 | padding: .3rem .5rem; 467 | } 468 | 469 | table.pvtTable tbody tr td { 470 | vertical-align: top; 471 | text-align: right; 472 | } 473 | table.pvtTable .pvtColLabel { 474 | text-align: center; 475 | } 476 | table.pvtTable .pvtTotalLabel { 477 | text-align: right; 478 | } 479 | .pvtTotal, 480 | .pvtGrandTotal { 481 | font-weight: bold; 482 | } 483 | .pvtRowOrder, 484 | .pvtColOrder { 485 | cursor: pointer; 486 | width: 15px; 487 | margin-left: 5px; 488 | display: inline-block; 489 | user-select: none; 490 | text-decoration: none !important; 491 | } 492 | .pvtAxisContainer, 493 | .pvtVals { 494 | padding: 5px; 495 | min-width: 20px; 496 | min-height: 20px; 497 | } 498 | .pvtVals { 499 | white-space: nowrap; 500 | vertical-align: top; 501 | } 502 | .pvtVals div { 503 | padding-bottom: 5px; 504 | } 505 | .pvtVals.pvtText { 506 | vertical-align: middle; 507 | font-size: 15px; 508 | font-weight: bold; 509 | user-select: none; 510 | text-align: start; 511 | } 512 | .pvtRenderers { 513 | border: 1px solid #ebe9f1; 514 | user-select: none; 515 | } 516 | 517 | /* pvtAxisContainer */ 518 | .pvtAxisContainer li { 519 | margin-bottom: 6px; 520 | padding: 8px 3px; 521 | list-style-type: none; 522 | cursor: move; 523 | } 524 | .pvtAxisContainer li.pvtPlaceholder { 525 | border-radius: 5px; 526 | border: 1px dashed #a2b1c6; 527 | } 528 | .pvtAxisContainer li.pvtPlaceholder span.pvtAttr { 529 | display: none; 530 | } 531 | .pvtAxisContainer li span.pvtAttr { 532 | box-shadow: none; 533 | font-weight: 500; 534 | padding: 0.786rem 1.5rem; 535 | white-space: nowrap; 536 | border-radius: 0.25rem; 537 | user-select: none; 538 | text-align: center; 539 | vertical-align: middle; 540 | background-color: transparent; 541 | border: 1px dashed #7367f0; 542 | border-radius: 0.25rem; 543 | color: #7367f0; 544 | } 545 | .pvtAxisContainer li span.pvtAttr.disabled { 546 | cursor: not-allowed; 547 | } 548 | .pvtHorizList li { 549 | display: inline-block; 550 | } 551 | .pvtVertList { 552 | vertical-align: top; 553 | } 554 | 555 | /* pvtFilterBox */ 556 | .sortable-chosen .pvtFilterBox { 557 | display: none; 558 | } 559 | .pvtFilterBox input { 560 | border: 1px solid #ebe9f1; 561 | border-radius: 5px; 562 | color: #506784; 563 | padding: 0 3px; 564 | font-size: 14px; 565 | } 566 | .pvtFilterBox input:focus { 567 | border-color: #7367f0; 568 | outline: none; 569 | } 570 | .pvtFilterBox { 571 | background-color: #f8f8f8; 572 | z-index: 100; 573 | color: #6e6b7b; 574 | border: 1px solid #ebe9f1; 575 | max-width: 600px; 576 | min-width: 210px; 577 | position: absolute; 578 | padding: 6px; 579 | user-select: none; 580 | min-height: 100px; 581 | } 582 | .pvtFilterBox .pvtButton { 583 | display: inline-block; 584 | user-select: none; 585 | padding: 0.8rem 2rem; 586 | padding-right: calc(2rem / 1.75); 587 | padding-left: calc(2rem / 1.75); 588 | border-radius: 0.358rem; 589 | text-decoration: none; 590 | border: 1px solid #ebe9f1; 591 | } 592 | .pvtFilterBox input[type='text'] { 593 | outline: none; 594 | display: block; 595 | width: 100%; 596 | border-radius: .375rem; 597 | border-color: #ebe9f1; 598 | margin-bottom: 5px; 599 | padding: .438rem 1rem 600 | } 601 | 602 | /* pvtSearchContainer */ 603 | .pvtSearchContainer a:nth-child(3) { 604 | border-top-right-radius: 0; 605 | border-bottom-right-radius: 0; 606 | } 607 | 608 | .pvtSearchContainer a:nth-child(4) { 609 | border-top-left-radius: 0; 610 | border-bottom-left-radius: 0; 611 | border-left-width: 0; 612 | } 613 | 614 | /* pvtCheckContainer */ 615 | .pvtCheckContainer { 616 | text-align: left; 617 | font-size: 14px; 618 | white-space: nowrap; 619 | overflow: scroll; 620 | width: 100%; 621 | max-height: 30vh; 622 | } 623 | .pvtCheckContainer p { 624 | margin: 0 0 1px 0; 625 | padding: 3px; 626 | cursor: default; 627 | } 628 | 629 | .pvtCheckContainer p input[type="checkbox"] { 630 | margin-right: 0.5rem; 631 | } 632 | 633 | .pvtCheckContainer p.selected { 634 | min-width: 100%; 635 | width: max-content; 636 | } 637 | 638 | .pvtCheckContainer p:hover.pvtOnly { 639 | display: block; 640 | } 641 | 642 | .pvtCheckContainer p:hover.pvtOnlySpacer { 643 | display: none; 644 | } 645 | ``` 646 | 647 | ::: -------------------------------------------------------------------------------- /docs/docs/ko/guide/utilities.md: -------------------------------------------------------------------------------- 1 | # 유틸리티 2 | 3 | ```js 4 | import { PivotUtilities } from 'vue-pivottable' 5 | ``` 6 | 7 | ## numberFormat 8 | 9 | ```js 10 | const usFmt = PivotUtilities.numberFormat() 11 | const usFmtInt = PivotUtilities.numberFormat({ digitsAfterDecimal: 0 }) 12 | const usFmtPct = PivotUtilities.numberFormat({ 13 | digitsAfterDecimal: 1, 14 | scaler: 100, 15 | suffix: '%' 16 | }) 17 | ``` 18 | 19 | ## sortAs 20 | 21 | ```js 22 | const sorters = function () { 23 | return { 24 | 'Day of Week': PivotUtilities.sortAs(['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']) 25 | } 26 | } 27 | ``` 28 | 29 | ## aggregatorTemplates 30 | 31 | 내장 집계기 및 집계기 템플릿 32 | 33 | ```js 34 | const aggregators = ((tpl) => ({ 35 | 'Count': tpl.count(usFmtInt), 36 | 'Count Unique Values': tpl.countUnique(usFmtInt), 37 | 'List Unique Values': tpl.listUnique(', '), 38 | Sum: tpl.sum(usFmt), 39 | 'Integer Sum': tpl.sum(usFmtInt), 40 | 'Average': tpl.average(usFmt), 41 | 'Median': tpl.median(usFmt), 42 | 'Sample Variance': tpl.var(1, usFmt), 43 | 'Sample Standard Deviation': tpl.stdev(1, usFmt), 44 | 'Minimum': tpl.min(usFmt), 45 | 'Maximum': tpl.max(usFmt), 46 | 'First': tpl.first(usFmt), 47 | 'Last': tpl.last(usFmt), 48 | 'Sum over Sum': tpl.sumOverSum(usFmt), 49 | 'Sum as Fraction of Total': tpl.fractionOf(tpl.sum(), 'total', usFmtPct), 50 | 'Sum as Fraction of Rows': tpl.fractionOf(tpl.sum(), 'row', usFmtPct), 51 | 'Sum as Fraction of Columns': tpl.fractionOf(tpl.sum(), 'col', usFmtPct), 52 | 'Count as Fraction of Total': tpl.fractionOf(tpl.count(), 'total', usFmtPct), 53 | 'Count as Fraction of Rows': tpl.fractionOf(tpl.count(), 'row', usFmtPct), 54 | 'Count as Fraction of Columns': tpl.fractionOf(tpl.count(), 'col', usFmtPct) 55 | }) 56 | )(PivotUtilities.aggregatorTemplates) 57 | ``` 58 | 59 | ## locales 60 | 61 | ```js 62 | const locales = { 63 | en: { 64 | aggregators, 65 | localeStrings: { 66 | renderError: 'An error occurred rendering the PivotTable results.', 67 | computeError: 'An error occurred computing the PivotTable results.', 68 | uiRenderError: 'An error occurred rendering the PivotTable UI.', 69 | selectAll: 'Select All', 70 | selectNone: 'Select None', 71 | tooMany: '(too many to list)', 72 | filterResults: 'Filter values', 73 | totals: 'Totals', 74 | vs: 'vs', 75 | by: 'by' 76 | } 77 | }, 78 | fr: { 79 | frAggregators, 80 | localeStrings: { 81 | renderError: 'Une erreur est survenue en dessinant le tableau croisé.', 82 | computeError: 'Une erreur est survenue en calculant le tableau croisé.', 83 | uiRenderError: "Une erreur est survenue en dessinant l'interface du tableau croisé dynamique.", 84 | selectAll: 'Sélectionner tout', 85 | selectNone: 'Ne rien sélectionner', 86 | tooMany: '(trop de valeurs à afficher)', 87 | filterResults: 'Filtrer les valeurs', 88 | totals: 'Totaux', 89 | vs: 'sur', 90 | by: 'par', 91 | apply: 'Appliquer', 92 | cancel: 'Annuler' 93 | } 94 | }, 95 | ko: { 96 | aggregators, 97 | localeStrings: { 98 | renderError: '피벗 테이블 결과를 렌더링하는 중 오류가 발생했습니다.', 99 | computeError: '피벗 테이블 결과를 계산하는 중 오류가 발생했습니다.', 100 | uiRenderError: '피벗 테이블 UI를 렌더링하는 중 오류가 발생했습니다.', 101 | selectAll: '모두 선택', 102 | selectNone: '선택 안 함', 103 | tooMany: '(목록에 표시하기에 너무 많음)', 104 | filterResults: '값 필터링', 105 | totals: '합계', 106 | vs: 'vs', 107 | by: '기준', 108 | apply: '적용', 109 | cancel: '취소' 110 | } 111 | } 112 | } 113 | ``` 114 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "description": "--- home: true title: Home actionText: Get Started actionLink: /guide/getting-started.html footer: MIT Licensed | Copyright © 2018-present Seungwoo Lee ---", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "type": "module", 7 | "scripts": { 8 | "docs:build": "vuepress build docs", 9 | "docs:clean-dev": "vuepress dev docs --clean-cache", 10 | "docs:dev": "vuepress dev docs", 11 | "docs:update-package": "pnpm dlx vp-update" 12 | }, 13 | "engines": { 14 | "node": ">=18.0.0" 15 | }, 16 | "devDependencies": { 17 | "@vuepress/bundler-vite": "2.0.0-rc.20", 18 | "@vuepress/theme-default": "2.0.0-rc.88", 19 | "sass-embedded": "^1.86.0", 20 | "vue": "^3.5.13", 21 | "vuepress": "2.0.0-rc.20" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-pivottable", 3 | "version": "0.4.68", 4 | "main": "dist/vue-pivottable.mjs", 5 | "files": [ 6 | "dist", 7 | "src" 8 | ], 9 | "exports": { 10 | ".": { 11 | "import": "./dist/vue-pivottable.mjs", 12 | "require": "./dist/vue-pivottable.umd.js" 13 | }, 14 | "./dist/": "./dist/" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/Seungwoo321/vue-pivottable.git" 19 | }, 20 | "keywords": [ 21 | "vue", 22 | "pivot", 23 | "pivottable", 24 | "vue-pivottable" 25 | ], 26 | "homepage": "https://seungwoo321.github.io/vue-pivottable/", 27 | "author": "Seungwoo, Lee (https://github.com/seungwoo321)", 28 | "license": "MIT", 29 | "scripts": { 30 | "clean": "rimraf dist packages/*/dist", 31 | "dev": "vite", 32 | "build": "vite build", 33 | "build:all": "pnpm run build && pnpm run build:packages", 34 | "build:packages": "pnpm --recursive --filter \"./packages/*\" run build", 35 | "serve": "vite preview", 36 | "lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src", 37 | "docs:dev": "pnpm --filter docs run docs:dev", 38 | "docs:build": "pnpm --filter docs run docs:build" 39 | }, 40 | "engines": { 41 | "node": ">=16.0.0" 42 | }, 43 | "peerDependencies": { 44 | "vue": "^2.6.10" 45 | }, 46 | "dependencies": { 47 | "vuedraggable": "^2.23.0" 48 | }, 49 | "devDependencies": { 50 | "@vitejs/plugin-vue2": "^2.0.0", 51 | "@vue/eslint-config-standard": "^4.0.0", 52 | "babel-eslint": "^10.1.0", 53 | "babel-preset-env": "^1.7.0", 54 | "eslint": "^5.16.0", 55 | "eslint-loader": "^3.0.2", 56 | "eslint-plugin-vue": "^5.2.3", 57 | "rimraf": "^6.0.1", 58 | "vite": "^3.1.8", 59 | "vite-plugin-static-copy": "^0.11.1" 60 | } 61 | } -------------------------------------------------------------------------------- /packages/plotly-renderer/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Seungwoo321 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/plotly-renderer/README.md: -------------------------------------------------------------------------------- 1 | # @vue-pivottable/plotly-renderer 2 | 3 | ⚠️ **This package supports Vue 2 only.** 4 | 👉 Looking for Vue 3? Use: [vue3-pivottable GitHub repo](https://github.com/vue-pivottable/vue3-pivottable) 5 | 6 | [See docs](https://seungwoo321.github.io/vue-pivottable/guide/renderer.html#plotly-renderer-v0-4-6) 7 | 8 | ## Usage 9 | 10 | ```vue 11 | 12 | 22 | 23 | 48 | 49 | ``` 50 | -------------------------------------------------------------------------------- /packages/plotly-renderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Plotly Renderer 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 22 | 23 |
24 | 45 | 46 | -------------------------------------------------------------------------------- /packages/plotly-renderer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vue-pivottable/plotly-renderer", 3 | "version": "1.0.1", 4 | "main": "dist/plotly-renderer.mjs", 5 | "files": [ 6 | "dist", 7 | "LICENSE", 8 | "README.md" 9 | ], 10 | "exports": { 11 | ".": { 12 | "import": "./dist/plotly-renderer.mjs", 13 | "require": "./dist/plotly-renderer.umd.js" 14 | } 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/Seungwoo321/vue-pivottable.git", 19 | "directory": "packages/plotly-renderer" 20 | }, 21 | "keywords": [ 22 | "vue", 23 | "pivot", 24 | "pivottable", 25 | "vue-pivottable", 26 | "@vue-pivottable/plotly-renderer" 27 | ], 28 | "homepage": "https://seungwoo321.github.io/vue-pivottable/", 29 | "author": "Seungwoo, Lee ", 30 | "license": "MIT", 31 | "scripts": { 32 | "build": "vite build" 33 | }, 34 | "peerDependencies": { 35 | "@seungwoo321/vue-plotly": "^1.1.0", 36 | "vue-pivottable": "^0.4.68" 37 | }, 38 | "dependencies": { 39 | "@seungwoo321/vue-plotly": "^1.1.0", 40 | "vue-pivottable": "^0.4.68" 41 | }, 42 | "devDependencies": { 43 | "@vitejs/plugin-vue2": "^2.0.0", 44 | "@vue/eslint-config-standard": "^4.0.0", 45 | "vite": "^3.1.8", 46 | "vite-plugin-static-copy": "^0.11.1" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/plotly-renderer/src/common.js: -------------------------------------------------------------------------------- 1 | import { PivotUtilities } from 'vue-pivottable' 2 | 3 | export default { 4 | props: { 5 | data: { 6 | type: [Array, Object, Function], 7 | required: true 8 | }, 9 | aggregators: { 10 | type: Object, 11 | default: function () { 12 | return PivotUtilities.aggregators 13 | } 14 | }, 15 | aggregatorName: { 16 | type: String, 17 | default: 'Count' 18 | }, 19 | heatmapMode: String, 20 | tableColorScaleGenerator: { 21 | type: Function 22 | }, 23 | tableOptions: { 24 | type: Object, 25 | default: function () { 26 | return {} 27 | } 28 | }, 29 | renderers: Object, 30 | rendererName: { 31 | type: String, 32 | default: 'Table' 33 | }, 34 | locale: { 35 | type: String, 36 | default: 'en' 37 | }, 38 | locales: { 39 | type: Object, 40 | default: function () { 41 | return PivotUtilities.locales 42 | } 43 | }, 44 | rowTotal: { 45 | type: Boolean, 46 | default: true 47 | }, 48 | colTotal: { 49 | type: Boolean, 50 | default: true 51 | }, 52 | cols: { 53 | type: Array, 54 | default: function () { 55 | return [] 56 | } 57 | }, 58 | rows: { 59 | type: Array, 60 | default: function () { 61 | return [] 62 | } 63 | }, 64 | vals: { 65 | type: Array, 66 | default: function () { 67 | return [] 68 | } 69 | }, 70 | attributes: { 71 | type: Array, 72 | default: function () { 73 | return [] 74 | } 75 | }, 76 | valueFilter: { 77 | type: Object, 78 | default: function () { 79 | return {} 80 | } 81 | }, 82 | sorters: { 83 | type: [Function, Object], 84 | default: function () { 85 | return {} 86 | } 87 | }, 88 | derivedAttributes: { 89 | type: [Function, Object], 90 | default: function () { 91 | return {} 92 | } 93 | }, 94 | rowOrder: { 95 | type: String, 96 | default: 'key_a_to_z', 97 | validator: function (value) { 98 | return ['key_a_to_z', 'value_a_to_z', 'value_z_to_a'].indexOf(value) !== -1 99 | } 100 | }, 101 | colOrder: { 102 | type: String, 103 | default: 'key_a_to_z', 104 | validator: function (value) { 105 | return ['key_a_to_z', 'value_a_to_z', 'value_z_to_a'].indexOf(value) !== -1 106 | } 107 | }, 108 | tableMaxWidth: { 109 | type: Number, 110 | default: 0, 111 | validator: function (value) { 112 | return value >= 0 113 | } 114 | } 115 | }, 116 | methods: { 117 | renderError (h) { 118 | return h('span', (this.locales[this.locale].localeStrings.renderError) || 'An error occurred rendering the PivotTable results.') 119 | }, 120 | computeError (h) { 121 | return h('span', (this.locales[this.locale].localeStrings.computeError) || 'An error occurred computing the PivotTable results.') 122 | }, 123 | uiRenderError (h) { 124 | return h('span', (this.locales[this.locale].localeStrings.uiRenderError) || 'An error occurred rendering the PivotTable UI.') 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /packages/plotly-renderer/src/index.js: -------------------------------------------------------------------------------- 1 | import { PivotUtilities } from 'vue-pivottable' 2 | import defaultProps from './common' 3 | import { Plotly } from '@seungwoo321/vue-plotly' 4 | 5 | function makeRenderer (opts = {}, traceOptions = {}, layoutOptions = {}, transpose = false) { 6 | const plotlyRenderer = { 7 | name: opts.name, 8 | mixins: [defaultProps], 9 | props: { 10 | plotlyOptions: { 11 | type: Object, 12 | default: function () { 13 | return {} 14 | } 15 | } 16 | }, 17 | render (h) { 18 | const pivotData = new PivotUtilities.PivotData(this.$props) 19 | const rowKeys = pivotData.getRowKeys() 20 | const colKeys = pivotData.getColKeys() 21 | const traceKeys = transpose ? colKeys : rowKeys 22 | if (traceKeys.length === 0) traceKeys.push([]) 23 | const datumKeys = transpose ? rowKeys : colKeys 24 | if (datumKeys.length === 0) datumKeys.push([]) 25 | 26 | let fullAggName = this.$props.aggregatorName 27 | const numInputs = this.$props.aggregators[fullAggName]([])().numInputs || 0 28 | if (numInputs !== 0) { 29 | fullAggName += ` of ${this.$props.vals.slice(0, numInputs).join(', ')}` 30 | } 31 | const data = traceKeys.map(traceKey => { 32 | const values = [] 33 | const labels = [] 34 | for (const datumKey of datumKeys) { 35 | const val = parseFloat( 36 | pivotData.getAggregator( 37 | transpose ? datumKey : traceKey, 38 | transpose ? traceKey : datumKey 39 | ).value() 40 | ) 41 | values.push(isFinite(val) ? val : null) 42 | labels.push(datumKey.join('-') || ' ') 43 | } 44 | const trace = { name: traceKey.join('-') || fullAggName } 45 | if (traceOptions.type === 'pie') { 46 | trace.values = values 47 | trace.labels = labels.length > 1 ? labels : [fullAggName] 48 | } else { 49 | trace.x = transpose ? values : labels 50 | trace.y = transpose ? labels : values 51 | } 52 | return Object.assign(trace, traceOptions) 53 | }) 54 | 55 | let titleText = fullAggName 56 | const hAxisTitle = transpose ? this.$props.rows.join('-') : this.$props.cols.join('-') 57 | const groupByTitle = transpose ? this.$props.cols.join('-') : this.$props.rows.join('-') 58 | if (hAxisTitle !== '') titleText += ` vs ${hAxisTitle}` 59 | if (groupByTitle !== '') titleText += ` by ${groupByTitle}` 60 | 61 | const layout = { 62 | title: titleText, 63 | hovermode: 'closest', 64 | width: window.innerWidth / 1.5, 65 | height: window.innerHeight / 1.4 - 50 66 | } 67 | 68 | if (traceOptions.type === 'pie') { 69 | const columns = Math.ceil(Math.sqrt(data.length)) 70 | const rows = Math.ceil(data.length / columns) 71 | layout.grid = { columns, rows } 72 | data.forEach((d, i) => { 73 | d.domain = { 74 | row: Math.floor(i / columns), 75 | column: i - columns * Math.floor(i / columns) 76 | } 77 | if (data.length > 1) { 78 | d.title = d.name 79 | } 80 | }) 81 | if (data[0].labels.length === 1) { 82 | layout.showlegend = false 83 | } 84 | } else { 85 | layout.xaxis = { 86 | title: transpose ? fullAggName : null, 87 | automargin: true 88 | } 89 | layout.yaxis = { 90 | title: transpose ? null : fullAggName, 91 | automargin: true 92 | } 93 | } 94 | return h(Plotly, { 95 | props: { 96 | data, 97 | layout: Object.assign({}, 98 | layout, 99 | layoutOptions, 100 | this.$props.plotlyOptions 101 | ) 102 | } 103 | }) 104 | } 105 | } 106 | return plotlyRenderer 107 | } 108 | function makeScatterRenderer (opts = {}) { 109 | const scatterRenderer = { 110 | name: opts.name, 111 | mixins: [defaultProps], 112 | props: { 113 | plotlyOptions: { 114 | type: Object, 115 | default: function () { 116 | return {} 117 | } 118 | } 119 | }, 120 | render (h) { 121 | const pivotData = new PivotUtilities.PivotData(this.$props) 122 | const rowKeys = pivotData.getRowKeys() 123 | const colKeys = pivotData.getColKeys() 124 | if (rowKeys.length === 0) rowKeys.push([]) 125 | if (colKeys.length === 0) colKeys.push([]) 126 | 127 | const data = { x: [], y: [], text: [], type: 'scatter', mode: 'markers' } 128 | rowKeys.map(rowKey => { 129 | colKeys.map(colKey => { 130 | const v = pivotData.getAggregator(rowKey, colKey).value() 131 | if (v !== null) { 132 | data.x.push(colKey.join('-')) 133 | data.y.push(rowKey.join('-')) 134 | data.text.push(v) 135 | } 136 | }) 137 | }) 138 | 139 | const layout = { 140 | title: this.$props.rows.join('-') + ' vs ' + this.$props.cols.join('-'), 141 | hovermode: 'closest', 142 | xaxis: { title: this.$props.cols.join('-'), automargin: true }, 143 | yaxis: { title: this.$props.rows.join('-'), automargin: true }, 144 | width: window.innerWidth / 1.5, 145 | height: window.innerHeight / 1.4 - 50 146 | } 147 | 148 | return h(Plotly, { 149 | props: { 150 | data: [data], 151 | 152 | layout: Object.assign({}, 153 | layout, 154 | this.$props.plotlyOptions 155 | ) 156 | } 157 | }) 158 | } 159 | } 160 | return scatterRenderer 161 | } 162 | 163 | export default { 164 | 'Grouped Column Chart': makeRenderer({ name: 'vue-grouped-column-chart' }, { type: 'bar' }, { barmode: 'group' }), 165 | 'Stacked Column Chart': makeRenderer({ name: 'vue-stacked-column-chart' }, { type: 'bar' }, { barmode: 'relative' }), 166 | 'Grouped Bar Chart': makeRenderer({ name: 'groupd-bar-chart' }, { type: 'bar', orientation: 'h' }, { barmode: 'group' }, true), 167 | 'Stacked Bar Chart': makeRenderer({ name: 'vue-stacked-bar-chart' }, { type: 'bar', orientation: 'h' }, { barmode: 'relative' }, true), 168 | 'Line Chart': makeRenderer({ name: 'vue-line-chart' }), 169 | 'Dot Chart': makeRenderer({ name: 'vue-dot-chart' }, { mode: 'markers' }, {}, true), 170 | 'Area Chart': makeRenderer({ name: 'vue-area-chart' }, { stackgroup: 1 }), 171 | 'Scatter Chart': makeScatterRenderer({ name: 'vue-scatter-chart' }), 172 | 'Multiple Pie Chart': makeRenderer({ name: 'vue-multiple-pie-chart' }, { type: 'pie', scalegroup: 1, hoverinfo: 'label+value', textinfo: 'none' }, {}, true) 173 | } 174 | -------------------------------------------------------------------------------- /packages/plotly-renderer/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import { resolve } from 'path' 3 | import vue from '@vitejs/plugin-vue2' 4 | 5 | export default defineConfig(({ command, mode, ssrBuild }) => { 6 | return { 7 | plugins: [ 8 | vue() 9 | ], 10 | publicDir: false, 11 | build: { 12 | type: ['es', 'umd'], 13 | lib: { 14 | entry: resolve(__dirname, 'src/index.js'), 15 | name: 'PlotlyRenderer', 16 | fileName: 'plotly-renderer' 17 | }, 18 | rollupOptions: { 19 | external: ['vue'], 20 | output: { 21 | exports: 'named', 22 | globals: { 23 | 'vue': 'Vue', 24 | 'vue-pivottable': 'VuePivottable' 25 | } 26 | } 27 | } 28 | } 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /packages/scroll-renderer/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Seungwoo321 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/scroll-renderer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vue-pivottable/scroll-renderer", 3 | "version": "1.0.1", 4 | "main": "dist/scroll-renderer.mjs", 5 | "files": [ 6 | "dist", 7 | "LICENSE", 8 | "README.md" 9 | ], 10 | "exports": { 11 | ".": { 12 | "import": "./dist/scroll-renderer.mjs", 13 | "require": "./dist/scroll-renderer.umd.js" 14 | } 15 | }, 16 | "publishConfig": { 17 | "registry": "https://registry.npmjs.org/", 18 | "access": "public" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/Seungwoo321/vue-pivottable.git", 23 | "directory": "packages/scroll-renderer" 24 | }, 25 | "keywords": [ 26 | "vue", 27 | "pivot", 28 | "pivottable", 29 | "vue-pivottable", 30 | "@vue-pivottable/scroll-renderer" 31 | ], 32 | "homepage": "https://seungwoo321.github.io/vue-pivottable/", 33 | "author": "Seungwoo, Lee ", 34 | "license": "MIT", 35 | "scripts": { 36 | "build": "vite build" 37 | }, 38 | "dependencies": { 39 | "lodash": "^4.17.21", 40 | "vue-pivottable": "^1.0.0" 41 | }, 42 | "devDependencies": { 43 | "@vitejs/plugin-vue2": "^2.0.0", 44 | "@vue/eslint-config-standard": "^4.0.0", 45 | "vite": "^3.1.8", 46 | "vite-plugin-static-copy": "^0.11.1" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/scroll-renderer/src/common.js: -------------------------------------------------------------------------------- 1 | import { PivotUtilities } from 'vue-pivottable' 2 | 3 | export default { 4 | props: { 5 | data: { 6 | type: [Array, Object, Function], 7 | required: true 8 | }, 9 | aggregators: { 10 | type: Object, 11 | default: function () { 12 | return PivotUtilities.aggregators 13 | } 14 | }, 15 | aggregatorName: { 16 | type: String, 17 | default: 'Count' 18 | }, 19 | heatmapMode: String, 20 | tableColorScaleGenerator: { 21 | type: Function 22 | }, 23 | tableOptions: { 24 | type: Object, 25 | default: function () { 26 | return {} 27 | } 28 | }, 29 | renderers: Object, 30 | rendererName: { 31 | type: String, 32 | default: 'Table' 33 | }, 34 | locale: { 35 | type: String, 36 | default: 'en' 37 | }, 38 | locales: { 39 | type: Object, 40 | default: function () { 41 | return PivotUtilities.locales 42 | } 43 | }, 44 | rowTotal: { 45 | type: Boolean, 46 | default: true 47 | }, 48 | colTotal: { 49 | type: Boolean, 50 | default: true 51 | }, 52 | cols: { 53 | type: Array, 54 | default: function () { 55 | return [] 56 | } 57 | }, 58 | rows: { 59 | type: Array, 60 | default: function () { 61 | return [] 62 | } 63 | }, 64 | vals: { 65 | type: Array, 66 | default: function () { 67 | return [] 68 | } 69 | }, 70 | attributes: { 71 | type: Array, 72 | default: function () { 73 | return [] 74 | } 75 | }, 76 | valueFilter: { 77 | type: Object, 78 | default: function () { 79 | return {} 80 | } 81 | }, 82 | sorters: { 83 | type: [Function, Object], 84 | default: function () { 85 | return {} 86 | } 87 | }, 88 | derivedAttributes: { 89 | type: [Function, Object], 90 | default: function () { 91 | return {} 92 | } 93 | }, 94 | rowOrder: { 95 | type: String, 96 | default: 'key_a_to_z', 97 | validator: function (value) { 98 | return ['key_a_to_z', 'value_a_to_z', 'value_z_to_a'].indexOf(value) !== -1 99 | } 100 | }, 101 | colOrder: { 102 | type: String, 103 | default: 'key_a_to_z', 104 | validator: function (value) { 105 | return ['key_a_to_z', 'value_a_to_z', 'value_z_to_a'].indexOf(value) !== -1 106 | } 107 | }, 108 | colLimit: { 109 | type: Number, 110 | default: 100 111 | }, 112 | rowLimit: { 113 | type: Number, 114 | default: 100 115 | } 116 | }, 117 | methods: { 118 | renderError (h) { 119 | return h('span', (this.locales[this.locale].localeStrings.renderError) || 'An error occurred rendering the PivotTable results.') 120 | }, 121 | computeError (h) { 122 | return h('span', (this.locales[this.locale].localeStrings.computeError) || 'An error occurred computing the PivotTable results.') 123 | }, 124 | uiRenderError (h) { 125 | return h('span', (this.locales[this.locale].localeStrings.uiRenderError) || 'An error occurred rendering the PivotTable UI.') 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /packages/scroll-renderer/src/index.js: -------------------------------------------------------------------------------- 1 | import { debounce } from 'lodash' 2 | import { PivotUtilities } from 'vue-pivottable' 3 | import defaultProps from './common' 4 | function redColorScaleGenerator (values) { 5 | const min = Math.min.apply(Math, values) 6 | const max = Math.max.apply(Math, values) 7 | return x => { 8 | // eslint-disable-next-line no-magic-numbers 9 | const nonRed = 255 - Math.round(255 * (x - min) / (max - min)) 10 | return { backgroundColor: `rgb(255,${nonRed},${nonRed})` } 11 | } 12 | } 13 | function makeRenderer (opts = {}) { 14 | const scrollRenderer = { 15 | name: opts.name, 16 | mixins: [defaultProps], 17 | props: { 18 | colLimit: { 19 | type: Number, 20 | default: 100 21 | }, 22 | rowLimit: { 23 | type: Number, 24 | default: 100 25 | }, 26 | heatmapMode: String, 27 | tableColorScaleGenerator: { 28 | type: Function, 29 | default: redColorScaleGenerator 30 | }, 31 | tableOptions: { 32 | type: Object, 33 | default: function () { 34 | return { 35 | clickCallback: null 36 | } 37 | } 38 | }, 39 | localeStrings: { 40 | type: Object, 41 | default: function () { 42 | return { 43 | totals: 'Totals' 44 | } 45 | } 46 | } 47 | }, 48 | data () { 49 | return { 50 | scrollEvent: null, 51 | colStart: 0, 52 | rowStart: 0, 53 | colEnd: 100, 54 | rowEnd: 100 55 | } 56 | }, 57 | created () { 58 | this.colEnd = this.colLimit 59 | this.rowEnd = this.rowLimit 60 | }, 61 | computed: { 62 | pivotData () { 63 | const props = { ...this.$props, ...this.$attrs.props } 64 | return new PivotUtilities.PivotData(props) 65 | }, 66 | maxRows () { 67 | return this.pivotData.getRowKeys().length - 1 68 | }, 69 | maxCols () { 70 | return this.pivotData.getColKeys().length - 1 71 | }, 72 | loaded () { 73 | return this.rowEnd >= this.maxRows && this.colEnd >= this.maxCols 74 | } 75 | }, 76 | methods: { 77 | isOverlap (arr, i, j) { 78 | if (j === 0) return -1 79 | if (arr[j].length - 1 === i) return -1 80 | if (i === 0) { 81 | if (arr[j][i] === arr[j - 1][i]) return 1 82 | else return -1 83 | } 84 | while (i > 0) { 85 | if (arr[j][i] === arr[j - 1][i] && arr[j][i - 1] === arr[j - 1][i - 1]) i-- 86 | else return -1 87 | } 88 | }, 89 | handleKeydownendEvent (e) { 90 | if (e.code === 'PageDown') { 91 | this.handleScrollRender() 92 | } 93 | if (e.code === 'end') { 94 | this.handleScrollRender() 95 | } 96 | if (e.ecode === 'ArrowDown') { 97 | this.handleScrollRender() 98 | } 99 | }, 100 | handleScrollRender () { 101 | if (this.rowEnd <= this.maxRows) { 102 | this.rowEnd += this.rowLimit 103 | } 104 | if (this.colEnd <= this.maxCols) { 105 | this.colEnd += this.colLimit 106 | } 107 | } 108 | }, 109 | mounted () { 110 | window.addEventListener('scroll', debounce(this.handleScrollRender.bind(this), 1000)) 111 | window.addEventListener('keydown', debounce(this.handleKeydownendEvent.bind(this), 1000)) 112 | }, 113 | destroyed () { 114 | window.removeEventListener('scroll', this.handleScrollRender) 115 | window.removeEventListener('keydown', this.handleKeydownendEvent) 116 | }, 117 | render (h) { 118 | if (!this.pivotData) { 119 | return this.computeError(h) 120 | } 121 | const { rowStart, rowEnd, colStart, colEnd, isOverlap, pivotData, rowTotal, colTotal, localeStrings } = this 122 | const { cols: colAttrs, rows: rowAttrs } = pivotData.props 123 | const rowKeys = pivotData.getRowKeys() 124 | const colKeys = pivotData.getColKeys() 125 | // eslint-disable-next-line no-unused-vars 126 | let valueCellColors = () => { } 127 | // eslint-disable-next-line no-unused-vars 128 | let rowTotalColors = () => { } 129 | // eslint-disable-next-line no-unused-vars 130 | let colTotalColors = () => { } 131 | if (opts.heatmapMode) { 132 | const colorScaleGenerator = this.tableColorScaleGenerator 133 | const rowTotalValues = colKeys.map(x => 134 | pivotData.getAggregator([], x).value() 135 | ) 136 | rowTotalColors = colorScaleGenerator(rowTotalValues) 137 | const colTotalValues = rowKeys.map(x => 138 | pivotData.getAggregator(x, []).value() 139 | ) 140 | colTotalColors = colorScaleGenerator(colTotalValues) 141 | 142 | if (opts.heatmapMode === 'full') { 143 | const allValues = [] 144 | rowKeys.map(r => 145 | colKeys.map(c => 146 | allValues.push(pivotData.getAggregator(r, c).value()) 147 | ) 148 | ) 149 | const colorScale = colorScaleGenerator(allValues) 150 | valueCellColors = (r, c, v) => colorScale(v) 151 | } else if (opts.heatmapMode === 'row') { 152 | const rowColorScales = {} 153 | rowKeys.map(r => { 154 | const rowValues = colKeys.map(x => 155 | pivotData.getAggregator(r, x).value() 156 | ) 157 | rowColorScales[r] = colorScaleGenerator(rowValues) 158 | }) 159 | valueCellColors = (r, c, v) => rowColorScales[r](v) 160 | } else if (opts.heatmapMode === 'col') { 161 | const colColorScales = {} 162 | colKeys.map(c => { 163 | const colValues = rowKeys.map(x => 164 | pivotData.getAggregator(x, c).value() 165 | ) 166 | colColorScales[c] = colorScaleGenerator(colValues) 167 | }) 168 | valueCellColors = (r, c, v) => colColorScales[c](v) 169 | } 170 | } 171 | const getClickHandler = (value, rowValues, colValues) => { 172 | const tableOptions = this.tableOptions 173 | if (tableOptions && tableOptions.clickCallback) { 174 | const filters = {} 175 | let attr = {} 176 | for (let i in colAttrs) { 177 | if (!colValues.hasOwnProperty(i)) continue 178 | attr = colAttrs[i] 179 | if (colValues[i] !== null) { 180 | filters[attr] = colValues[i] 181 | } 182 | } 183 | for (let i in rowAttrs) { 184 | if (!rowValues.hasOwnProperty(i)) continue 185 | attr = rowAttrs[i] 186 | if (rowValues[i] !== null) { 187 | filters[attr] = rowValues[i] 188 | } 189 | } 190 | return e => tableOptions.clickCallback(e, value, filters, pivotData) 191 | } 192 | } 193 | const theadTh = (h, i, colIndex) => { 194 | const elements = [] 195 | const [colStart, colEnd] = colIndex 196 | for (let j = colStart; j < colEnd; j++) { 197 | if (!colKeys.hasOwnProperty(j)) continue 198 | const x = isOverlap(colKeys, i, j) 199 | elements.push( 200 | h('th', { 201 | class: { 202 | pvtColLabel: true 203 | }, 204 | style: { 205 | 'border-right': 'none', 206 | 'border-left': x !== -1 ? 'none' : null 207 | }, 208 | attrs: { 209 | rowspan: (i === colAttrs.length - 1) && rowAttrs.length ? 2 : null 210 | } 211 | }, x === -1 ? colKeys[j][i] : null) 212 | ) 213 | } 214 | if (rowTotal && i === 0) { 215 | elements.push( 216 | h('th', { 217 | className: { 218 | pvtTotalLabel: true, 219 | pvtRowTotalLabel: true 220 | }, 221 | attrs: { 222 | rowspan: colAttrs.length + (rowAttrs.length === 0 ? 0 : 1) 223 | } 224 | }, localeStrings.totals) 225 | ) 226 | } 227 | return elements 228 | } 229 | const theadThTotal = (h) => { 230 | return rowAttrs.length !== 0 ? h('tr', 231 | [ 232 | rowAttrs.map((r, i) => { 233 | return h('th', { 234 | class: { 235 | pvtAxisLabel: true 236 | }, 237 | attrs: { 238 | key: `rowAttr${i}` 239 | } 240 | }, r) 241 | }), 242 | rowTotal || colAttrs.length ? h('th', { 243 | class: { 244 | pvtTotalLabel: true 245 | } 246 | }, '') : null 247 | ] 248 | ) : null 249 | } 250 | const tbodyTr = (h, colIndex, rowIndex) => { 251 | const elements = [] 252 | const [rowStart, rowEnd] = rowIndex 253 | for (let i = rowStart; i < rowEnd; i++) { 254 | if (!rowKeys.hasOwnProperty(i)) continue 255 | elements.push( 256 | h('tr', [ 257 | tbodyTrTh(h, i), 258 | tbodyTrTd(h, i, colIndex) 259 | ] 260 | ) 261 | ) 262 | } 263 | return elements 264 | } 265 | const tbodyTrTh = (h, i) => { 266 | const elements = [] 267 | const rowKey = rowKeys[i] 268 | for (let j = 0; j < rowKey.length; j++) { 269 | if (!rowKey.hasOwnProperty(j)) continue 270 | const x = isOverlap(rowKeys, j, i) 271 | elements.push( 272 | h('th', { 273 | className: { 274 | pvtRowLabel: true 275 | }, 276 | style: { 277 | 'border-bottom': 'none', 278 | 'border-top': x !== -1 ? 'none' : null 279 | }, 280 | attrs: { 281 | key: `rowKeyLabel${i}-${j}`, 282 | colspan: colAttrs.length && j === rowKey.length - 1 ? 2 : null 283 | } 284 | }, x === -1 ? rowKey[j] : null) 285 | ) 286 | } 287 | return elements 288 | } 289 | const tbodyTrTd = (h, i, colIndex) => { 290 | const elements = [] 291 | const rowKey = rowKeys[i] 292 | const [colStart, colEnd] = colIndex 293 | for (let j = colStart; j < colEnd; j++) { 294 | if (!colKeys.hasOwnProperty(j)) continue 295 | const colKey = colKeys[j] 296 | const aggregator = pivotData.getAggregator(rowKey, colKey) 297 | const val = aggregator.value() 298 | elements.push( 299 | h('td', { 300 | class: { 301 | pvVal: true 302 | }, 303 | style: valueCellColors(rowKey, colKey, val), 304 | attrs: { 305 | key: `pvtVal${i}-${j}` 306 | }, 307 | on: this.tableOptions.clickCallback ? { 308 | click: getClickHandler(val, rowKey, colKey) 309 | } : {} 310 | }, aggregator.format(val)) 311 | ) 312 | } 313 | if (rowTotal) elements.push(rowTotalTd(h, i)) 314 | return elements 315 | } 316 | const rowTotalTd = (h, i) => { 317 | const rowKey = rowKeys[i] 318 | const totalAggregator = pivotData.getAggregator(rowKey, []) 319 | return h('td', { 320 | class: { 321 | pvtTotal: true 322 | }, 323 | style: colTotalColors(totalAggregator.value()), 324 | on: this.tableOptions.clickCallback ? { 325 | click: getClickHandler(totalAggregator.value(), rowKey, []) 326 | } : {} 327 | }, totalAggregator.format(totalAggregator.value())) 328 | } 329 | const colTotalTh = (h) => { 330 | return colTotal ? h('th', { 331 | class: { 332 | pvtTotalLabel: true 333 | }, 334 | attrs: { 335 | colspan: rowAttrs.length + (colAttrs.length === 0 ? 0 : 1) 336 | } 337 | }, localeStrings.totals) : null 338 | } 339 | const colTotalTd = (h, colIndex) => { 340 | const [colStart, colEnd] = colIndex 341 | const elements = [] 342 | for (let i = colStart; i < colEnd; i++) { 343 | if (!colKeys.hasOwnProperty(i)) continue 344 | const colKey = colKeys[i] 345 | const totalAggregator = pivotData.getAggregator([], colKey) 346 | elements.push(h('td', { 347 | staticClass: ['pvtTotal'], 348 | style: rowTotalColors(totalAggregator.value()), 349 | attrs: { 350 | key: `total${i}` 351 | }, 352 | on: this.tableOptions.clickCallback ? { 353 | click: getClickHandler(totalAggregator.value(), [], colKey) 354 | } : {} 355 | }, totalAggregator.format(totalAggregator.value()))) 356 | } 357 | return elements 358 | } 359 | const grandTotalTd = (h) => { 360 | const grandTotalAggregator = pivotData.getAggregator([], []) 361 | return colTotal && rowTotal ? h('td', { 362 | staticClass: ['pvtGrandTotal'], 363 | on: this.tableOptions.clickCallback ? { 364 | click: getClickHandler(grandTotalAggregator.value(), [], []) 365 | } : {} 366 | }, grandTotalAggregator.format(grandTotalAggregator.value())) : undefined 367 | } 368 | return h('table', { 369 | ref: 'pvtOutput', 370 | staticClass: ['pvtTable'] 371 | }, [ 372 | h('thead', 373 | [ 374 | colAttrs.map((c, j) => { 375 | return h('tr', { 376 | attrs: { 377 | key: `colAttrs${j}` 378 | } 379 | }, 380 | [ 381 | j === 0 && rowAttrs.length !== 0 ? h('th', { 382 | attrs: { 383 | colspan: rowAttrs.length, 384 | rowspan: colAttrs.length 385 | } 386 | }) : null, 387 | h('th', { 388 | class: { 389 | pvtAxisLabel: true 390 | } 391 | }, c), 392 | theadTh(h, j, [colStart, colEnd]) 393 | ]) 394 | }), 395 | theadThTotal(h) 396 | ] 397 | ), 398 | h('tbody', 399 | [ 400 | tbodyTr(h, [colStart, colEnd], [rowStart, rowEnd]), 401 | !this.loaded && rowEnd < this.maxRows ? h('tr', [ 402 | h('td', { 403 | style: { 404 | 'text-align': 'center' 405 | }, 406 | attrs: { 407 | colspan: rowAttrs.length + colEnd + 1 408 | } 409 | }, 'loading...') 410 | ]) : null, 411 | h('tr', [ 412 | colTotalTh(h), 413 | colTotalTd(h, [colStart, colEnd]), 414 | grandTotalTd(h) 415 | ]) 416 | ] 417 | ) 418 | ]) 419 | }, 420 | renderError (h, error) { 421 | return this.computeError(h) 422 | } 423 | } 424 | return scrollRenderer 425 | } 426 | 427 | export default { 428 | 'Table': makeRenderer({ name: 'vue-table' }), 429 | 'Table Heatmap': makeRenderer({ heatmapMode: 'full', name: 'vue-table-heatmap' }), 430 | 'Table Col Heatmap': makeRenderer({ heatmapMode: 'col', name: 'vue-table-col-heatmap' }), 431 | 'Table Row Heatmap': makeRenderer({ heatmapMode: 'row', name: 'vue-table-col-heatmap' }) 432 | } 433 | -------------------------------------------------------------------------------- /packages/scroll-renderer/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import { resolve } from 'path' 3 | import vue from '@vitejs/plugin-vue2' 4 | 5 | export default defineConfig(({ command, mode, ssrBuild }) => { 6 | return { 7 | plugins: [ 8 | vue() 9 | ], 10 | publicDir: false, 11 | build: { 12 | type: ['es', 'umd'], 13 | lib: { 14 | entry: resolve(__dirname, 'src/index.js'), 15 | name: 'ScrollRenderer', 16 | fileName: 'scroll-renderer' 17 | }, 18 | rollupOptions: { 19 | external: ['vue'], 20 | output: { 21 | exports: 'named', 22 | globals: { 23 | 'vue': 'Vue', 24 | 'vue-pivottable': 'VuePivottable' 25 | } 26 | } 27 | } 28 | } 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | - 'docs' -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /setting.json: -------------------------------------------------------------------------------- 1 | { 2 | "vetur.format.options.tabSize": 2 3 | } -------------------------------------------------------------------------------- /src/DraggableAttribute.js: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | name: 'draggable-attribute', 4 | props: { 5 | open: { 6 | type: Boolean, 7 | default: false 8 | }, 9 | sortable: { 10 | type: Boolean, 11 | default: true 12 | }, 13 | draggable: { 14 | type: Boolean, 15 | default: true 16 | }, 17 | name: { 18 | type: String, 19 | required: true 20 | }, 21 | attrValues: { 22 | type: Object, 23 | required: false 24 | }, 25 | valueFilter: { 26 | type: Object, 27 | default: function () { 28 | return {} 29 | } 30 | }, 31 | sorter: { 32 | type: Function, 33 | required: true 34 | }, 35 | localeStrings: { 36 | type: Object, 37 | default: function () { 38 | return { 39 | selectAll: 'Select All', 40 | selectNone: 'Select None', 41 | tooMany: '(too many to list)', // too many values to show 42 | filterResults: 'Filter values', 43 | only: 'only' 44 | } 45 | } 46 | }, 47 | menuLimit: Number, 48 | zIndex: Number, 49 | async: Boolean, 50 | unused: Boolean 51 | }, 52 | data () { 53 | return { 54 | filterText: '' 55 | } 56 | }, 57 | computed: { 58 | disabled () { 59 | return !this.sortable && !this.draggable 60 | }, 61 | sortonly () { 62 | return this.sortable && !this.draggable 63 | } 64 | }, 65 | methods: { 66 | setValuesInFilter (attribute, values) { 67 | const valueFilter = values.reduce((r, v) => { 68 | r[v] = true 69 | return r 70 | }, {}) 71 | this.$emit('update:filter', { attribute, valueFilter }) 72 | }, 73 | addValuesToFilter (attribute, values) { 74 | const valueFilter = values.reduce((r, v) => { 75 | r[v] = true 76 | return r 77 | }, Object.assign({}, this.valueFilter)) 78 | this.$emit('update:filter', { attribute, valueFilter }) 79 | }, 80 | removeValuesFromFilter (attribute, values) { 81 | const valueFilter = values.reduce((r, v) => { 82 | if (r[v]) { 83 | delete r[v] 84 | } 85 | return r 86 | }, Object.assign({}, this.valueFilter)) 87 | this.$emit('update:filter', { attribute, valueFilter }) 88 | }, 89 | moveFilterBoxToTop (attribute) { 90 | this.$emit('moveToTop:filterbox', { attribute }) 91 | }, 92 | toggleValue (value) { 93 | if (value in this.valueFilter) { 94 | this.removeValuesFromFilter(this.name, [value]) 95 | } else { 96 | this.addValuesToFilter(this.name, [value]) 97 | } 98 | }, 99 | matchesFilter (x) { 100 | return x 101 | .toLowerCase() 102 | .trim() 103 | .includes(this.filterText.toLowerCase().trim()) 104 | }, 105 | selectOnly (e, value) { 106 | e.stopPropagation() 107 | this.value = value 108 | this.setValuesInFilter(this.name, Object.keys(this.attrValues).filter(y => y !== value)) 109 | }, 110 | getFilterBox (h) { 111 | const showMenu = Object.keys(this.attrValues).length < this.menuLimit 112 | const values = Object.keys(this.attrValues) 113 | const shown = values.filter(this.matchesFilter.bind(this)).sort(this.sorter) 114 | return h('div', { 115 | staticClass: ['pvtFilterBox'], 116 | style: { 117 | display: 'block', 118 | cursor: 'initial', 119 | zIndex: this.zIndex 120 | }, 121 | on: { 122 | click: (e) => { 123 | e.stopPropagation() 124 | this.moveFilterBoxToTop(this.name) 125 | } 126 | } 127 | }, 128 | [ 129 | h('div', { 130 | staticClass: 'pvtSearchContainer' 131 | }, 132 | [ 133 | showMenu || h('p', this.localeStrings.tooMany), 134 | showMenu && h('input', { 135 | staticClass: ['pvtSearch'], 136 | attrs: { 137 | type: 'text', 138 | placeholder: this.localeStrings.filterResults 139 | }, 140 | domProps: { 141 | value: this.filterText 142 | }, 143 | on: { 144 | input: e => { 145 | this.filterText = e.target.value 146 | this.$emit('input', e.target.value) 147 | } 148 | } 149 | }), 150 | h('a', { 151 | staticClass: ['pvtFilterTextClear'], 152 | on: { 153 | click: () => { this.filterText = '' } 154 | } 155 | }), 156 | h('a', { 157 | staticClass: ['pvtButton'], 158 | attrs: { 159 | role: 'button' 160 | }, 161 | on: { 162 | click: () => this.removeValuesFromFilter(this.name, Object.keys(this.attrValues).filter(this.matchesFilter.bind(this))) 163 | } 164 | }, this.localeStrings.selectAll), 165 | h('a', { 166 | staticClass: ['pvtButton'], 167 | attrs: { 168 | role: 'button' 169 | }, 170 | on: { 171 | click: () => this.addValuesToFilter(this.name, Object.keys(this.attrValues).filter(this.matchesFilter.bind(this))) 172 | } 173 | }, this.localeStrings.selectNone) 174 | ]), 175 | showMenu && h('div', { 176 | staticClass: ['pvtCheckContainer'] 177 | }, 178 | shown.map(x => { 179 | const checked = !(x in this.valueFilter) 180 | return h('p', { 181 | class: { 182 | selected: checked 183 | }, 184 | attrs: { 185 | key: x 186 | }, 187 | on: { 188 | 'click': () => this.toggleValue(x) 189 | } 190 | }, 191 | [ 192 | h('input', { 193 | attrs: { 194 | type: 'checkbox' 195 | }, 196 | domProps: { 197 | checked: checked 198 | } 199 | }), 200 | x, 201 | h('a', { 202 | staticClass: ['pvtOnly'], 203 | on: { 204 | click: e => this.selectOnly(e, x) 205 | } 206 | }, this.localeStrings.only), 207 | h('a', { 208 | staticClass: ['pvtOnlySpacer'] 209 | }) 210 | ]) 211 | }) 212 | ) 213 | ]) 214 | }, 215 | toggleFilterBox (event) { 216 | event.stopPropagation() 217 | if (!this.attrValues) { 218 | if (this.$listeners['no:filterbox']) { 219 | this.$emit('no:filterbox') 220 | } 221 | return 222 | } 223 | this.openFilterBox(this.name, !this.open) 224 | this.moveFilterBoxToTop(this.name) 225 | }, 226 | openFilterBox (attribute, open) { 227 | this.$emit('open:filterbox', { attribute, open }) 228 | } 229 | }, 230 | render (h) { 231 | const filtered = Object.keys(this.valueFilter).length !== 0 ? 'pvtFilteredAttribute' : '' 232 | const pvtAttrScopedSlot = this.$scopedSlots.pvtAttr 233 | return h('li', { 234 | attrs: { 235 | 'data-id': !this.disabled ? this.name : undefined 236 | } 237 | }, 238 | [ 239 | h('span', { 240 | staticClass: ['pvtAttr ' + filtered], 241 | class: { 242 | sortonly: this.sortonly, 243 | disabled: this.disabled 244 | } 245 | }, 246 | [ 247 | pvtAttrScopedSlot ? pvtAttrScopedSlot({ name: this.name }) : this.name, 248 | !this.disabled && 249 | (!this.async || (!this.unused && this.async)) ? h('span', { 250 | staticClass: ['pvtTriangle'], 251 | on: { 252 | 'click': this.toggleFilterBox.bind(this) 253 | } 254 | }, ' ▾') : undefined, 255 | this.open ? this.getFilterBox(h) : undefined 256 | ] 257 | ) 258 | ]) 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /src/Dropdown.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: ['values', 'value'], 3 | model: { 4 | prop: 'value', 5 | event: 'input' 6 | }, 7 | created () { 8 | this.$emit('input', this.value || this.values[0]) 9 | }, 10 | methods: { 11 | handleChange (e) { 12 | this.$emit('input', e.target.value) 13 | } 14 | }, 15 | render (h) { 16 | return h('select', { 17 | staticClass: ['pvtDropdown'], 18 | domProps: { 19 | value: this.value 20 | }, 21 | on: { 22 | change: this.handleChange 23 | } 24 | }, 25 | [ 26 | this.values.map(r => { 27 | const text = r 28 | return h('option', { 29 | attrs: { 30 | value: r, 31 | selected: r === this.value ? 'selected' : undefined 32 | } 33 | }, text) 34 | }) 35 | ]) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Pivottable.js: -------------------------------------------------------------------------------- 1 | import TableRenderer from './TableRenderer' 2 | import defaultProps from './helper/common' 3 | export default { 4 | name: 'vue-pivottable', 5 | mixins: [ 6 | defaultProps 7 | ], 8 | computed: { 9 | rendererItems () { 10 | return this.renderers || Object.assign({}, TableRenderer) 11 | } 12 | }, 13 | methods: { 14 | createPivottable (h) { 15 | const props = this.$props 16 | return h(this.rendererItems[this.rendererName], { 17 | props: Object.assign( 18 | props, 19 | { localeStrings: props.locales[props.locale].localeStrings } 20 | ) 21 | }) 22 | }, 23 | createWrapperContainer (h) { 24 | return h('div', { 25 | style: { 26 | display: 'block', 27 | width: '100%', 28 | 'overflow-x': 'auto', 29 | 'max-width': this.tableMaxWidth ? `${this.tableMaxWidth}px` : undefined 30 | } 31 | }, [ 32 | this.createPivottable(h) 33 | ]) 34 | } 35 | }, 36 | render (h) { 37 | return this.createWrapperContainer(h) 38 | }, 39 | renderError (h, error) { 40 | return this.renderError(h) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/TableRenderer.js: -------------------------------------------------------------------------------- 1 | import { PivotData } from './helper/utils' 2 | import defaultProps from './helper/common' 3 | function redColorScaleGenerator (values) { 4 | const min = Math.min.apply(Math, values) 5 | const max = Math.max.apply(Math, values) 6 | return x => { 7 | // eslint-disable-next-line no-magic-numbers 8 | const nonRed = 255 - Math.round(255 * (x - min) / (max - min)) 9 | return { backgroundColor: `rgb(255,${nonRed},${nonRed})` } 10 | } 11 | } 12 | function makeRenderer (opts = {}) { 13 | const TableRenderer = { 14 | name: opts.name, 15 | mixins: [ 16 | defaultProps 17 | ], 18 | props: { 19 | heatmapMode: String, 20 | tableColorScaleGenerator: { 21 | type: Function, 22 | default: redColorScaleGenerator 23 | }, 24 | tableOptions: { 25 | type: Object, 26 | default: function () { 27 | return { 28 | clickCallback: null 29 | } 30 | } 31 | }, 32 | localeStrings: { 33 | type: Object, 34 | default: function () { 35 | return { 36 | totals: 'Totals' 37 | } 38 | } 39 | } 40 | }, 41 | methods: { 42 | spanSize (arr, i, j) { 43 | // helper function for setting row/col-span in pivotTableRenderer 44 | let x 45 | if (i !== 0) { 46 | let asc, end 47 | let noDraw = true 48 | for ( 49 | x = 0, end = j, asc = end >= 0; 50 | asc ? x <= end : x >= end; 51 | asc ? x++ : x-- 52 | ) { 53 | if (arr[i - 1][x] !== arr[i][x]) { 54 | noDraw = false 55 | } 56 | } 57 | if (noDraw) { 58 | return -1 59 | } 60 | } 61 | let len = 0 62 | while (i + len < arr.length) { 63 | let asc1, end1 64 | let stop = false 65 | for ( 66 | x = 0, end1 = j, asc1 = end1 >= 0; 67 | asc1 ? x <= end1 : x >= end1; 68 | asc1 ? x++ : x-- 69 | ) { 70 | if (arr[i][x] !== arr[i + len][x]) { 71 | stop = true 72 | } 73 | } 74 | if (stop) { 75 | break 76 | } 77 | len++ 78 | } 79 | return len 80 | } 81 | }, 82 | render (h) { 83 | let pivotData = null 84 | try { 85 | const props = Object.assign({}, 86 | this.$props, 87 | this.$attrs.props 88 | ) 89 | pivotData = new PivotData(props) 90 | } catch (error) { 91 | // eslint-disable-next-line no-console 92 | if (console && console.error(error.stack)) { 93 | return this.computeError(h) 94 | } 95 | } 96 | const colAttrs = pivotData.props.cols 97 | const rowAttrs = pivotData.props.rows 98 | const rowKeys = pivotData.getRowKeys() 99 | const colKeys = pivotData.getColKeys() 100 | const grandTotalAggregator = pivotData.getAggregator([], []) 101 | // eslint-disable-next-line no-unused-vars 102 | let valueCellColors = () => { } 103 | // eslint-disable-next-line no-unused-vars 104 | let rowTotalColors = () => { } 105 | // eslint-disable-next-line no-unused-vars 106 | let colTotalColors = () => { } 107 | if (opts.heatmapMode) { 108 | const colorScaleGenerator = this.tableColorScaleGenerator 109 | const rowTotalValues = colKeys.map(x => 110 | pivotData.getAggregator([], x).value() 111 | ) 112 | rowTotalColors = colorScaleGenerator(rowTotalValues) 113 | const colTotalValues = rowKeys.map(x => 114 | pivotData.getAggregator(x, []).value() 115 | ) 116 | colTotalColors = colorScaleGenerator(colTotalValues) 117 | 118 | if (opts.heatmapMode === 'full') { 119 | const allValues = [] 120 | rowKeys.map(r => 121 | colKeys.map(c => 122 | allValues.push(pivotData.getAggregator(r, c).value()) 123 | ) 124 | ) 125 | const colorScale = colorScaleGenerator(allValues) 126 | valueCellColors = (r, c, v) => colorScale(v) 127 | } else if (opts.heatmapMode === 'row') { 128 | const rowColorScales = {} 129 | rowKeys.map(r => { 130 | const rowValues = colKeys.map(x => 131 | pivotData.getAggregator(r, x).value() 132 | ) 133 | rowColorScales[r] = colorScaleGenerator(rowValues) 134 | }) 135 | valueCellColors = (r, c, v) => rowColorScales[r](v) 136 | } else if (opts.heatmapMode === 'col') { 137 | const colColorScales = {} 138 | colKeys.map(c => { 139 | const colValues = rowKeys.map(x => 140 | pivotData.getAggregator(x, c).value() 141 | ) 142 | colColorScales[c] = colorScaleGenerator(colValues) 143 | }) 144 | valueCellColors = (r, c, v) => colColorScales[c](v) 145 | } 146 | } 147 | const getClickHandler = (value, rowValues, colValues) => { 148 | const tableOptions = this.tableOptions 149 | if (tableOptions && tableOptions.clickCallback) { 150 | const filters = {} 151 | let attr = {} 152 | for (let i in colAttrs) { 153 | if (!colValues.hasOwnProperty(i)) continue 154 | attr = colAttrs[i] 155 | if (colValues[i] !== null) { 156 | filters[attr] = colValues[i] 157 | } 158 | } 159 | for (let i in rowAttrs) { 160 | if (!rowValues.hasOwnProperty(i)) continue 161 | attr = rowAttrs[i] 162 | if (rowValues[i] !== null) { 163 | filters[attr] = rowValues[i] 164 | } 165 | } 166 | return e => tableOptions.clickCallback(e, value, filters, pivotData) 167 | } 168 | } 169 | return h('table', { 170 | staticClass: ['pvtTable'] 171 | }, [ 172 | h('thead', 173 | [ 174 | colAttrs.map((c, j) => { 175 | return h('tr', { 176 | attrs: { 177 | key: `colAttrs${j}` 178 | } 179 | }, 180 | [ 181 | j === 0 && rowAttrs.length !== 0 ? h('th', { 182 | attrs: { 183 | colSpan: rowAttrs.length, 184 | rowSpan: colAttrs.length 185 | } 186 | }) : undefined, 187 | 188 | h('th', { 189 | staticClass: ['pvtAxisLabel'] 190 | }, c), 191 | 192 | colKeys.map((colKey, i) => { 193 | const x = this.spanSize(colKeys, i, j) 194 | if (x === -1) { 195 | return null 196 | } 197 | return h('th', { 198 | staticClass: ['pvtColLabel'], 199 | attrs: { 200 | key: `colKey${i}`, 201 | colSpan: x, 202 | rowSpan: j === colAttrs.length - 1 && rowAttrs.length !== 0 ? 2 : 1 203 | } 204 | }, colKey[j]) 205 | }), 206 | j === 0 && this.rowTotal ? h('th', { 207 | staticClass: ['pvtTotalLabel'], 208 | attrs: { 209 | rowSpan: colAttrs.length + (rowAttrs.length === 0 ? 0 : 1) 210 | } 211 | }, this.localeStrings.totals) : undefined 212 | ]) 213 | }), 214 | 215 | rowAttrs.length !== 0 ? h('tr', 216 | [ 217 | rowAttrs.map((r, i) => { 218 | return h('th', { 219 | staticClass: ['pvtAxisLabel'], 220 | attrs: { 221 | key: `rowAttr${i}` 222 | } 223 | }, r) 224 | }), 225 | 226 | this.rowTotal 227 | ? h('th', { staticClass: ['pvtTotalLabel'] }, colAttrs.length === 0 ? this.localeStrings.totals : null) 228 | : (colAttrs.length === 0 ? undefined : h('th', { staticClass: ['pvtTotalLabel'] }, null)) 229 | ] 230 | ) : undefined 231 | 232 | ] 233 | ), 234 | 235 | h('tbody', 236 | [ 237 | rowKeys.map((rowKey, i) => { 238 | const totalAggregator = pivotData.getAggregator(rowKey, []) 239 | return h('tr', { 240 | attrs: { 241 | key: `rowKeyRow${i}` 242 | } 243 | }, 244 | [ 245 | rowKey.map((text, j) => { 246 | const x = this.spanSize(rowKeys, i, j) 247 | if (x === -1) { 248 | return null 249 | } 250 | return h('th', { 251 | staticClass: ['pvtRowLabel'], 252 | attrs: { 253 | key: `rowKeyLabel${i}-${j}`, 254 | rowSpan: x, 255 | colSpan: j === rowAttrs.length - 1 && colAttrs.length !== 0 ? 2 : 1 256 | } 257 | }, text) 258 | }), 259 | 260 | colKeys.map((colKey, j) => { 261 | const aggregator = pivotData.getAggregator(rowKey, colKey) 262 | return h('td', { 263 | staticClass: ['pvVal'], 264 | style: valueCellColors(rowKey, colKey, aggregator.value()), 265 | attrs: { 266 | key: `pvtVal${i}-${j}` 267 | }, 268 | on: this.tableOptions.clickCallback ? { 269 | click: getClickHandler(aggregator.value(), rowKey, colKey) 270 | } : {} 271 | }, aggregator.format(aggregator.value())) 272 | }), 273 | 274 | this.rowTotal ? h('td', { 275 | staticClass: ['pvtTotal'], 276 | style: colTotalColors(totalAggregator.value()), 277 | on: this.tableOptions.clickCallback ? { 278 | click: getClickHandler(totalAggregator.value(), rowKey, []) 279 | } : {} 280 | }, totalAggregator.format(totalAggregator.value())) : undefined 281 | ]) 282 | }), 283 | 284 | h('tr', 285 | [ 286 | this.colTotal ? h('th', { 287 | staticClass: ['pvtTotalLabel'], 288 | attrs: { 289 | colSpan: rowAttrs.length + (colAttrs.length === 0 ? 0 : 1) 290 | } 291 | }, this.localeStrings.totals) : undefined, 292 | 293 | this.colTotal ? colKeys.map((colKey, i) => { 294 | const totalAggregator = pivotData.getAggregator([], colKey) 295 | return h('td', { 296 | staticClass: ['pvtTotal'], 297 | style: rowTotalColors(totalAggregator.value()), 298 | attrs: { 299 | key: `total${i}` 300 | }, 301 | on: this.tableOptions.clickCallback ? { 302 | click: getClickHandler(totalAggregator.value(), [], colKey) 303 | } : {} 304 | }, totalAggregator.format(totalAggregator.value())) 305 | }) : undefined, 306 | 307 | this.colTotal && this.rowTotal ? h('td', { 308 | staticClass: ['pvtGrandTotal'], 309 | on: this.tableOptions.clickCallback ? { 310 | click: getClickHandler(grandTotalAggregator.value(), [], []) 311 | } : {} 312 | }, grandTotalAggregator.format(grandTotalAggregator.value())) : undefined 313 | ] 314 | ) 315 | ]) 316 | 317 | ]) 318 | }, 319 | renderError (h, error) { 320 | return this.renderError(h) 321 | } 322 | } 323 | return TableRenderer 324 | } 325 | 326 | const TSVExportRenderer = { 327 | name: 'tsv-export-renderers', 328 | mixins: [defaultProps], 329 | render (h) { 330 | let pivotData = null 331 | try { 332 | const props = Object.assign({}, 333 | this.$props, 334 | this.$attrs.props 335 | ) 336 | pivotData = new PivotData(props) 337 | } catch (error) { 338 | // eslint-disable-next-line no-console 339 | if (console && console.error(error.stack)) { 340 | return this.computeError(h) 341 | } 342 | } 343 | const rowKeys = pivotData.getRowKeys() 344 | const colKeys = pivotData.getColKeys() 345 | if (rowKeys.length === 0) { 346 | rowKeys.push([]) 347 | } 348 | if (colKeys.length === 0) { 349 | colKeys.push([]) 350 | } 351 | const headerRow = pivotData.props.rows.map(r => r) 352 | if (colKeys.length === 1 && colKeys[0].length === 0) { 353 | headerRow.push(this.aggregatorName) 354 | } else { 355 | colKeys.map(c => headerRow.push(c.join('-'))) 356 | } 357 | 358 | const result = rowKeys.map(r => { 359 | const row = r.map(x => x) 360 | colKeys.map(c => { 361 | const v = pivotData.getAggregator(r, c).value() 362 | row.push(v || '') 363 | }) 364 | return row 365 | }) 366 | result.unshift(headerRow) 367 | return h('textarea', { 368 | style: { 369 | width: `100%`, 370 | height: `${window.innerHeight / 2}px` 371 | }, 372 | attrs: { 373 | readOnly: true 374 | }, 375 | domProps: { 376 | value: result.map(r => r.join('\t')).join('\n') 377 | } 378 | }) 379 | }, 380 | renderError (h, error) { 381 | return this.renderError(h) 382 | } 383 | } 384 | 385 | export default { 386 | Table: makeRenderer({ name: 'vue-table' }), 387 | 'Table Heatmap': makeRenderer({ heatmapMode: 'full', name: 'vue-table-heatmap' }), 388 | 'Table Col Heatmap': makeRenderer({ heatmapMode: 'col', name: 'vue-table-col-heatmap' }), 389 | 'Table Row Heatmap': makeRenderer({ heatmapMode: 'row', name: 'vue-table-col-heatmap' }), 390 | 'Export Table TSV': TSVExportRenderer 391 | } 392 | -------------------------------------------------------------------------------- /src/assets/vue-pivottable.css: -------------------------------------------------------------------------------- 1 | 2 | * { 3 | box-sizing: border-box 4 | } 5 | 6 | .pvtUi { 7 | table-layout: fixed; 8 | width: 100%; 9 | color: #2a3f5f; 10 | font-family: Verdana; 11 | border-collapse: collapse; 12 | } 13 | .pvtUi select { 14 | user-select: none; 15 | -webkit-user-select: none; 16 | -moz-user-select: none; 17 | -khtml-user-select: none; 18 | -ms-user-select: none; 19 | border-radius: 5px ; 20 | } 21 | .pvtUi td { 22 | overflow-x: auto; 23 | } 24 | .pvtUi td.pvtOutput { 25 | padding: 6px; 26 | vertical-align: top; 27 | border: 1px solid #a2b1c6; 28 | overflow-x: auto; 29 | } 30 | 31 | table.pvtTable { 32 | font-size: 8pt; 33 | text-align: left; 34 | border-collapse: collapse; 35 | font-family: Verdana; 36 | } 37 | 38 | table.pvtTable thead tr th, 39 | table.pvtTable tbody tr th { 40 | background-color: #ebf0f8; 41 | border: 1px solid #c8d4e3; 42 | font-size: 8pt; 43 | padding: 5px; 44 | } 45 | 46 | table.pvtTable .pvtColLabel { 47 | text-align: center; 48 | } 49 | table.pvtTable .pvtTotalLabel { 50 | text-align: right; 51 | } 52 | 53 | table.pvtTable tbody tr td { 54 | color: #2a3f5f; 55 | padding: 3px; 56 | background-color: #fff; 57 | border: 1px solid #c8d4e3; 58 | vertical-align: top; 59 | text-align: right; 60 | } 61 | 62 | .pvtTotal, 63 | .pvtGrandTotal { 64 | font-weight: bold; 65 | } 66 | 67 | .pvtRowOrder, 68 | .pvtColOrder { 69 | cursor: pointer; 70 | width: 15px; 71 | margin-left: 5px; 72 | display: inline-block; 73 | user-select: none; 74 | text-decoration: none !important; 75 | -webkit-user-select: none; 76 | -moz-user-select: none; 77 | -khtml-user-select: none; 78 | -ms-user-select: none; 79 | } 80 | 81 | .pvtAxisContainer, 82 | .pvtVals { 83 | border: 1px solid #a2b1c6; 84 | background: #fff; 85 | padding: 5px; 86 | min-width: 20px; 87 | min-height: 20px; 88 | } 89 | .pvtVals.pvtText { 90 | vertical-align: middle; 91 | font-size: 15px; 92 | font-weight: bold; 93 | color: #000000; 94 | user-select: none; 95 | padding-left: 16px; 96 | text-align: start; 97 | } 98 | 99 | .pvtRenderers { 100 | border: 1px solid #a2b1c6; 101 | user-select: none; 102 | width: 25%; 103 | } 104 | @media screen and (max-width:576px) { 105 | .pvtRenderers { 106 | width: 100%; 107 | } 108 | .pvtAxisContainer{ 109 | display: none; 110 | } 111 | } 112 | @media screen and (width: 768px) { 113 | .pvtRenderers { 114 | width: 40%; 115 | } 116 | } 117 | .pvtDropdown { 118 | border: 1px solid #a2b1c6; 119 | align-items: center; 120 | display: block; 121 | background-color: transparent; 122 | background-image: none; 123 | border-radius: 0px; 124 | padding: 0px 0px 0px 3px; 125 | height: 40px; 126 | font-size: 15px; 127 | font-weight: bold; 128 | outline: none; 129 | line-height: 1 !important; 130 | margin: 3px; 131 | user-select: none; 132 | -webkit-user-select: none; 133 | -moz-user-select: none; 134 | -khtml-user-select: none; 135 | -ms-user-select: none; 136 | } 137 | 138 | .pvtRenderers .pvtDropdown { 139 | padding: 0px 0px 0px 12px; 140 | border: none; 141 | width: 100%; 142 | margin: 0px; 143 | } 144 | 145 | .pvtDropdown:last-child { 146 | margin-bottom: 0px; 147 | } 148 | 149 | .pvtVals { 150 | white-space: nowrap; 151 | vertical-align: top; 152 | padding-bottom: 12px; 153 | } 154 | 155 | .pvtRows { 156 | height: 35px; 157 | } 158 | 159 | .pvtAxisContainer li { 160 | padding: 8px 6px; 161 | list-style-type: none; 162 | cursor: move; 163 | } 164 | .pvtAxisContainer li.pvtPlaceholder { 165 | -webkit-border-radius: 5px; 166 | padding: 3px 15px; 167 | -moz-border-radius: 5px; 168 | border-radius: 5px; 169 | border: 1px dashed #a2b1c6; 170 | } 171 | .pvtAxisContainer li.pvtPlaceholder span.pvtAttr { 172 | display: none; 173 | } 174 | 175 | .pvtAxisContainer li span.pvtAttr { 176 | -webkit-text-size-adjust: 100%; 177 | background: #f3f6fa; 178 | border: 2px dashed #c8d4e3; 179 | padding: 4px 7px; 180 | white-space: nowrap; 181 | -webkit-border-radius: 5px; 182 | -moz-border-radius: 5px; 183 | border-radius: 5px; 184 | user-select: none; 185 | -webkit-user-select: none; 186 | -moz-user-select: none; 187 | -khtml-user-select: none; 188 | -ms-user-select: none; 189 | } 190 | .pvtAxisContainer li span.pvtAttr.sortonly { 191 | border: 1px solid #c8d4e3; 192 | } 193 | .pvtAxisContainer li span.pvtAttr.disabled { 194 | border: 1px solid #c8d4e3; 195 | color: #c5c5c5; 196 | cursor: not-allowed; 197 | } 198 | .pvtTriangle { 199 | cursor: pointer; 200 | color: #506784; 201 | } 202 | 203 | .pvtHorizList li { 204 | display: inline-block; 205 | } 206 | 207 | .pvtVertList { 208 | vertical-align: top; 209 | } 210 | 211 | .pvtFilteredAttribute { 212 | font-style: italic; 213 | } 214 | 215 | .sortable-chosen .pvtFilterBox { 216 | display: none !important; 217 | } 218 | 219 | .pvtFilterTextClear { 220 | position: absolute; 221 | right: 5px; 222 | top: 5px; 223 | font-size: 18px; 224 | cursor: pointer; 225 | text-decoration: none !important; 226 | } 227 | 228 | .pvtButton { 229 | display: inline-block; 230 | color: #506784; 231 | border-radius: 5px; 232 | padding: 3px 6px; 233 | background: #fff; 234 | border: 1px solid; 235 | border-color: #c8d4e3; 236 | font-size: 14px; 237 | margin: 3px 3px 6px 0px; 238 | transition: 0.34s all cubic-bezier(0.19, 1, 0.22, 1); 239 | text-decoration: none !important; 240 | } 241 | 242 | .pvtButton:hover { 243 | background: #e2e8f0; 244 | border-color: #a2b1c6; 245 | } 246 | 247 | .pvtButton:active { 248 | background: #d1dae6; 249 | } 250 | 251 | .pvtFilterBox input { 252 | border: 1px solid #c8d4e3; 253 | border-radius: 5px; 254 | color: #506784; 255 | padding: 0 3px; 256 | font-size: 14px; 257 | } 258 | 259 | .pvtFilterBox input:focus { 260 | border-color: #119dff; 261 | outline: none; 262 | } 263 | 264 | .pvtFilterBox { 265 | z-index: 100; 266 | border: 1px solid #506784; 267 | max-width: 600px; 268 | min-width: 210px; 269 | background-color: #fff; 270 | position: absolute; 271 | padding: 6px; 272 | user-select: none; 273 | min-height: 100px; 274 | -webkit-user-select: none; 275 | -moz-user-select: none; 276 | -khtml-user-select: none; 277 | -ms-user-select: none; 278 | } 279 | 280 | .pvtFilterBox input[type='text'] { 281 | display: block; 282 | width: 100%; 283 | color: #2a3f5f; 284 | margin-bottom: 5px; 285 | padding: 6px; 286 | } 287 | 288 | .pvtFilterBox { 289 | padding: 3px; 290 | } 291 | 292 | .pvtCheckContainer { 293 | text-align: left; 294 | font-size: 14px; 295 | white-space: nowrap; 296 | overflow-y: scroll; 297 | width: 100%; 298 | max-height: 30vh; 299 | border-top: 1px solid #dfe8f3; 300 | } 301 | 302 | .pvtCheckContainer p { 303 | margin: 0; 304 | margin-bottom: 1px; 305 | padding: 3px; 306 | cursor: default; 307 | } 308 | 309 | .pvtCheckContainer p.selected { 310 | background: #ebf0f8; 311 | min-width: 100%; 312 | width: max-content; 313 | } 314 | 315 | .pvtOnly { 316 | display: none; 317 | width: 35px; 318 | float: right; 319 | font-size: 12px; 320 | padding-left: 5px; 321 | cursor: pointer; 322 | } 323 | 324 | .pvtOnlySpacer { 325 | display: block; 326 | width: 35px; 327 | float: right; 328 | } 329 | 330 | .pvtCheckContainer p:hover .pvtOnly { 331 | display: block; 332 | } 333 | 334 | .pvtCheckContainer p:hover .pvtOnlySpacer { 335 | display: none; 336 | } 337 | 338 | .pvtRendererArea { 339 | padding: 5px; 340 | } 341 | -------------------------------------------------------------------------------- /src/helper/common.js: -------------------------------------------------------------------------------- 1 | import { aggregators, locales } from './utils' 2 | 3 | export default { 4 | props: { 5 | data: { 6 | type: [Array, Object, Function], 7 | required: true 8 | }, 9 | aggregators: { 10 | type: Object, 11 | default: function () { 12 | return aggregators 13 | } 14 | }, 15 | aggregatorName: { 16 | type: String, 17 | default: 'Count' 18 | }, 19 | heatmapMode: String, 20 | tableColorScaleGenerator: { 21 | type: Function 22 | }, 23 | tableOptions: { 24 | type: Object, 25 | default: function () { 26 | return {} 27 | } 28 | }, 29 | renderers: Object, 30 | rendererName: { 31 | type: String, 32 | default: 'Table' 33 | }, 34 | locale: { 35 | type: String, 36 | default: 'en' 37 | }, 38 | locales: { 39 | type: Object, 40 | default: function () { 41 | return locales 42 | } 43 | }, 44 | rowTotal: { 45 | type: Boolean, 46 | default: true 47 | }, 48 | colTotal: { 49 | type: Boolean, 50 | default: true 51 | }, 52 | cols: { 53 | type: Array, 54 | default: function () { 55 | return [] 56 | } 57 | }, 58 | rows: { 59 | type: Array, 60 | default: function () { 61 | return [] 62 | } 63 | }, 64 | vals: { 65 | type: Array, 66 | default: function () { 67 | return [] 68 | } 69 | }, 70 | attributes: { 71 | type: Array, 72 | default: function () { 73 | return [] 74 | } 75 | }, 76 | valueFilter: { 77 | type: Object, 78 | default: function () { 79 | return {} 80 | } 81 | }, 82 | sorters: { 83 | type: [Function, Object], 84 | default: function () { 85 | return {} 86 | } 87 | }, 88 | derivedAttributes: { 89 | type: [Function, Object], 90 | default: function () { 91 | return {} 92 | } 93 | }, 94 | rowOrder: { 95 | type: String, 96 | default: 'key_a_to_z', 97 | validator: function (value) { 98 | return ['key_a_to_z', 'value_a_to_z', 'value_z_to_a'].indexOf(value) !== -1 99 | } 100 | }, 101 | colOrder: { 102 | type: String, 103 | default: 'key_a_to_z', 104 | validator: function (value) { 105 | return ['key_a_to_z', 'value_a_to_z', 'value_z_to_a'].indexOf(value) !== -1 106 | } 107 | }, 108 | tableMaxWidth: { 109 | type: Number, 110 | default: 0, 111 | validator: function (value) { 112 | return value >= 0 113 | } 114 | }, 115 | colLimit: { 116 | type: Number, 117 | default: 100 118 | }, 119 | rowLimit: { 120 | type: Number, 121 | default: 100 122 | } 123 | }, 124 | methods: { 125 | renderError (h) { 126 | return h('span', (this.locales[this.locale].localeStrings.renderError) || 'An error occurred rendering the PivotTable results.') 127 | }, 128 | computeError (h) { 129 | return h('span', (this.locales[this.locale].localeStrings.computeError) || 'An error occurred computing the PivotTable results.') 130 | }, 131 | uiRenderError (h) { 132 | return h('span', (this.locales[this.locale].localeStrings.uiRenderError) || 'An error occurred rendering the PivotTable UI.') 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import VuePivottable from './Pivottable' 2 | import VuePivottableUi from './PivottableUi' 3 | import TableRenderer from './TableRenderer' 4 | 5 | import { 6 | aggregatorTemplates, 7 | aggregators, 8 | derivers, 9 | locales, 10 | naturalSort, 11 | numberFormat, 12 | getSort, 13 | sortAs, 14 | PivotData 15 | } from './helper/utils' 16 | 17 | const PivotUtilities = { 18 | aggregatorTemplates, 19 | aggregators, 20 | derivers, 21 | locales, 22 | naturalSort, 23 | numberFormat, 24 | getSort, 25 | sortAs, 26 | PivotData 27 | } 28 | 29 | const Renderer = { 30 | TableRenderer 31 | } 32 | 33 | const components = { 34 | VuePivottable, 35 | VuePivottableUi 36 | } 37 | 38 | if (typeof window !== 'undefined' && window.Vue) window.Vue.use(VuePivottable) 39 | 40 | export { 41 | VuePivottable, 42 | VuePivottableUi, 43 | PivotUtilities, 44 | Renderer 45 | } 46 | 47 | export default (Vue) => { 48 | for (const key in components) { 49 | Vue.component(components[key].name, components[key]) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import { resolve } from 'path' 3 | import { viteStaticCopy } from 'vite-plugin-static-copy' 4 | import vue from '@vitejs/plugin-vue2' 5 | 6 | export default defineConfig(({ command, mode, ssrBuild }) => { 7 | return { 8 | plugins: [ 9 | vue(), 10 | viteStaticCopy({ 11 | targets: [ 12 | { 13 | src: 'src/assets/vue-pivottable.css', 14 | dest: '.' 15 | } 16 | ] 17 | }) 18 | ], 19 | publicDir: false, 20 | build: { 21 | type: ['es', 'umd'], 22 | lib: { 23 | entry: resolve(__dirname, 'src/index.js'), 24 | name: 'VuePivottable', 25 | fileName: 'vue-pivottable' 26 | }, 27 | rollupOptions: { 28 | external: ['vue'], 29 | output: { 30 | exports: 'named', 31 | globals: { 32 | 'vue': 'Vue' 33 | } 34 | } 35 | } 36 | } 37 | } 38 | }) 39 | --------------------------------------------------------------------------------