├── .eslintrc.cjs ├── .github └── workflows │ └── npm-publish.yml ├── .gitignore ├── .npmignore ├── .vscode └── extensions.json ├── README.md ├── cypress.json ├── cypress ├── fixtures │ └── example.json ├── integration │ └── example.spec.ts ├── plugins │ ├── index.ts │ └── tsconfig.json ├── support │ ├── commands.ts │ └── index.ts └── tsconfig.json ├── env.d.ts ├── index.html ├── package-lock.json ├── package.json ├── packages ├── .npmrc ├── README.md ├── build │ ├── index.cjs.js │ └── index.es.js ├── index.js ├── package.json ├── vuenique-group │ ├── Group.vue │ ├── __tests__ │ │ └── Group.spec.ts │ └── index.ts ├── vuenique-legend │ ├── index.ts │ ├── legends │ │ ├── Legend │ │ │ ├── LegendItem.vue │ │ │ ├── LegendLabel.vue │ │ │ ├── LegendShape.vue │ │ │ ├── Threshold.vue │ │ │ └── index.vue │ │ ├── Linear.vue │ │ ├── Ordinal.vue │ │ ├── Quantile.vue │ │ └── Size.vue │ ├── types │ │ └── index.ts │ └── utility │ │ ├── defaultDomain.ts │ │ ├── identity.ts │ │ ├── labelTransformFactory.ts │ │ └── valueOrIdentity.ts ├── vuenique-scale │ ├── __tests__ │ │ ├── band.spec.ts │ │ ├── scaleLinear.spec.ts │ │ ├── scaleLog.spec.ts │ │ ├── scaleOrdinal.spec.ts │ │ ├── scaleQuantile.spec.ts │ │ ├── scaleThreshold.spec.ts │ │ ├── scaleUtc.spec.ts │ │ └── time.spec.ts │ ├── index.ts │ ├── operators │ │ ├── align.ts │ │ ├── base.ts │ │ ├── clamp.ts │ │ ├── domain.ts │ │ ├── interpolate.ts │ │ ├── nice.ts │ │ ├── padding.ts │ │ ├── range.ts │ │ ├── reverse.ts │ │ ├── round.ts │ │ ├── scaleOperator.ts │ │ ├── unknown.ts │ │ └── zero.ts │ ├── scales │ │ ├── band.ts │ │ ├── linear.ts │ │ ├── log.ts │ │ ├── ordinal.ts │ │ ├── quantile.ts │ │ ├── threshold.ts │ │ ├── time.ts │ │ └── utc.ts │ └── utils │ │ ├── coerceNumber.ts │ │ ├── createColorInterpolator.ts │ │ ├── getTicks.ts │ │ ├── inferScaleType.ts │ │ ├── isUtcScale.ts │ │ ├── scaleCanBeZeroed.ts │ │ └── toString.ts └── vuenique-shape │ ├── Bar.vue │ ├── Circle.vue │ ├── LinePath.vue │ ├── __tests__ │ ├── Bar.spec.ts │ ├── Circle.spec.ts │ └── LinePath.spec.ts │ ├── index.ts │ ├── types │ ├── D3ShapeConfig.ts │ ├── accessor.ts │ └── index.ts │ └── utils │ ├── D3ShapeFactory.ts │ └── setNumberOrNumberAccessor.ts ├── src ├── App.vue ├── assets │ ├── base.css │ ├── logo.svg │ ├── logo_gradient.svg │ └── placeholderLogo.svg ├── components │ ├── BarGraph.vue │ ├── LineGraph.vue │ ├── Scatter.vue │ └── icons │ │ ├── IconCommunity.vue │ │ ├── IconDocumentation.vue │ │ ├── IconEcosystem.vue │ │ ├── IconSupport.vue │ │ └── IconTooling.vue └── main.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.vite-config.json ├── tsconfig.vitest.json └── vite.config.ts /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require("@rushstack/eslint-patch/modern-module-resolution"); 3 | 4 | module.exports = { 5 | root: true, 6 | extends: [ 7 | "plugin:vue/vue3-essential", 8 | "eslint:recommended", 9 | "@vue/eslint-config-typescript/recommended", 10 | "@vue/eslint-config-prettier", 11 | ], 12 | env: { 13 | "vue/setup-compiler-macros": true, 14 | }, 15 | overrides: [ 16 | { 17 | files: ["cypress/integration/**.spec.{js,ts,jsx,tsx}"], 18 | extends: ["plugin:cypress/recommended"], 19 | }, 20 | ], 21 | }; 22 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to NPM 2 | on: 3 | release: 4 | types: [created] 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v2 11 | - name: Setup Node 12 | uses: actions/setup-node@v2 13 | with: 14 | node-version: "14.x" 15 | registry-url: "https://registry.npmjs.org" 16 | - name: Install dependencies and build 17 | run: npm ci --ignore-scripts && npm run build 18 | - name: Run tests 19 | run: npm run test:unit 20 | - name: Publish package on NPM 21 | run: | 22 | cd packages 23 | npm publish --access public 24 | env: 25 | NODE_AUTH_TOKEN: ${{ secrets.VUENIQUE_CI }} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | .env -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["johnsoncodehk.volar", "johnsoncodehk.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 |

4 | Logo 5 |

Vuenique

6 | 7 |

8 | A low level visualization component library built for Vue 9 |

10 | 11 | 12 |

13 | 14 | GitHub release (latest by date including pre-releases) 15 | 16 | GitHub stars 17 | 18 | GitHub forks 19 |
20 | 21 | GitHub issues 22 | 23 | Contributions welcome 24 | 25 |

26 |

27 | 28 |
29 | 30 | 31 |
32 | Table of Contents 33 |
    34 |
  1. Introduction
  2. 35 |
  3. Installation
  4. 36 |
  5. Getting Started
  6. 37 |
  7. 38 | Documentation 39 | 45 |
  8. 46 |
  9. Contributing
  10. 47 |
  11. License
  12. 48 |
  13. Creators
  14. 49 |
  15. Acknowledgments
  16. 50 |
51 |
52 | 53 |
54 | 55 | ## Introduction 56 | 57 | Vuenique is a reusable low-level component visualization library that brings the power of D3 and visx to Vue 58 | 59 | ## Installation 60 | 61 | Install with npm: 62 | 63 | ```sh 64 | npm install @vueniquejs/vuenique 65 | ``` 66 | 67 | ## Getting Started 68 | 69 | 1. Import the components you need from the package. 70 | 71 | ```js 72 | import { Bar } from @vueniquejs/vuenique 73 | ``` 74 | 75 | 2. Pass in the necessary props. 76 | 77 | ```js 78 | 79 | ``` 80 | 81 | 3. See your component render! 82 | 83 | Check out our gallery for more examples of what you can build. 84 | 85 | ## Documentation 86 | 87 | Vuenique visualization components can be roughly grouped into several categories: 88 | 89 | ### Shape 90 | 91 | These are the building blocks of the framework. Map to shapes you can utilize within your visualizations. 92 | 93 | #### `` 94 | 95 | **Description:** Staple of the bar chart, the component represents a single rectangular bar in your visualization. 96 |
97 | 98 | **Inputs:**
99 | _(Optional):_ 100 | 101 | - className: class to be passed into components class 102 | - any valid attributes (x, y, rx, ry, width, height, etc.) for SVG < rect> elements can be passed through props 103 |
104 | 105 | **Outputs:** Returns a Vue component wrapped around a < rect> HTML element with any props passed in rendering as attributes on the element 106 | 107 | #### `` 108 | 109 | **Description:** Staple of the scatter plot and other chart types utilizing circles, the component represents a single circle in your visualization 110 |
111 | 112 | **Inputs:**
113 | _(Optional):_ 114 | 115 | - className: class to be passed into components class 116 | - any valid attributes (cx, cy, r, etc.) for SVG < circle> elements can be passed through props 117 |
118 | 119 | **Outputs:** Returns a Vue component wrapped around a < circle> HTML element with any props passed in rendering as attributes on the element 120 | 121 | #### `` 122 | 123 | **Description:** Staple of the line chart, the component represents a single line path in your visualization 124 |
125 | 126 | **Inputs:**
127 | _(Optional):_ 128 | 129 | - className: class to be passed into components class 130 | - any valid attributes for SVG < path> elements can be passed through props 131 |
132 | 133 | **Outputs:** Returns a Vue component wrapped around a < path> HTML element with any props passed in 134 | 135 |
136 | 137 | ### Group 138 | 139 | Built around the < g> SVG element, Group acts as a wrapper around Shape components, allowing you to group pieces of a visualization together. 140 | 141 | #### `` 142 | 143 | **Inputs:** Props get passed to underlying < g> SVG element are rendered as HTML attributes on element. Components passed as children to Group get rendered within < g> container 144 |
145 | 146 | **Outputs:** Container of Shape components passed a children in the form of < g> element 147 | 148 |
149 | 150 | ### Scale 151 | 152 | Scales package provides wrappers around D3.js library scale packages, which contains functions that help you to map your data into the graph's pixels. 153 | 154 | #### `scaleBand()` 155 | 156 | **Description:** Provides a wrapper function around the D3 scaleBand() function, projecting discrete values into uniform bands of range. 157 |
158 | 159 | **Inputs:** 160 | 161 | - Config: scale object that optionally contains domain, range, round, reverse, align, padding properties. 162 |
163 | 164 | **Outputs:** Returns an inner function that passes the invocation d3 scaleBand() function and the config object, which will create a new band scale and set the properties according to user input properties in the config object. 165 | 166 | #### `scaleLinear()` 167 | 168 | **Description:** Provides a wrapper function around the D3 scaleLinear() function, project continuous input values(domain) to out values(range) 169 |
170 | 171 | **Inputs:** 172 | 173 | - Config: scale object that optionally contains domain, range, reverse, clamp, interpolate, nice, round, zero, properties. 174 |
175 | 176 | **Outputs:** Returns an inner function that passes the invocation d3 scaleLinear() function and the config object, which will create a new linear scale and set the properties according to user input properties in the config object. 177 | 178 | #### `scaleTime()` 179 | 180 | **Description:** Applies D3 scaleTime function to a configuration object to return a time scale. 181 |
182 | 183 | **Inputs:** 184 | 185 | - Config: user inputed scale object that accepts the domain, range, reverse, clamp, interpolate, nice, and round properties. 186 |
187 | 188 | #### `scaleUTC()` 189 | 190 | **Description:** Applies D3 scaleUtc function to a configuration object to return a time scale. 191 |
192 | 193 | **Inputs:** 194 | 195 | - Config: user inputed scale object that accepts the domain, range, reverse, clamp, interpolate, nice, and round properties. 196 |
197 | 198 | #### `scaleOrdinal()` 199 | 200 | **Description:** Provides a wrapper function around the D3 scaleOrdinal() function, project input values(domain) to out values(range) 201 |
202 | 203 | **Inputs:** 204 | 205 | - Config: scale object that optionally contains domain, range, reverse and unknown properties. 206 |
207 | 208 | **Outputs:** Returns an inner function that passes the invocation d3 scaleOrdinal() function and the config object, which will create a new ordinal scale and set the properties according to user input properties in the config object. 209 | 210 | #### `scaleLog()` 211 | 212 | **Description:** Applies D3 scaleLog function to a configuration object to return a log scale. 213 |
214 | 215 | **Inputs:** 216 | 217 | - Config: user inputed scale object that accepts the domain, range, base, reverse, clamp, interpolate, nice, and round properties. 218 |
219 | 220 | #### `scaleQuantile()` 221 | 222 | **Description:** Applies D3 scaleQuantile function to a configuration object to return a quantile scale. 223 |
224 | 225 | **Inputs:** 226 | 227 | - Config: user inputed scale object that accepts the domain, range, and reverse properties. 228 |
229 | 230 | #### `scaleThreshold()` 231 | 232 | **Description:** Provides a wrapper function around the D3 scaleThreshold() function, allowing to map arbitrary subsets of the domain to discrete values to the range. Continuous domain values are divided into pieces based on a set of threshold values. 233 |
234 | 235 | **Inputs:** 236 | 237 | - Config: scale config object that optionally contains domain, range, and reverse properties. 238 |
239 | 240 | **Outputs:** Returns an inner function that passes the invocation d3 scaleThreshold() function and the config object, which will create a new ordinal scale and set the properties according to user input properties in the config object. 241 | 242 |
243 | 244 | ### Legend 245 | 246 | Legends match the colors and shapes in your graph to your data, and are created using scales. 247 | 248 | #### `` 249 | 250 | **Description:** Renders a legend with values that increment in a linear fashion
251 | **Inputs:**
252 | _(Required):_ 253 | 254 | - scale: scale object generated from scale functions, used to create the legend items
255 | 256 | _(Optional):_ 257 | 258 | - style: styles applied to the legend container 259 | - domain: legend domain, a default is calculated if none is provided 260 | - shapeWidth: width of the legend shape 261 | - shapeHeight: height of the legend shape 262 | - shapeMargin: margin of the legend shape 263 | - labelAlign: flex-box alignment of legend item labels 264 | - labelFlex: flex-box flex of legend item labels 265 | - labelMargin: margin of legend item labels 266 | - itemMargin: margin of legend items 267 | - direction: flex direction of legend container 268 | - itemDirection: flex direction of legend items 269 | - fill: legend item fill accessor function 270 | - size: legend item size accessor function 271 | - shape: legend shape string preset 272 | - shapeStyle: styles applied to legend shapes 273 | - labelFormat: callback function that returns an item label from a provided label object 274 | - labelTransform: given the legend scale and labelFormat, returns a callback function that generates a label object containing its datum, index, value, and label properties 275 | - legendLabelProps: additional props that are passed down to LegendLabel component 276 | - steps: number of legend items 277 | 278 |
279 | 280 | ## Contributing 281 | 282 | Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. 283 | If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again! 284 | 285 | 1. Fork the Project 286 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 287 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 288 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 289 | 5. Open a Pull Request 290 | 291 | ## License 292 | 293 | Distributed under the MIT License. 294 | 295 | ## Creators 296 | 297 | 304 | 305 | ## Acknowledgments 306 | 307 | https://airbnb.io/visx 308 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "http://localhost:5050" 3 | } 4 | -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /cypress/integration/example.spec.ts: -------------------------------------------------------------------------------- 1 | // https://docs.cypress.io/api/introduction/api.html 2 | 3 | describe("My First Test", () => { 4 | it("visits the app root url", () => { 5 | cy.visit("/"); 6 | cy.contains("h1", "You did it!"); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /cypress/plugins/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | // *********************************************************** 3 | // This example plugins/index.ts can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | export default ((on, config) => { 16 | // `on` is used to hook into various events Cypress emits 17 | // `config` is the resolved Cypress config 18 | return config; 19 | }) as Cypress.PluginConfig; 20 | -------------------------------------------------------------------------------- /cypress/plugins/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.node.json", 3 | "include": ["./**/*"], 4 | "compilerOptions": { 5 | "module": "CommonJS", 6 | "preserveValueImports": false, 7 | "types": ["node", "cypress/types/cypress"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /cypress/support/commands.ts: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /cypress/support/index.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import "./commands"; 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.web.json", 3 | "include": ["./integration/**/*", "./support/**/*"], 4 | "compilerOptions": { 5 | "isolatedModules": false, 6 | "target": "es5", 7 | "lib": ["es5", "dom"], 8 | "types": ["cypress"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuenique", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vue-tsc --noEmit && vite build", 7 | "preview": "vite preview --port 5050", 8 | "test:unit": "vitest --environment jsdom", 9 | "test:e2e": "start-server-and-test preview http://127.0.0.1:5050/ 'cypress open'", 10 | "test:e2e:ci": "start-server-and-test preview http://127.0.0.1:5050/ 'cypress run'", 11 | "typecheck": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false", 12 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore" 13 | }, 14 | "dependencies": { 15 | "@vueniquejs/vuenique": "file:packages", 16 | "d3-array": "^3.1.3", 17 | "d3-interpolate": "^3.0.1", 18 | "d3-scale": "^4.0.2", 19 | "d3-shape": "^3.1.0", 20 | "d3-time": "^3.0.0", 21 | "timezone-mock": "^1.3.1", 22 | "vue": "^3.2.31" 23 | }, 24 | "devDependencies": { 25 | "@rushstack/eslint-patch": "^1.1.0", 26 | "@types/d3-array": "^3.0.2", 27 | "@types/d3-scale": "^4.0.2", 28 | "@types/d3-shape": "^3.0.2", 29 | "@types/jsdom": "^16.2.14", 30 | "@types/node": "^16.11.25", 31 | "@vitejs/plugin-vue": "^2.2.2", 32 | "@vitejs/plugin-vue-jsx": "^1.3.7", 33 | "@vue/eslint-config-prettier": "^7.0.0", 34 | "@vue/eslint-config-typescript": "^10.0.0", 35 | "@vue/test-utils": "^2.0.0-rc.18", 36 | "@vue/tsconfig": "^0.1.3", 37 | "cypress": "^9.5.0", 38 | "eslint": "^8.5.0", 39 | "eslint-plugin-cypress": "^2.12.1", 40 | "eslint-plugin-vue": "^8.2.0", 41 | "jsdom": "^19.0.0", 42 | "prettier": "^2.5.1", 43 | "start-server-and-test": "^1.14.0", 44 | "typescript": "~4.5.5", 45 | "vite": "^2.8.4", 46 | "vitest": "^0.5.0", 47 | "vue-tsc": "^0.31.4" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false -------------------------------------------------------------------------------- /packages/README.md: -------------------------------------------------------------------------------- 1 | # Vuenique 2 | 3 | Vuenique is a low-level component visualization library that utilizes Vue's use of a virtual DOM to bring the power of D3 to Vue without competiton for the DOM. 4 | 5 | 6 | Current features include: 7 | * High utility chart types such as bar charts, line graphs, and scatter plots 8 | * Primitive components built around Vue and D3 9 | * Utility-first approach that allows you to customize visuals as needed 10 | * Declarative components that are simple and logical to use 11 | 12 |
13 | 14 | ## Installation 15 | Install with npm: 16 | 17 | ```sh 18 | npm install @vueniquejs/vuenique 19 | ``` 20 |
21 | 22 | ## Getting Started 23 | 24 | 1. Import the components you need from the package. 25 | ```js 26 | import { Bar } from @vueniquejs/vuenique 27 | ``` 28 | 2. Pass in the necessary props. 29 | ```js 30 | 31 | ``` 32 | 3. See your component render! 33 | 34 | Check out our gallery for more examples of what you can build. 35 | 36 |
37 | 38 | ## Contributing 39 | Any contributions are greatly appreciated! If you have a suggestion that would make this better, please check out our Github repo here. You can fork the repo and create a pull request, or you can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again! 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /packages/build/index.cjs.js: -------------------------------------------------------------------------------- 1 | "use strict";var Lr=Object.defineProperty,Ar=Object.defineProperties;var Hr=Object.getOwnPropertyDescriptors;var Ve=Object.getOwnPropertySymbols;var Pr=Object.prototype.hasOwnProperty,Yr=Object.prototype.propertyIsEnumerable;var Qe=(n,e,t)=>e in n?Lr(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,Pn=(n,e)=>{for(var t in e||(e={}))Pr.call(e,t)&&Qe(n,t,e[t]);if(Ve)for(var t of Ve(e))Yr.call(e,t)&&Qe(n,t,e[t]);return n},Vn=(n,e)=>Ar(n,Hr(e));Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var m=require("vue");function En(n,e){return n==null||e==null?NaN:ne?1:n>=e?0:NaN}function Ne(n){let e=n,t=n,r=n;n.length!==2&&(e=(o,l)=>n(o)-l,t=En,r=(o,l)=>En(n(o),l));function i(o,l,c=0,f=o.length){if(c>>1;r(o[s],l)<0?c=s+1:f=s}while(c>>1;r(o[s],l)<=0?c=s+1:f=s}while(cc&&e(o[s-1],l)>-e(o[s],l)?s-1:s}return{left:i,center:a,right:u}}function At(n){return n===null?NaN:+n}const Ir=Ne(En),Or=Ir.right;Ne(At).center;var _e=Or;class Xe extends Map{constructor(e,t=Rr){super();if(Object.defineProperties(this,{_intern:{value:new Map},_key:{value:t}}),e!=null)for(const[r,i]of e)this.set(r,i)}get(e){return super.get(Ge(this,e))}has(e){return super.has(Ge(this,e))}set(e,t){return super.set(Er(this,e),t)}delete(e){return super.delete($r(this,e))}}function Ge({_intern:n,_key:e},t){const r=e(t);return n.has(r)?n.get(r):t}function Er({_intern:n,_key:e},t){const r=e(t);return n.has(r)?n.get(r):(n.set(r,t),t)}function $r({_intern:n,_key:e},t){const r=e(t);return n.has(r)&&(t=n.get(r),n.delete(r)),t}function Rr(n){return n!==null&&typeof n=="object"?n.valueOf():n}var oe=Math.sqrt(50),le=Math.sqrt(10),ce=Math.sqrt(2);function fe(n,e,t){var r,i=-1,u,a,o;if(e=+e,n=+n,t=+t,n===e&&t>0)return[n];if((r=e0){let l=Math.round(n/o),c=Math.round(e/o);for(l*oe&&--c,a=new Array(u=c-l+1);++ie&&--c,a=new Array(u=c-l+1);++i=0?(u>=oe?10:u>=le?5:u>=ce?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(u>=oe?10:u>=le?5:u>=ce?2:1)}function se(n,e,t){var r=Math.abs(e-n)/Math.max(0,t),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),u=r/i;return u>=oe?i*=10:u>=le?i*=5:u>=ce&&(i*=2),e=1)return+t(n[r-1],r-1,n);var r,i=(r-1)*e,u=Math.floor(i),a=+t(n[u],u,n),o=+t(n[u+1],u+1,n);return a+(o-a)*(i-u)}}function Wr(n,e,t){n=+n,e=+e,t=(i=arguments.length)<2?(e=n,n=0,1):i<3?1:+t;for(var r=-1,i=Math.max(0,Math.ceil((e-n)/t))|0,u=new Array(i);++r>8&15|e>>4&240,e>>4&15|e&240,(e&15)<<4|e&15,1):t===8?Yn(e>>24&255,e>>16&255,e>>8&255,(e&255)/255):t===4?Yn(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|e&240,((e&15)<<4|e&15)/255):null):(e=qr.exec(n))?new F(e[1],e[2],e[3],1):(e=jr.exec(n))?new F(e[1]*255/100,e[2]*255/100,e[3]*255/100,1):(e=Zr.exec(n))?Yn(e[1],e[2],e[3],e[4]):(e=Vr.exec(n))?Yn(e[1]*255/100,e[2]*255/100,e[3]*255/100,e[4]):(e=Qr.exec(n))?ut(e[1],e[2]/100,e[3]/100,1):(e=Xr.exec(n))?ut(e[1],e[2]/100,e[3]/100,e[4]):Ke.hasOwnProperty(n)?tt(Ke[n]):n==="transparent"?new F(NaN,NaN,NaN,0):null}function tt(n){return new F(n>>16&255,n>>8&255,n&255,1)}function Yn(n,e,t,r){return r<=0&&(n=e=t=NaN),new F(n,e,t,r)}function De(n){return n instanceof nn||(n=Cn(n)),n?(n=n.rgb(),new F(n.r,n.g,n.b,n.opacity)):new F}function he(n,e,t,r){return arguments.length===1?De(n):new F(n,e,t,r==null?1:r)}function F(n,e,t,r){this.r=+n,this.g=+e,this.b=+t,this.opacity=+r}Mn(F,he,Dn(nn,{brighter:function(n){return n=n==null?xn:Math.pow(xn,n),new F(this.r*n,this.g*n,this.b*n,this.opacity)},darker:function(n){return n=n==null?on:Math.pow(on,n),new F(this.r*n,this.g*n,this.b*n,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:rt,formatHex:rt,formatRgb:it,toString:it}));function rt(){return"#"+Qn(this.r)+Qn(this.g)+Qn(this.b)}function it(){var n=this.opacity;return n=isNaN(n)?1:Math.max(0,Math.min(1,n)),(n===1?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(n===1?")":", "+n+")")}function Qn(n){return n=Math.max(0,Math.min(255,Math.round(n)||0)),(n<16?"0":"")+n.toString(16)}function ut(n,e,t,r){return r<=0?n=e=t=NaN:t<=0||t>=1?n=e=NaN:e<=0&&(n=NaN),new B(n,e,t,r)}function Yt(n){if(n instanceof B)return new B(n.h,n.s,n.l,n.opacity);if(n instanceof nn||(n=Cn(n)),!n)return new B;if(n instanceof B)return n;n=n.rgb();var e=n.r/255,t=n.g/255,r=n.b/255,i=Math.min(e,t,r),u=Math.max(e,t,r),a=NaN,o=u-i,l=(u+i)/2;return o?(e===u?a=(t-r)/o+(t0&&l<1?0:a,new B(a,o,l,n.opacity)}function ge(n,e,t,r){return arguments.length===1?Yt(n):new B(n,e,t,r==null?1:r)}function B(n,e,t,r){this.h=+n,this.s=+e,this.l=+t,this.opacity=+r}Mn(B,ge,Dn(nn,{brighter:function(n){return n=n==null?xn:Math.pow(xn,n),new B(this.h,this.s,this.l*n,this.opacity)},darker:function(n){return n=n==null?on:Math.pow(on,n),new B(this.h,this.s,this.l*n,this.opacity)},rgb:function(){var n=this.h%360+(this.h<0)*360,e=isNaN(n)||isNaN(this.s)?0:this.s,t=this.l,r=t+(t<.5?t:1-t)*e,i=2*t-r;return new F(Xn(n>=240?n-240:n+120,i,r),Xn(n,i,r),Xn(n<120?n+240:n-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var n=this.opacity;return n=isNaN(n)?1:Math.max(0,Math.min(1,n)),(n===1?"hsl(":"hsla(")+(this.h||0)+", "+(this.s||0)*100+"%, "+(this.l||0)*100+"%"+(n===1?")":", "+n+")")}}));function Xn(n,e,t){return(n<60?e+(t-e)*n/60:n<180?t:n<240?e+(t-e)*(240-n)/60:e)*255}const It=Math.PI/180,Ot=180/Math.PI,$n=18,Et=.96422,$t=1,Rt=.82521,Bt=4/29,yn=6/29,Wt=3*yn*yn,Jr=yn*yn*yn;function zt(n){if(n instanceof z)return new z(n.l,n.a,n.b,n.opacity);if(n instanceof j)return qt(n);n instanceof F||(n=De(n));var e=ne(n.r),t=ne(n.g),r=ne(n.b),i=Gn((.2225045*e+.7168786*t+.0606169*r)/$t),u,a;return e===t&&t===r?u=a=i:(u=Gn((.4360747*e+.3850649*t+.1430804*r)/Et),a=Gn((.0139322*e+.0971045*t+.7141733*r)/Rt)),new z(116*i-16,500*(u-i),200*(i-a),n.opacity)}function me(n,e,t,r){return arguments.length===1?zt(n):new z(n,e,t,r==null?1:r)}function z(n,e,t,r){this.l=+n,this.a=+e,this.b=+t,this.opacity=+r}Mn(z,me,Dn(nn,{brighter:function(n){return new z(this.l+$n*(n==null?1:n),this.a,this.b,this.opacity)},darker:function(n){return new z(this.l-$n*(n==null?1:n),this.a,this.b,this.opacity)},rgb:function(){var n=(this.l+16)/116,e=isNaN(this.a)?n:n+this.a/500,t=isNaN(this.b)?n:n-this.b/200;return e=Et*Jn(e),n=$t*Jn(n),t=Rt*Jn(t),new F(Kn(3.1338561*e-1.6168667*n-.4906146*t),Kn(-.9787684*e+1.9161415*n+.033454*t),Kn(.0719453*e-.2289914*n+1.4052427*t),this.opacity)}}));function Gn(n){return n>Jr?Math.pow(n,1/3):n/Wt+Bt}function Jn(n){return n>yn?n*n*n:Wt*(n-Bt)}function Kn(n){return 255*(n<=.0031308?12.92*n:1.055*Math.pow(n,1/2.4)-.055)}function ne(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Kr(n){if(n instanceof j)return new j(n.h,n.c,n.l,n.opacity);if(n instanceof z||(n=zt(n)),n.a===0&&n.b===0)return new j(NaN,0()=>n;function Zt(n,e){return function(t){return n+t*e}}function ei(n,e,t){return n=Math.pow(n,t),e=Math.pow(e,t)-n,t=1/t,function(r){return Math.pow(n+r*e,t)}}function Ae(n,e){var t=e-n;return t?Zt(n,t>180||t<-180?t-360*Math.round(t/360):t):zn(isNaN(n)?e:n)}function ti(n){return(n=+n)==1?_:function(e,t){return t-e?ei(e,t,n):zn(isNaN(e)?t:e)}}function _(n,e){var t=e-n;return t?Zt(n,t):zn(isNaN(n)?e:n)}var ye=function n(e){var t=ti(e);function r(i,u){var a=t((i=he(i)).r,(u=he(u)).r),o=t(i.g,u.g),l=t(i.b,u.b),c=_(i.opacity,u.opacity);return function(f){return i.r=a(f),i.g=o(f),i.b=l(f),i.opacity=c(f),i+""}}return r.gamma=n,r}(1);function ri(n,e){e||(e=[]);var t=n?Math.min(e.length,n.length):0,r=e.slice(),i;return function(u){for(i=0;it&&(u=e.slice(t,u),o[a]?o[a]+=u:o[++a]=u),(r=r[0])===(i=i[0])?o[a]?o[a]+=i:o[++a]=i:(o[++a]=null,l.push({i:a,x:Rn(r,i)})),t=ee.lastIndex;return te&&(t=n,n=e,e=t),function(r){return Math.max(n,Math.min(e,r))}}function wi(n,e,t){var r=n[0],i=n[1],u=e[0],a=e[1];return i2?vi:wi,l=c=null,s}function s(g){return g==null||isNaN(g=+g)?u:(l||(l=o(n.map(r),e,t)))(r(a(g)))}return s.invert=function(g){return a(i((c||(c=o(e,n.map(r),Rn)))(g)))},s.domain=function(g){return arguments.length?(n=Array.from(g,bi),f()):n.slice()},s.range=function(g){return arguments.length?(e=Array.from(g),f()):e.slice()},s.rangeRound=function(g){return e=Array.from(g),t=Vt,f()},s.clamp=function(g){return arguments.length?(a=g?!0:dn,f()):a!==dn},s.interpolate=function(g){return arguments.length?(t=g,f()):t},s.unknown=function(g){return arguments.length?(u=g,s):u},function(g,x){return r=g,i=x,f()}}function Kt(){return Jt()(dn,dn)}function Ti(n){return Math.abs(n=Math.round(n))>=1e21?n.toLocaleString("en").replace(/,/g,""):n.toString(10)}function Bn(n,e){if((t=(n=e?n.toExponential(e-1):n.toExponential()).indexOf("e"))<0)return null;var t,r=n.slice(0,t);return[r.length>1?r[0]+r.slice(2):r,+n.slice(t+1)]}function bn(n){return n=Bn(Math.abs(n)),n?n[1]:NaN}function ki(n,e){return function(t,r){for(var i=t.length,u=[],a=0,o=n[0],l=0;i>0&&o>0&&(l+o+1>r&&(o=Math.max(1,r-l)),u.push(t.substring(i-=o,i+o)),!((l+=o+1)>r));)o=n[a=(a+1)%n.length];return u.reverse().join(e)}}function Ci(n){return function(e){return e.replace(/[0-9]/g,function(t){return n[+t]})}}var Si=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Nn(n){if(!(e=Si.exec(n)))throw new Error("invalid format: "+n);var e;return new Ye({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}Nn.prototype=Ye.prototype;function Ye(n){this.fill=n.fill===void 0?" ":n.fill+"",this.align=n.align===void 0?">":n.align+"",this.sign=n.sign===void 0?"-":n.sign+"",this.symbol=n.symbol===void 0?"":n.symbol+"",this.zero=!!n.zero,this.width=n.width===void 0?void 0:+n.width,this.comma=!!n.comma,this.precision=n.precision===void 0?void 0:+n.precision,this.trim=!!n.trim,this.type=n.type===void 0?"":n.type+""}Ye.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type};function Ni(n){n:for(var e=n.length,t=1,r=-1,i;t0&&(r=0);break}return r>0?n.slice(0,r)+n.slice(i+1):n}var nr;function _i(n,e){var t=Bn(n,e);if(!t)return n+"";var r=t[0],i=t[1],u=i-(nr=Math.max(-8,Math.min(8,Math.floor(i/3)))*3)+1,a=r.length;return u===a?r:u>a?r+new Array(u-a+1).join("0"):u>0?r.slice(0,u)+"."+r.slice(u):"0."+new Array(1-u).join("0")+Bn(n,Math.max(0,e+u-1))[0]}function ft(n,e){var t=Bn(n,e);if(!t)return n+"";var r=t[0],i=t[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}var st={"%":(n,e)=>(n*100).toFixed(e),b:n=>Math.round(n).toString(2),c:n=>n+"",d:Ti,e:(n,e)=>n.toExponential(e),f:(n,e)=>n.toFixed(e),g:(n,e)=>n.toPrecision(e),o:n=>Math.round(n).toString(8),p:(n,e)=>ft(n*100,e),r:ft,s:_i,X:n=>Math.round(n).toString(16).toUpperCase(),x:n=>Math.round(n).toString(16)};function ht(n){return n}var gt=Array.prototype.map,mt=["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"];function Ui(n){var e=n.grouping===void 0||n.thousands===void 0?ht:ki(gt.call(n.grouping,Number),n.thousands+""),t=n.currency===void 0?"":n.currency[0]+"",r=n.currency===void 0?"":n.currency[1]+"",i=n.decimal===void 0?".":n.decimal+"",u=n.numerals===void 0?ht:Ci(gt.call(n.numerals,String)),a=n.percent===void 0?"%":n.percent+"",o=n.minus===void 0?"\u2212":n.minus+"",l=n.nan===void 0?"NaN":n.nan+"";function c(s){s=Nn(s);var g=s.fill,x=s.align,y=s.sign,k=s.symbol,C=s.zero,S=s.width,R=s.comma,N=s.precision,H=s.trim,L=s.type;L==="n"?(R=!0,L="g"):st[L]||(N===void 0&&(N=12),H=!0,L="g"),(C||g==="0"&&x==="=")&&(C=!0,g="0",x="=");var p=k==="$"?t:k==="#"&&/[boxX]/.test(L)?"0"+L.toLowerCase():"",A=k==="$"?r:/[%p]/.test(L)?a:"",tn=st[L],Zn=/[defgprs%]/.test(L);N=N===void 0?6:/[gprs]/.test(L)?Math.max(1,Math.min(21,N)):Math.max(0,Math.min(20,N));function An(M){var q=p,P=A,Q,Hn,sn;if(L==="c")P=tn(M)+P,M="";else{M=+M;var hn=M<0||1/M<0;if(M=isNaN(M)?l:tn(Math.abs(M),N),H&&(M=Ni(M)),hn&&+M==0&&y!=="+"&&(hn=!1),q=(hn?y==="("?y:o:y==="-"||y==="("?"":y)+q,P=(L==="s"?mt[8+nr/3]:"")+P+(hn&&y==="("?")":""),Zn){for(Q=-1,Hn=M.length;++Qsn||sn>57){P=(sn===46?i+M.slice(Q+1):M.slice(Q))+P,M=M.slice(0,Q);break}}}R&&!C&&(M=e(M,1/0));var gn=q.length+M.length+P.length,$=gn>1)+q+M+P+$.slice(gn);break;default:M=$+q+M+P;break}return u(M)}return An.toString=function(){return s+""},An}function f(s,g){var x=c((s=Nn(s),s.type="f",s)),y=Math.max(-8,Math.min(8,Math.floor(bn(g)/3)))*3,k=Math.pow(10,-y),C=mt[8+y/3];return function(S){return x(k*S)+C}}return{format:c,formatPrefix:f}}var In,Ie,er;Di({thousands:",",grouping:[3],currency:["$",""]});function Di(n){return In=Ui(n),Ie=In.format,er=In.formatPrefix,In}function Fi(n){return Math.max(0,-bn(Math.abs(n)))}function Li(n,e){return Math.max(0,Math.max(-8,Math.min(8,Math.floor(bn(e)/3)))*3-bn(Math.abs(n)))}function Ai(n,e){return n=Math.abs(n),e=Math.abs(e)-n,Math.max(0,bn(e)-bn(n))+1}function Hi(n,e,t,r){var i=se(n,e,t),u;switch(r=Nn(r==null?",f":r),r.type){case"s":{var a=Math.max(Math.abs(n),Math.abs(e));return r.precision==null&&!isNaN(u=Li(i,a))&&(r.precision=u),er(r,a)}case"":case"e":case"g":case"p":case"r":{r.precision==null&&!isNaN(u=Ai(i,Math.max(Math.abs(n),Math.abs(e))))&&(r.precision=u-(r.type==="e"));break}case"f":case"%":{r.precision==null&&!isNaN(u=Fi(i))&&(r.precision=u-(r.type==="%")*2);break}}return Ie(r)}function Pi(n){var e=n.domain;return n.ticks=function(t){var r=e();return fe(r[0],r[r.length-1],t==null?10:t)},n.tickFormat=function(t,r){var i=e();return Hi(i[0],i[i.length-1],t==null?10:t,r)},n.nice=function(t){t==null&&(t=10);var r=e(),i=0,u=r.length-1,a=r[i],o=r[u],l,c,f=10;for(o0;){if(c=Ht(a,o,t),c===l)return r[i]=a,r[u]=o,e(r);if(c>0)a=Math.floor(a/c)*c,o=Math.ceil(o/c)*c;else if(c<0)a=Math.ceil(a*c)/c,o=Math.floor(o*c)/c;else break;l=c}return n},n}function tr(){var n=Kt();return n.copy=function(){return Pe(n,tr())},K.apply(n,arguments),Pi(n)}function rr(n,e){n=n.slice();var t=0,r=n.length-1,i=n[t],u=n[r],a;return uMath.pow(n,e)}function $i(n){return n===Math.E?Math.log:n===10&&Math.log10||n===2&&Math.log2||(n=Math.log(n),e=>Math.log(e)/n)}function yt(n){return(e,t)=>-n(-e,t)}function Ri(n){const e=n(dt,pt),t=e.domain;let r=10,i,u;function a(){return i=$i(r),u=Ei(r),t()[0]<0?(i=yt(i),u=yt(u),n(Yi,Ii)):n(dt,pt),e}return e.base=function(o){return arguments.length?(r=+o,a()):r},e.domain=function(o){return arguments.length?(t(o),a()):t()},e.ticks=o=>{const l=t();let c=l[0],f=l[l.length-1];const s=f0){for(;g<=x;++g)for(y=1;yf)break;S.push(k)}}else for(;g<=x;++g)for(y=r-1;y>=1;--y)if(k=g>0?y/u(-g):y*u(g),!(kf)break;S.push(k)}S.length*2{if(o==null&&(o=10),l==null&&(l=r===10?"s":","),typeof l!="function"&&(!(r%1)&&(l=Nn(l)).precision==null&&(l.trim=!0),l=Ie(l)),o===1/0)return l;const c=Math.max(1,r*o/e.ticks().length);return f=>{let s=f/u(Math.round(i(f)));return s*rt(rr(t(),{floor:o=>u(Math.floor(i(o))),ceil:o=>u(Math.ceil(i(o)))})),e}function ir(){const n=Ri(Jt()).domain([1,10]);return n.copy=()=>Pe(n,ir()).base(n.base()),K.apply(n,arguments),n}function ur(){var n=[],e=[],t=[],r;function i(){var a=0,o=Math.max(1,e.length);for(t=new Array(o-1);++a0?t[o-1]:n[0],o0))return l;do l.push(c=new Date(+u)),e(u,o),n(u);while(c=a)for(;n(a),!u(a);)a.setTime(a-1)},function(a,o){if(a>=a)if(o<0)for(;++o<=0;)for(;e(a,-1),!u(a););else for(;--o>=0;)for(;e(a,1),!u(a););})},t&&(i.count=function(u,a){return te.setTime(+u),re.setTime(+a),n(te),n(re),Math.floor(t(te,re))},i.every=function(u){return u=Math.floor(u),!isFinite(u)||!(u>0)?null:u>1?i.filter(r?function(a){return r(a)%u===0}:function(a){return i.count(0,a)%u===0}):i}),i}var Me=U(function(){},function(n,e){n.setTime(+n+e)},function(n,e){return e-n});Me.every=function(n){return n=Math.floor(n),!isFinite(n)||!(n>0)?null:n>1?U(function(e){e.setTime(Math.floor(e/n)*n)},function(e,t){e.setTime(+e+t*n)},function(e,t){return(t-e)/n}):Me};var Bi=Me;const Z=1e3,E=Z*60,V=E*60,ln=V*24,Oe=ln*7,xt=ln*30,ie=ln*365;var Wi=U(function(n){n.setTime(n-n.getMilliseconds())},function(n,e){n.setTime(+n+e*Z)},function(n,e){return(e-n)/Z},function(n){return n.getUTCSeconds()}),X=Wi,zi=U(function(n){n.setTime(n-n.getMilliseconds()-n.getSeconds()*Z)},function(n,e){n.setTime(+n+e*E)},function(n,e){return(e-n)/E},function(n){return n.getMinutes()}),Ee=zi,qi=U(function(n){n.setTime(n-n.getMilliseconds()-n.getSeconds()*Z-n.getMinutes()*E)},function(n,e){n.setTime(+n+e*V)},function(n,e){return(e-n)/V},function(n){return n.getHours()}),$e=qi,ji=U(n=>n.setHours(0,0,0,0),(n,e)=>n.setDate(n.getDate()+e),(n,e)=>(e-n-(e.getTimezoneOffset()-n.getTimezoneOffset())*E)/ln,n=>n.getDate()-1),Fn=ji;function cn(n){return U(function(e){e.setDate(e.getDate()-(e.getDay()+7-n)%7),e.setHours(0,0,0,0)},function(e,t){e.setDate(e.getDate()+t*7)},function(e,t){return(t-e-(t.getTimezoneOffset()-e.getTimezoneOffset())*E)/Oe})}var qn=cn(0),we=cn(1);cn(2);cn(3);var _n=cn(4);cn(5);cn(6);var Zi=U(function(n){n.setDate(1),n.setHours(0,0,0,0)},function(n,e){n.setMonth(n.getMonth()+e)},function(n,e){return e.getMonth()-n.getMonth()+(e.getFullYear()-n.getFullYear())*12},function(n){return n.getMonth()}),Re=Zi,or=U(function(n){n.setMonth(0,1),n.setHours(0,0,0,0)},function(n,e){n.setFullYear(n.getFullYear()+e)},function(n,e){return e.getFullYear()-n.getFullYear()},function(n){return n.getFullYear()});or.every=function(n){return!isFinite(n=Math.floor(n))||!(n>0)?null:U(function(e){e.setFullYear(Math.floor(e.getFullYear()/n)*n),e.setMonth(0,1),e.setHours(0,0,0,0)},function(e,t){e.setFullYear(e.getFullYear()+t*n)})};var G=or,Vi=U(function(n){n.setUTCSeconds(0,0)},function(n,e){n.setTime(+n+e*E)},function(n,e){return(e-n)/E},function(n){return n.getUTCMinutes()}),Be=Vi,Qi=U(function(n){n.setUTCMinutes(0,0,0)},function(n,e){n.setTime(+n+e*V)},function(n,e){return(e-n)/V},function(n){return n.getUTCHours()}),We=Qi,Xi=U(function(n){n.setUTCHours(0,0,0,0)},function(n,e){n.setUTCDate(n.getUTCDate()+e)},function(n,e){return(e-n)/ln},function(n){return n.getUTCDate()-1}),Ln=Xi;function fn(n){return U(function(e){e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-n)%7),e.setUTCHours(0,0,0,0)},function(e,t){e.setUTCDate(e.getUTCDate()+t*7)},function(e,t){return(t-e)/Oe})}var jn=fn(0),ve=fn(1);fn(2);fn(3);var Un=fn(4);fn(5);fn(6);var Gi=U(function(n){n.setUTCDate(1),n.setUTCHours(0,0,0,0)},function(n,e){n.setUTCMonth(n.getUTCMonth()+e)},function(n,e){return e.getUTCMonth()-n.getUTCMonth()+(e.getUTCFullYear()-n.getUTCFullYear())*12},function(n){return n.getUTCMonth()}),ze=Gi,lr=U(function(n){n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)},function(n,e){n.setUTCFullYear(n.getUTCFullYear()+e)},function(n,e){return e.getUTCFullYear()-n.getUTCFullYear()},function(n){return n.getUTCFullYear()});lr.every=function(n){return!isFinite(n=Math.floor(n))||!(n>0)?null:U(function(e){e.setUTCFullYear(Math.floor(e.getUTCFullYear()/n)*n),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},function(e,t){e.setUTCFullYear(e.getUTCFullYear()+t*n)})};var J=lr;function cr(n,e,t,r,i,u){const a=[[X,1,Z],[X,5,5*Z],[X,15,15*Z],[X,30,30*Z],[u,1,E],[u,5,5*E],[u,15,15*E],[u,30,30*E],[i,1,V],[i,3,3*V],[i,6,6*V],[i,12,12*V],[r,1,ln],[r,2,2*ln],[t,1,Oe],[e,1,xt],[e,3,3*xt],[n,1,ie]];function o(c,f,s){const g=fC).right(a,g);if(x===a.length)return n.every(se(c/ie,f/ie,s));if(x===0)return Bi.every(Math.max(se(c,f,s),1));const[y,k]=a[g/a[x-1][2]53)return null;"w"in h||(h.w=1),"Z"in h?(T=ae(wn(h.y,0,1)),I=T.getUTCDay(),T=I>4||I===0?ve.ceil(T):ve(T),T=Ln.offset(T,(h.V-1)*7),h.y=T.getUTCFullYear(),h.m=T.getUTCMonth(),h.d=T.getUTCDate()+(h.w+6)%7):(T=ue(wn(h.y,0,1)),I=T.getDay(),T=I>4||I===0?we.ceil(T):we(T),T=Fn.offset(T,(h.V-1)*7),h.y=T.getFullYear(),h.m=T.getMonth(),h.d=T.getDate()+(h.w+6)%7)}else("W"in h||"U"in h)&&("w"in h||(h.w="u"in h?h.u%7:"W"in h?1:0),I="Z"in h?ae(wn(h.y,0,1)).getUTCDay():ue(wn(h.y,0,1)).getDay(),h.m=0,h.d="W"in h?(h.w+6)%7+h.W*7-(I+5)%7:h.w+h.U*7-(I+6)%7);return"Z"in h?(h.H+=h.Z/100|0,h.M+=h.Z%100,ae(h)):ue(h)}}function tn(d,b,w,h){for(var Y=0,T=b.length,I=w.length,O,rn;Y=I)return-1;if(O=b.charCodeAt(Y++),O===37){if(O=b.charAt(Y++),rn=L[O in bt?b.charAt(Y++):O],!rn||(h=rn(d,w,h))<0)return-1}else if(O!=w.charCodeAt(h++))return-1}return h}function Zn(d,b,w){var h=c.exec(b.slice(w));return h?(d.p=f.get(h[0].toLowerCase()),w+h[0].length):-1}function An(d,b,w){var h=x.exec(b.slice(w));return h?(d.w=y.get(h[0].toLowerCase()),w+h[0].length):-1}function M(d,b,w){var h=s.exec(b.slice(w));return h?(d.w=g.get(h[0].toLowerCase()),w+h[0].length):-1}function q(d,b,w){var h=S.exec(b.slice(w));return h?(d.m=R.get(h[0].toLowerCase()),w+h[0].length):-1}function P(d,b,w){var h=k.exec(b.slice(w));return h?(d.m=C.get(h[0].toLowerCase()),w+h[0].length):-1}function Q(d,b,w){return tn(d,e,b,w)}function Hn(d,b,w){return tn(d,t,b,w)}function sn(d,b,w){return tn(d,r,b,w)}function hn(d){return a[d.getDay()]}function gn(d){return u[d.getDay()]}function $(d){return l[d.getMonth()]}function Tr(d){return o[d.getMonth()]}function kr(d){return i[+(d.getHours()>=12)]}function Cr(d){return 1+~~(d.getMonth()/3)}function Sr(d){return a[d.getUTCDay()]}function Nr(d){return u[d.getUTCDay()]}function _r(d){return l[d.getUTCMonth()]}function Ur(d){return o[d.getUTCMonth()]}function Dr(d){return i[+(d.getUTCHours()>=12)]}function Fr(d){return 1+~~(d.getUTCMonth()/3)}return{format:function(d){var b=p(d+="",N);return b.toString=function(){return d},b},parse:function(d){var b=A(d+="",!1);return b.toString=function(){return d},b},utcFormat:function(d){var b=p(d+="",H);return b.toString=function(){return d},b},utcParse:function(d){var b=A(d+="",!0);return b.toString=function(){return d},b}}}var bt={"-":"",_:" ","0":"0"},D=/^\s*\d+/,ru=/^%/,iu=/[\\^$*+?|[\]().{}]/g;function v(n,e,t){var r=n<0?"-":"",i=(r?-n:n)+"",u=i.length;return r+(u[e.toLowerCase(),t]))}function au(n,e,t){var r=D.exec(e.slice(t,t+1));return r?(n.w=+r[0],t+r[0].length):-1}function ou(n,e,t){var r=D.exec(e.slice(t,t+1));return r?(n.u=+r[0],t+r[0].length):-1}function lu(n,e,t){var r=D.exec(e.slice(t,t+2));return r?(n.U=+r[0],t+r[0].length):-1}function cu(n,e,t){var r=D.exec(e.slice(t,t+2));return r?(n.V=+r[0],t+r[0].length):-1}function fu(n,e,t){var r=D.exec(e.slice(t,t+2));return r?(n.W=+r[0],t+r[0].length):-1}function Mt(n,e,t){var r=D.exec(e.slice(t,t+4));return r?(n.y=+r[0],t+r[0].length):-1}function wt(n,e,t){var r=D.exec(e.slice(t,t+2));return r?(n.y=+r[0]+(+r[0]>68?1900:2e3),t+r[0].length):-1}function su(n,e,t){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(t,t+6));return r?(n.Z=r[1]?0:-(r[2]+(r[3]||"00")),t+r[0].length):-1}function hu(n,e,t){var r=D.exec(e.slice(t,t+1));return r?(n.q=r[0]*3-3,t+r[0].length):-1}function gu(n,e,t){var r=D.exec(e.slice(t,t+2));return r?(n.m=r[0]-1,t+r[0].length):-1}function vt(n,e,t){var r=D.exec(e.slice(t,t+2));return r?(n.d=+r[0],t+r[0].length):-1}function mu(n,e,t){var r=D.exec(e.slice(t,t+3));return r?(n.m=0,n.d=+r[0],t+r[0].length):-1}function Tt(n,e,t){var r=D.exec(e.slice(t,t+2));return r?(n.H=+r[0],t+r[0].length):-1}function du(n,e,t){var r=D.exec(e.slice(t,t+2));return r?(n.M=+r[0],t+r[0].length):-1}function pu(n,e,t){var r=D.exec(e.slice(t,t+2));return r?(n.S=+r[0],t+r[0].length):-1}function yu(n,e,t){var r=D.exec(e.slice(t,t+3));return r?(n.L=+r[0],t+r[0].length):-1}function xu(n,e,t){var r=D.exec(e.slice(t,t+6));return r?(n.L=Math.floor(r[0]/1e3),t+r[0].length):-1}function bu(n,e,t){var r=ru.exec(e.slice(t,t+1));return r?t+r[0].length:-1}function Mu(n,e,t){var r=D.exec(e.slice(t));return r?(n.Q=+r[0],t+r[0].length):-1}function wu(n,e,t){var r=D.exec(e.slice(t));return r?(n.s=+r[0],t+r[0].length):-1}function kt(n,e){return v(n.getDate(),e,2)}function vu(n,e){return v(n.getHours(),e,2)}function Tu(n,e){return v(n.getHours()%12||12,e,2)}function ku(n,e){return v(1+Fn.count(G(n),n),e,3)}function fr(n,e){return v(n.getMilliseconds(),e,3)}function Cu(n,e){return fr(n,e)+"000"}function Su(n,e){return v(n.getMonth()+1,e,2)}function Nu(n,e){return v(n.getMinutes(),e,2)}function _u(n,e){return v(n.getSeconds(),e,2)}function Uu(n){var e=n.getDay();return e===0?7:e}function Du(n,e){return v(qn.count(G(n)-1,n),e,2)}function sr(n){var e=n.getDay();return e>=4||e===0?_n(n):_n.ceil(n)}function Fu(n,e){return n=sr(n),v(_n.count(G(n),n)+(G(n).getDay()===4),e,2)}function Lu(n){return n.getDay()}function Au(n,e){return v(we.count(G(n)-1,n),e,2)}function Hu(n,e){return v(n.getFullYear()%100,e,2)}function Pu(n,e){return n=sr(n),v(n.getFullYear()%100,e,2)}function Yu(n,e){return v(n.getFullYear()%1e4,e,4)}function Iu(n,e){var t=n.getDay();return n=t>=4||t===0?_n(n):_n.ceil(n),v(n.getFullYear()%1e4,e,4)}function Ou(n){var e=n.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+v(e/60|0,"0",2)+v(e%60,"0",2)}function Ct(n,e){return v(n.getUTCDate(),e,2)}function Eu(n,e){return v(n.getUTCHours(),e,2)}function $u(n,e){return v(n.getUTCHours()%12||12,e,2)}function Ru(n,e){return v(1+Ln.count(J(n),n),e,3)}function hr(n,e){return v(n.getUTCMilliseconds(),e,3)}function Bu(n,e){return hr(n,e)+"000"}function Wu(n,e){return v(n.getUTCMonth()+1,e,2)}function zu(n,e){return v(n.getUTCMinutes(),e,2)}function qu(n,e){return v(n.getUTCSeconds(),e,2)}function ju(n){var e=n.getUTCDay();return e===0?7:e}function Zu(n,e){return v(jn.count(J(n)-1,n),e,2)}function gr(n){var e=n.getUTCDay();return e>=4||e===0?Un(n):Un.ceil(n)}function Vu(n,e){return n=gr(n),v(Un.count(J(n),n)+(J(n).getUTCDay()===4),e,2)}function Qu(n){return n.getUTCDay()}function Xu(n,e){return v(ve.count(J(n)-1,n),e,2)}function Gu(n,e){return v(n.getUTCFullYear()%100,e,2)}function Ju(n,e){return n=gr(n),v(n.getUTCFullYear()%100,e,2)}function Ku(n,e){return v(n.getUTCFullYear()%1e4,e,4)}function na(n,e){var t=n.getUTCDay();return n=t>=4||t===0?Un(n):Un.ceil(n),v(n.getUTCFullYear()%1e4,e,4)}function ea(){return"+0000"}function St(){return"%"}function Nt(n){return+n}function _t(n){return Math.floor(+n/1e3)}var On,mr,dr;ta({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function ta(n){return On=tu(n),mr=On.format,dr=On.utcFormat,On}function ra(n){return new Date(n)}function ia(n){return n instanceof Date?+n:+new Date(+n)}function qe(n,e,t,r,i,u,a,o,l,c){var f=Kt(),s=f.invert,g=f.domain,x=c(".%L"),y=c(":%S"),k=c("%I:%M"),C=c("%I %p"),S=c("%a %d"),R=c("%b %d"),N=c("%B"),H=c("%Y");function L(p){return(l(p)e.has(r));return function(i,u){return typeof u!="undefined"&&t.forEach(a=>{ka[a](i,u)}),i}}const Ca=en("domain","range","reverse","align","padding","round");function Sa(n){return Ca(Pt(),n)}const Na=en("domain","range","reverse","clamp","interpolate","nice","round","zero");function _a(n){return Na(tr(),n)}const Ua=en("domain","range","reverse","clamp","interpolate","nice","round");function Da(n){return Ua(ua(),n)}const Fa=en("domain","range","reverse","base","clamp","interpolate","nice","round");function La(n){return Fa(ir(),n)}const Aa=en("domain","range","reverse","unknown");function Ha(n){return Aa(Ue(),n)}const Pa=en("domain","range","reverse");function Ya(n){return Pa(ur(),n)}const Ia=en("domain","range","reverse");function Oa(n){return Ia(ar(),n)}const Ea=en("domain","range","reverse","clamp","interpolate","nice","round");function $a(n){return Ea(aa(),n)}const Te=Math.PI,ke=2*Te,un=1e-6,Ra=ke-un;function Ce(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function pr(){return new Ce}Ce.prototype=pr.prototype={constructor:Ce,moveTo:function(n,e){this._+="M"+(this._x0=this._x1=+n)+","+(this._y0=this._y1=+e)},closePath:function(){this._x1!==null&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")},lineTo:function(n,e){this._+="L"+(this._x1=+n)+","+(this._y1=+e)},quadraticCurveTo:function(n,e,t,r){this._+="Q"+ +n+","+ +e+","+(this._x1=+t)+","+(this._y1=+r)},bezierCurveTo:function(n,e,t,r,i,u){this._+="C"+ +n+","+ +e+","+ +t+","+ +r+","+(this._x1=+i)+","+(this._y1=+u)},arcTo:function(n,e,t,r,i){n=+n,e=+e,t=+t,r=+r,i=+i;var u=this._x1,a=this._y1,o=t-n,l=r-e,c=u-n,f=a-e,s=c*c+f*f;if(i<0)throw new Error("negative radius: "+i);if(this._x1===null)this._+="M"+(this._x1=n)+","+(this._y1=e);else if(s>un)if(!(Math.abs(f*o-l*c)>un)||!i)this._+="L"+(this._x1=n)+","+(this._y1=e);else{var g=t-u,x=r-a,y=o*o+l*l,k=g*g+x*x,C=Math.sqrt(y),S=Math.sqrt(s),R=i*Math.tan((Te-Math.acos((y+s-k)/(2*C*S)))/2),N=R/S,H=R/C;Math.abs(N-1)>un&&(this._+="L"+(n+N*c)+","+(e+N*f)),this._+="A"+i+","+i+",0,0,"+ +(f*g>c*x)+","+(this._x1=n+H*o)+","+(this._y1=e+H*l)}},arc:function(n,e,t,r,i,u){n=+n,e=+e,t=+t,u=!!u;var a=t*Math.cos(r),o=t*Math.sin(r),l=n+a,c=e+o,f=1^u,s=u?r-i:i-r;if(t<0)throw new Error("negative radius: "+t);this._x1===null?this._+="M"+l+","+c:(Math.abs(this._x1-l)>un||Math.abs(this._y1-c)>un)&&(this._+="L"+l+","+c),t&&(s<0&&(s=s%ke+ke),s>Ra?this._+="A"+t+","+t+",0,1,"+f+","+(n-a)+","+(e-o)+"A"+t+","+t+",0,1,"+f+","+(this._x1=l)+","+(this._y1=c):s>un&&(this._+="A"+t+","+t+",0,"+ +(s>=Te)+","+f+","+(this._x1=n+t*Math.cos(i))+","+(this._y1=e+t*Math.sin(i))))},rect:function(n,e,t,r){this._+="M"+(this._x0=this._x1=+n)+","+(this._y0=this._y1=+e)+"h"+ +t+"v"+ +r+"h"+-t+"Z"},toString:function(){return this._}};function mn(n){return function(){return n}}function Ba(n){return typeof n=="object"&&"length"in n?n:Array.from(n)}function yr(n){this._context=n}yr.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(n,e){switch(n=+n,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(n,e):this._context.moveTo(n,e);break;case 1:this._point=2;default:this._context.lineTo(n,e);break}}};function Wa(n){return new yr(n)}function za(n){return n[0]}function qa(n){return n[1]}function ja(n,e){var t=mn(!0),r=null,i=Wa,u=null;n=typeof n=="function"?n:n===void 0?za:mn(n),e=typeof e=="function"?e:e===void 0?qa:mn(e);function a(o){var l,c=(o=Ba(o)).length,f,s=!1,g;for(r==null&&(u=i(g=pr())),l=0;l<=c;++l)!(l(m.openBlock(),m.createElementBlock("rect",m.mergeProps({class:e.class},t.$attrs),null,16))}}),Va=["className","d","fill"],Qa=m.defineComponent({props:{data:null,fill:{default:"transparent"},className:null,defined:{type:Function,default:()=>!0},curve:null,x:null,y:null},setup(n){const e=n,t=m.computed(()=>{const r=e.x,i=e.y,u=e.defined,a=e.curve;return xr({x:r,y:i,defined:u,curve:a})});return(r,i)=>(m.openBlock(),m.createElementBlock("path",m.mergeProps({className:e.className,d:n.data?m.unref(t)(n.data)||"":m.unref(t)([])||"",fill:e.fill,strokeLinecap:"round"},r.$attrs),null,16,Va))}}),Xa=m.defineComponent({props:{class:null},setup(n){const e=n;return(t,r)=>(m.openBlock(),m.createElementBlock("circle",m.mergeProps({class:e.class},t.$attrs),null,16))}});function Ga({steps:n,scale:e}){const t=e.domain(),r=(t[t.length-1]-t[0])/(n-1),i=new Array(5);i[0]=t[0];for(let u=1;u({datum:t,index:r,text:`${e(t,r)}`,value:n(t)})}function je(n){return n&&typeof n=="object"&&"value"in n&&typeof n.value!="undefined"?n.value:n}function Se(n){return String(je(n))}const br=m.defineComponent({props:{flexDirection:{default:"row"},alignItems:{default:"center"},margin:{default:"0"},display:{default:"flex"}},setup(n){const e=n,t=m.computed(()=>{const{flexDirection:r,alignItems:i,margin:u,display:a}=e;return{flexDirection:r,alignItems:i,margin:u,display:a}});return(r,i)=>(m.openBlock(),m.createElementBlock("div",m.mergeProps({class:"vuenique-legend-item",style:m.unref(t)},r.$attrs),[m.renderSlot(r.$slots,"default")],16))}}),Mr=m.defineComponent({props:{align:{default:"left"},label:null,flex:{default:1},margin:{default:"5px 0"},children:null},setup(n){const e=n;return(t,r)=>(m.openBlock(),m.createElementBlock("text",m.mergeProps({className:"legendLabels"},t.$attrs,{style:{justifyContent:e.align,display:"flex",flex:e.flex,margin:e.margin}}),m.toDisplayString(e.label),17))}}),Ka={width:10,height:10},no=["width","height","fill"],eo=["v-bind"],to={width:10,height:10},ro=["r","fill"],wr=m.defineComponent({props:{label:null,itemIndex:null,margin:{default:5},fill:null,shape:{default:"rect"},width:{default:"10"},height:{default:"10"},radius:{default:"5"}},setup(n){const e=n,t=m.computed(()=>{const{fill:r,label:i}=e;return r(Pn({},i))});return(r,i)=>e.shape==="rect"?(m.openBlock(),m.createElementBlock("g",m.mergeProps({key:0,className:"legendShape"},r.$attrs),[(m.openBlock(),m.createElementBlock("svg",Ka,[m.createElementVNode("rect",{width:e.width,height:e.height,fill:m.unref(t)},null,8,no)]))],16)):(m.openBlock(),m.createElementBlock("g",{key:1,className:"legendShape","v-bind":r.$attrs},[(m.openBlock(),m.createElementBlock("svg",to,[m.createElementVNode("circle",{cx:5,cy:5,r:e.radius,fill:m.unref(t)},null,8,ro)]))],8,eo))}}),vr=m.defineComponent({props:{scale:null,style:{default:{display:"flex"}},domain:null,shapeWidth:{default:15},shapeHeight:{default:15},shapeMargin:{default:"2px 4px 2px 0"},labelAlign:{default:"left"},labelFlex:{default:"1"},labelMargin:{default:"0 4px"},itemMargin:{default:"0"},direction:{default:"column"},itemDirection:{default:"row"},fill:{type:Function,default:Se},size:{type:Function,default:Se},shape:{default:"circle"},shapeStyle:null,labelFormat:{type:Function,default:je},labelTransform:{type:Function,default:Ja},legendLabelProps:null},setup(n){const e=n,t=e.domain||[],r=m.computed(()=>{const{scale:a,labelFormat:o,labelTransform:l}=e,c=l({scale:a,labelFormat:o});return t.map(c)}),i=m.computed(()=>r.value.map((a,o)=>{const l=`legend-${a.text}-${o}`,c=t[o];return{key:l,item:c,itemIndex:o,label:a}})),u=m.computed(()=>{const{style:a,direction:o}=e;return Vn(Pn({},a),{direction:o})});return(a,o)=>(m.openBlock(),m.createElementBlock("g",{class:"vuenique-legend",style:m.normalizeStyle(m.unref(u).value)},[(m.openBlock(!0),m.createElementBlock(m.Fragment,null,m.renderList(m.unref(i),l=>(m.openBlock(),m.createBlock(br,m.mergeProps({key:l.key,margin:e.itemMargin,flexDirection:e.itemDirection,label:l.label},a.$attrs),{default:m.withCtx(()=>[m.createVNode(wr,{item:l.item,itemIndex:l.itemIndex,shape:e.shape,width:e.shapeWidth,height:e.shapeHeight,margin:e.shapeMargin,label:l.label,fill:e.fill,size:e.size,shapeStyle:e.shapeStyle},null,8,["item","itemIndex","shape","width","height","margin","label","fill","size","shapeStyle"]),m.createVNode(Mr,{label:l.label.text,flex:e.labelFlex,margin:e.labelMargin,align:e.labelAlign,legendLabelProps:e.legendLabelProps},null,8,["label","flex","margin","align","legendLabelProps"])]),_:2},1040,["margin","flexDirection","label"]))),128))],4))}}),io=m.defineComponent({props:{scale:null,style:null,domain:null,shapeWidth:null,shapeHeight:null,shapeMargin:null,labelAlign:null,labelFlex:null,labelMargin:null,itemMargin:null,direction:null,itemDirection:null,fill:null,size:null,shape:null,shapeStyle:null,labelFormat:null,labelTransform:null,legendLabelProps:null,steps:{default:5}},setup(n){const e=n,t=m.computed(()=>{if(e.domain)return e.domain;const{steps:r,scale:i}=e;return Ga({steps:r,scale:i})});return(r,i)=>(m.openBlock(),m.createBlock(vr,m.mergeProps(r.$attrs,{scale:e.scale,domain:m.unref(t)}),null,16,["scale","domain"]))}}),uo=["transform"],ao={inheritAttrs:!0},oo=m.defineComponent(Vn(Pn({},ao),{props:{top:{default:0},left:{default:0},transform:null,class:null},setup(n){const e=n;return(t,r)=>(m.openBlock(),m.createElementBlock("g",m.mergeProps({class:e.class,transform:e.transform?e.transform:`translate(${e.left}, ${e.top})`},t.$attrs),[m.renderSlot(t.$slots,"default")],16,uo))}}));exports.Bar=Za;exports.Circle=Xa;exports.Group=oo;exports.Legend=vr;exports.LegendItem=br;exports.LegendLabel=Mr;exports.LegendLinear=io;exports.LegendShape=wr;exports.LinePath=Qa;exports.line=xr;exports.scaleBand=Sa;exports.scaleLinear=_a;exports.scaleLog=La;exports.scaleOrdinal=Ha;exports.scaleQuantile=Ya;exports.scaleThreshold=Oa;exports.scaleTime=Da;exports.scaleUtc=$a;exports.valueOrIdentity=je;exports.valueOrIdentityString=Se; 2 | -------------------------------------------------------------------------------- /packages/index.js: -------------------------------------------------------------------------------- 1 | export * from "./vuenique-scale/index.ts"; 2 | export * from "./vuenique-shape/index.ts"; 3 | export * from "./vuenique-legend/index.ts"; 4 | export * from "./vuenique-group/index.ts"; 5 | -------------------------------------------------------------------------------- /packages/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vueniquejs/vuenique", 3 | "version": "1.0.3", 4 | "description": "Low level visualization component library built for Vue", 5 | "main": "build/index.cjs.js", 6 | "module": "build/index.es.js", 7 | "scripts": { 8 | "test": "npx vitest", 9 | "build": "npx tsc" 10 | }, 11 | "dependencies": { 12 | "d3-array": "^3.1.3", 13 | "d3-interpolate": "^3.0.1", 14 | "d3-scale": "^4.0.2", 15 | "d3-shape": "^3.1.0", 16 | "d3-time": "^3.0.0", 17 | "timezone-mock": "^1.3.1", 18 | "vue": "^3.2.31" 19 | }, 20 | "devDependencies": { 21 | "@rushstack/eslint-patch": "^1.1.0", 22 | "@types/d3-array": "^3.0.2", 23 | "@types/d3-scale": "^4.0.2", 24 | "@types/d3-shape": "^3.0.2", 25 | "@types/jsdom": "^16.2.14", 26 | "@types/node": "^16.11.25", 27 | "@vitejs/plugin-vue": "^2.2.2", 28 | "@vitejs/plugin-vue-jsx": "^1.3.7", 29 | "@vue/eslint-config-prettier": "^7.0.0", 30 | "@vue/eslint-config-typescript": "^10.0.0", 31 | "@vue/test-utils": "^2.0.0-rc.18", 32 | "@vue/tsconfig": "^0.1.3", 33 | "cypress": "^9.5.0", 34 | "eslint": "^8.5.0", 35 | "eslint-plugin-cypress": "^2.12.1", 36 | "eslint-plugin-vue": "^8.2.0", 37 | "jsdom": "^19.0.0", 38 | "prettier": "^2.5.1", 39 | "start-server-and-test": "^1.14.0", 40 | "typescript": "~4.5.5", 41 | "vite": "^2.8.4", 42 | "vitest": "^0.5.0", 43 | "vue-tsc": "^0.31.4" 44 | }, 45 | "repository": { 46 | "type": "git", 47 | "url": "git+https://github.com/oslabs-beta/Vuenique.git" 48 | }, 49 | "keywords": [ 50 | "vuenique", 51 | "vue", 52 | "d3", 53 | "visualizations", 54 | "charts" 55 | ], 56 | "author": "@jamesma1", 57 | "contributors": [ 58 | "James Ma", 59 | "Trevor Gray", 60 | "Miaowen Zeng", 61 | "Alex Corlin", 62 | "Alex Haile" 63 | ], 64 | "license": "MIT", 65 | "bugs": { 66 | "url": "https://github.com/oslabs-beta/Vuenique/issues" 67 | }, 68 | "homepage": "https://vuenique.net/" 69 | } 70 | -------------------------------------------------------------------------------- /packages/vuenique-group/Group.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 26 | 27 | 42 | -------------------------------------------------------------------------------- /packages/vuenique-group/__tests__/Group.spec.ts: -------------------------------------------------------------------------------- 1 | import {ref} from 'vue'; 2 | import { describe, it, expect, test } from 'vitest'; 3 | import { mount } from '@vue/test-utils'; 4 | 5 | import Group from "../Group.vue" 6 | 7 | const groupWrapper = (props = {}, attrs = {}) => mount(Group, { 8 | shallow: true, 9 | propsData: { 10 | ...props, 11 | }, 12 | attrs: { 13 | ...attrs, 14 | } 15 | }); 16 | 17 | describe(' component', () => { 18 | 19 | test('should be defined', () => { 20 | expect(Group).toBeDefined(); 21 | }); 22 | 23 | test('should have default props top=0 and left=0', () => { 24 | const groupWrapper1 = groupWrapper(); 25 | expect(groupWrapper1.find('g').attributes().transform).toEqual('translate(0, 0)'); 26 | }); 27 | 28 | test('should setprops top, left, and class', () => { 29 | const groupWrapper2 = groupWrapper({ class : 'test', left: 4, top: 5 }); 30 | expect(groupWrapper2.find('g').attributes().transform).toEqual('translate(4, 5)'); 31 | expect(groupWrapper2.props('class')).toEqual('test'); 32 | }); 33 | 34 | test('should set restProps', () => { 35 | const groupWrapper3 = groupWrapper({clipPath: 'url(#vuenique)', stroke: 'orange'}); 36 | expect(groupWrapper3.vm.$attrs.clipPath).toEqual('url(#vuenique)'); 37 | expect(groupWrapper3.vm.$attrs.stroke).toEqual('orange'); 38 | }); 39 | 40 | }); 41 | -------------------------------------------------------------------------------- /packages/vuenique-group/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Group } from "./Group.vue"; 2 | -------------------------------------------------------------------------------- /packages/vuenique-legend/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./utility/defaultDomain"; 2 | export * from "./utility/identity"; 3 | export * from "./utility/labelTransformFactory"; 4 | export * from "./utility/valueOrIdentity"; 5 | 6 | export { default as Legend } from "./legends/Legend/index.vue"; 7 | export { default as LegendItem } from "./legends/Legend/LegendItem.vue"; 8 | export { default as LegendLabel } from "./legends/Legend/LegendLabel.vue"; 9 | export { default as LegendShape } from "./legends/Legend/LegendShape.vue"; 10 | export { default as LegendLinear } from "./legends/Linear.vue"; 11 | -------------------------------------------------------------------------------- /packages/vuenique-legend/legends/Legend/LegendItem.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /packages/vuenique-legend/legends/Legend/LegendLabel.vue: -------------------------------------------------------------------------------- 1 | 20 | 34 | -------------------------------------------------------------------------------- /packages/vuenique-legend/legends/Legend/LegendShape.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /packages/vuenique-legend/legends/Legend/Threshold.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Vuenique/f3dd4b71c12f3861001122bf53f34e9fbb2d60a7/packages/vuenique-legend/legends/Legend/Threshold.vue -------------------------------------------------------------------------------- /packages/vuenique-legend/legends/Legend/index.vue: -------------------------------------------------------------------------------- 1 | 120 | 121 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /packages/vuenique-legend/legends/Linear.vue: -------------------------------------------------------------------------------- 1 | 63 | 70 | -------------------------------------------------------------------------------- /packages/vuenique-legend/legends/Ordinal.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Vuenique/f3dd4b71c12f3861001122bf53f34e9fbb2d60a7/packages/vuenique-legend/legends/Ordinal.vue -------------------------------------------------------------------------------- /packages/vuenique-legend/legends/Quantile.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Vuenique/f3dd4b71c12f3861001122bf53f34e9fbb2d60a7/packages/vuenique-legend/legends/Quantile.vue -------------------------------------------------------------------------------- /packages/vuenique-legend/legends/Size.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Vuenique/f3dd4b71c12f3861001122bf53f34e9fbb2d60a7/packages/vuenique-legend/legends/Size.vue -------------------------------------------------------------------------------- /packages/vuenique-legend/types/index.ts: -------------------------------------------------------------------------------- 1 | import type * as Vue from "vue"; 2 | 3 | // legend 4 | export type LegendProps = { 5 | // name for legend wrapper/container 6 | className?: string; 7 | // styles for legend container 8 | style?: any; 9 | // domain of legend 10 | domain?: any[]; 11 | // legend width 12 | shapeWidth?: string | number; 13 | // legend height 14 | shapeHeight?: string | number; 15 | // legend margin 16 | shapeMargin?: string | number; 17 | // alignment of legend labels 18 | labelAlign?: string; 19 | // scale function 20 | scale: any; 21 | // flex-box flex of legend item labels 22 | labelFlex?: string | number; 23 | // margin for the legend labels 24 | labelMargin?: string | number; 25 | // margin for legend items 26 | itemMargin?: string | number; 27 | // legend's own direction 28 | direction?: FlexDirection; 29 | // direction of items 30 | itemDirection?: FlexDirection; 31 | // fill accessor function 32 | fill?: (label: any) => string | undefined; 33 | // size accessor function 34 | size?: (label: any) => string | number | undefined; 35 | // legend shape string preset or Element or Component 36 | shape?: LegendShape; 37 | // styles for the legend shapes 38 | shapeStyle?: (label: any) => Vue.CSSProperties; 39 | // given a legend item and its index, returns an item label 40 | labelFormat?: any; 41 | // given the legend scale and labelFormatter, returns a label with datum, index, value, and label 42 | labelTransform?: any; 43 | // extra props 44 | legendLabelProps?: Partial; 45 | }; 46 | 47 | // types for legendLinear 48 | export type LinearProps = LegendProps & { steps?: number }; 49 | 50 | // shape types 51 | export type LegendShape = "rect" | "circle" | "line"; 52 | 53 | // label formatter function 54 | export type LabelFormatter = ( 55 | item: Datum, 56 | itemIndex: number 57 | ) => Datum | string | number | undefined; 58 | 59 | // label object 60 | export type FormattedLabel = { 61 | datum: Datum; 62 | index: number; 63 | text: string; 64 | value?: Output; 65 | } & ExtraAttributes; 66 | 67 | // renderShape props 68 | export type RenderShapeProvidedProps = { 69 | width?: string | number; 70 | height?: string | number; 71 | label: FormattedLabel; 72 | item: Data; 73 | itemIndex: number; 74 | fill?: string; 75 | size?: string | number; 76 | style?: Vue.CSSProperties; 77 | }; 78 | 79 | // accepted types for flexDirection attribute 80 | export type FlexDirection = 81 | | "inherit" 82 | | "initial" 83 | | "revert" 84 | | "unset" 85 | | "column" 86 | | "column-reverse" 87 | | "row" 88 | | "row-reverse"; 89 | 90 | // legendLabel own properties 91 | export type LegendLabelOwnProps = { 92 | align?: string; 93 | label?: Vue.VNode; 94 | flex?: string | number; 95 | margin?: string | number; 96 | children?: Vue.VNode; 97 | }; 98 | 99 | // all legendLabel accepted props 100 | export type LegendLabelProps = LegendLabelOwnProps & 101 | Omit; 102 | 103 | // legendItem own props 104 | export type LegendItemOwnProps = { 105 | flexDirection?: FlexDirection; 106 | alignItems?: string; 107 | margin?: string | number; 108 | // children?: Vue.VNode; // don't need to type check children because of slots 109 | display?: string; 110 | }; 111 | 112 | // all accepted legendItem props 113 | export type LegendItemProps = LegendItemOwnProps & 114 | Omit; 115 | 116 | // legendShape 117 | export type LegendShapeProps = { 118 | label?: any; 119 | //confused on label, left as any for now 120 | //item?: string; 121 | //item?: Data; 122 | itemIndex: number; 123 | margin?: string | number; 124 | shape?: string; 125 | //shape?: LegendShape; 126 | //fill?: () => string | undefined; 127 | //size?: () => string | number | undefined; 128 | //shapeStyle?: any; 129 | // width?: string | number; 130 | // height?: string | number; 131 | //EXPLAINER: removed a bunch of stuff for the shape, since we will be doing just rect and circle for now 132 | //we will be leveraging the svg defaults for these then 133 | }; 134 | -------------------------------------------------------------------------------- /packages/vuenique-legend/utility/defaultDomain.ts: -------------------------------------------------------------------------------- 1 | export default function defaultDomain({ 2 | steps, 3 | scale, 4 | }: { 5 | steps: number; 6 | scale: any; 7 | }) { 8 | const domain = scale.domain(); 9 | const increment = (domain[domain.length - 1] - domain[0]) / (steps - 1); 10 | const result = new Array(5); 11 | result[0] = domain[0]; 12 | for (let i = 1; i < steps; i++) { 13 | result[i] = result[i - 1] + increment; 14 | } 15 | return result; 16 | } 17 | -------------------------------------------------------------------------------- /packages/vuenique-legend/utility/identity.ts: -------------------------------------------------------------------------------- 1 | export default function identity(x: any) { 2 | return x; 3 | } 4 | -------------------------------------------------------------------------------- /packages/vuenique-legend/utility/labelTransformFactory.ts: -------------------------------------------------------------------------------- 1 | export default function labelTransformFactory({ 2 | scale, 3 | labelFormat, 4 | }: { 5 | scale: any; 6 | labelFormat: any; 7 | }) { 8 | return (d, i) => ({ 9 | datum: d, 10 | index: i, 11 | // text of legend item 12 | text: `${labelFormat(d, i)}`, 13 | value: scale(d), 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /packages/vuenique-legend/utility/valueOrIdentity.ts: -------------------------------------------------------------------------------- 1 | export type ValueOrIdentity = T | { value?: T }; 2 | 3 | /** returns the value of the argument's value property, if defined, or the argument itself. */ 4 | export function valueOrIdentity(_: ValueOrIdentity): T { 5 | if ( 6 | _ && 7 | typeof _ === "object" && 8 | "value" in _ && 9 | typeof _.value !== "undefined" 10 | ) 11 | return _.value; 12 | return _ as T; 13 | } 14 | 15 | /** returns the value of the argument's value property, if defined, or the argument itself coerced to a string. */ 16 | export function valueOrIdentityString(_: ValueOrIdentity): string { 17 | return String(valueOrIdentity(_)); 18 | } 19 | -------------------------------------------------------------------------------- /packages/vuenique-scale/__tests__/band.spec.ts: -------------------------------------------------------------------------------- 1 | import band from '../scales/band.ts'; 2 | 3 | describe('scaleBand as band', () => { 4 | 5 | test('should be defined', () => { 6 | expect(band).toBeDefined(); 7 | }); 8 | 9 | test('set domain', () => { 10 | const domain = [0, 350]; 11 | const scale = band({ domain }); 12 | expect(scale.domain()).toEqual(domain); 13 | }); 14 | 15 | test('set range', () => { 16 | const range = [2, 3] 17 | const scale = band({ range }); 18 | expect(scale.range()).toEqual(range); 19 | }); 20 | 21 | test('set align', () => { 22 | expect(band({ align: 0.7}).align()).toEqual(0.7); 23 | }); 24 | 25 | test('set padding', () => { 26 | expect(band({ padding: 0.2 }).padding()).toEqual(0.2); 27 | }); 28 | 29 | test('set paddingInner', () => { 30 | expect(band({ paddingInner: 0.5 }).paddingInner()).toEqual(0.5); 31 | }); 32 | 33 | test('set paddingOuter', () => { 34 | expect(band({paddingOuter: 0.9}).paddingOuter()).toEqual(0.9); 35 | }); 36 | 37 | describe('set round', () => { 38 | test('true', () => { 39 | const scale = band({ domain: ['a', 'b', 'c', 'd'], range: [1.1, 3.5], round: true }); 40 | expect(scale('a')).toEqual(2); 41 | expect(scale('b')).toEqual(2); 42 | expect(scale('c')).toEqual(2); 43 | expect(scale('c')).toEqual(2); 44 | }); 45 | test('false', () => { 46 | const scale = band({ domain: ['a', 'b', 'c', 'd'], range: [1.1, 3.1], round: false }); 47 | expect(scale('a')).toEqual(1.1); 48 | expect(scale('b')).toEqual(1.6); 49 | expect(scale('c')).toEqual(2.1); 50 | expect(scale('d')).toEqual(2.6); 51 | }) 52 | 53 | }) 54 | }) -------------------------------------------------------------------------------- /packages/vuenique-scale/__tests__/scaleLinear.spec.ts: -------------------------------------------------------------------------------- 1 | import { scaleLinear } from '..'; 2 | 3 | describe('scaleLinear()', () => { 4 | 5 | test('should be defined', () =>{ 6 | expect(scaleLinear).toBeDefined(); 7 | }); 8 | 9 | test('set domain', () => { 10 | const domain = [1, 2]; 11 | expect(scaleLinear({ domain }).domain()).toEqual([1, 2]); 12 | }); 13 | 14 | test('set range', () => { 15 | const range = [1, 2]; 16 | expect(scaleLinear({ range }).range()).toEqual([1, 2]); 17 | }); 18 | 19 | test('set reverse', () => { 20 | expect(scaleLinear({ reverse: true }).range()).toEqual([1, 0]); 21 | expect(scaleLinear({ range: [2, 5], reverse: true }).range()).toEqual([5, 2]); 22 | }); 23 | 24 | describe('set clamp', () => { 25 | test('true', () => { 26 | const scale = scaleLinear({ clamp: true}); 27 | expect(scale(20)).toEqual(1); 28 | }); 29 | 30 | test('false', () => { 31 | const scale = scaleLinear({ clamp: false}); 32 | expect(scale(20)).toEqual(20); 33 | }); 34 | }); 35 | 36 | describe('set color interpolate', () => { 37 | 38 | test('string', () => { 39 | const scale = scaleLinear({ 40 | domain: [0, 10], 41 | range: ['#ff0000', '#000000'], 42 | interpolate: 'lab', 43 | }); 44 | expect(scale(5)).toEqual('rgb(122, 27, 11)'); 45 | }); 46 | 47 | test('config object', () => { 48 | const scale = scaleLinear({ 49 | domain: [0, 10], 50 | range: ['#ff0000', '#000000'], 51 | interpolate: { 52 | type: 'rgb', 53 | }, 54 | }); 55 | expect(scale(5)).toEqual('rgb(128, 0, 0)'); 56 | }); 57 | 58 | test('config object with gamma', () => { 59 | const scale = scaleLinear({ 60 | domain: [0, 10], 61 | range:['#ff0000', '#000000'], 62 | interpolate:{ 63 | type: 'rgb', 64 | gamma: 0.9, 65 | }, 66 | }); 67 | expect(scale(5)).toEqual('rgb(118, 0, 0)'); 68 | }); 69 | }); 70 | 71 | describe('set nice', () => { 72 | 73 | test('true', () => { 74 | const scale = scaleLinear({domain: [0.1, 0.91], nice: true}); 75 | expect(scale.domain()).toEqual([0.1, 1]); 76 | }); 77 | 78 | test('false', () => { 79 | const scale = scaleLinear({ domain: [0.1, 0.91], nice: false}); 80 | }); 81 | }); 82 | 83 | describe('set round', () => { 84 | 85 | test('true', () => { 86 | const scale = scaleLinear({ domain: [0, 10], range: [0, 10], round: true }); 87 | expect(scale(2.2)).toEqual(2); 88 | expect(scale(2.6)).toEqual(3); 89 | }); 90 | 91 | test('false', () => { 92 | const scale = scaleLinear({ domain: [0, 10], range: [0, 10], round: false }); 93 | expect(scale(2.2)).toEqual(2.2); 94 | expect(scale(2.6)).toEqual(2.6); 95 | }); 96 | 97 | test('warns if do both interpolate and round', () => { 98 | //can be accomplished either using global to access warn or spyOn 99 | global.console = {warn: vi.fn()}; 100 | scaleLinear({ 101 | domain: [0, 10], 102 | range: [0, 10], 103 | interpolate: 'hsl', 104 | round: true, 105 | }); 106 | //vi.spyOn(global.console, 'warn'); 107 | expect(console.warn).toHaveBeenCalledTimes(1); 108 | }); 109 | }); 110 | 111 | describe('set zero', () => { 112 | test('true', () => { 113 | expect(scaleLinear({ domain: [1, 2], zero: true }).domain()).toEqual([0, 2]); 114 | expect(scaleLinear({ domain: [1, -2], zero: true }).domain()).toEqual([1, -2]); 115 | expect(scaleLinear({ domain: [-2, -1], zero: true }).domain()).toEqual([-2, 0]); 116 | expect(scaleLinear({ domain: [-2, 3], zero: true }).domain()).toEqual([-2, 3]); 117 | }); 118 | test('false', () => { 119 | expect(scaleLinear({ domain: [1, 2], zero: false }).domain()).toEqual([1, 2]); 120 | expect(scaleLinear({ domain: [-2, -1], zero: false }).domain()).toEqual([-2, -1]); 121 | expect(scaleLinear({ domain: [-2, 3], zero: false }).domain()).toEqual([-2, 3]); 122 | }); 123 | 124 | }); 125 | }); -------------------------------------------------------------------------------- /packages/vuenique-scale/__tests__/scaleLog.spec.ts: -------------------------------------------------------------------------------- 1 | import { scaleLog } from '..'; 2 | 3 | describe('scaleLog()', () => { 4 | 5 | test('should be defined', () => { 6 | expect(scaleLog).toBeDefined(); 7 | }); 8 | 9 | test('set domain', () => { 10 | const domain = [2, 3]; 11 | expect(scaleLog({ domain: [2, 3] }).domain()).toEqual(domain); 12 | }); 13 | 14 | test('set range', () => { 15 | const range = [3, 4]; 16 | expect(scaleLog({ range: [3, 4] }).range()).toEqual(range); 17 | }); 18 | 19 | test('set base', () => { 20 | expect(scaleLog({ base: 5 }).base()).toEqual(5); 21 | }); 22 | 23 | describe('set clamp', () => { 24 | 25 | test('true', () => { 26 | const scale = scaleLog({ range: [1, 4], clamp: true }); 27 | expect(scale(100)).toEqual(4); 28 | }); 29 | 30 | test('false', () => { 31 | const scale = scaleLog({ range: [1, 2], clamp: false }); 32 | expect(scale(10000)).toEqual(5); 33 | }); 34 | }); 35 | 36 | test('set (color) interpolate', () => { 37 | const scale = scaleLog({ 38 | domain: [1, 100], 39 | range: ['#ff0000', '#000000'], 40 | interpolate: 'lab', 41 | }); 42 | expect(scale(10)).toEqual('rgb(122, 27, 11)'); 43 | }); 44 | 45 | describe('set nice', () => { 46 | 47 | test('true', () => { 48 | const scale = scaleLog({ domain: [0.1, 0.89], nice: true }); 49 | expect(scale.domain()).toEqual([0.1, 1]); 50 | }); 51 | 52 | test('false', () => { 53 | const scale = scaleLog({ domain: [0.1, 0.78], nice: false }); 54 | expect(scale.domain()).toEqual([0.1, 0.78]); 55 | }); 56 | }); 57 | 58 | describe('set round', () => { 59 | 60 | test('true', () => { 61 | const scale = scaleLog({ domain: [1, 10], range: [1, 10], round: true }); 62 | expect(scale(2.2)).toEqual(4); 63 | }); 64 | 65 | test('false', () => { 66 | const scale = scaleLog({ domain: [1, 10], range: [1, 10], round: false }); 67 | expect(scale(5)?.toFixed(2)).toEqual('7.29'); 68 | }); 69 | }); 70 | }); -------------------------------------------------------------------------------- /packages/vuenique-scale/__tests__/scaleOrdinal.spec.ts: -------------------------------------------------------------------------------- 1 | import { scaleOrdinal } from '..'; 2 | 3 | describe('scaleOrdinal', () => { 4 | 5 | test('should be defined', () => { 6 | expect(scaleOrdinal).toBeDefined(); 7 | }); 8 | 9 | test('set domain', () => { 10 | const domain = ['noodle', 'burger']; 11 | const scale = scaleOrdinal({ domain }); 12 | expect(scale.domain()).toEqual(domain); 13 | }); 14 | 15 | test('set range', () => { 16 | const range = ['red', 'green']; 17 | const scale = scaleOrdinal({ range }); 18 | expect(scale.range()).toEqual(range); 19 | }); 20 | 21 | test('set unknown', () => { 22 | const scale = scaleOrdinal({ domain: ['noodle', 'burger'], unknown: 'green' }); 23 | expect(scale('hello')).toEqual('green'); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /packages/vuenique-scale/__tests__/scaleQuantile.spec.ts: -------------------------------------------------------------------------------- 1 | import { scaleQuantile } from '..'; 2 | 3 | describe('scaleQuantile', () => { 4 | 5 | test('should be defined', () => { 6 | expect(scaleQuantile).toBeDefined(); 7 | }); 8 | 9 | test('set domain', () => { 10 | const domain = [0, 350]; 11 | const scale = scaleQuantile({ domain }); 12 | expect(scale.domain()).toEqual(domain); 13 | }); 14 | 15 | test('set range', () => { 16 | const range = [2, 3]; 17 | const scale = scaleQuantile({ range }); 18 | expect(scale.range()).toEqual(range); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/vuenique-scale/__tests__/scaleThreshold.spec.ts: -------------------------------------------------------------------------------- 1 | import { scaleThreshold } from '..'; 2 | 3 | describe('scaleThreshold', () => { 4 | 5 | test('should be defined', () => { 6 | expect(scaleThreshold).toBeDefined(); 7 | }); 8 | 9 | test('set domain', () => { 10 | const domain = [0, 350]; 11 | const scale = scaleThreshold({ domain }); 12 | expect(scale.domain()).toEqual(domain); 13 | }); 14 | 15 | test('set range', () => { 16 | const range = [2, 3]; 17 | const scale = scaleThreshold({ range }); 18 | expect(scale.range()).toEqual(range); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/vuenique-scale/__tests__/scaleUtc.spec.ts: -------------------------------------------------------------------------------- 1 | import TimezoneMock from 'timezone-mock'; 2 | import { scaleUtc } from '..'; 3 | 4 | describe('scaleUtc()', () => { 5 | let domain: [Date, Date]; 6 | let unniceDomain: [Date, Date]; 7 | 8 | beforeAll(() => { 9 | TimezoneMock.register('US/Pacific'); 10 | domain = [new Date(Date.UTC(2020, 0, 1)), new Date(Date.UTC(2020, 0, 10))]; 11 | unniceDomain = [new Date(Date.UTC(2020, 0, 1)), new Date(Date.UTC(2020, 0, 9, 20))]; 12 | }); 13 | 14 | afterAll(() => { 15 | TimezoneMock.unregister(); 16 | }); 17 | 18 | test('should be defined', () => { 19 | expect(scaleUtc).toBeDefined(); 20 | }); 21 | 22 | test('set domain', () => { 23 | expect(scaleUtc({ domain }).domain()).toEqual(domain); 24 | }); 25 | 26 | test('set range', () => { 27 | const range = [1, 2]; 28 | expect(scaleUtc({ range: [1, 2] }).range()).toEqual(range); 29 | }); 30 | 31 | describe('set clamp', () => { 32 | 33 | test('true', () => { 34 | const scale = scaleUtc({ domain, range: [0, 10], clamp: true }); 35 | expect(scale(new Date(Date.UTC(2019, 11, 31)))).toEqual(0); 36 | }); 37 | 38 | test('false', () => { 39 | const scale = scaleUtc({ domain, range: [0, 10], clamp: false }); 40 | expect(scale(new Date(Date.UTC(2019, 11, 31)))?.toFixed(2)).toEqual('-1.11'); 41 | }); 42 | }); 43 | 44 | test('set (color) interpolate', () => { 45 | const scale = scaleUtc({ 46 | domain, 47 | range: ['#ff0000', '#000000'], 48 | interpolate: 'lab', 49 | }); 50 | expect(scale(new Date(Date.UTC(2020, 0, 5)))).toEqual('rgb(136, 28, 11)'); 51 | }); 52 | 53 | describe('set nice', () => { 54 | 55 | test('true', () => { 56 | const scale = scaleUtc({ 57 | domain: unniceDomain, 58 | nice: true, 59 | }); 60 | expect(scale.domain()).toEqual(domain); 61 | }); 62 | 63 | test('false', () => { 64 | const scale = scaleUtc({ domain: unniceDomain, nice: false }); 65 | expect(scale.domain()).toEqual(unniceDomain); 66 | }); 67 | 68 | test('number', () => { 69 | const scale = scaleUtc({ domain: unniceDomain, nice: 5 }); 70 | expect(scale.domain()).toEqual([ 71 | new Date(Date.UTC(2020, 0, 1)), 72 | new Date(Date.UTC(2020, 0, 11)), 73 | ]); 74 | }); 75 | 76 | test('time unit string', () => { 77 | const scale = scaleUtc({ domain: unniceDomain, nice: 'hour' }); 78 | expect(scale.domain()).toEqual(unniceDomain); 79 | }); 80 | 81 | test('nice object', () => { 82 | const scale = scaleUtc({ domain: unniceDomain, nice: { interval: 'hour', step: 3 } }); 83 | expect(scale.domain()).toEqual([ 84 | new Date(Date.UTC(2020, 0, 1)), 85 | new Date(Date.UTC(2020, 0, 9, 21)), 86 | ]); 87 | }); 88 | }); 89 | 90 | describe('set round', () => { 91 | 92 | test('true', () => { 93 | const scale = scaleUtc({ 94 | domain, 95 | range: [1, 5], 96 | round: true, 97 | }); 98 | expect(scale(new Date(Date.UTC(2020, 0, 5)))).toEqual(3); 99 | }); 100 | 101 | test('false', () => { 102 | const scale = scaleUtc({ 103 | domain, 104 | range: [1, 5], 105 | round: false, 106 | }); 107 | expect(scale(new Date(Date.UTC(2020, 0, 5)))?.toFixed(2)).toEqual('2.78'); 108 | }); 109 | }); 110 | }); 111 | -------------------------------------------------------------------------------- /packages/vuenique-scale/__tests__/time.spec.ts: -------------------------------------------------------------------------------- 1 | import TimezoneMock from 'timezone-mock'; 2 | import time from '../scales/time.ts'; 3 | 4 | describe('time()', () => { 5 | let domain: [Date, Date]; 6 | let unniceDomain: [Date, Date]; 7 | 8 | beforeAll(() => { 9 | TimezoneMock.register('US/Pacific'); 10 | domain = [new Date(2020, 0, 1), new Date(2020, 0, 10)]; 11 | unniceDomain = [new Date(2020, 0, 1), new Date(2020, 0, 9, 20)]; 12 | }); 13 | 14 | afterAll(() => { 15 | TimezoneMock.unregister(); 16 | }); 17 | 18 | test('should be defined', () => { 19 | expect(time).toBeDefined(); 20 | }); 21 | 22 | test('set domain', () => { 23 | expect(time({ domain }).domain()).toEqual(domain); 24 | }); 25 | 26 | test('set range', () => { 27 | const range = [1, 2]; 28 | expect(time({ range: [1, 2] }).range()).toEqual(range); 29 | }); 30 | 31 | describe('set clamp', () => { 32 | 33 | test('true', () => { 34 | const scale = time({ domain, range: [0, 10], clamp: true }); 35 | expect(scale(new Date(2019, 11, 31))).toEqual(0); 36 | }); 37 | 38 | test('false', () => { 39 | const scale = time({ domain, range: [0, 10], clamp: false }); 40 | expect(scale(new Date(2019, 11, 31))?.toFixed(2)).toEqual('-1.11'); 41 | }); 42 | }); 43 | 44 | test('set (color) interpolate', () => { 45 | const scale = time({ 46 | domain, 47 | range: ['#ff0000', '#000000'], 48 | interpolate: 'lab', 49 | }); 50 | expect(scale(new Date(2020, 0, 5))).toEqual('rgb(136, 28, 11)'); 51 | }); 52 | 53 | describe('set nice', () => { 54 | test('true', () => { 55 | const scale = time({ 56 | domain: unniceDomain, 57 | nice: true, 58 | }); 59 | expect(scale.domain()).toEqual(domain); 60 | }); 61 | 62 | test('false', () => { 63 | const scale = time({ domain: unniceDomain, nice: false }); 64 | expect(scale.domain()).toEqual(unniceDomain); 65 | }); 66 | 67 | test('number', () => { 68 | const scale = time({ domain: unniceDomain, nice: 5 }); 69 | expect(scale.domain()).toEqual([new Date(2020, 0, 1), new Date(2020, 0, 11)]); 70 | }); 71 | 72 | test('time unit string', () => { 73 | const scale = time({ domain: unniceDomain, nice: 'hour' }); 74 | expect(scale.domain()).toEqual(unniceDomain); 75 | }); 76 | 77 | test('nice object', () => { 78 | const scale = time({ domain: unniceDomain, nice: { interval: 'hour', step: 3 } }); 79 | expect(scale.domain()).toEqual([new Date(2020, 0, 1), new Date(2020, 0, 9, 21)]); 80 | }); 81 | test('invalid nice object', () => { 82 | const scale = time({ domain: unniceDomain, nice: { interval: 'hour', step: NaN } }); 83 | expect(scale.domain()).toEqual(unniceDomain); 84 | }); 85 | }); 86 | 87 | describe('set round', () => { 88 | test('true', () => { 89 | const scale = time({ 90 | domain, 91 | range: [1, 5], 92 | round: true, 93 | }); 94 | expect(scale(new Date(2020, 0, 5))).toEqual(3); 95 | }); 96 | test('false', () => { 97 | const scale = time({ 98 | domain, 99 | range: [1, 5], 100 | round: false, 101 | }); 102 | expect(scale(new Date(2020, 0, 5))?.toFixed(2)).toEqual('2.78'); 103 | }); 104 | }); 105 | }); -------------------------------------------------------------------------------- /packages/vuenique-scale/index.ts: -------------------------------------------------------------------------------- 1 | export { default as scaleBand } from './scales/band'; 2 | export { default as scaleLinear } from './scales/linear'; 3 | export { default as scaleTime } from './scales/time'; 4 | export { default as scaleLog } from './scales/log'; 5 | export { default as scaleOrdinal } from './scales/ordinal'; 6 | export { default as scaleQuantile } from './scales/quantile'; 7 | export { default as scaleThreshold } from './scales/threshold'; 8 | export { default as scaleUtc } from './scales/utc'; -------------------------------------------------------------------------------- /packages/vuenique-scale/operators/align.ts: -------------------------------------------------------------------------------- 1 | /** Operator function to apply the D3 base method with the user's align value as the argument 2 | Requires the config object to have the align property 3 | 4 | Refer to D3-Scale for D3 align method: {@link https://github.com/d3/d3-scale/} 5 | **/ 6 | export default function applyAlign(scale: any, config: any) { 7 | if ( 8 | "align" in scale && 9 | "align" in config && 10 | typeof config.align !== "undefined" 11 | ) { 12 | scale.align(config.align); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/vuenique-scale/operators/base.ts: -------------------------------------------------------------------------------- 1 | /** Operator function to apply the D3 base method with the user's base value as the argument 2 | Requires the config object to have the base property 3 | 4 | Refer to D3-Scale for more info on D3 base method: {@link https://github.com/d3/d3-scale/} 5 | **/ 6 | export default function applyBase(scale: any, config: any) { 7 | if ( 8 | "base" in scale && 9 | "base" in config && 10 | typeof config.base !== "undefined" 11 | ) { 12 | scale.base(config.base); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/vuenique-scale/operators/clamp.ts: -------------------------------------------------------------------------------- 1 | /** Operator function to apply the D3 clamp method with the user's clamp value as the argument 2 | Requires the config object to have the clamp property 3 | 4 | Refer to D3-Scale for more info on D3 clamp method: {@link https://github.com/d3/d3-scale/} 5 | **/ 6 | 7 | export default function applyClamp(scale: any, config: any) { 8 | if ( 9 | "clamp" in scale && 10 | "clamp" in config && 11 | typeof config.clamp !== "undefined" 12 | ) { 13 | scale.clamp(config.clamp); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/vuenique-scale/operators/domain.ts: -------------------------------------------------------------------------------- 1 | /** Operator function that applys the D3 domain method and asserts the passed in domain's argument type 2 | Refer to D3-Scale for info on D3 domain method: {@link https://github.com/d3/d3-scale/} 3 | 4 | Plan on adding type distinctions between scales with padding properties and scales without 5 | **/ 6 | 7 | export default function applyDomain(scale: any, config: any) { 8 | if (config.domain) { 9 | if ("nice" in scale || "quantiles" in scale) { 10 | // continuous input scales 11 | scale.domain(config.domain as number[] | Date[]); 12 | } else if ("padding" in scale) { 13 | // point and band scales 14 | scale.domain(config.domain); 15 | } else { 16 | // ordinal and threshold scale 17 | scale.domain(config.domain); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/vuenique-scale/operators/interpolate.ts: -------------------------------------------------------------------------------- 1 | import createColorInterpolator from "../utils/createColorInterpolator"; 2 | 3 | /** Operator function to apply the D3 interpolate method with the returned color interpolatation as the argument 4 | Requires the config object to have the interpolate property 5 | 6 | Refer to D3-Scale for more info on D3 interpolate method: {@link https://github.com/d3/d3-scale/} 7 | **/ 8 | 9 | export default function applyInterpolate(scale: any, config: any) { 10 | if ( 11 | "interpolate" in config && 12 | "interpolate" in scale && 13 | typeof config.interpolate !== "undefined" 14 | ) { 15 | const interpolator = createColorInterpolator(config.interpolate); 16 | scale.interpolate(interpolator); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/vuenique-scale/operators/nice.ts: -------------------------------------------------------------------------------- 1 | import { 2 | timeSecond, 3 | timeMinute, 4 | timeHour, 5 | timeDay, 6 | timeYear, 7 | timeMonth, 8 | timeWeek, 9 | utcSecond, 10 | utcMinute, 11 | utcHour, 12 | utcDay, 13 | utcWeek, 14 | utcMonth, 15 | utcYear, 16 | // CountableTimeInterval, 17 | } from "d3-time"; 18 | 19 | import isUtcScale from "../utils/isUtcScale"; 20 | 21 | const localTimeIntervals = { 22 | day: timeDay, 23 | hour: timeHour, 24 | minute: timeMinute, 25 | month: timeMonth, 26 | second: timeSecond, 27 | week: timeWeek, 28 | year: timeYear, 29 | }; 30 | 31 | const utcIntervals = { 32 | day: utcDay, 33 | hour: utcHour, 34 | minute: utcMinute, 35 | month: utcMonth, 36 | second: utcSecond, 37 | week: utcWeek, 38 | year: utcYear, 39 | }; 40 | 41 | /** Operator function to apply the D3 nice method on different arguments dependent on the argument data type 42 | 43 | Refer to D3-Scale for more info on D3 interpolate method: {@link https://github.com/d3/d3-scale/} 44 | **/ 45 | 46 | export default function applyNice(scale: any, config: any) { 47 | if ( 48 | "nice" in config && 49 | typeof config.nice !== "undefined" && 50 | "nice" in scale 51 | ) { 52 | const { nice } = config; 53 | if (typeof nice === "boolean") { 54 | if (nice) { 55 | scale.nice(); 56 | } 57 | } else if (typeof nice === "number") { 58 | scale.nice(nice); 59 | } else { 60 | const timeScale = scale; 61 | const isUtc = isUtcScale(timeScale); 62 | if (typeof nice === "string") { 63 | timeScale.nice(isUtc ? utcIntervals[nice] : localTimeIntervals[nice]); 64 | } else { 65 | const { interval, step } = nice; 66 | const parsedInterval = ( 67 | isUtc ? utcIntervals[interval] : localTimeIntervals[interval] 68 | ).every(step); 69 | if (parsedInterval != null) { 70 | timeScale.nice(parsedInterval); 71 | } 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /packages/vuenique-scale/operators/padding.ts: -------------------------------------------------------------------------------- 1 | /** Operator function applying various types of padding 2 | 3 | Refer to D3-Scale for more info on D3 padding methods: {@link https://github.com/d3/d3-scale/} 4 | **/ 5 | export default function applyPadding(scale: any, config: any) { 6 | if ( 7 | "padding" in scale && 8 | "padding" in config && 9 | typeof config.padding !== "undefined" 10 | ) { 11 | scale.padding(config.padding); 12 | } 13 | if ( 14 | "paddingInner" in scale && 15 | "paddingInner" in config && 16 | typeof config.paddingInner !== "undefined" 17 | ) { 18 | scale.paddingInner(config.paddingInner); 19 | } 20 | if ( 21 | "paddingOuter" in scale && 22 | "paddingOuter" in config && 23 | typeof config.paddingOuter !== "undefined" 24 | ) { 25 | scale.paddingOuter(config.paddingOuter); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/vuenique-scale/operators/range.ts: -------------------------------------------------------------------------------- 1 | // set the range of the scale according to 2 | // the range property passed within config object 3 | export default function applyRange( scale: any, config: any ) { 4 | if (config.range) { 5 | if ('padding' in scale) { 6 | // config.domain will be type [number, number] 7 | // this only apply to band scale 8 | scale.range(config.range as [number, number]); 9 | } else { 10 | // config.domain will have different type if there isn't a padding in scale 11 | scale.range(config.range); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /packages/vuenique-scale/operators/reverse.ts: -------------------------------------------------------------------------------- 1 | // reverse the range of the scale if there 2 | // is a defined reverse property passed within config object 3 | export default function applyReverse(scale: any, config: any) { 4 | if (config.reverse) { 5 | const reversedRange = scale.range().slice().reverse(); 6 | if ("padding" in scale) { 7 | // reverseRnge will be type [number, number] 8 | // only apply to band scale 9 | scale.range(reversedRange); 10 | } else { 11 | // reverseRange will have different type if there isn't a padding in scale 12 | scale.range(reversedRange); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/vuenique-scale/operators/round.ts: -------------------------------------------------------------------------------- 1 | import { interpolateRound } from 'd3-interpolate'; 2 | 3 | // set round property of the scale if there 4 | // is a defined round property passed within config object 5 | export default function applyRound( scale: any, config: any ) { 6 | if ('round' in config && typeof config.round !== 'undefined') { 7 | // Warns the user if passed config has both round and interpolate 8 | if (config.round && 'interpolate' in config && typeof config.interpolate !== 'undefined') { 9 | console.warn( 10 | `[visx/scale/applyRound] ignoring round: scale config contains round and interpolate. only applying interpolate. config:`, 11 | config 12 | ); 13 | } else if ('round' in scale) { 14 | // for band scales, set the round property of it 15 | scale.round(config.round); 16 | } else if ('interpolate' in scale && config.round) { 17 | // for continuous output scales 18 | // setting config.round = true 19 | // is actually setting interpolator to interpolateRound 20 | // as these scales do not have scale.round() function 21 | scale.interpolate(interpolateRound); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/vuenique-scale/operators/scaleOperator.ts: -------------------------------------------------------------------------------- 1 | // imports for all scale operators 2 | import domain from "./domain"; 3 | import range from "./range"; 4 | import align from "./align"; 5 | import base from "./base"; 6 | import clamp from "./clamp"; 7 | // import constant from './constant'; 8 | // import exponent from './exponent'; 9 | import interpolate from "./interpolate"; 10 | import nice from "./nice"; 11 | import padding from "./padding"; 12 | import reverse from "./reverse"; 13 | import round from "./round"; 14 | import unknown from "./unknown"; 15 | import zero from "./zero"; 16 | 17 | // hold all operators in order and prevents array from being modified during compilation 18 | export const ALL_OPERATORS = [ 19 | // domain => nice => zero 20 | "domain", 21 | "nice", 22 | "zero", 23 | 24 | // interpolate before round 25 | "interpolate", 26 | "round", 27 | 28 | // set range then reverse 29 | "range", 30 | "reverse", 31 | 32 | // Order does not matter for these operators 33 | "align", 34 | "base", 35 | "clamp", 36 | // 'constant', 37 | // 'exponent', 38 | "padding", 39 | "unknown", 40 | ] as const; 41 | 42 | type OperatorType = typeof ALL_OPERATORS[number]; 43 | 44 | // Use Record to enforce that all keys in OperatorType must exist. 45 | const operators: Record = { 46 | domain, 47 | nice, 48 | zero, 49 | interpolate, 50 | round, 51 | align, 52 | base, 53 | clamp, 54 | // constant, 55 | // exponent, 56 | padding, 57 | range, 58 | reverse, 59 | unknown, 60 | }; 61 | 62 | export default function scaleOperator(...ops: OperatorType[]) { 63 | const selection = new Set(ops); 64 | const selectedOps = ALL_OPERATORS.filter((o) => selection.has(o)); 65 | 66 | return function applyOperators(scale: any, config?: any) { 67 | if (typeof config !== "undefined") { 68 | selectedOps.forEach((op) => { 69 | operators[op](scale, config); 70 | }); 71 | } 72 | 73 | return scale; 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /packages/vuenique-scale/operators/unknown.ts: -------------------------------------------------------------------------------- 1 | // set of the scale unknown property if there is a defined unknow property 2 | // passed within the config object 3 | export default function applyUnknown(scale: any, config: any) { 4 | if ( 5 | "unknown" in scale && 6 | "unknown" in config && 7 | typeof config.unknown !== "undefined" 8 | ) { 9 | (scale.unknown as Function)(config.unknown); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/vuenique-scale/operators/zero.ts: -------------------------------------------------------------------------------- 1 | // update the domain property of the scale if the zero property 2 | // passed within the config object is set to true 3 | export default function applyZero(scale: any, config: any) { 4 | if ("zero" in config && config.zero === true) { 5 | const domain = scale.domain() as number[]; 6 | const [a, b] = domain; 7 | const isDescending = b < a; 8 | // if domain of the scale is in descending order, change it to be ascending order 9 | // with lower bound less than or equal to zero, higher bound greater or equal to zero 10 | const [min, max] = isDescending ? [b, a] : [a, b]; 11 | const domainWithZero = [Math.min(0, min), Math.max(0, max)]; 12 | scale.domain(isDescending ? domainWithZero.reverse() : domainWithZero); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/vuenique-scale/scales/band.ts: -------------------------------------------------------------------------------- 1 | import { scaleBand } from "d3-scale"; 2 | import scaleOperator from "../operators/scaleOperator"; 3 | 4 | /** Returns applyOperator function */ 5 | export const updateBandScale = scaleOperator( 6 | "domain", 7 | "range", 8 | "reverse", 9 | "align", 10 | "padding", 11 | "round" 12 | ); 13 | 14 | export default function createBandScale(config?: any) { 15 | return updateBandScale(scaleBand(), config); 16 | } 17 | -------------------------------------------------------------------------------- /packages/vuenique-scale/scales/linear.ts: -------------------------------------------------------------------------------- 1 | import { scaleLinear } from "d3-scale"; 2 | import scaleOperator from "../operators/scaleOperator"; 3 | 4 | /** Returns applyOperator function */ 5 | export const updateLinearScale = scaleOperator( 6 | "domain", 7 | "range", 8 | "reverse", 9 | "clamp", 10 | "interpolate", 11 | "nice", 12 | "round", 13 | "zero" 14 | ); 15 | 16 | export default function createLinearScale(config?: any) { 17 | return updateLinearScale(scaleLinear(), config); 18 | } 19 | -------------------------------------------------------------------------------- /packages/vuenique-scale/scales/log.ts: -------------------------------------------------------------------------------- 1 | import { scaleLog } from "d3-scale"; 2 | 3 | import scaleOperator from "../operators/scaleOperator"; 4 | 5 | /** Returns applyOperator function */ 6 | export const updateLogScale = scaleOperator( 7 | "domain", 8 | "range", 9 | "reverse", 10 | "base", 11 | "clamp", 12 | "interpolate", 13 | "nice", 14 | "round" 15 | ); 16 | 17 | export default function createLogScale(config?: any) { 18 | return updateLogScale(scaleLog(), config); 19 | } 20 | -------------------------------------------------------------------------------- /packages/vuenique-scale/scales/ordinal.ts: -------------------------------------------------------------------------------- 1 | import { scaleOrdinal } from "d3-scale"; 2 | import scaleOperator from "../operators/scaleOperator"; 3 | 4 | /** Returns applyOperator function */ 5 | export const updateOrdinalScale = scaleOperator( 6 | "domain", 7 | "range", 8 | "reverse", 9 | "unknown" 10 | ); 11 | 12 | export default function createOrdinalScale(config?: any) { 13 | return updateOrdinalScale(scaleOrdinal(), config); 14 | } 15 | -------------------------------------------------------------------------------- /packages/vuenique-scale/scales/quantile.ts: -------------------------------------------------------------------------------- 1 | import { scaleQuantile } from "d3-scale"; 2 | 3 | import scaleOperator from "../operators/scaleOperator"; 4 | 5 | /** Returns applyOperator function */ 6 | export const updateQuantileScale = scaleOperator("domain", "range", "reverse"); 7 | 8 | export default function createQuantileScale(config?: any) { 9 | return updateQuantileScale(scaleQuantile(), config); 10 | } 11 | -------------------------------------------------------------------------------- /packages/vuenique-scale/scales/threshold.ts: -------------------------------------------------------------------------------- 1 | import { scaleThreshold } from "d3-scale"; 2 | import scaleOperator from "../operators/scaleOperator"; 3 | 4 | export const updateThresholdScale = scaleOperator("domain", "range", "reverse"); 5 | 6 | /** Returns applyOperator function */ 7 | export default function createThresholdScale(config?: any) { 8 | return updateThresholdScale(scaleThreshold(), config); 9 | } 10 | -------------------------------------------------------------------------------- /packages/vuenique-scale/scales/time.ts: -------------------------------------------------------------------------------- 1 | import { scaleTime } from "d3-scale"; 2 | 3 | import scaleOperator from "../operators/scaleOperator"; 4 | 5 | type DefaultOutput = number | string | boolean | null; 6 | 7 | /** Returns applyOperator function */ 8 | export const updateTimeScale = scaleOperator( 9 | "domain", 10 | "range", 11 | "reverse", 12 | "clamp", 13 | "interpolate", 14 | "nice", 15 | "round" 16 | ); 17 | 18 | export default function createTimeScale(config?: any) { 19 | return updateTimeScale(scaleTime(), config); 20 | } 21 | -------------------------------------------------------------------------------- /packages/vuenique-scale/scales/utc.ts: -------------------------------------------------------------------------------- 1 | import { scaleUtc } from "d3-scale"; 2 | import scaleOperator from "../operators/scaleOperator"; 3 | 4 | /** Returns applyOperator function */ 5 | export const updateUtcScale = scaleOperator( 6 | "domain", 7 | "range", 8 | "reverse", 9 | "clamp", 10 | "interpolate", 11 | "nice", 12 | "round" 13 | ); 14 | 15 | export default function createUtcScale(config?: any) { 16 | return updateUtcScale(scaleUtc(), config); 17 | } 18 | -------------------------------------------------------------------------------- /packages/vuenique-scale/utils/coerceNumber.ts: -------------------------------------------------------------------------------- 1 | export default function coerceNumber(val: any): T | number { 2 | if ( 3 | (typeof val === "function" || (typeof val === "object" && !!val)) && 4 | "valueOf" in val 5 | ) { 6 | const num = val.valueOf(); 7 | if (typeof num === "number") return num; 8 | } 9 | 10 | return val as T; 11 | } 12 | -------------------------------------------------------------------------------- /packages/vuenique-scale/utils/createColorInterpolator.ts: -------------------------------------------------------------------------------- 1 | import { 2 | interpolateRgb, 3 | interpolateLab, 4 | interpolateHcl, 5 | interpolateHclLong, 6 | interpolateHsl, 7 | interpolateHslLong, 8 | interpolateCubehelix, 9 | interpolateCubehelixLong, 10 | } from "d3-interpolate"; 11 | 12 | const interpolatorMap = { 13 | lab: interpolateLab, 14 | hcl: interpolateHcl, 15 | "hcl-long": interpolateHclLong, 16 | hsl: interpolateHsl, 17 | "hsl-long": interpolateHslLong, 18 | cubehelix: interpolateCubehelix, 19 | "cubehelix-long": interpolateCubehelixLong, 20 | rgb: interpolateRgb, 21 | } as const; 22 | 23 | export default function createColorInterpolator(interpolate: any) { 24 | switch (interpolate) { 25 | case "lab": 26 | case "hcl": 27 | case "hcl-long": 28 | case "hsl": 29 | case "hsl-long": 30 | case "cubehelix": 31 | case "cubehelix-long": 32 | case "rgb": 33 | return interpolatorMap[interpolate]; 34 | default: 35 | } 36 | 37 | const { type, gamma } = interpolate; 38 | const interpolator = interpolatorMap[type]; 39 | return typeof gamma === "undefined" 40 | ? interpolator 41 | : interpolator.gamma(gamma); 42 | } 43 | -------------------------------------------------------------------------------- /packages/vuenique-scale/utils/getTicks.ts: -------------------------------------------------------------------------------- 1 | export default function getTicks(scale: any, numTicks?: number) { 2 | const s = scale; 3 | if ("ticks" in s) { 4 | return s.ticks(numTicks); 5 | } 6 | return s 7 | .domain() 8 | .filter( 9 | (_, index, arr) => 10 | numTicks == null || 11 | arr.length <= numTicks || 12 | index % Math.round((arr.length - 1) / numTicks) === 0 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/vuenique-scale/utils/inferScaleType.ts: -------------------------------------------------------------------------------- 1 | import isUtcScale from './isUtcScale'; 2 | 3 | export default function inferScaleType(scale) 4 | 5 | if ('paddingInner' in scale) { 6 | return 'band'; 7 | } 8 | 9 | if ('padding' in scale) { 10 | return 'point'; 11 | } 12 | 13 | if ('quantiles' in scale) { 14 | return 'quantile'; 15 | } 16 | 17 | if ('base' in scale) { 18 | return 'log'; 19 | } 20 | 21 | if ('exponent' in scale) { 22 | return scale.exponent() === 0.5 ? 'sqrt' : 'pow'; 23 | } 24 | 25 | if ('constant' in scale) { 26 | return 'symlog'; 27 | } 28 | 29 | if ('clamp' in scale) { 30 | // Linear, Time or Utc scales 31 | if (scale.ticks()[0] instanceof Date) { 32 | return isUtcScale(scale) ? 'utc' : 'time'; 33 | } 34 | return 'linear'; 35 | } 36 | 37 | if ('nice' in scale) { 38 | return 'quantize'; 39 | } 40 | 41 | if ('invertExtent' in scale) { 42 | return 'threshold'; 43 | } 44 | 45 | return 'ordinal'; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /packages/vuenique-scale/utils/isUtcScale.ts: -------------------------------------------------------------------------------- 1 | const TEST_TIME = new Date(Date.UTC(2020, 1, 2, 3, 4, 5)); 2 | const TEST_FORMAT = "%Y-%m-%d %H:%M"; 3 | 4 | /** 5 | * Check if the scale is UTC or Time scale 6 | * When local time is equal to UTC, always return true 7 | * @param scale time or utc scale 8 | */ 9 | export default function isUtcScale(scale) { 10 | // The only difference between time and utc scale is 11 | // whether the tick format function is utcFormat or timeFormat 12 | const output = scale.tickFormat(1, TEST_FORMAT)(TEST_TIME); 13 | return output === "2020-02-02 03:04"; 14 | } 15 | -------------------------------------------------------------------------------- /packages/vuenique-scale/utils/scaleCanBeZeroed.ts: -------------------------------------------------------------------------------- 1 | const zeroableScaleTypes = new Set([ 2 | "linear", 3 | "pow", 4 | "quantize", 5 | "sqrt", 6 | "symlog", 7 | ]); 8 | 9 | export default function scaleCanBeZeroed(scaleConfig) { 10 | return zeroableScaleTypes.has(scaleConfig.type); 11 | } 12 | -------------------------------------------------------------------------------- /packages/vuenique-scale/utils/toString.ts: -------------------------------------------------------------------------------- 1 | export default function toString(x?: T) { 2 | return x?.toString(); 3 | } 4 | -------------------------------------------------------------------------------- /packages/vuenique-shape/Bar.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 14 | -------------------------------------------------------------------------------- /packages/vuenique-shape/Circle.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/vuenique-shape/LinePath.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /packages/vuenique-shape/__tests__/Bar.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from "vitest"; 2 | import { mount } from "@vue/test-utils"; 3 | 4 | import Bar from "../Bar.vue"; 5 | 6 | // Creates wrapper component around Bar for testing 7 | const BarWrapper = (props = {}, attrs = {}) => mount(Bar, { 8 | shallow: true, 9 | propsData: { 10 | ...props, 11 | }, 12 | attrs: { 13 | ...attrs, 14 | }, 15 | }); 16 | 17 | describe("Bar component...", () => { 18 | test("should be defined", () => { 19 | expect(Bar).toBeDefined(); 20 | }); 21 | 22 | test("should have test class passed in through props", () => { 23 | expect( 24 | BarWrapper({ 25 | class: "test", 26 | }).props("class") 27 | ).toBe("test"); 28 | }); 29 | 30 | test("should have additional attributes passed in through attrs", () => { 31 | expect( 32 | BarWrapper({}, { 33 | x: 10, 34 | y: 10, 35 | } 36 | ).vm.$attrs 37 | ).toEqual({ x: 10, y: 10 }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/vuenique-shape/__tests__/Circle.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from "vitest"; 2 | import { mount } from "@vue/test-utils"; 3 | 4 | import Circle from "../Circle.vue"; 5 | 6 | // test the following properties 7 | // circle defaults to black 8 | // can define fill color 9 | // can pass props 10 | // can pass attributes 11 | 12 | const CircleWrapper = (props = {}, attrs = {}) => 13 | mount(Circle, { 14 | shallow: true, 15 | propsData: { 16 | ...props, 17 | }, 18 | attrs: { 19 | ...attrs, 20 | }, 21 | }); 22 | 23 | describe("Circle component...", () => { 24 | test("should be defined", () => { 25 | expect(Circle).toBeDefined(); 26 | }); 27 | 28 | test("should have test class passed in through props", () => { 29 | expect( 30 | CircleWrapper({ 31 | class: "test", 32 | }).props("class") 33 | ).toBe("test"); 34 | }); 35 | 36 | test("should pass additional props through to element attributes", () => { 37 | const testCircle = CircleWrapper({ cx: 50, cy: 50, r: 50 }); 38 | expect(testCircle.element.getAttribute("cx")).toBe("50"); 39 | expect(testCircle.element.getAttribute("cy")).toBe("50"); 40 | expect(testCircle.element.getAttribute("r")).toBe("50"); 41 | expect(testCircle.element.getAttribute("class")).toBeNull(); 42 | }); 43 | 44 | test("should have fill overwritten when passed through props", () => { 45 | const testCircle = CircleWrapper({ fill: "white" }); 46 | expect(testCircle.element.getAttribute("fill")).toBe("white"); 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /packages/vuenique-shape/__tests__/LinePath.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test, vi } from "vitest"; 2 | import { mount } from "@vue/test-utils"; 3 | import LinePath from "../LinePath.vue"; 4 | 5 | interface Datum { 6 | x: number; 7 | y: number; 8 | } 9 | 10 | const linePathProps = { 11 | data: [ 12 | { x: 0, y: 0 }, 13 | { x: 1, y: 1 }, 14 | ], 15 | x: (d: Datum) => d.x, 16 | y: (d: Datum) => d.y, 17 | }; 18 | 19 | const LinePathWrapper = (props = {}, attrs = {}) => 20 | mount(LinePath, { 21 | shallow: true, 22 | propsData: { 23 | ...props, 24 | }, 25 | attrs: { 26 | ...attrs, 27 | }, 28 | }); 29 | 30 | describe("", () => { 31 | test("should be defined", () => { 32 | expect(LinePath).toBeDefined(); 33 | }); 34 | 35 | test('should default to strokeLinecap="round" for superior missing data rendering', () => { 36 | //seems strokeLinecap is auto converted to lower case 37 | expect( 38 | LinePathWrapper(linePathProps).find("path").attributes().strokelinecap 39 | ).toBe("round"); 40 | }); 41 | 42 | test("should contain paths", () => { 43 | expect(LinePathWrapper(linePathProps).find("path").getComponent); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /packages/vuenique-shape/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./utils/D3ShapeFactory"; 2 | export * from "./utils/setNumberOrNumberAccessor"; 3 | 4 | export { default as Bar } from "./Bar.vue"; 5 | export { default as LinePath } from "./LinePath.vue"; 6 | export { default as Circle } from "./Circle.vue"; 7 | -------------------------------------------------------------------------------- /packages/vuenique-shape/types/D3ShapeConfig.ts: -------------------------------------------------------------------------------- 1 | import type { AccessorForArrayItem } from "./accessor"; 2 | import type { CurveFactory, CurveFactoryLineOnly } from "d3-shape"; 3 | 4 | export type LinePathConfig = { 5 | /** Function that returns a boolean when line path points are passed as arguments. Line path is representative of points that return true */ 6 | defined?: AccessorForArrayItem; 7 | /** Sets the curve factory from d3-line curve for the line generator. Defaults to curveLinear. */ 8 | curve?: CurveFactory | CurveFactoryLineOnly; 9 | /** Sets the x0 accessor function, and sets x1 to null. */ 10 | x?: number | AccessorForArrayItem; 11 | /** Sets the y0 accessor function, and sets y1 to null. */ 12 | y?: number | AccessorForArrayItem; 13 | }; 14 | -------------------------------------------------------------------------------- /packages/vuenique-shape/types/accessor.ts: -------------------------------------------------------------------------------- 1 | // used in D3ShapConfig 2 | export type AccessorForArrayItem = ( 3 | d: Datum, 4 | index: number, 5 | data: Datum[] 6 | ) => Output; 7 | -------------------------------------------------------------------------------- /packages/vuenique-shape/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./D3ShapeConfig"; 2 | export * from "./accessor"; 3 | -------------------------------------------------------------------------------- /packages/vuenique-shape/utils/D3ShapeFactory.ts: -------------------------------------------------------------------------------- 1 | import { line as d3Line } from "d3-shape"; 2 | import type { LinePathConfig } from "../types"; 3 | import setNumberOrNumberAccessor from "./setNumberOrNumberAccessor"; 4 | 5 | export function line({ 6 | x, 7 | y, 8 | defined, 9 | curve, 10 | }: LinePathConfig = {}) { 11 | const path = d3Line(); 12 | if (x) setNumberOrNumberAccessor(path.x, x); 13 | if (y) setNumberOrNumberAccessor(path.y, y); 14 | if (defined) path.defined(defined); 15 | if (curve) path.curve(curve); 16 | return path; 17 | } 18 | -------------------------------------------------------------------------------- /packages/vuenique-shape/utils/setNumberOrNumberAccessor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a workaround for TypeScript not inferring the correct 3 | * method overload/signature for some d3 shape methods. 4 | */ 5 | export default function setNumberOrNumberAccessor( 6 | func: (d: number | NumAccessor) => void, 7 | value: number | NumAccessor 8 | ) { 9 | if (typeof value === "number") func(value); 10 | else func(value); 11 | } 12 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 29 | 30 | 92 | -------------------------------------------------------------------------------- /src/assets/base.css: -------------------------------------------------------------------------------- 1 | /* color palette from */ 2 | :root { 3 | --vt-c-white: #ffffff; 4 | --vt-c-white-soft: #f8f8f8; 5 | --vt-c-white-mute: #f2f2f2; 6 | 7 | --vt-c-black: #181818; 8 | --vt-c-black-soft: #222222; 9 | --vt-c-black-mute: #282828; 10 | 11 | --vt-c-indigo: #2c3e50; 12 | 13 | --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); 14 | --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); 15 | --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); 16 | --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); 17 | 18 | --vt-c-text-light-1: var(--vt-c-indigo); 19 | --vt-c-text-light-2: rgba(60, 60, 60, 0.66); 20 | --vt-c-text-dark-1: var(--vt-c-white); 21 | --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); 22 | } 23 | 24 | /* semantic color variables for this project */ 25 | :root { 26 | --color-background: var(--vt-c-white); 27 | --color-background-soft: var(--vt-c-white-soft); 28 | --color-background-mute: var(--vt-c-white-mute); 29 | 30 | --color-border: var(--vt-c-divider-light-2); 31 | --color-border-hover: var(--vt-c-divider-light-1); 32 | 33 | --color-heading: var(--vt-c-text-light-1); 34 | --color-text: var(--vt-c-text-light-1); 35 | 36 | --section-gap: 160px; 37 | } 38 | 39 | @media (prefers-color-scheme: dark) { 40 | :root { 41 | --color-background: var(--vt-c-black); 42 | --color-background-soft: var(--vt-c-black-soft); 43 | --color-background-mute: var(--vt-c-black-mute); 44 | 45 | --color-border: var(--vt-c-divider-dark-2); 46 | --color-border-hover: var(--vt-c-divider-dark-1); 47 | 48 | --color-heading: var(--vt-c-text-dark-1); 49 | --color-text: var(--vt-c-text-dark-2); 50 | } 51 | } 52 | 53 | *, 54 | *::before, 55 | *::after { 56 | box-sizing: border-box; 57 | margin: 0; 58 | position: relative; 59 | font-weight: normal; 60 | } 61 | 62 | body { 63 | min-height: 100vh; 64 | color: var(--color-text); 65 | background: var(--color-background); 66 | transition: color 0.5s, background-color 0.5s; 67 | line-height: 1.6; 68 | font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, 69 | Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 70 | font-size: 15px; 71 | text-rendering: optimizeLegibility; 72 | -webkit-font-smoothing: antialiased; 73 | -moz-osx-font-smoothing: grayscale; 74 | } 75 | -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/BarGraph.vue: -------------------------------------------------------------------------------- 1 | 80 | 81 | 99 | -------------------------------------------------------------------------------- /src/components/LineGraph.vue: -------------------------------------------------------------------------------- 1 | 138 | 139 | 150 | -------------------------------------------------------------------------------- /src/components/Scatter.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 58 | -------------------------------------------------------------------------------- /src/components/icons/IconCommunity.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /src/components/icons/IconDocumentation.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /src/components/icons/IconEcosystem.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /src/components/icons/IconSupport.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /src/components/icons/IconTooling.vue: -------------------------------------------------------------------------------- 1 | 2 | 20 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import App from "./App.vue"; 3 | 4 | createApp(App).mount("#app"); 5 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.web.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue", "packages/**/*"], 4 | "exclude": ["src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "composite": true, 7 | "baseUrl": ".", 8 | "paths": { 9 | "@/*": ["./*"] 10 | } 11 | } 12 | } 13 | 14 | //changed compiler option paths from "@/*": ["./src/*"] to "@/*": ["./*"] -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { 5 | "path": "./tsconfig.vite-config.json" 6 | }, 7 | { 8 | "path": "./tsconfig.app.json" 9 | }, 10 | { 11 | "path": "./tsconfig.vitest.json" 12 | } 13 | ], 14 | "jsx": "preserve" 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.vite-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.node.json", 3 | "include": ["vite.config.*"], 4 | "compilerOptions": { 5 | "composite": true, 6 | "types": ["node", "vitest"], 7 | "paths": { 8 | "@/*": ["./*"] 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.vitest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.app.json", 3 | "exclude": [], 4 | "compilerOptions": { 5 | "composite": true, 6 | "lib": [], 7 | "types": ["node", "jsdom"], 8 | "paths": { 9 | "@/*": ["./*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from "url"; 2 | import path from "path"; 3 | import { defineConfig } from "vite"; 4 | import vue from "@vitejs/plugin-vue"; 5 | import vueJsx from "@vitejs/plugin-vue-jsx"; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | build: { 10 | lib: { 11 | entry: path.resolve(__dirname, 'packages/index.js'), 12 | // name: "MyLib", 13 | fileName: (format) => `index.${format}.js`, 14 | formats: ["cjs", "es"], 15 | }, 16 | rollupOptions: { 17 | external: ['vue'], 18 | output: { 19 | // Provide global variables to use in the UMD build 20 | // Add external deps here 21 | globals: { 22 | vue: 'Vue', 23 | }, 24 | }, 25 | }, 26 | outDir: "packages/build", 27 | }, 28 | plugins: [vue(), vueJsx()], 29 | resolve: { 30 | alias: { 31 | "@": fileURLToPath(new URL("./src", import.meta.url)), 32 | }, 33 | }, 34 | test: { 35 | globals: true, 36 | environment: "jsdom", 37 | }, 38 | }); 39 | --------------------------------------------------------------------------------