├── .circleci └── config.yml ├── .dependabot └── config.yml ├── .eslintignore ├── .eslintrc.json ├── .github ├── FUNDING.yml └── workflows │ └── pages.yml ├── .gitignore ├── .prettierignore ├── AUTHORS.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── lerna.json ├── package.json ├── packages ├── grid-template-utils │ ├── .babelrc │ ├── LICENSE.txt │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ └── src │ │ ├── index.js │ │ └── index.test.js ├── react-split-grid │ ├── LICENSE.txt │ ├── README.md │ ├── index.d.ts │ ├── package.json │ ├── rollup.config.js │ └── src │ │ ├── index.js │ │ └── index.test.js ├── react-split │ ├── LICENSE.txt │ ├── README.md │ ├── index.d.ts │ ├── package.json │ ├── rollup.config.js │ └── src │ │ ├── index.js │ │ └── index.test.js ├── split-generator │ ├── .dependabot │ │ └── config.yml │ ├── .eslintignore │ ├── .eslintrc.json │ ├── .github │ │ └── workflows │ │ │ └── ci.yml │ ├── .gitignore │ ├── .prettierignore │ ├── README.md │ ├── package.json │ ├── postcss.config.js │ ├── prettier.config.js │ ├── public │ │ ├── bundle.css │ │ ├── bundle.js │ │ ├── horizontal.png │ │ ├── index.html │ │ └── vertical.png │ ├── rollup.config.js │ ├── src │ │ ├── App.svelte │ │ ├── Dashboard.svelte │ │ ├── SplitGenerator.svelte │ │ ├── SplitGridGenerator.svelte │ │ ├── components │ │ │ ├── Logo.svelte │ │ │ ├── LogoSvg.svelte │ │ │ ├── Stepper.svelte │ │ │ └── Toggle.svelte │ │ ├── icons │ │ │ ├── Copied.svelte │ │ │ └── Copy.svelte │ │ ├── index.js │ │ └── less │ │ │ ├── carbon.less │ │ │ ├── markdown.less │ │ │ └── prism-atom-dark.less │ ├── tailwind.config.js │ ├── tests │ │ └── test.js │ └── yarn.lock ├── split-grid │ ├── .babelrc │ ├── LICENSE.txt │ ├── README.md │ ├── index.d.ts │ ├── package.json │ ├── rollup.config.js │ └── src │ │ ├── Gutter.js │ │ ├── getMatchedCSSRules.js │ │ ├── index.js │ │ ├── util.js │ │ └── util.test.js └── splitjs │ ├── LICENSE.txt │ ├── README.md │ ├── grips │ ├── horizontal.png │ └── vertical.png │ ├── index.d.ts │ ├── karma.conf.js │ ├── logo.svg │ ├── package.json │ ├── rollup.config.js │ ├── src │ └── split.js │ └── test │ ├── SpecRunner.html │ ├── lib │ └── jasmine-3.5.0 │ │ ├── boot.js │ │ ├── jasmine-html.js │ │ ├── jasmine.css │ │ ├── jasmine.js │ │ └── jasmine_favicon.png │ └── split.spec.js ├── prettier.config.js └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: circleci/node:14-stretch-browsers 6 | 7 | working_directory: ~/repo 8 | 9 | steps: 10 | - checkout 11 | 12 | - run: yarn install 13 | 14 | # run tests! 15 | - run: yarn run lerna link 16 | - run: yarn run lint 17 | - run: yarn run build 18 | - run: yarn test 19 | - run: yarn run saucelabs 20 | -------------------------------------------------------------------------------- /.dependabot/config.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | update_configs: 4 | - package_manager: "javascript" 5 | directory: "/" 6 | update_schedule: "live" 7 | automerged_updates: 8 | - match: 9 | update_type: "all" 10 | target_branch: master 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/dist/* 2 | **/node_modules/* 3 | docs 4 | packages/splitjs/test/lib 5 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true 4 | }, 5 | "extends": ["airbnb", "prettier"], 6 | "parserOptions": { 7 | "ecmaVersion": 2018, 8 | "sourceType": "module" 9 | }, 10 | "rules": { 11 | "react/jsx-filename-extension": [1, { "extensions": [".js"] }], 12 | "react/jsx-indent": [1, 4], 13 | "react/jsx-indent-props": [1, 4], 14 | "react/jsx-props-no-spreading": [0], 15 | "import/no-unresolved": [0], 16 | "import/no-extraneous-dependencies": [0] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: nathancahill 4 | open_collective: splitjs 5 | -------------------------------------------------------------------------------- /.github/workflows/pages.yml: -------------------------------------------------------------------------------- 1 | name: github pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | - name: Setup Node 15 | uses: actions/setup-node@v2 16 | 17 | - run: yarn install 18 | - run: yarn build 19 | - run: yarn workspace split-generator run build 20 | 21 | - name: Deploy 22 | uses: peaceiris/actions-gh-pages@v3 23 | with: 24 | github_token: ${{ secrets.GITHUB_TOKEN }} 25 | publish_dir: ./packages/split-generator/public 26 | cname: split.js.org 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | dist/ 3 | node_modules/ 4 | yarn-error.log 5 | public/bundle.* -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | *.d.ts 3 | *.spec.js 4 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | ## Authors 2 | 3 | ### Lead 4 | 5 | - Nathan Cahill @nathancahill 6 | 7 | ### Contributors 8 | 9 | - Rod Montgomery @RodMontgomery 10 | - pedrogit @pedrogit 11 | - Özgür Uysal @ozguruysal 12 | - Sánta Gergely @gsanta 13 | - Basil Musa @basilmusa 14 | - justdoo @justdoo 15 | - Jacque Goupil @DominoPivot 16 | - Max Waterman @davidmaxwaterman 17 | - Basarat Ali Syed @basarat 18 | - Adam Jimenez @adamjimenez 19 | - @ambischof 20 | - Kushagra Gour @chinchang 21 | - TIm St.Clair @frumbert 22 | - Turkhan @turok1997 23 | - @SCrusader 24 | - OpenCollective @opencollective 25 | - David Evans @davidje13 26 | - @saluce65 27 | - Peter Lightbody @pet1330 28 | - Jakob Gillich @jgillich 29 | - Searene @searene 30 | - Gabriel Pedro @gpedro 31 | - Chi Wang @patr0nus 32 | - Adrian Jones @adrianbj 33 | - Bradley Kemp @bradleyjkemp 34 | - André Victor @av1ctor 35 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## react-split-grid v1.0.4 2 | 3 | - Publishes Typescript types on NPM 4 | - Corrects misspelling of `maxSize` property 5 | - Corrects type of parameters from object to array 6 | 7 | ## react-split v2.0.14 8 | 9 | - Omits incompatible properties from the React HTMLAttributes 10 | 11 | ## split.js v1.6.5 12 | 13 | - Allows `snapOffset` to be passed as an array 14 | 15 | ## react-split v2.0.13 16 | 17 | - Extends React component types from React.HTMLAttributes 18 | 19 | ## react-split v2.0.12 20 | 21 | - Publishes Typescript types on npm 22 | 23 | ## react-split v2.0.11 24 | 25 | - Fixes a bug where `maxSize` was being passed to the DOM component 26 | 27 | ## split.js v1.6.4, react-split v2.0.10 28 | 29 | - Updates types with new `maxSize` option 30 | 31 | ## split-grid v1.0.10 32 | 33 | - Fixes a bug in storing columns and rows in the instance 34 | - Adds types 35 | 36 | ## split.js v1.6.3 37 | 38 | - Adds `maxSize` option 39 | 40 | ## react-split-grid v1.0.3 41 | 42 | - Fix `React.Children` typo 43 | 44 | ## split.js v1.6.2 45 | 46 | - Update `onDrag` type definitions on include `sizes` parameter 47 | 48 | ## split.js v1.6.1, react-split v2.0.9 49 | 50 | - Revert from `.mjs` modules for Webpack 51 | 52 | ## react-split v2.0.8 53 | 54 | - Fix module resolution 55 | 56 | ## split.js v1.6.0 57 | 58 | - Remove support of IE8 59 | - Support SSR 60 | - Bundle Typescript types in published build 61 | - Most stable testing with CircleCI and Saucelabs 62 | - Upgrade all dev dependencies and setup Dependabot for automatic upgrades 63 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Developing 4 | 5 | The tooling is [Yarn](https://yarnpkg.com/), [Lerna](https://lerna.js.org/), 6 | [Prettier](https://prettier.io/) and [Eslint](https://eslint.org/). To get started: 7 | 8 | ```bash 9 | $ yarn install 10 | $ yarn run lerna link 11 | ``` 12 | 13 | It's easiest to have Prettier format your changes in your editor on save: https://prettier.io/docs/en/editors.html 14 | 15 | **Building** 16 | 17 | ```bash 18 | $ yarn run build 19 | ``` 20 | 21 | **Linting** 22 | 23 | ```bash 24 | $ yarn run lint 25 | ``` 26 | 27 | ## Testing 28 | 29 | ```bash 30 | $ yarn test 31 | ``` 32 | 33 | Each package has unit tests using Jest. `split.js` uses Jasmine 2.6 for browser testing with IE8 support. 34 | Karma is the test runner for headless browsers. Recent versions of Chrome and Firefox 35 | support headless testing locally. By default, both browsers are tested. If you want to test 36 | with just one or the other, run: 37 | 38 | ```bash 39 | $ yarn workspace split.js run test --browsers FirefoxHeadless 40 | ``` 41 | 42 | _or_ 43 | 44 | ```bash 45 | $ yarn workspace split.js run test --browsers ChromeHeadless 46 | ``` 47 | 48 | On the CI, [SauceLabs](https://saucelabs.com/) provides cross-platform testing. 49 | Headless Firefox and Chrome are also tested via Docker container. 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Nathan Cahill 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Split [![CI](https://img.shields.io/circleci/project/github/nathancahill/split/master.svg)](https://circleci.com/gh/nathancahill/split) ![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen) [![Backers on Open Collective](https://opencollective.com/splitjs/backers/badge.svg)](https://opencollective.com/splitjs/) [![Sponsors on Open Collective](https://opencollective.com/splitjs/sponsors/badge.svg)](https://opencollective.com/splitjs/) 2 | 3 | > Unopinionated utilities for resizeable split views. 4 | 5 | - **Zero Deps** 6 | - **Tiny:** Each is between 1-2kb gzipped. 7 | - **Fast:** No overhead or attached window event listeners, uses pure CSS for resizing. 8 | - **Unopinionated:** Only compute view sizes. Everything else is up to you. 9 | 10 | Two utilities: 11 | 12 | - **[Split.js](https://github.com/nathancahill/split/tree/master/packages/splitjs)** - The original library, maintained since 2014, works with `float` and `flex` layouts. Supports all browsers. 13 | - **[Split Grid](https://github.com/nathancahill/split/tree/master/packages/split-grid)** - Successor to Split.js, for `grid` layouts. Supports modern browsers. 14 | 15 | Two React wrappers: 16 | 17 | - **[React Split](https://github.com/nathancahill/split/tree/master/packages/react-split)** - Thin wrapper component for Split.js. 18 | - **[React Split Grid](https://github.com/nathancahill/split/tree/master/packages/react-split-grid)** - Thin wrapper component for Split Grid. 19 | 20 | ## Credits 21 | 22 | ### Contributors 23 | 24 | This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. 25 | 26 | 27 | ### Backers 28 | 29 | Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/splitjs#backer)] 30 | 31 | 32 | 33 | ### Sponsors 34 | 35 | Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/splitjs#sponsor)] 36 | 37 | [Sauce Labs](https://saucelabs.com) 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ### Used By 51 | 52 | - [JSFiddle](https://jsfiddle.net/) - Code playground 53 | - [Viz.js](http://viz-js.com/) - Graphviz in your browser 54 | - [Perchance](https://perchance.org/welcome) - Platform for creating and sharing random text generators 55 | - [Babylon.js Playground](https://www.babylonjs-playground.com/) 56 | - And many more. Submit a PR to list your project here. 57 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "npmClient": "yarn", 4 | "useWorkspaces": true 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "packages/*" 5 | ], 6 | "scripts": { 7 | "build": "lerna run build", 8 | "test": "lerna run test", 9 | "lint": "eslint .", 10 | "saucelabs": "lerna run saucelabs", 11 | "deploy": "git push origin `git subtree split --prefix packages/split-generator/public master`:gh-pages --force" 12 | }, 13 | "version": "1.0.0", 14 | "main": "index.js", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@babel/core": "^7.10.2", 18 | "@babel/preset-env": "^7.10.2", 19 | "@rollup/plugin-buble": "^0.21.3", 20 | "@rollup/plugin-node-resolve": "^11.0.0", 21 | "@types/react": "^17.0.3", 22 | "babel-jest": "^26.0.1", 23 | "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0", 24 | "eslint-config-airbnb": "^18.2.0", 25 | "eslint-config-prettier": "^8.0.0", 26 | "eslint-plugin-import": "^2.21.2", 27 | "eslint-plugin-jsx-a11y": "^6.3.1", 28 | "eslint-plugin-react": "^7.20.0", 29 | "eslint-plugin-react-hooks": "^4.0.4", 30 | "gzip-size-cli": "^4.0.0", 31 | "jasmine-core": "3.7.1", 32 | "jest": "^26.0.1", 33 | "karma": "^6.0.4", 34 | "karma-chrome-launcher": "^3.1.0", 35 | "karma-firefox-launcher": "^2.0.0", 36 | "karma-jasmine": "^4.0.0", 37 | "karma-sauce-launcher": "^4.1.5", 38 | "lerna": "^4.0.0", 39 | "prettier": "^2.0.5", 40 | "react": "^17.0.0", 41 | "rollup": "^2.15.0", 42 | "rollup-plugin-terser": "^7.0.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/grid-template-utils/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /packages/grid-template-utils/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Nathan Cahill 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/grid-template-utils/README.md: -------------------------------------------------------------------------------- 1 | # Grid Template Utils [![CI](https://img.shields.io/circleci/project/github/nathancahill/split/master.svg)](https://circleci.com/gh/nathancahill/split) ![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen) [![File size](https://img.badgesize.io/https://unpkg.com/grid-template-utils/dist/grid-template-utils.min.js?compression=gzip&label=size&v=1.0.0)](https://unpkg.com/grid-template-utils/dist/grid-template-utils.min.js) 2 | 3 | ## Installation 4 | 5 | Yarn: 6 | 7 | ``` 8 | $ yarn add grid-template-utils 9 | ``` 10 | 11 | npm: 12 | 13 | ``` 14 | $ npm install --save grid-template-utils 15 | ``` 16 | 17 | Include with a module bundler like [rollup](http://rollupjs.org/) or [webpack](https://webpack.github.io/): 18 | 19 | ```js 20 | // using ES6 modules 21 | import { parse, combine, getSizeAtTrack } from 'grid-template-utils' 22 | 23 | // using CommonJS modules 24 | var utils = require('grid-template-utils') 25 | ``` 26 | 27 | The [UMD](https://github.com/umdjs/umd) build is also available on [unpkg](http://unpkg.com/): 28 | 29 | ```html 30 | 31 | ``` 32 | 33 | You can find the library on `window.GridTemplateUtils`. 34 | 35 | ## Example 36 | 37 | ```js 38 | import { parse, combine, getSizeAtTrack } from 'grid-template-utils' 39 | 40 | > parse('1fr 10px 1fr') 41 | [ 42 | { 43 | value: '1fr', 44 | type: 'fr', 45 | numeric: 1, 46 | }, 47 | { 48 | value: '10px', 49 | type: 'px', 50 | numeric: 10, 51 | }, 52 | { 53 | value: '1fr', 54 | type: 'fr', 55 | numeric: 1, 56 | }, 57 | ] 58 | 59 | > combine('1fr 10px 1fr', [,{ value: '20px' }]) 60 | '1fr 20px 1fr' 61 | 62 | 63 | > getSizeAtTrack(1, parse('10px 10px 10px')) 64 | 20 65 | 66 | > getSizeAtTrack(1, parse('10px 10px 10px'), 20) 67 | 40 68 | ``` 69 | 70 | ## Reference 71 | 72 | ##### `Track { value: string, type: 'fr' | 'px' | '%' | 'auto', numeric: number }` 73 | 74 | Object describing CSS values for a single track in a grid template. 75 | 76 | ##### `parse(rule: string) => Track[]` 77 | 78 | Parses a `grid-template-rows` or `grid-template-columns` CSS rule to 79 | an array of `Track` objects with `value`, `type` and `numeric` keys. 80 | 81 | ##### `combine(rule: string, tracks: Track[]) => string` 82 | 83 | Updates a CSS rule with values from an array of `Track` objects. The array can be sparse, 84 | only included indices will be updated. If `Track.value` is specified, 85 | that string value will be used directly. If not, `Track.numeric` and `Track.type` are 86 | joined before interpolation. 87 | 88 | ##### `getSizeAtTrack(index: number, tracks: Track[], gap?: number = 0, end?: boolean = false) => number` 89 | 90 | Returns the pixel size measured from the start of the grid layout up to the track 91 | specified by `index`. Each `Track.numeric` value should be a `px` value 92 | (for example, as returned by `parse(getComputedStyle())`). 93 | Optional argument `gap` is the pixel size of `grid-gap`. Optional agrument `end` 94 | is a flag that determines whether to measure up to the start or end of the track. 95 | Defaults to `false` (measure to the start of the track). 96 | 97 | ## License 98 | 99 | Copyright (c) 2018 Nathan Cahill 100 | 101 | Permission is hereby granted, free of charge, to any person obtaining a copy 102 | of this software and associated documentation files (the "Software"), to deal 103 | in the Software without restriction, including without limitation the rights 104 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 105 | copies of the Software, and to permit persons to whom the Software is 106 | furnished to do so, subject to the following conditions: 107 | 108 | The above copyright notice and this permission notice shall be included in 109 | all copies or substantial portions of the Software. 110 | 111 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 112 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 113 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 114 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 115 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 116 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 117 | THE SOFTWARE. 118 | -------------------------------------------------------------------------------- /packages/grid-template-utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grid-template-utils", 3 | "version": "1.0.1", 4 | "description": "Utility functions for working with grid templates", 5 | "main": "dist/grid-template-utils.js", 6 | "minified:main": "dist/grid-template-utils.min.js", 7 | "module": "dist/grid-template-utils.es.js", 8 | "scripts": { 9 | "build": "rollup -c && npm run size", 10 | "watch": "rollup -cw", 11 | "test": "jest", 12 | "size": "echo \"gzip size: $(gzip-size --raw $npm_package_minified_main) bytes\"" 13 | }, 14 | "repository": "https://github.com/nathancahill/split", 15 | "author": "Nathan Cahill ", 16 | "homepage": "https://split.js.org/", 17 | "files": [ 18 | "dist" 19 | ], 20 | "license": "MIT", 21 | "collective": { 22 | "type": "opencollective", 23 | "url": "https://opencollective.com/splitjs" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/grid-template-utils/rollup.config.js: -------------------------------------------------------------------------------- 1 | import buble from '@rollup/plugin-buble' 2 | import { terser } from 'rollup-plugin-terser' 3 | 4 | const pkg = require('./package.json') 5 | 6 | export default [ 7 | { 8 | input: './src/index.js', 9 | output: [ 10 | { 11 | name: 'GridTemplateUtils', 12 | file: pkg.main, 13 | format: 'umd', 14 | sourcemap: false, 15 | banner: `/*! ${pkg.name} - v${pkg.version} */\n`, 16 | }, 17 | { 18 | file: pkg.module, 19 | format: 'esm', 20 | sourcemap: false, 21 | }, 22 | ], 23 | plugins: [ 24 | buble({ 25 | exclude: 'node_modules/**', 26 | objectAssign: 'Object.assign', 27 | transforms: { 28 | forOf: false, 29 | }, 30 | }), 31 | ], 32 | }, 33 | { 34 | input: './src/index.js', 35 | output: { 36 | name: 'GridTemplateUtils', 37 | file: pkg['minified:main'], 38 | format: 'umd', 39 | sourcemap: true, 40 | banner: `/*! ${pkg.name} - v${pkg.version} */\n`, 41 | }, 42 | plugins: [ 43 | buble({ 44 | exclude: 'node_modules/**', 45 | objectAssign: 'Object.assign', 46 | transforms: { 47 | forOf: false, 48 | }, 49 | }), 50 | terser(), 51 | ], 52 | }, 53 | ] 54 | -------------------------------------------------------------------------------- /packages/grid-template-utils/src/index.js: -------------------------------------------------------------------------------- 1 | const numeric = (value, unit) => Number(value.slice(0, -1 * unit.length)) 2 | 3 | const parseValue = value => { 4 | if (value.endsWith('px')) 5 | return { value, type: 'px', numeric: numeric(value, 'px') } 6 | if (value.endsWith('fr')) 7 | return { value, type: 'fr', numeric: numeric(value, 'fr') } 8 | if (value.endsWith('%')) 9 | return { value, type: '%', numeric: numeric(value, '%') } 10 | if (value === 'auto') return { value, type: 'auto' } 11 | return null 12 | } 13 | 14 | export const parse = rule => rule.split(' ').map(parseValue) 15 | 16 | export const combine = (rule, tracks) => { 17 | const prevTracks = rule ? rule.split(' ') : [] 18 | 19 | tracks.forEach((track, i) => { 20 | if (i > prevTracks.length - 1) { 21 | throw new Error( 22 | `Unable to set size of track index ${i}, there are only ${ 23 | prevTracks.length 24 | } tracks in the grid layout.`, 25 | ) 26 | } 27 | 28 | prevTracks[i] = track.value 29 | ? track.value 30 | : `${track.numeric}${track.type}` 31 | }) 32 | 33 | return prevTracks.join(' ') 34 | } 35 | 36 | export const getSizeAtTrack = (index, tracks, gap = 0, end = false) => { 37 | const newIndex = end ? index + 1 : index 38 | const trackSum = tracks 39 | .slice(0, newIndex) 40 | .reduce((accum, value) => accum + value.numeric, 0) 41 | const gapSum = gap ? index * gap : 0 42 | 43 | return trackSum + gapSum 44 | } 45 | -------------------------------------------------------------------------------- /packages/grid-template-utils/src/index.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | 3 | import { parse, combine, getSizeAtTrack } from './index' 4 | 5 | const sizeTracks = [ 6 | { value: '50px', type: 'px', numeric: 50 }, 7 | { value: '10px', type: 'px', numeric: 10 }, 8 | { value: '5px', type: 'px', numeric: 5 }, 9 | ] 10 | ;[ 11 | { 12 | input: '1px 2px 3px', 13 | output: [ 14 | { value: '1px', type: 'px', numeric: 1 }, 15 | { value: '2px', type: 'px', numeric: 2 }, 16 | { value: '3px', type: 'px', numeric: 3 }, 17 | ], 18 | }, 19 | { 20 | input: '1fr 2px 3fr', 21 | output: [ 22 | { value: '1fr', type: 'fr', numeric: 1 }, 23 | { value: '2px', type: 'px', numeric: 2 }, 24 | { value: '3fr', type: 'fr', numeric: 3 }, 25 | ], 26 | }, 27 | { 28 | input: '40% auto 10%', 29 | output: [ 30 | { value: '40%', type: '%', numeric: 40 }, 31 | { value: 'auto', type: 'auto' }, 32 | { value: '10%', type: '%', numeric: 10 }, 33 | ], 34 | }, 35 | { 36 | input: '1unsupported 2unsupported', 37 | output: [null, null], 38 | }, 39 | ].forEach(({ input, output }) => { 40 | test(`parse ${input}`, () => { 41 | expect(parse(input)).toEqual(output) 42 | }) 43 | }) 44 | ;[ 45 | { 46 | input: { 47 | rule: '1px 1px 1px', 48 | tracks: [ 49 | { value: '1px', type: 'px', numeric: 1 }, 50 | { value: '2px', type: 'px', numeric: 2 }, 51 | { value: '3px', type: 'px', numeric: 3 }, 52 | ], 53 | }, 54 | output: '1px 2px 3px', 55 | }, 56 | { 57 | input: { 58 | rule: '1px 1px 1px', 59 | tracks: [ 60 | { value: '1fr', type: 'fr', numeric: 1 }, 61 | { value: '2px', type: 'px', numeric: 2 }, 62 | { value: '3fr', type: 'fr', numeric: 3 }, 63 | ], 64 | }, 65 | output: '1fr 2px 3fr', 66 | }, 67 | { 68 | input: { 69 | rule: '1px 1px 1px', 70 | tracks: (() => { 71 | const sparse = [] 72 | sparse[1] = { value: '2px', type: 'px', numeric: 2 } 73 | return sparse 74 | })(), 75 | }, 76 | output: '1px 2px 1px', 77 | }, 78 | { 79 | input: { 80 | rule: '1px 1px 1px', 81 | tracks: [ 82 | { type: 'fr', numeric: 1 }, 83 | { type: 'px', numeric: 2 }, 84 | { type: 'fr', numeric: 3 }, 85 | ], 86 | }, 87 | output: '1fr 2px 3fr', 88 | }, 89 | ].forEach(({ input: { rule, tracks }, output }) => { 90 | test(`combine ${rule}`, () => { 91 | expect(combine(rule, tracks)).toEqual(output) 92 | }) 93 | }) 94 | ;[ 95 | { 96 | input: { 97 | index: 0, 98 | gap: 0, 99 | end: false, 100 | }, 101 | output: 0, 102 | }, 103 | { 104 | input: { 105 | index: 0, 106 | gap: 20, 107 | end: false, 108 | }, 109 | output: 0, 110 | }, 111 | { 112 | input: { 113 | index: 0, 114 | gap: 0, 115 | end: true, 116 | }, 117 | output: 50, 118 | }, 119 | { 120 | input: { 121 | index: 0, 122 | gap: 20, 123 | end: true, 124 | }, 125 | output: 50, 126 | }, 127 | { 128 | input: { 129 | index: 1, 130 | gap: 0, 131 | end: false, 132 | }, 133 | output: 50, 134 | }, 135 | { 136 | input: { 137 | index: 1, 138 | gap: 0, 139 | end: true, 140 | }, 141 | output: 60, 142 | }, 143 | { 144 | input: { 145 | index: 1, 146 | gap: 20, 147 | end: true, 148 | }, 149 | output: 80, 150 | }, 151 | { 152 | input: { 153 | index: 2, 154 | gap: 0, 155 | end: false, 156 | }, 157 | output: 60, 158 | }, 159 | { 160 | input: { 161 | index: 2, 162 | gap: 20, 163 | end: false, 164 | }, 165 | output: 100, 166 | }, 167 | { 168 | input: { 169 | index: 2, 170 | gap: 0, 171 | end: true, 172 | }, 173 | output: 65, 174 | }, 175 | { 176 | input: { 177 | index: 2, 178 | gap: 20, 179 | end: true, 180 | }, 181 | output: 105, 182 | }, 183 | { 184 | input: { 185 | index: 2, 186 | gap: undefined, 187 | end: true, 188 | }, 189 | output: 65, 190 | }, 191 | ].forEach(({ input: { index, gap, end }, output }) => { 192 | test(`getSizeAtTrack ${index} ${gap} ${end}`, () => { 193 | expect(getSizeAtTrack(index, sizeTracks, gap, end)).toEqual(output) 194 | }) 195 | }) 196 | -------------------------------------------------------------------------------- /packages/react-split-grid/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Nathan Cahill 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/react-split-grid/README.md: -------------------------------------------------------------------------------- 1 | # React Split Grid   [![CI](https://img.shields.io/circleci/project/github/nathancahill/split/master.svg)](https://circleci.com/gh/nathancahill/split) ![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen) ![](https://img.badgesize.io/https://unpkg.com/react-split-grid/dist/react-split-grid.min.js?compression=gzip&label=size) 2 | 3 | React component for [Split Grid](https://github.com/nathancahill/split/tree/master/packages/split-grid) 4 | 5 | ## Installation 6 | 7 | Yarn: 8 | 9 | ``` 10 | $ yarn add react-split-grid 11 | ``` 12 | 13 | npm: 14 | 15 | ``` 16 | $ npm install --save react-split-grid 17 | ``` 18 | 19 | Include with a module bundler like [rollup](http://rollupjs.org/) or [webpack](https://webpack.github.io/): 20 | 21 | ```js 22 | // using ES6 modules 23 | import Split from 'react-split-grid' 24 | 25 | // using CommonJS modules 26 | var Split = require('react-split-grid') 27 | ``` 28 | 29 | The [UMD](https://github.com/umdjs/umd) build is also available on [unpkg](http://unpkg.com/): 30 | 31 | ```html 32 | 33 | ``` 34 | 35 | You can find the library on `window.ReactSplitGrid`. 36 | 37 | ## `` 38 | 39 | `` is a light component wrapper around the [Split Grid](https://github.com/nathancahill/split/tree/master/packages/split-grid) library. 40 | It uses the render prop pattern, but also supports calling the `children` prop or 41 | the `component` prop. 42 | 43 | ### Example 44 | 45 | ```js 46 | import Split from 'react-split-grid' 47 | 48 | ( 55 |
56 |
57 |
58 |
59 |
60 | )} 61 | /> 62 | ``` 63 | 64 | ## Reference 65 | 66 | ### Split render methods and props 67 | 68 | There are three ways to render a Split Grid with `` 69 | 70 | - `` 71 | - `` 72 | - `` 73 | 74 | All three render methods will be passed the same props: 75 | 76 | ### Props 77 | 78 | Refer to [Split Grid documentation](https://github.com/nathancahill/split/tree/master/packages/split-grid#reference) for the options the component accepts as props. 79 | 80 | Here's the full list: 81 | 82 | - `minSize: number` 83 | - `maxSize: number` 84 | - `columnMinSize: number` 85 | - `rowMinSize: number` 86 | - `columnMaxSize: number` 87 | - `rowMaxSize: number` 88 | - `columnMinSizes: { [track: number]: number }` 89 | - `rowMinSizes: { [track: number]: number }` 90 | - `columnMaxSizes: { [track: number]: number }` 91 | - `rowMaxSizes: { [track: number]: number }` 92 | - `snapOffset: number` 93 | - `columnSnapOffset: number` 94 | - `rowSnapOffset: number` 95 | - `dragInterval: number` 96 | - `columnDragInterval: number` 97 | - `rowDragInterval: number` 98 | - `cursor: string` 99 | - `columnCursor: string` 100 | - `rowCursor: string` 101 | - `onDrag: (direction: 'row' | 'column', track: number, gridTemplateStyle: string) => void` 102 | - `onDragStart: (direction: 'row' | 'column', track: number) => void` 103 | - `onDragEnd: (direction: 'row' | 'column', track: number) => void` 104 | - `gridTemplateColumns: string` 105 | - `gridTemplateRows: string` 106 | 107 | See the note below on using `gridTemplateColumns` / `gridTemplateRows` props. 108 | 109 | ### `component` 110 | 111 | ```js 112 | import Split from 'react-split-grid' 113 | 114 | 119 | 120 | const Grid = ({ 121 | getGridProps, 122 | getGutterProps, 123 | }) => ( 124 |
125 |
126 |
127 |
128 |
129 | ) 130 | ``` 131 | 132 | **Warning:** `` takes precendence over `` so don’t use both in the same ``. 133 | 134 | ### `render: (props: Props) => ReactNode` 135 | 136 | ```js 137 | import Split from 'react-split-grid' 138 | 139 | ( 146 |
147 |
148 |
149 |
150 |
151 | )} 152 | /> 153 | ``` 154 | 155 | ### `children: func` 156 | 157 | ```js 158 | import Split from 'react-split-grid' 159 | 160 | 164 | {({ 165 | getGridProps, 166 | getGutterProps, 167 | }) => ( 168 |
169 |
170 |
171 |
172 |
173 | )} 174 | 175 | ``` 176 | 177 | ### Using `gridTemplateColumns` / `gridTemplateRows` props 178 | 179 | If `gridTemplateColumns` or `gridTemplateRows` are passed to ``, 180 | a handler for `onDrag` must be passed as well to update the prop: 181 | 182 | ```js 183 | class Wrapper extends React.Component { 184 | constructor() { 185 | super() 186 | 187 | this.state = { 188 | gridTemplateColumns: '1fr 10px 1fr', 189 | } 190 | 191 | this.handleDrag = this.handleDrag.bind(this) 192 | } 193 | 194 | handleDrag(direction, track, style) { 195 | this.setState({ 196 | gridTemplateColumns: style, 197 | }) 198 | } 199 | 200 | render() { 201 | const { gridTemplateColumns } = this.state 202 | 203 | return ( 204 | ( 208 |
209 |
210 |
211 |
212 |
213 | )} 214 | /> 215 | ) 216 | } 217 | } 218 | ``` 219 | 220 | ## License 221 | 222 | Copyright (c) 2019 Nathan Cahill 223 | 224 | Permission is hereby granted, free of charge, to any person obtaining a copy 225 | of this software and associated documentation files (the "Software"), to deal 226 | in the Software without restriction, including without limitation the rights 227 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 228 | copies of the Software, and to permit persons to whom the Software is 229 | furnished to do so, subject to the following conditions: 230 | 231 | The above copyright notice and this permission notice shall be included in 232 | all copies or substantial portions of the Software. 233 | 234 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 235 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 236 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 237 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 238 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 239 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 240 | THE SOFTWARE. 241 | -------------------------------------------------------------------------------- /packages/react-split-grid/index.d.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { SplitOptions } from 'split-grid' 3 | 4 | export interface SplitProps { 5 | columnGutters?: SplitOptions["columnGutters"] 6 | rowGutters?: SplitOptions["rowGutters"] 7 | minSize?: SplitOptions["minSize"] 8 | maxSize?: SplitOptions["maxSize"] 9 | columnMinSize?: SplitOptions["columnMinSize"] 10 | rowMinSize?: SplitOptions["rowMinSize"] 11 | columnMaxSize?: SplitOptions["columnMaxSize"] 12 | rowMaxSize?: SplitOptions["rowMaxSize"] 13 | columnMinSizes?: SplitOptions["columnMinSizes"] 14 | rowMinSizes?: SplitOptions["rowMinSizes"] 15 | columnMaxSizes?: SplitOptions["columnMaxSizes"] 16 | rowMaxSizes?: SplitOptions["rowMaxSizes"] 17 | snapOffset?: SplitOptions["snapOffset"] 18 | columnSnapOffset?: SplitOptions["columnSnapOffset"] 19 | rowSnapOffset?: SplitOptions["rowSnapOffset"] 20 | dragInterval?: SplitOptions["dragInterval"] 21 | columnDragInterval?: SplitOptions["columnDragInterval"] 22 | rowDragInterval?: SplitOptions["rowDragInterval"] 23 | cursor?: SplitOptions["cursor"] 24 | columnCursor?: SplitOptions["columnCursor"] 25 | rowCursor?: SplitOptions["rowCursor"] 26 | onDrag?: SplitOptions["onDrag"] 27 | onDragStart?: SplitOptions["onDragStart"] 28 | onDragEnd?: SplitOptions["onDragEnd"] 29 | writeStyle?: SplitOptions["writeStyle"] 30 | gridTemplateColumns?: SplitOptions["gridTemplateColumns"] 31 | gridTemplateRows?: SplitOptions["gridTemplateRows"] 32 | } 33 | 34 | declare class Split extends React.Component {} 35 | 36 | export default Split 37 | -------------------------------------------------------------------------------- /packages/react-split-grid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-split-grid", 3 | "version": "1.0.4", 4 | "description": "React component for Split Grid", 5 | "main": "dist/react-split-grid.js", 6 | "minified:main": "dist/react-split-grid.min.js", 7 | "module": "dist/react-split-grid.es.js", 8 | "types": "index.d.ts", 9 | "scripts": { 10 | "prepublish": "rollup -c", 11 | "build": "rollup -c && npm run size", 12 | "watch": "rollup -cw", 13 | "test": "jest", 14 | "size": "echo \"gzip size: $(gzip-size --raw $npm_package_minified_main) bytes\"" 15 | }, 16 | "repository": "https://github.com/nathancahill/split", 17 | "author": "Nathan Cahill ", 18 | "homepage": "https://split.js.org/", 19 | "files": [ 20 | "index.d.ts", 21 | "dist" 22 | ], 23 | "license": "MIT", 24 | "dependencies": { 25 | "prop-types": "^15.5.7", 26 | "split-grid": "^1.0.9" 27 | }, 28 | "peerDependencies": { 29 | "react": "*" 30 | }, 31 | "collective": { 32 | "type": "opencollective", 33 | "url": "https://opencollective.com/splitjs" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/react-split-grid/rollup.config.js: -------------------------------------------------------------------------------- 1 | import buble from '@rollup/plugin-buble' 2 | import { terser } from 'rollup-plugin-terser' 3 | 4 | const pkg = require('./package.json') 5 | 6 | export default [ 7 | { 8 | input: './src/index.js', 9 | output: [ 10 | { 11 | name: 'ReactSplitGrid', 12 | file: pkg.main, 13 | format: 'umd', 14 | sourcemap: false, 15 | banner: `/*! ${pkg.name} - v${pkg.version} */\n`, 16 | globals: { 17 | react: 'React', 18 | 'split-grid': 'Split', 19 | 'prop-types': 'PropTypes', 20 | }, 21 | }, 22 | { 23 | file: pkg.module, 24 | format: 'esm', 25 | sourcemap: false, 26 | }, 27 | ], 28 | external: ['split-grid', 'react', 'prop-types'], 29 | plugins: [ 30 | buble({ 31 | exclude: 'node_modules/**', 32 | objectAssign: 'Object.assign', 33 | transforms: { 34 | forOf: false, 35 | }, 36 | }), 37 | ], 38 | }, 39 | { 40 | input: './src/index.js', 41 | output: { 42 | name: 'ReactSplitGrid', 43 | file: pkg['minified:main'], 44 | format: 'umd', 45 | sourcemap: true, 46 | banner: `/*! ${pkg.name} - v${pkg.version} */\n`, 47 | globals: { 48 | react: 'React', 49 | 'split-grid': 'Split', 50 | 'prop-types': 'PropTypes', 51 | }, 52 | }, 53 | external: ['split-grid', 'react', 'prop-types'], 54 | plugins: [ 55 | buble({ 56 | exclude: 'node_modules/**', 57 | objectAssign: 'Object.assign', 58 | transforms: { 59 | forOf: false, 60 | }, 61 | }), 62 | terser(), 63 | ], 64 | }, 65 | ] 66 | -------------------------------------------------------------------------------- /packages/react-split-grid/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import Split from 'split-grid' 4 | 5 | class ReactSplitGrid extends React.Component { 6 | constructor(props) { 7 | super(props) 8 | 9 | this.columnGutters = {} 10 | this.rowGutters = {} 11 | 12 | this.state = { 13 | gridTemplateColumns: props.gridTemplateColumns 14 | ? props.gridTemplateColumns 15 | : null, 16 | gridTemplateRows: props.gridTemplateRows 17 | ? props.gridTemplateRows 18 | : null, 19 | } 20 | 21 | this.getGridProps = this.getGridProps.bind(this) 22 | this.getGutterProps = this.getGutterProps.bind(this) 23 | this.handleDragStart = this.handleDragStart.bind(this) 24 | this.writeStyle = this.writeStyle.bind(this) 25 | this.onDrag = this.onDrag.bind(this) 26 | } 27 | 28 | componentDidMount() { 29 | const { children, ...options } = this.props 30 | 31 | options.writeStyle = this.writeStyle 32 | options.onDrag = this.onDrag 33 | 34 | this.split = Split(options) 35 | } 36 | 37 | componentDidUpdate(prevProps) { 38 | const { 39 | columnMinSizes, 40 | rowMinSizes, 41 | columnMaxSizes, 42 | rowMaxSizes, 43 | children, 44 | ...options 45 | } = this.props 46 | 47 | const { 48 | columnMinSizes: prevColumnMinSizes, 49 | rowMinSizes: prevRowMinSizes, 50 | columnMaxSizes: prevColumnMaxSizes, 51 | rowMaxSizes: prevRowMaxSizes, 52 | } = prevProps 53 | 54 | const otherProps = [ 55 | 'minSize', 56 | 'maxSize', 57 | 'columnMinSize', 58 | 'rowMinSize', 59 | 'columnMaxSize', 60 | 'rowMaxSize', 61 | 'columnMinSizes', 62 | 'rowMinSizes', 63 | 'columnMaxSizes', 64 | 'rowMaxSizes', 65 | 'snapOffset', 66 | 'columnSnapOffset', 67 | 'rowSnapOffset', 68 | 'dragInterval', 69 | 'columnDragInterval', 70 | 'rowDragInterval', 71 | 'cursor', 72 | 'columnCursor', 73 | 'rowCursor', 74 | ] 75 | 76 | let needsRecreate = otherProps 77 | // eslint-disable-next-line react/destructuring-assignment 78 | .map(prop => this.props[prop] !== prevProps[prop]) 79 | .reduce((accum, same) => accum || same, false) 80 | 81 | // TODO use deep equals 82 | if (columnMinSizes !== prevColumnMinSizes) { 83 | needsRecreate = true 84 | } 85 | 86 | if (rowMinSizes !== prevRowMinSizes) { 87 | needsRecreate = true 88 | } 89 | 90 | if (rowMaxSizes !== prevRowMaxSizes) { 91 | needsRecreate = true 92 | } 93 | 94 | // Destroy and re-create split if options changed 95 | if (needsRecreate) { 96 | options.columnMinSizes = columnMinSizes 97 | options.rowMinSizes = rowMinSizes 98 | 99 | this.split.destroy(false) 100 | 101 | this.split = Split(options) 102 | } 103 | } 104 | 105 | componentWillUnmount() { 106 | this.split.destroy() 107 | delete this.split 108 | } 109 | 110 | static getDerivedStateFromProps(nextProps, prevState) { 111 | const state = {} 112 | let needsSetState = false 113 | 114 | if ( 115 | nextProps.gridTemplateColumns && 116 | nextProps.gridTemplateColumns !== prevState.gridTemplateColumns 117 | ) { 118 | state.gridTemplateColumns = nextProps.gridTemplateColumns 119 | needsSetState = true 120 | } 121 | 122 | if ( 123 | nextProps.gridTemplateRows && 124 | nextProps.gridTemplateRows !== prevState.prevGridTemplateRows 125 | ) { 126 | state.gridTemplateRows = nextProps.gridTemplateRows 127 | needsSetState = true 128 | } 129 | 130 | if (needsSetState) { 131 | return state 132 | } 133 | 134 | return null 135 | } 136 | 137 | onDrag(direction, track, style) { 138 | const { onDrag } = this.props 139 | 140 | if (onDrag) { 141 | onDrag(direction, track, style) 142 | } 143 | } 144 | 145 | getGridProps() { 146 | const { gridTemplateColumns, gridTemplateRows } = this.state 147 | const style = {} 148 | 149 | if (gridTemplateColumns) { 150 | style.gridTemplateColumns = gridTemplateColumns 151 | } 152 | 153 | if (gridTemplateRows) { 154 | style.gridTemplateRows = gridTemplateRows 155 | } 156 | 157 | return { 158 | style, 159 | } 160 | } 161 | 162 | getGutterProps(direction, track) { 163 | return { 164 | onMouseDown: this.handleDragStart(direction, track), 165 | onTouchStart: this.handleDragStart(direction, track), 166 | } 167 | } 168 | 169 | handleDragStart(direction, track) { 170 | return e => { 171 | this.split.handleDragStart(e, direction, track) 172 | } 173 | } 174 | 175 | writeStyle(element, gridTemplateProp, style) { 176 | const state = {} 177 | 178 | if (gridTemplateProp === 'grid-template-columns') { 179 | state.gridTemplateColumns = style 180 | } else if (gridTemplateProp === 'grid-template-rows') { 181 | state.gridTemplateRows = style 182 | } 183 | 184 | this.setState(state) 185 | } 186 | 187 | render() { 188 | const { component, render, children } = this.props 189 | const props = { 190 | getGridProps: this.getGridProps, 191 | getGutterProps: this.getGutterProps, 192 | } 193 | 194 | /* eslint-disable no-nested-ternary */ 195 | return component 196 | ? React.createElement(component, props) 197 | : render 198 | ? render(props) 199 | : children 200 | ? typeof children === 'function' 201 | ? children(props) 202 | : !(React.Children.count(children) === 0) 203 | ? React.Children.only(children) 204 | : null 205 | : null 206 | } 207 | } 208 | 209 | ReactSplitGrid.propTypes = { 210 | component: PropTypes.element, 211 | render: PropTypes.func, 212 | children: PropTypes.element, 213 | gridTemplateColumns: PropTypes.string, 214 | gridTemplateRows: PropTypes.string, 215 | columnMinSizes: PropTypes.objectOf(PropTypes.number), 216 | rowMinSizes: PropTypes.objectOf(PropTypes.number), 217 | columnMaxSizes: PropTypes.objectOf(PropTypes.number), 218 | rowMaxSizes: PropTypes.objectOf(PropTypes.number), 219 | onDrag: PropTypes.func, 220 | } 221 | 222 | ReactSplitGrid.defaultProps = { 223 | component: undefined, 224 | render: undefined, 225 | children: undefined, 226 | gridTemplateColumns: undefined, 227 | gridTemplateRows: undefined, 228 | columnMinSizes: undefined, 229 | rowMinSizes: undefined, 230 | columnMaxSizes: undefined, 231 | rowMaxSizes: undefined, 232 | onDrag: undefined, 233 | } 234 | 235 | export default ReactSplitGrid 236 | -------------------------------------------------------------------------------- /packages/react-split-grid/src/index.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | 3 | test('', () => {}) 4 | -------------------------------------------------------------------------------- /packages/react-split/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Nathan Cahill 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/react-split/README.md: -------------------------------------------------------------------------------- 1 | # React-Split   [![CI](https://img.shields.io/circleci/project/github/nathancahill/split/master.svg)](https://circleci.com/gh/nathancahill/split) ![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen) ![](https://img.badgesize.io/https://unpkg.com/react-split/dist/react-split.min.js?compression=gzip&label=size&v=2.0.5) 2 | 3 | React component for [Split.js](https://github.com/nathancahill/Split.js/) 4 | 5 | ## Installation 6 | 7 | Yarn: 8 | 9 | ``` 10 | $ yarn add react-split 11 | ``` 12 | 13 | npm: 14 | 15 | ``` 16 | $ npm install --save react-split 17 | ``` 18 | 19 | Include with a module bundler like [rollup](http://rollupjs.org/) or [webpack](https://webpack.github.io/): 20 | 21 | ```js 22 | // using ES6 modules 23 | import Split from 'react-split' 24 | 25 | // using CommonJS modules 26 | var Split = require('react-split') 27 | ``` 28 | 29 | The [UMD](https://github.com/umdjs/umd) build is also available on [unpkg](http://unpkg.com/): 30 | 31 | ```html 32 | 33 | ``` 34 | 35 | You can find the library on `window.ReactSplit`. 36 | 37 | ## Usage 38 | 39 | The `` component wraps multiple children components to create a resizeable split view. The component is a 40 | light wrapper around the [Split.js](https://github.com/nathancahill/Split.js/) library and accepts (mostly) the same options. 41 | 42 | ```js 43 | import Split from 'react-split' 44 | 45 | 46 | 47 | 48 | 49 | ``` 50 | 51 | ## Reference 52 | 53 | ### `` 54 | 55 | Creates a Split instance and a `
` wrapper around the children components. 56 | All additional props are passed through to the to the `
` component. 57 | 58 | ### Example 59 | 60 | ```js 61 | import Split from 'react-split' 62 | 63 | 74 | 75 | 76 | 77 | ``` 78 | 79 | ### Props 80 | 81 | #### `sizes` 82 | 83 | `sizes?: [number]` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#sizes) 84 | 85 | #### `minSize` 86 | 87 | `minSize?: number | [number]` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#minsize-default-100) 88 | 89 | #### `expandToMin` 90 | 91 | `expandToMin?: boolean` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#expandtomin-default-false) 92 | 93 | #### `gutterSize` 94 | 95 | `gutterSize?: number` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#guttersize-default-10) 96 | 97 | #### `gutterAlign` 98 | 99 | `gutterAlign?: 'center' | 'start' | 'end'` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#gutteralign-default-center) 100 | 101 | #### `snapOffset` 102 | 103 | `snapOffset?: number` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#snapoffset-default-30) 104 | 105 | #### `dragInterval` 106 | 107 | `dragInterval?: number` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#draginterval-default-1) 108 | 109 | #### `direction` 110 | 111 | `direction?: 'horizontal' | 'vertical'` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#direction-default-horizontal) 112 | 113 | #### `cursor` 114 | 115 | `cursor?: string` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#cursor-default-col-resize) 116 | 117 | #### `gutter` 118 | 119 | `gutter?: (index, direction, pairElement) => HTMLElement` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#gutter) 120 | 121 | #### `elementStyle` 122 | 123 | `elementStyle?: (dimension, elementSize, gutterSize, index) => Object` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#elementstyle) 124 | 125 | #### `gutterStyle` 126 | 127 | `gutterStyle?: (dimension, gutterSize, index) => Object` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#gutterstyle) 128 | 129 | #### `onDrag` 130 | 131 | `onDrag?: sizes => void` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#ondrag-ondragstart-ondragend) 132 | 133 | #### `onDragStart` 134 | 135 | `onDragStart?: sizes => void` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#ondrag-ondragstart-ondragend) 136 | 137 | #### `onDragEnd` 138 | 139 | `onDragEnd?: sizes => void` - [Docs](https://github.com/nathancahill/split/tree/master/packages/splitjs#ondrag-ondragstart-ondragend) 140 | 141 | ## Migrating from Split.js 142 | 143 | Refer to [Split.js documentation](https://github.com/nathancahill/split/tree/master/packages/splitjs#documentation) for the options the component accepts as props. The differences are noted below: 144 | 145 | A few props are exempt from updating. These props are functions, these props will not trigger a `componentDidUpdate`. 146 | Following React best practices, and do not create functions in the render method. Instead, create them once and pass them as props. 147 | 148 | - `gutter` 149 | - `elementStyle` 150 | - `gutterStyle` 151 | - `onDrag` 152 | - `onDragStart` 153 | - `onDragEnd` 154 | 155 | #### API 156 | 157 | - `.setSizes(sizes)` becomes the prop `sizes={sizes}` 158 | - `.getSizes()` is unavailable, but sizes are passed to `onDragStart` and `onDragEnd` 159 | - `.collapse(index)` becomes the prop: `collapsed={index}` 160 | - `.destroy()` is triggered automatically on `componentWillUnmount` 161 | 162 | ## License 163 | 164 | Copyright (c) 2019 Nathan Cahill 165 | 166 | Permission is hereby granted, free of charge, to any person obtaining a copy 167 | of this software and associated documentation files (the "Software"), to deal 168 | in the Software without restriction, including without limitation the rights 169 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 170 | copies of the Software, and to permit persons to whom the Software is 171 | furnished to do so, subject to the following conditions: 172 | 173 | The above copyright notice and this permission notice shall be included in 174 | all copies or substantial portions of the Software. 175 | 176 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 177 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 178 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 179 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 180 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 181 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 182 | THE SOFTWARE. 183 | -------------------------------------------------------------------------------- /packages/react-split/index.d.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Options } from 'split.js' 3 | 4 | export interface SplitProps extends Omit, 'onDrag'|'onDragStart'|'onDragEnd'> { 5 | sizes?: Options["sizes"] 6 | minSize?: Options["minSize"] 7 | maxSize?: Options["maxSize"] 8 | expandToMin?: Options["expandToMin"] 9 | gutterSize?: Options["gutterSize"] 10 | gutterAlign?: Options["gutterAlign"] 11 | snapOffset?: Options["snapOffset"] 12 | dragInterval?: Options["dragInterval"] 13 | direction?: Options["direction"] 14 | cursor?: Options["cursor"] 15 | gutter?: Options["gutter"] 16 | elementStyle?: Options["elementStyle"] 17 | gutterStyle?: Options["gutterStyle"] 18 | onDrag?: Options["onDrag"] 19 | onDragStart?: Options["onDragStart"] 20 | onDragEnd?: Options["onDragEnd"] 21 | collapsed?: Number 22 | } 23 | 24 | declare class Split extends React.Component {} 25 | 26 | export default Split 27 | -------------------------------------------------------------------------------- /packages/react-split/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-split", 3 | "version": "2.0.14", 4 | "description": "React component for Split.js", 5 | "main": "dist/react-split.js", 6 | "minified:main": "dist/react-split.min.js", 7 | "module": "dist/react-split.es.js", 8 | "types": "index.d.ts", 9 | "scripts": { 10 | "prepublish": "rollup -c", 11 | "build": "rollup -c && npm run size", 12 | "watch": "rollup -cw", 13 | "test": "jest", 14 | "size": "echo \"gzip size: $(gzip-size --raw $npm_package_minified_main) bytes\"" 15 | }, 16 | "repository": "https://github.com/nathancahill/split", 17 | "author": "Nathan Cahill ", 18 | "homepage": "https://split.js.org/", 19 | "files": [ 20 | "dist", 21 | "index.d.ts" 22 | ], 23 | "license": "MIT", 24 | "dependencies": { 25 | "prop-types": "^15.5.7", 26 | "split.js": "^1.6.0" 27 | }, 28 | "peerDependencies": { 29 | "react": "*" 30 | }, 31 | "collective": { 32 | "type": "opencollective", 33 | "url": "https://opencollective.com/splitjs" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/react-split/rollup.config.js: -------------------------------------------------------------------------------- 1 | import buble from '@rollup/plugin-buble' 2 | import { terser } from 'rollup-plugin-terser' 3 | 4 | const pkg = require('./package.json') 5 | 6 | export default [ 7 | { 8 | input: './src/index.js', 9 | output: [ 10 | { 11 | name: 'ReactSplit', 12 | file: pkg.main, 13 | format: 'umd', 14 | sourcemap: false, 15 | banner: `/*! ${pkg.name} - v${pkg.version} */\n`, 16 | globals: { 17 | react: 'React', 18 | 'prop-types': 'PropTypes', 19 | 'split.js': 'Split', 20 | }, 21 | }, 22 | { 23 | file: pkg.module, 24 | format: 'esm', 25 | sourcemap: false, 26 | }, 27 | ], 28 | external: ['split.js', 'react', 'prop-types'], 29 | plugins: [ 30 | buble({ 31 | exclude: 'node_modules/**', 32 | objectAssign: 'Object.assign', 33 | transforms: { 34 | forOf: false, 35 | }, 36 | }), 37 | ], 38 | }, 39 | { 40 | input: './src/index.js', 41 | output: { 42 | name: 'ReactSplit', 43 | file: pkg['minified:main'], 44 | format: 'umd', 45 | sourcemap: true, 46 | banner: `/*! ${pkg.name} - v${pkg.version} */\n`, 47 | globals: { 48 | react: 'React', 49 | 'prop-types': 'PropTypes', 50 | 'split.js': 'Split', 51 | }, 52 | }, 53 | external: ['split.js', 'react', 'prop-types'], 54 | plugins: [ 55 | buble({ 56 | exclude: 'node_modules/**', 57 | objectAssign: 'Object.assign', 58 | transforms: { 59 | forOf: false, 60 | }, 61 | }), 62 | terser(), 63 | ], 64 | }, 65 | ] 66 | -------------------------------------------------------------------------------- /packages/react-split/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import Split from 'split.js' 4 | 5 | class SplitWrapper extends React.Component { 6 | componentDidMount() { 7 | const { children, gutter, ...options } = this.props 8 | 9 | options.gutter = (index, direction) => { 10 | let gutterElement 11 | 12 | if (gutter) { 13 | gutterElement = gutter(index, direction) 14 | } else { 15 | gutterElement = document.createElement('div') 16 | gutterElement.className = `gutter gutter-${direction}` 17 | } 18 | 19 | // eslint-disable-next-line no-underscore-dangle 20 | gutterElement.__isSplitGutter = true 21 | return gutterElement 22 | } 23 | 24 | this.split = Split(this.parent.children, options) 25 | } 26 | 27 | componentDidUpdate(prevProps) { 28 | const { children, minSize, sizes, collapsed, ...options } = this.props 29 | const { 30 | minSize: prevMinSize, 31 | sizes: prevSizes, 32 | collapsed: prevCollapsed, 33 | } = prevProps 34 | 35 | const otherProps = [ 36 | 'maxSize', 37 | 'expandToMin', 38 | 'gutterSize', 39 | 'gutterAlign', 40 | 'snapOffset', 41 | 'dragInterval', 42 | 'direction', 43 | 'cursor', 44 | ] 45 | 46 | let needsRecreate = otherProps 47 | // eslint-disable-next-line react/destructuring-assignment 48 | .map(prop => this.props[prop] !== prevProps[prop]) 49 | .reduce((accum, same) => accum || same, false) 50 | 51 | // Compare minSize when both are arrays, when one is an array and when neither is an array 52 | if (Array.isArray(minSize) && Array.isArray(prevMinSize)) { 53 | let minSizeChanged = false 54 | 55 | minSize.forEach((minSizeI, i) => { 56 | minSizeChanged = minSizeChanged || minSizeI !== prevMinSize[i] 57 | }) 58 | 59 | needsRecreate = needsRecreate || minSizeChanged 60 | } else if (Array.isArray(minSize) || Array.isArray(prevMinSize)) { 61 | needsRecreate = true 62 | } else { 63 | needsRecreate = needsRecreate || minSize !== prevMinSize 64 | } 65 | 66 | // Destroy and re-create split if options changed 67 | if (needsRecreate) { 68 | options.minSize = minSize 69 | options.sizes = sizes || this.split.getSizes() 70 | this.split.destroy(true, true) 71 | options.gutter = (index, direction, pairB) => pairB.previousSibling 72 | this.split = Split( 73 | Array.from(this.parent.children).filter( 74 | // eslint-disable-next-line no-underscore-dangle 75 | element => !element.__isSplitGutter, 76 | ), 77 | options, 78 | ) 79 | } else if (sizes) { 80 | // If only the size has changed, set the size. No need to do this if re-created. 81 | let sizeChanged = false 82 | 83 | sizes.forEach((sizeI, i) => { 84 | sizeChanged = sizeChanged || sizeI !== prevSizes[i] 85 | }) 86 | 87 | if (sizeChanged) { 88 | // eslint-disable-next-line react/destructuring-assignment 89 | this.split.setSizes(this.props.sizes) 90 | } 91 | } 92 | 93 | // Collapse after re-created or when collapsed changed. 94 | if ( 95 | Number.isInteger(collapsed) && 96 | (collapsed !== prevCollapsed || needsRecreate) 97 | ) { 98 | this.split.collapse(collapsed) 99 | } 100 | } 101 | 102 | componentWillUnmount() { 103 | this.split.destroy() 104 | delete this.split 105 | } 106 | 107 | render() { 108 | const { 109 | sizes, 110 | minSize, 111 | maxSize, 112 | expandToMin, 113 | gutterSize, 114 | gutterAlign, 115 | snapOffset, 116 | dragInterval, 117 | direction, 118 | cursor, 119 | gutter, 120 | elementStyle, 121 | gutterStyle, 122 | onDrag, 123 | onDragStart, 124 | onDragEnd, 125 | collapsed, 126 | children, 127 | ...rest 128 | } = this.props 129 | 130 | return ( 131 |
{ 133 | this.parent = parent 134 | }} 135 | {...rest} 136 | > 137 | {children} 138 |
139 | ) 140 | } 141 | } 142 | 143 | SplitWrapper.propTypes = { 144 | sizes: PropTypes.arrayOf(PropTypes.number), 145 | minSize: PropTypes.oneOfType([ 146 | PropTypes.number, 147 | PropTypes.arrayOf(PropTypes.number), 148 | ]), 149 | maxSize: PropTypes.oneOfType([ 150 | PropTypes.number, 151 | PropTypes.arrayOf(PropTypes.number), 152 | ]), 153 | expandToMin: PropTypes.bool, 154 | gutterSize: PropTypes.number, 155 | gutterAlign: PropTypes.string, 156 | snapOffset: PropTypes.oneOfType([ 157 | PropTypes.number, 158 | PropTypes.arrayOf(PropTypes.number), 159 | ]), 160 | dragInterval: PropTypes.number, 161 | direction: PropTypes.string, 162 | cursor: PropTypes.string, 163 | gutter: PropTypes.func, 164 | elementStyle: PropTypes.func, 165 | gutterStyle: PropTypes.func, 166 | onDrag: PropTypes.func, 167 | onDragStart: PropTypes.func, 168 | onDragEnd: PropTypes.func, 169 | collapsed: PropTypes.number, 170 | children: PropTypes.arrayOf(PropTypes.element), 171 | } 172 | 173 | SplitWrapper.defaultProps = { 174 | sizes: undefined, 175 | minSize: undefined, 176 | maxSize: undefined, 177 | expandToMin: undefined, 178 | gutterSize: undefined, 179 | gutterAlign: undefined, 180 | snapOffset: undefined, 181 | dragInterval: undefined, 182 | direction: undefined, 183 | cursor: undefined, 184 | gutter: undefined, 185 | elementStyle: undefined, 186 | gutterStyle: undefined, 187 | onDrag: undefined, 188 | onDragStart: undefined, 189 | onDragEnd: undefined, 190 | collapsed: undefined, 191 | children: undefined, 192 | } 193 | 194 | export default SplitWrapper 195 | -------------------------------------------------------------------------------- /packages/react-split/src/index.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | 3 | test('', () => {}) 4 | -------------------------------------------------------------------------------- /packages/split-generator/.dependabot/config.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | update_configs: 4 | - package_manager: "javascript" 5 | directory: "/" 6 | update_schedule: "live" 7 | automerged_updates: 8 | - match: 9 | update_type: "all" 10 | target_branch: master 11 | -------------------------------------------------------------------------------- /packages/split-generator/.eslintignore: -------------------------------------------------------------------------------- 1 | /node_modules/* 2 | /dist/* 3 | /public/* -------------------------------------------------------------------------------- /packages/split-generator/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "jest": true 5 | }, 6 | "extends": ["airbnb-base", "prettier"], 7 | "rules": { 8 | "import/no-extraneous-dependencies": [ 9 | "error", 10 | { "devDependencies": true } 11 | ], 12 | "no-new": 0 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/split-generator/.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions/setup-node@v2 12 | with: 13 | node-version: '14' 14 | - run: yarn install 15 | - run: yarn run test 16 | env: 17 | CI: true 18 | -------------------------------------------------------------------------------- /packages/split-generator/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | public/*.js* 90 | public/*.css* 91 | 92 | # vuepress build output 93 | .vuepress/dist 94 | 95 | # Serverless directories 96 | .serverless/ 97 | 98 | # FuseBox cache 99 | .fusebox/ 100 | 101 | # DynamoDB Local files 102 | .dynamodb/ 103 | 104 | # TernJS port file 105 | .tern-port 106 | -------------------------------------------------------------------------------- /packages/split-generator/.prettierignore: -------------------------------------------------------------------------------- 1 | public/ -------------------------------------------------------------------------------- /packages/split-generator/README.md: -------------------------------------------------------------------------------- 1 | # mapgrid-svelte-tailwind 2 | -------------------------------------------------------------------------------- /packages/split-generator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "split-generator", 3 | "private": true, 4 | "version": "1.0.0", 5 | "repository": "git@github.com:nathancahill/split.git", 6 | "author": "Nathan Cahill ", 7 | "license": "MIT", 8 | "devDependencies": { 9 | "@fullhuman/postcss-purgecss": "^4.0.3", 10 | "@rollup/plugin-commonjs": "^18.0.0", 11 | "@rollup/plugin-node-resolve": "^11.2.1", 12 | "autoprefixer": "^10.2.5", 13 | "eslint": "^7.23.0", 14 | "eslint-config-airbnb-base": "^14.2.1", 15 | "eslint-config-prettier": "^8.1.0", 16 | "eslint-plugin-import": "^2.22.1", 17 | "jest": "^26.6.3", 18 | "postcss": "^8.2.8", 19 | "postcss-load-config": "^3.0.1", 20 | "prettier": "^2.2.1", 21 | "rollup": "^2.43.0", 22 | "rollup-plugin-css-only": "^3.1.0", 23 | "rollup-plugin-livereload": "^2.0.0", 24 | "rollup-plugin-svelte": "^7.1.0", 25 | "rollup-plugin-terser": "^7.0.2", 26 | "sirv-cli": "^1.0.11", 27 | "svelte": "^3.35.0", 28 | "svelte-preprocess": "^4.7.0", 29 | "tailwindcss": "^2.0.4" 30 | }, 31 | "scripts": { 32 | "build": "NODE_ENV=production rollup -c", 33 | "watch": "rollup -cw", 34 | "format": "prettier --write \"**/*.js\" \"**/*.json\"", 35 | "lint": "eslint .", 36 | "test": "jest && yarn run lint", 37 | "serve": "sirv public --single --dev" 38 | }, 39 | "dependencies": { 40 | "@tailwindcss/forms": "^0.3.2", 41 | "less": "^4.1.1", 42 | "qs": "^6.10.1", 43 | "split-grid": "^1.0.9", 44 | "split.js": "^1.6.2", 45 | "svelte-prismjs": "^1.0.1", 46 | "svelte-spa-router": "^3.1.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/split-generator/postcss.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable global-require */ 2 | const purgecss = require('@fullhuman/postcss-purgecss')({ 3 | content: ['./src/**/*.html', './src/**/*.svelte'], 4 | 5 | safelist: [/svelte-/], 6 | 7 | defaultExtractor: content => 8 | content 9 | .match(/[A-Za-z0-9-_:./]+/g) 10 | .map(c => (c.startsWith('class:') ? c.substring(6) : c)) || [], 11 | }) 12 | 13 | const plugins = [require('tailwindcss'), require('autoprefixer')] 14 | 15 | if (process.env.NODE_ENV === 'production') { 16 | plugins.push(purgecss) 17 | } 18 | 19 | module.exports = { 20 | plugins, 21 | } 22 | -------------------------------------------------------------------------------- /packages/split-generator/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 80, 3 | trailingComma: 'all', 4 | tabWidth: 4, 5 | semi: false, 6 | singleQuote: true, 7 | arrowParens: 'avoid', 8 | } 9 | -------------------------------------------------------------------------------- /packages/split-generator/public/horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathancahill/split/48759432a50510e2ed762109d5a8d12a3aac9e63/packages/split-generator/public/horizontal.png -------------------------------------------------------------------------------- /packages/split-generator/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/split-generator/public/vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathancahill/split/48759432a50510e2ed762109d5a8d12a3aac9e63/packages/split-generator/public/vertical.png -------------------------------------------------------------------------------- /packages/split-generator/rollup.config.js: -------------------------------------------------------------------------------- 1 | import commonjs from '@rollup/plugin-commonjs' 2 | import resolve from '@rollup/plugin-node-resolve' 3 | import svelte from 'rollup-plugin-svelte' 4 | import livereload from 'rollup-plugin-livereload' 5 | import { terser } from 'rollup-plugin-terser' 6 | import sveltePreprocess from 'svelte-preprocess' 7 | import css from 'rollup-plugin-css-only' 8 | 9 | const prod = process.env.NODE_ENV === 'production' 10 | const watch = process.env.ROLLUP_WATCH 11 | 12 | export default { 13 | input: 'src/index.js', 14 | output: { 15 | file: 'public/bundle.js', 16 | format: 'iife', 17 | }, 18 | plugins: [ 19 | svelte({ 20 | preprocess: sveltePreprocess({ 21 | postcss: true, 22 | }), 23 | compilerOptions: { 24 | dev: !prod, 25 | }, 26 | }), 27 | css({ 28 | output: 'bundle.css', 29 | }), 30 | resolve({ 31 | dedupe: ['svelte', 'svelte/internal'], 32 | browser: true, 33 | }), 34 | commonjs(), 35 | !!watch && livereload(), 36 | !!prod && terser(), 37 | ], 38 | } 39 | -------------------------------------------------------------------------------- /packages/split-generator/src/App.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 21 | -------------------------------------------------------------------------------- /packages/split-generator/src/Dashboard.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 |
20 |
21 |
22 |
23 | 24 |
25 | Split.js 26 | 27 |
28 | 29 | 30 | 66 |
67 |
68 |
69 |
70 | 85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | 96 |
97 |
98 |
99 |
100 |
103 | © {(new Date()).getFullYear()} 105 | Nathan Cahill 107 |
108 |
109 |
110 |
111 | -------------------------------------------------------------------------------- /packages/split-generator/src/components/Logo.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 |
21 |
22 |
23 | 26 |
27 |
28 |
29 |
33 | 36 |
37 |
38 |
39 | -------------------------------------------------------------------------------- /packages/split-generator/src/components/LogoSvg.svelte: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/split-generator/src/components/Stepper.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 |
29 | 33 | 40 | 46 | 47 | 48 | 49 | 55 | 56 | 60 | 67 | 73 | 74 | 75 |
76 | -------------------------------------------------------------------------------- /packages/split-generator/src/components/Toggle.svelte: -------------------------------------------------------------------------------- 1 | 11 |
12 | {#each options as option, i} 13 | 48 | {/each} 49 |
50 | -------------------------------------------------------------------------------- /packages/split-generator/src/icons/Copied.svelte: -------------------------------------------------------------------------------- 1 | 8 | 14 | 15 | -------------------------------------------------------------------------------- /packages/split-generator/src/icons/Copy.svelte: -------------------------------------------------------------------------------- 1 | 8 | 14 | 15 | -------------------------------------------------------------------------------- /packages/split-generator/src/index.js: -------------------------------------------------------------------------------- 1 | import App from './App.svelte' 2 | 3 | new App({ 4 | target: document.body, 5 | }) 6 | -------------------------------------------------------------------------------- /packages/split-generator/src/less/carbon.less: -------------------------------------------------------------------------------- 1 | #carbonads * { 2 | margin: initial; 3 | padding: initial; 4 | } 5 | #carbonads { 6 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 7 | Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial, 8 | sans-serif; 9 | } 10 | #carbonads { 11 | display: flex; 12 | z-index: 100; 13 | } 14 | #carbonads a { 15 | color: inherit; 16 | text-decoration: none; 17 | } 18 | #carbonads a:hover { 19 | color: inherit; 20 | } 21 | #carbonads span { 22 | position: relative; 23 | display: block; 24 | overflow: hidden; 25 | } 26 | #carbonads .carbon-wrap { 27 | display: flex; 28 | } 29 | #carbonads .carbon-img { 30 | display: block; 31 | margin: 0; 32 | line-height: 1; 33 | } 34 | #carbonads .carbon-img img { 35 | display: block; 36 | } 37 | #carbonads .carbon-text { 38 | font-size: 13px; 39 | padding: 10px; 40 | margin-bottom: 16px; 41 | line-height: 1.5; 42 | text-align: left; 43 | } 44 | #carbonads .carbon-poweredby { 45 | display: block; 46 | padding: 6px 8px; 47 | background: #f1f1f2; 48 | text-align: center; 49 | text-transform: uppercase; 50 | letter-spacing: 0.5px; 51 | font-weight: 600; 52 | font-size: 8px; 53 | line-height: 1; 54 | border-top-left-radius: 3px; 55 | position: absolute; 56 | bottom: 0; 57 | right: 0; 58 | } 59 | -------------------------------------------------------------------------------- /packages/split-generator/src/less/markdown.less: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-code-green: #b5f4a5; 3 | --color-code-yellow: #ffe484; 4 | --color-code-purple: #d9a9ff; 5 | --color-code-red: #ff8383; 6 | --color-code-blue: #93ddfd; 7 | --color-code-white: #fff; 8 | } 9 | 10 | @import 'src/less/prism-atom-dark'; 11 | 12 | .markdown { 13 | > p code, 14 | > ul li *:not(pre) code, 15 | > ul li > code, 16 | > ol li *:not(pre) code, 17 | > ol li > code, 18 | p& code& { 19 | @apply inline-block; 20 | @apply bg-gray-100; 21 | @apply rounded-sm; 22 | @apply text-sm; 23 | @apply px-1; 24 | @apply leading-none; 25 | @apply whitespace-nowrap; 26 | @apply text-gray-800; 27 | @apply font-mono; 28 | @apply align-baseline; 29 | font-weight: 400; 30 | } 31 | 32 | > pre, 33 | pre& { 34 | @apply font-mono; 35 | @apply text-sm; 36 | @apply rounded-lg; 37 | font-weight: 400; 38 | scrollbar-width: none; 39 | &::-webkit-scrollbar { 40 | display: none; 41 | } 42 | 43 | overflow: auto; 44 | } 45 | 46 | > pre, 47 | pre&, 48 | > ul li pre, 49 | > ol li pre { 50 | @apply flex p-0 bg-gray-800; 51 | @apply text-sm leading-normal; 52 | } 53 | 54 | > pre code, 55 | pre code&, 56 | > ul li pre code, 57 | > ol li pre code { 58 | @apply p-4; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/split-generator/src/less/prism-atom-dark.less: -------------------------------------------------------------------------------- 1 | /** 2 | * atom-dark theme for `prism.js` 3 | * Based on Atom's `atom-dark` theme: https://github.com/atom/atom-dark-syntax 4 | * @author Joe Gibson (@gibsjose) 5 | */ 6 | 7 | code[class*='language-'], 8 | pre[class*='language-'] { 9 | @apply font-mono text-gray-200; 10 | // color: #c5c8c6; 11 | // text-shadow: 0 1px rgba(0, 0, 0, 0.3); 12 | // font-family: Inconsolata, Monaco, Consolas, 'Courier New', Courier, monospace; 13 | direction: ltr; 14 | text-align: left; 15 | white-space: pre; 16 | word-spacing: normal; 17 | word-break: normal; 18 | line-height: 1.5; 19 | 20 | -moz-tab-size: 4; 21 | -o-tab-size: 4; 22 | tab-size: 4; 23 | 24 | -webkit-hyphens: none; 25 | -moz-hyphens: none; 26 | -ms-hyphens: none; 27 | hyphens: none; 28 | } 29 | 30 | /* Code blocks */ 31 | pre[class*='language-'] { 32 | @apply p-4; 33 | // padding: 1em; 34 | // margin: .5em 0; 35 | overflow: auto; 36 | border-radius: 0.3em; 37 | } 38 | 39 | :not(pre) > code[class*='language-'], 40 | pre[class*='language-'] { 41 | background: #1d1f21; 42 | } 43 | 44 | /* Inline code */ 45 | :not(pre) > code[class*='language-'] { 46 | padding: 0.1em; 47 | border-radius: 0.3em; 48 | } 49 | 50 | .token.comment, 51 | .token.prolog, 52 | .token.doctype, 53 | .token.cdata { 54 | @apply text-gray-500; 55 | // color: #7C7C7C; 56 | } 57 | 58 | .token.punctuation { 59 | color: #c5c8c6; 60 | } 61 | 62 | .namespace { 63 | opacity: 0.7; 64 | } 65 | 66 | .token.property, 67 | .token.keyword, 68 | .token.tag { 69 | color: #96cbfe; 70 | } 71 | 72 | .token.class-name { 73 | color: #ffffb6; 74 | text-decoration: underline; 75 | } 76 | 77 | .token.boolean, 78 | .token.constant { 79 | color: #99cc99; 80 | } 81 | 82 | .token.symbol, 83 | .token.deleted { 84 | color: #f92672; 85 | } 86 | 87 | .token.number { 88 | color: #ff73fd; 89 | } 90 | 91 | .token.selector, 92 | .token.attr-name, 93 | .token.string, 94 | .token.char, 95 | .token.builtin, 96 | .token.inserted { 97 | color: #a8ff60; 98 | } 99 | 100 | .token.variable { 101 | color: #c6c5fe; 102 | } 103 | 104 | .token.operator { 105 | color: #ededed; 106 | } 107 | 108 | .token.entity { 109 | color: #ffffb6; 110 | /* text-decoration: underline; */ 111 | } 112 | 113 | .token.url { 114 | color: #96cbfe; 115 | } 116 | 117 | .language-css .token.string, 118 | .style .token.string { 119 | color: #87c38a; 120 | } 121 | 122 | .token.atrule, 123 | .token.attr-value { 124 | color: #f9ee98; 125 | } 126 | 127 | .token.function { 128 | color: #dad085; 129 | } 130 | 131 | .token.regex { 132 | color: #e9c062; 133 | } 134 | 135 | .token.important { 136 | color: #fd971f; 137 | } 138 | 139 | .token.important, 140 | .token.bold { 141 | font-weight: bold; 142 | } 143 | .token.italic { 144 | font-style: italic; 145 | } 146 | 147 | .token.entity { 148 | cursor: help; 149 | } 150 | 151 | :not(pre) > code[class*='language-'], 152 | pre[class*='language-'] { 153 | background: transparent; 154 | @apply text-sm; 155 | } 156 | 157 | // Custom overrides 158 | /* .token.atrule, .token.atrule .token.number { 159 | color: #fff; 160 | } 161 | .token.atrule .token.rule { 162 | color: #f9ee98; 163 | } 164 | .token.function { 165 | color: #f9ee98; 166 | } 167 | .language-css .token.string, .style .token.string { 168 | color: #a8ff60; 169 | } */ 170 | 171 | // New overrides 172 | code[class*='language-'], 173 | pre[class*='language-'] { 174 | @apply subpixel-antialiased !important; 175 | @apply text-code-white !important; 176 | } 177 | .token.comment { 178 | @apply text-gray-500 !important; 179 | } 180 | .token.atrule { 181 | @apply text-code-yellow !important; 182 | } 183 | .token.atrule > .token.property { 184 | @apply text-code-white !important; 185 | } 186 | .token.atrule > .token.property + .token.punctuation { 187 | @apply text-code-white !important; 188 | } 189 | .token.atrule 190 | > .token.property 191 | + .token.punctuation 192 | + .token.number 193 | + .token.unit { 194 | @apply text-code-white !important; 195 | } 196 | .token.atrule > .token.number { 197 | @apply text-code-white !important; 198 | } 199 | .token.atrule > .token.unit { 200 | @apply text-code-white !important; 201 | } 202 | .token.function { 203 | @apply text-code-blue !important; 204 | } 205 | .token.number { 206 | @apply text-code-red !important; 207 | } 208 | .token.unit { 209 | @apply text-code-red !important; 210 | } 211 | .token.punctuation { 212 | @apply text-code-blue !important; 213 | } 214 | .token.hexcode { 215 | @apply text-code-blue !important; 216 | } 217 | .token.tag { 218 | @apply text-code-red !important; 219 | } 220 | .token.attr-name { 221 | @apply text-code-yellow !important; 222 | } 223 | .token.attr-value { 224 | @apply text-code-green !important; 225 | } 226 | .token.string { 227 | @apply text-code-green !important; 228 | } 229 | .token.url { 230 | @apply text-code-green !important; 231 | } 232 | .token.selector { 233 | @apply text-code-yellow !important; 234 | } 235 | .token.property { 236 | @apply text-code-yellow !important; 237 | } 238 | .token.rule { 239 | @apply text-code-purple !important; 240 | } 241 | .token.important { 242 | font-weight: inherit !important; 243 | @apply text-code-purple !important; 244 | } 245 | 246 | code.language-js, 247 | pre.language-js { 248 | .token.operator { 249 | @apply text-code-blue !important; 250 | } 251 | .token.punctuation { 252 | @apply text-code-white !important; 253 | } 254 | .token.boolean { 255 | @apply text-code-red !important; 256 | } 257 | .token.regex { 258 | @apply text-code-yellow !important; 259 | } 260 | } 261 | 262 | code.language-bash, 263 | pre.language-bash { 264 | .token.function { 265 | @apply text-code-white !important; 266 | } 267 | } 268 | 269 | code.language-diff, 270 | pre.language-diff { 271 | @apply text-gray-400 !important; 272 | .token.deleted { 273 | @apply text-code-red !important; 274 | } 275 | .token.inserted { 276 | @apply text-code-green !important; 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /packages/split-generator/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const forms = require('@tailwindcss/forms') 2 | 3 | module.exports = { 4 | purge: false, 5 | theme: { 6 | extend: { 7 | colors: { 8 | code: { 9 | green: 'var(--color-code-green)', 10 | yellow: 'var(--color-code-yellow)', 11 | purple: 'var(--color-code-purple)', 12 | red: 'var(--color-code-red)', 13 | blue: 'var(--color-code-blue)', 14 | white: 'var(--color-code-white)', 15 | }, 16 | }, 17 | }, 18 | }, 19 | variants: {}, 20 | plugins: [forms], 21 | } 22 | -------------------------------------------------------------------------------- /packages/split-generator/tests/test.js: -------------------------------------------------------------------------------- 1 | test('', () => {}) 2 | -------------------------------------------------------------------------------- /packages/split-grid/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /packages/split-grid/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Nathan Cahill 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/split-grid/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Split Grid [![CI](https://img.shields.io/circleci/project/github/nathancahill/split/master.svg)](https://circleci.com/gh/nathancahill/split) ![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen) [![File size](https://img.badgesize.io/https://unpkg.com/split-grid/dist/split-grid.min.js?compression=gzip&label=size&v=1.0.7)](https://unpkg.com/split-grid/dist/split-grid.min.js) 3 | 4 | The spiritual successor of [Split.js](https://github.com/nathancahill/split/tree/master/packages/splitjs), built for CSS Grid. 5 | 6 | - __Zero Deps__ 7 | - __Tiny:__ Weights 2kb gzipped. 8 | - __Fast:__ No overhead or attached window event listeners, uses pure CSS for resizing. 9 | - __Unopinionated:__ Only modifies `grid-template-*` rules, the rest of the layout is up to you. 10 | 11 | ## Table of Contents 12 | 13 | - [Installation](#installation) 14 | - [Example](#example) 15 | - [Reference](#reference) 16 | - [API](#api) 17 | - [Migrating from Split.js](#migrating-from-splitjs) 18 | - [License](#license) 19 | 20 | ## Installation 21 | 22 | Yarn: 23 | 24 | ``` 25 | $ yarn add split-grid 26 | ``` 27 | 28 | npm: 29 | 30 | ``` 31 | $ npm install --save split-grid 32 | ``` 33 | 34 | Include with a module bundler like [rollup](http://rollupjs.org/) or [webpack](https://webpack.github.io/): 35 | 36 | ```js 37 | // using ES6 modules 38 | import Split from 'split-grid' 39 | 40 | // using CommonJS modules 41 | var Split = require('split-grid') 42 | ``` 43 | 44 | The [UMD](https://github.com/umdjs/umd) build is also available on [unpkg](http://unpkg.com/): 45 | 46 | ```html 47 | 48 | ``` 49 | 50 | You can find the library on `window.Split`. 51 | 52 | ## Example 53 | 54 | ```js 55 | import Split from 'split-grid' 56 | 57 | Split({ 58 | columnGutters: [{ 59 | track: 1, 60 | element: document.querySelector('.column-1'), 61 | }, { 62 | track: 3, 63 | element: document.querySelector('.column-3'), 64 | }], 65 | rowGutters: [{ 66 | track: 1, 67 | element: document.querySelector('.row-1'), 68 | }] 69 | }) 70 | ``` 71 | 72 | ## Reference 73 | 74 | ```js 75 | Split(options: Options) 76 | ``` 77 | 78 | ### Supported CSS values 79 | 80 | Current CSS values that are supported in `grid-template, grid-template-columns, grid-template-rows`: 81 | 82 | - [x] `fr` 83 | - [x] `px` 84 | - [x] `%` 85 | - [x] `repeat` 86 | 87 | Not supported (yet): 88 | 89 | - [ ] `auto` 90 | - [ ] other CSS values (`em, vmin, cm, etc`) 91 | 92 | ### Options 93 | 94 | Most of the options can be specified as `option`, `columnOption` and `rowOption`. 95 | This allows default option values to be set for both, or specified individually for each axis. 96 | 97 | ##### `columnGutters: [{ element: HTMLElement, track: number }]` 98 | 99 | An array of objects, with `element` and `track` keys. `element` is the element 100 | in the grid to enable as a draggable gutter. `track` is the grid track the gutter element 101 | is positioned on. These must match. 102 | 103 | ##### `rowGutters: [{ element: HTMLElement, track: number }]` 104 | 105 | An array of objects, with `element` and `track` keys. `element` is the element 106 | in the grid to enable as a draggable gutter. `track` is the grid track the gutter element 107 | is positioned on. These must match. 108 | 109 | ##### `minSize: number` 110 | 111 | The minimum size in pixels for all tracks. Default: `0` 112 | 113 | ##### `maxSize: number` 114 | 115 | The maximum size in pixels for all tracks. Default: `Infinity` 116 | 117 | ##### `columnMinSize: number` 118 | 119 | The minimum size in pixels for all tracks. Default: `options.minSize` 120 | 121 | ##### `rowMinSize: number` 122 | 123 | The minimum size in pixels for all tracks. Default: `options.minSize` 124 | 125 | ##### `columnMaxSize: number` 126 | 127 | The maximum size in pixels for all tracks. Default: `options.maxSize` 128 | 129 | ##### `rowMaxSize: number` 130 | 131 | The maximum size in pixels for all tracks. Default: `options.maxSize` 132 | 133 | ##### `columnMinSizes: { [track: number]: number }` 134 | 135 | An object keyed by `track` index, with values set to the minimum size in pixels for the 136 | track at that index. Allows individual minSizes to be specified by track. 137 | Note this option is plural with an `s`, while the two fallback options are singular. 138 | Default: `options.columnMinSize` 139 | 140 | ##### `rowMinSizes: { [track: number]: number }` 141 | 142 | An object keyed by `track` index, with values set to the minimum size in pixels for the 143 | track at that index. Allows individual minSizes to be specified by track. 144 | Note this option is plural with an `s`, while the two fallback options are singular. 145 | Default: `options.rowMinSize` 146 | 147 | ##### `columnMaxSizes: { [track: number]: number }` 148 | 149 | An object keyed by `track` index, with values set to the maximum size in pixels for the 150 | track at that index. Allows individual maxSizes to be specified by track. 151 | Note this option is plural with an `s`, while the two fallback options are singular. 152 | Default: `options.columnMaxSize` 153 | 154 | ##### `rowMaxSizes: { [track: number]: number }` 155 | 156 | An object keyed by `track` index, with values set to the maximum size in pixels for the 157 | track at that index. Allows individual maxSizes to be specified by track. 158 | Note this option is plural with an `s`, while the two fallback options are singular. 159 | Default: `options.rowMaxSize` 160 | 161 | ##### `snapOffset: number` 162 | 163 | Snap to minimum size at this offset in pixels. Set to `0` to disable snap. Default: `30` 164 | 165 | ##### `columnSnapOffset: number` 166 | 167 | Snap to minimum size at this offset in pixels. Set to `0` to disable snap. Default: `options.snapOffset` 168 | 169 | ##### `rowSnapOffset: number` 170 | 171 | Snap to minimum size at this offset in pixels. Set to `0` to disable snap. Default: `options.snapOffset` 172 | 173 | ##### `dragInterval: number` 174 | 175 | Drag this number of pixels at a time. Defaults to `1` for smooth dragging, 176 | but can be set to a pixel value to give more control over the resulting sizes. Default: `1` 177 | 178 | ##### `columnDragInterval: number` 179 | 180 | Drag this number of pixels at a time. Defaults to `1` for smooth dragging, 181 | but can be set to a pixel value to give more control over the resulting sizes. Default: `options.dragInterval` 182 | 183 | ##### `rowDragInterval: number` 184 | 185 | Drag this number of pixels at a time. Defaults to `1` for smooth dragging, 186 | but can be set to a pixel value to give more control over the resulting sizes. Default: `options.dragInterval` 187 | 188 | ##### `cursor: string` 189 | 190 | Cursor to show while dragging. Defaults to `'col-resize'` for column gutters and 191 | `'row-resize'` for row gutters. 192 | 193 | ##### `columnCursor: string` 194 | 195 | Cursor to show while dragging. Default: `'col-resize'` 196 | 197 | ##### `rowCursor: string` 198 | 199 | Cursor to show while dragging. Default: `'row-resize'` 200 | 201 | ##### `onDrag: (direction: 'row' | 'column', track: number, gridTemplateStyle: string) => void` 202 | 203 | Called continously on drag. For process intensive code, add a debounce function to rate limit this callback. 204 | `gridTemplateStyle` is the computed CSS value for `grid-template-column` or `grid-template-row`, depending on `direction`. 205 | 206 | ##### `onDragStart: (direction: 'row' | 'column', track: number) => void` 207 | 208 | Called on drag start. 209 | 210 | ##### `onDragEnd: (direction: 'row' | 'column', track: number) => void` 211 | 212 | Called on drag end. 213 | 214 | ##### `writeStyle: (grid: HTMLElement, gridTemplateProp: 'grid-template-column' | 'grid-template-row', gridTemplateStyle: string) => void` 215 | 216 | Called to update the CSS properties of the grid element. Must eventually apply the 217 | CSS value to the CSS prop, or the grid will not change. `gridTemplateStyle` is the computed CSS value of CSS rule `gridTemplateProp`. 218 | 219 | Default: 220 | 221 | ```js 222 | writeStyle: (grid, gridTemplateProp, gridTemplateStyle) => { 223 | grid.style[gridTemplateProp] = gridTemplateStyle 224 | } 225 | ``` 226 | 227 | ##### `gridTemplateColumns` `gridTemplateRows` 228 | 229 | Helper options for determining initial CSS values for `grid-template-columns` and `grid-template-rows`. 230 | Most of the time this option is not needed, as Split Grid reads the CSS rules applied to the grid element, 231 | but security settings may prevent that, for example, when the CSS is served from a 3rd-party domain. 232 | This is ONLY NEEDED if the default method of reading the CSS values errors. 233 | This option does not immediately apply CSS rules, it's only used on drag. 234 | 235 | ## API 236 | 237 | ```js 238 | const split = Split(options: Options) 239 | ``` 240 | 241 | Split Grid returns an instance with a couple of functions. The instance is returned on creation. 242 | 243 | ##### `split.addColumnGutter(element: HTMLElement, track: number)` 244 | 245 | Adds a draggable row gutter. The element must be a direct descendant 246 | of the element with grid layout, and positioned in the specified track. 247 | 248 | ```js 249 | const grid = document.querySelector('.grid') 250 | const gutter = document.createElement('div') 251 | 252 | grid.appendChild(gutter) // append to DOM 253 | split.addColumnGutter(gutter, 1) // add to Split Grid 254 | ``` 255 | 256 | ##### `split.addRowGutter(element: HTMLElement, track: number)` 257 | 258 | Adds a draggable row gutter. The element must be a direct descendant 259 | of the element with grid layout, and positioned in the specified track. 260 | 261 | ```js 262 | const grid = document.querySelector('.grid') 263 | const gutter = document.createElement('div') 264 | 265 | grid.appendChild(gutter) // append to DOM 266 | split.addRowGutter(gutter, 1) // add to Split Grid 267 | ``` 268 | 269 | ##### `split.removeColumnGutter(track: number, immediate?: true)` 270 | 271 | Removes event listeners from a column gutter by track number. If `immediate = false` is passed, 272 | event handlers are removed after dragging ends. If a gutter isn't currently being dragged, 273 | it's event handlers are removed immediately. 274 | 275 | ##### `split.removeRowGutter(track: number, immediate?: true)` 276 | 277 | Removes event listeners from a row gutter by track number. If `immediate = false` is passed, 278 | event handlers are removed after dragging ends. If a gutter isn't currently being dragged, 279 | it's event handlers are removed immediately. 280 | 281 | ##### `split.destroy(immediate?: true)` 282 | 283 | Destroy the instance by removing the attached event listeners. If `immediate = false` is passed, 284 | the instance is destroyed after dragging ends. If a gutter isn't currently being dragged, 285 | it's destroyed immediately. 286 | 287 | ## Migrating from Split.js 288 | 289 | #### Bring your own gutters 290 | 291 | In Split.js, gutter elements were created by Split.js and inserted in to the DOM. 292 | This is not the case in Split Grid. Create the gutter elements in the HTML as children 293 | of the grid element, and lay them out in the tracks like any other element. 294 | Pass the gutter elements in the options with their track index. 295 | 296 | __Split.js__ 297 | 298 | ```js 299 | Split(options) // gutters created implicitly 300 | ``` 301 | 302 | __Split Grid__ 303 | 304 | Gutters are part of the grid layout: 305 | 306 | ```html 307 |
308 |
Column One
309 |
310 |
Column Two
311 |
312 |
Column Three
313 |
Row One
314 |
315 |
Row Two
316 |
317 | ``` 318 | 319 | ```js 320 | Split({ // gutters specified in options 321 | columnGutters: [{ 322 | track: 1, 323 | element: document.querySelector('.gutter-column-1'), 324 | }, { 325 | track: 3, 326 | element: document.querySelector('.gutter-column-3'), 327 | }], 328 | rowGutters: [{ 329 | track: 1, 330 | element: document.querySelector('.gutter-row-1'), 331 | }] 332 | }) 333 | ``` 334 | 335 | #### CSS values replace `sizes` option 336 | 337 | CSS grid layout offers more flexibility than Split.js's percentage values, 338 | so Split Grid uses the `grid-template` values directly. Instead of setting 339 | the initial sizes as an option in Javascript, set the initial sizes in CSS. 340 | 341 | __Split.js__ 342 | 343 | ```js 344 | Split({ 345 | sizes: [50, 50] 346 | }) 347 | ``` 348 | 349 | __Split Grid__ 350 | 351 | ```html 352 |
353 | ``` 354 | 355 | _or_ 356 | 357 | ```js 358 | > document.querySelector('.grid').style['grid-template-columns'] = '1fr 10px 1fr' 359 | ``` 360 | 361 | #### `split.getSizes()` is replaced by CSS values 362 | 363 | Likewise, the `.getSizes()` function is replaced by reading the CSS values directly. 364 | 365 | __Split.js__ 366 | 367 | ```js 368 | > split.getSizes() 369 | [50, 50] 370 | ``` 371 | 372 | __Split Grid__ 373 | 374 | ```js 375 | > document.querySelector('.grid').style['grid-template-columns'] 376 | "1fr 10px 1fr" 377 | ``` 378 | 379 | #### `split.setSizes()` is replaced by CSS values 380 | 381 | In the same way, the `.getSizes()` function is replaced by setting the CSS values directly. 382 | 383 | __Split.js__ 384 | 385 | ```js 386 | > split.setSizes([50, 50]) 387 | ``` 388 | 389 | __Split Grid__ 390 | 391 | ```js 392 | > document.querySelector('.grid').style['grid-template-columns'] = '1fr 10px 1fr' 393 | ``` 394 | 395 | #### `split.destroy()` has different parameters in Split Grid 396 | 397 | Since there's no styles or gutters added by Split Grid, there's no need for Split.js' 398 | `preserveStyles` and `preserveGutters` parameters. Instead, `.destroy()` takes 399 | one parameter, `immediate: boolean`, whether to wait until the gutter has stopped dragging 400 | before removing event listeners. 401 | 402 | ## License 403 | 404 | Copyright (c) 2019 Nathan Cahill 405 | 406 | Permission is hereby granted, free of charge, to any person obtaining a copy 407 | of this software and associated documentation files (the "Software"), to deal 408 | in the Software without restriction, including without limitation the rights 409 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 410 | copies of the Software, and to permit persons to whom the Software is 411 | furnished to do so, subject to the following conditions: 412 | 413 | The above copyright notice and this permission notice shall be included in 414 | all copies or substantial portions of the Software. 415 | 416 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 417 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 418 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 419 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 420 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 421 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 422 | THE SOFTWARE. 423 | -------------------------------------------------------------------------------- /packages/split-grid/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'split-grid' { 2 | // Options passed when creating a new Split instance. 3 | export interface SplitOptions { 4 | // An array of objects, with `element` and `track` keys. `element` is the element in the grid to enable as a draggable gutter. `track` is the grid track the gutter element is positioned on. These must match. 5 | columnGutters?: Gutter[]; 6 | // An array of objects, with `element` and `track` keys. `element` is the element in the grid to enable as a draggable gutter. `track` is the grid track the gutter element is positioned on. These must match. 7 | rowGutters?: Gutter[]; 8 | // The minimum size in pixels for all tracks. Default: `0` 9 | minSize?: number; 10 | // The maximum size in pixels for all tracks. Default: `Infinity` 11 | maxSize?: number; 12 | 13 | // The minimum size in pixels for all tracks. Default: `options.minSize` 14 | columnMinSize?: number; 15 | // The minimum size in pixels for all tracks. Default: `options.minSize` 16 | rowMinSize?: number; 17 | 18 | // The maximum size in pixels for all tracks. Default: `options.maxSize` 19 | columnMaxSize?: number; 20 | // The maximum size in pixels for all tracks. Default: `options.maxSize` 21 | rowMaxSize?: number; 22 | 23 | // An object keyed by `track` index, with values set to the minimum size in pixels for the track at that index. Allows individual minSizes to be specified by track. Note this option is plural with an `s`, while the two fallback options are singular. Default: `options.columnMinSize` 24 | columnMinSizes?: Sizes; 25 | // An object keyed by `track` index, with values set to the minimum size in pixels for the track at that index. Allows individual minSizes to be specified by track. Note this option is plural with an `s`, while the two fallback options are singular. Default: `options.rowMinSize` 26 | rowMinSizes?: Sizes; 27 | 28 | // An object keyed by `track` index, with values set to the maximum size in pixels for the track at that index. Allows individual maxSizes to be specified by track. Note this option is plural with an `s`, while the two fallback options are singular. Default: `options.columnMaxSize` 29 | columnMaxSizes?: Sizes; 30 | // An object keyed by `track` index, with values set to the maximum size in pixels for the track at that index. Allows individual maxSizes to be specified by track. Note this option is plural with an `s`, while the two fallback options are singular. Default: `options.rowMaxSize` 31 | rowMaxSizes?: Sizes; 32 | 33 | // Snap to minimum size at this offset in pixels. Set to `0` to disable snap. Default: `30` 34 | snapOffset?: number; 35 | // Snap to minimum size at this offset in pixels. Set to `0` to disable snap. Default: `options.snapOffset` 36 | columnSnapOffset?: number; 37 | // Snap to minimum size at this offset in pixels. Set to `0` to disable snap. Default: `options.snapOffset` 38 | rowSnapOffset?: number; 39 | // Drag this number of pixels at a time. Defaults to `1` for smooth dragging, but can be set to a pixel value to give more control over the resulting sizes. Default: `1` 40 | dragInterval?: number; 41 | // Drag this number of pixels at a time. Defaults to `1` for smooth dragging, but can be set to a pixel value to give more control over the resulting sizes. Default: `options.dragInterval` 42 | columnDragInterval?: number; 43 | // Drag this number of pixels at a time. Defaults to `1` for smooth dragging, but can be set to a pixel value to give more control over the resulting sizes. Default: `options.dragInterval` 44 | rowDragInterval?: number; 45 | // Cursor to show while dragging. Defaults to `'col-resize'` for column gutters and `'row-resize'` for row gutters. 46 | cursor?: string; 47 | // Cursor to show while dragging. Default: `'col-resize'` 48 | columnCursor?: string; 49 | // Cursor to show while dragging. Default: `'row-resize'` 50 | rowCursor?: string; 51 | // Called continuously on drag. For process intensive code, add a debounce function to rate limit this callback. `gridTemplateStyle` is the computed CSS value for `grid-template-column` or `grid-template-row`, depending on `direction`. 52 | onDrag?: (direction: Direction, track: number, gridTemplateStyle: string) => void; 53 | // Called on drag start. 54 | onDragStart?: (direction: Direction, track: number) => void; 55 | // Called on drag end. 56 | onDragEnd?: (direction: Direction, track: number) => void; 57 | // Called to update the CSS properties of the grid element. Must eventually apply the CSS value to the CSS prop, or the grid will not change. `gridTemplateStyle` is the computed CSS value of CSS rule `gridTemplateProp`. 58 | writeStyle?: (grid: HTMLElement, gridTemplateProp: GridTemplateProperty, gridTemplateStyle: string) => void; 59 | gridTemplateColumns?: string; 60 | gridTemplateRows?: string; 61 | } 62 | 63 | // Returns from creating a new Split instance. 64 | export interface SplitInstance { 65 | // Adds a draggable row gutter. The element must be a direct descendant of the element with grid layout, and positioned in the specified track. 66 | addColumnGutter: (element: HTMLElement, track: number) => void; 67 | // Adds a draggable row gutter. The element must be a direct descendant of the element with grid layout, and positioned in the specified track. 68 | addRowGutter: (element: HTMLElement, track: number) => void; 69 | // Removes event listeners from a column gutter by track number. If `immediate = false` is passed, event handlers are removed after dragging ends. If a gutter isn't currently being dragged, it's event handlers are removed immediately. 70 | removeColumnGutter: (track: number, immediate?: boolean) => void; 71 | // Removes event listeners from a row gutter by track number. If `immediate = false` is passed, event handlers are removed after dragging ends. If a gutter isn't currently being dragged, it's event handlers are removed immediately. 72 | removeRowGutter: (track: number, immediate?: boolean) => void; 73 | // Destroy the instance by removing the attached event listeners. If `immediate = false` is passed, the instance is destroyed after dragging ends. If a gutter isn't currently being dragged, it's destroyed immediately. 74 | destroy: (immediate?: boolean) => void; 75 | } 76 | 77 | export type Gutter = { 78 | element: HTMLElement; 79 | track: number; 80 | }; 81 | 82 | export type Sizes = { [track: number]: number }; 83 | 84 | export type Direction = 'row' | 'column'; 85 | 86 | export type GridTemplateProperty = 'grid-template-columns' | 'grid-template-rows'; 87 | 88 | export default function Split(options: SplitOptions): SplitInstance; 89 | } 90 | -------------------------------------------------------------------------------- /packages/split-grid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "split-grid", 3 | "version": "1.0.11", 4 | "description": "The spiritual successor of Split.js, built for CSS Grid", 5 | "main": "dist/split-grid.js", 6 | "minified:main": "dist/split-grid.min.js", 7 | "module": "dist/split-grid.mjs", 8 | "types": "index.d.ts", 9 | "scripts": { 10 | "prepublish": "rollup -c", 11 | "build": "rollup -c && npm run size", 12 | "watch": "rollup -cw", 13 | "test": "jest", 14 | "size": "echo \"gzip size: $(gzip-size --raw $npm_package_minified_main) bytes\"" 15 | }, 16 | "repository": "https://github.com/nathancahill/split", 17 | "author": "Nathan Cahill ", 18 | "homepage": "https://split.js.org/", 19 | "files": [ 20 | "index.d.ts", 21 | "dist" 22 | ], 23 | "license": "MIT", 24 | "collective": { 25 | "type": "opencollective", 26 | "url": "https://opencollective.com/splitjs" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/split-grid/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve' 2 | import buble from '@rollup/plugin-buble' 3 | import { terser } from 'rollup-plugin-terser' 4 | 5 | const pkg = require('./package.json') 6 | 7 | export default [ 8 | { 9 | input: './src/index.js', 10 | output: [ 11 | { 12 | name: 'Split', 13 | file: pkg.main, 14 | format: 'umd', 15 | sourcemap: false, 16 | banner: `/*! ${pkg.name} - v${pkg.version} */\n`, 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'esm', 21 | sourcemap: false, 22 | }, 23 | ], 24 | plugins: [ 25 | resolve(), 26 | buble({ 27 | exclude: 'node_modules/**', 28 | objectAssign: 'Object.assign', 29 | transforms: { 30 | forOf: false, 31 | }, 32 | }), 33 | ], 34 | }, 35 | { 36 | input: './src/index.js', 37 | output: { 38 | name: 'Split', 39 | file: pkg['minified:main'], 40 | format: 'umd', 41 | sourcemap: true, 42 | banner: `/*! ${pkg.name} - v${pkg.version} */\n`, 43 | }, 44 | plugins: [ 45 | resolve(), 46 | buble({ 47 | exclude: 'node_modules/**', 48 | objectAssign: 'Object.assign', 49 | transforms: { 50 | forOf: false, 51 | }, 52 | }), 53 | terser(), 54 | ], 55 | }, 56 | ] 57 | -------------------------------------------------------------------------------- /packages/split-grid/src/Gutter.js: -------------------------------------------------------------------------------- 1 | import { parse, getSizeAtTrack } from 'grid-template-utils' 2 | import { 3 | getStyles, 4 | getGapValue, 5 | firstNonZero, 6 | NOOP, 7 | defaultWriteStyle, 8 | getOption, 9 | } from './util' 10 | import getMatchedCSSRules from './getMatchedCSSRules' 11 | 12 | const gridTemplatePropColumns = 'grid-template-columns' 13 | const gridTemplatePropRows = 'grid-template-rows' 14 | 15 | class Gutter { 16 | constructor(direction, options, parentOptions) { 17 | this.direction = direction 18 | this.element = options.element 19 | this.track = options.track 20 | 21 | if (direction === 'column') { 22 | this.gridTemplateProp = gridTemplatePropColumns 23 | this.gridGapProp = 'grid-column-gap' 24 | this.cursor = getOption( 25 | parentOptions, 26 | 'columnCursor', 27 | getOption(parentOptions, 'cursor', 'col-resize'), 28 | ) 29 | this.snapOffset = getOption( 30 | parentOptions, 31 | 'columnSnapOffset', 32 | getOption(parentOptions, 'snapOffset', 30), 33 | ) 34 | this.dragInterval = getOption( 35 | parentOptions, 36 | 'columnDragInterval', 37 | getOption(parentOptions, 'dragInterval', 1), 38 | ) 39 | this.clientAxis = 'clientX' 40 | this.optionStyle = getOption(parentOptions, 'gridTemplateColumns') 41 | } else if (direction === 'row') { 42 | this.gridTemplateProp = gridTemplatePropRows 43 | this.gridGapProp = 'grid-row-gap' 44 | this.cursor = getOption( 45 | parentOptions, 46 | 'rowCursor', 47 | getOption(parentOptions, 'cursor', 'row-resize'), 48 | ) 49 | this.snapOffset = getOption( 50 | parentOptions, 51 | 'rowSnapOffset', 52 | getOption(parentOptions, 'snapOffset', 30), 53 | ) 54 | this.dragInterval = getOption( 55 | parentOptions, 56 | 'rowDragInterval', 57 | getOption(parentOptions, 'dragInterval', 1), 58 | ) 59 | this.clientAxis = 'clientY' 60 | this.optionStyle = getOption(parentOptions, 'gridTemplateRows') 61 | } 62 | 63 | this.onDragStart = getOption(parentOptions, 'onDragStart', NOOP) 64 | this.onDragEnd = getOption(parentOptions, 'onDragEnd', NOOP) 65 | this.onDrag = getOption(parentOptions, 'onDrag', NOOP) 66 | this.writeStyle = getOption( 67 | parentOptions, 68 | 'writeStyle', 69 | defaultWriteStyle, 70 | ) 71 | 72 | this.startDragging = this.startDragging.bind(this) 73 | this.stopDragging = this.stopDragging.bind(this) 74 | this.drag = this.drag.bind(this) 75 | 76 | this.minSizeStart = options.minSizeStart 77 | this.minSizeEnd = options.minSizeEnd 78 | this.maxSizeStart = options.maxSizeStart 79 | this.maxSizeEnd = options.maxSizeEnd 80 | 81 | if (options.element) { 82 | this.element.addEventListener('mousedown', this.startDragging) 83 | this.element.addEventListener('touchstart', this.startDragging) 84 | } 85 | } 86 | 87 | getDimensions() { 88 | const { 89 | width, 90 | height, 91 | top, 92 | bottom, 93 | left, 94 | right, 95 | } = this.grid.getBoundingClientRect() 96 | 97 | if (this.direction === 'column') { 98 | this.start = top 99 | this.end = bottom 100 | this.size = height 101 | } else if (this.direction === 'row') { 102 | this.start = left 103 | this.end = right 104 | this.size = width 105 | } 106 | } 107 | 108 | getSizeAtTrack(track, end) { 109 | return getSizeAtTrack( 110 | track, 111 | this.computedPixels, 112 | this.computedGapPixels, 113 | end, 114 | ) 115 | } 116 | 117 | getSizeOfTrack(track) { 118 | return this.computedPixels[track].numeric 119 | } 120 | 121 | getRawTracks() { 122 | const tracks = getStyles( 123 | this.gridTemplateProp, 124 | [this.grid], 125 | getMatchedCSSRules(this.grid), 126 | ) 127 | if (!tracks.length) { 128 | if (this.optionStyle) return this.optionStyle 129 | 130 | throw Error('Unable to determine grid template tracks from styles.') 131 | } 132 | return tracks[0] 133 | } 134 | 135 | getGap() { 136 | const gap = getStyles( 137 | this.gridGapProp, 138 | [this.grid], 139 | getMatchedCSSRules(this.grid), 140 | ) 141 | if (!gap.length) { 142 | return null 143 | } 144 | return gap[0] 145 | } 146 | 147 | getRawComputedTracks() { 148 | return window.getComputedStyle(this.grid)[this.gridTemplateProp] 149 | } 150 | 151 | getRawComputedGap() { 152 | return window.getComputedStyle(this.grid)[this.gridGapProp] 153 | } 154 | 155 | setTracks(raw) { 156 | this.tracks = raw.split(' ') 157 | this.trackValues = parse(raw) 158 | } 159 | 160 | setComputedTracks(raw) { 161 | this.computedTracks = raw.split(' ') 162 | this.computedPixels = parse(raw) 163 | } 164 | 165 | setGap(raw) { 166 | this.gap = raw 167 | } 168 | 169 | setComputedGap(raw) { 170 | this.computedGap = raw 171 | this.computedGapPixels = getGapValue('px', this.computedGap) || 0 172 | } 173 | 174 | getMousePosition(e) { 175 | if ('touches' in e) return e.touches[0][this.clientAxis] 176 | return e[this.clientAxis] 177 | } 178 | 179 | startDragging(e) { 180 | if ('button' in e && e.button !== 0) { 181 | return 182 | } 183 | 184 | // Don't actually drag the element. We emulate that in the drag function. 185 | e.preventDefault() 186 | 187 | if (this.element) { 188 | this.grid = this.element.parentNode 189 | } else { 190 | this.grid = e.target.parentNode 191 | } 192 | 193 | this.getDimensions() 194 | this.setTracks(this.getRawTracks()) 195 | this.setComputedTracks(this.getRawComputedTracks()) 196 | this.setGap(this.getGap()) 197 | this.setComputedGap(this.getRawComputedGap()) 198 | 199 | const trackPercentage = this.trackValues.filter( 200 | track => track.type === '%', 201 | ) 202 | const trackFr = this.trackValues.filter(track => track.type === 'fr') 203 | 204 | this.totalFrs = trackFr.length 205 | 206 | if (this.totalFrs) { 207 | const track = firstNonZero(trackFr) 208 | 209 | if (track !== null) { 210 | this.frToPixels = 211 | this.computedPixels[track].numeric / trackFr[track].numeric 212 | } 213 | } 214 | 215 | if (trackPercentage.length) { 216 | const track = firstNonZero(trackPercentage) 217 | 218 | if (track !== null) { 219 | this.percentageToPixels = 220 | this.computedPixels[track].numeric / 221 | trackPercentage[track].numeric 222 | } 223 | } 224 | 225 | // get start of gutter track 226 | const gutterStart = this.getSizeAtTrack(this.track, false) + this.start 227 | this.dragStartOffset = this.getMousePosition(e) - gutterStart 228 | 229 | this.aTrack = this.track - 1 230 | 231 | if (this.track < this.tracks.length - 1) { 232 | this.bTrack = this.track + 1 233 | } else { 234 | throw Error( 235 | `Invalid track index: ${this.track}. Track must be between two other tracks and only ${this.tracks.length} tracks were found.`, 236 | ) 237 | } 238 | 239 | this.aTrackStart = this.getSizeAtTrack(this.aTrack, false) + this.start 240 | this.bTrackEnd = this.getSizeAtTrack(this.bTrack, true) + this.start 241 | 242 | // Set the dragging property of the pair object. 243 | this.dragging = true 244 | 245 | // All the binding. `window` gets the stop events in case we drag out of the elements. 246 | window.addEventListener('mouseup', this.stopDragging) 247 | window.addEventListener('touchend', this.stopDragging) 248 | window.addEventListener('touchcancel', this.stopDragging) 249 | window.addEventListener('mousemove', this.drag) 250 | window.addEventListener('touchmove', this.drag) 251 | 252 | // Disable selection. Disable! 253 | this.grid.addEventListener('selectstart', NOOP) 254 | this.grid.addEventListener('dragstart', NOOP) 255 | 256 | this.grid.style.userSelect = 'none' 257 | this.grid.style.webkitUserSelect = 'none' 258 | this.grid.style.MozUserSelect = 'none' 259 | this.grid.style.pointerEvents = 'none' 260 | 261 | // Set the cursor at multiple levels 262 | this.grid.style.cursor = this.cursor 263 | window.document.body.style.cursor = this.cursor 264 | 265 | this.onDragStart(this.direction, this.track) 266 | } 267 | 268 | stopDragging() { 269 | this.dragging = false 270 | 271 | // Remove the stored event listeners. This is why we store them. 272 | this.cleanup() 273 | 274 | this.onDragEnd(this.direction, this.track) 275 | 276 | if (this.needsDestroy) { 277 | if (this.element) { 278 | this.element.removeEventListener( 279 | 'mousedown', 280 | this.startDragging, 281 | ) 282 | this.element.removeEventListener( 283 | 'touchstart', 284 | this.startDragging, 285 | ) 286 | } 287 | this.destroyCb() 288 | this.needsDestroy = false 289 | this.destroyCb = null 290 | } 291 | } 292 | 293 | drag(e) { 294 | let mousePosition = this.getMousePosition(e) 295 | 296 | const gutterSize = this.getSizeOfTrack(this.track) 297 | const minMousePosition = Math.max( 298 | this.aTrackStart + 299 | this.minSizeStart + 300 | this.dragStartOffset + 301 | this.computedGapPixels, 302 | this.bTrackEnd - 303 | this.maxSizeEnd - 304 | this.computedGapPixels - 305 | (gutterSize - this.dragStartOffset), 306 | ) 307 | const maxMousePosition = Math.min( 308 | this.bTrackEnd - 309 | this.minSizeEnd - 310 | this.computedGapPixels - 311 | (gutterSize - this.dragStartOffset), 312 | this.aTrackStart + 313 | this.maxSizeStart + 314 | this.dragStartOffset + 315 | this.computedGapPixels, 316 | ) 317 | const minMousePositionOffset = minMousePosition + this.snapOffset 318 | const maxMousePositionOffset = maxMousePosition - this.snapOffset 319 | 320 | if (mousePosition < minMousePositionOffset) { 321 | mousePosition = minMousePosition 322 | } 323 | 324 | if (mousePosition > maxMousePositionOffset) { 325 | mousePosition = maxMousePosition 326 | } 327 | 328 | if (mousePosition < minMousePosition) { 329 | mousePosition = minMousePosition 330 | } else if (mousePosition > maxMousePosition) { 331 | mousePosition = maxMousePosition 332 | } 333 | 334 | let aTrackSize = 335 | mousePosition - 336 | this.aTrackStart - 337 | this.dragStartOffset - 338 | this.computedGapPixels 339 | let bTrackSize = 340 | this.bTrackEnd - 341 | mousePosition + 342 | this.dragStartOffset - 343 | gutterSize - 344 | this.computedGapPixels 345 | 346 | if (this.dragInterval > 1) { 347 | const aTrackSizeIntervaled = 348 | Math.round(aTrackSize / this.dragInterval) * this.dragInterval 349 | bTrackSize -= aTrackSizeIntervaled - aTrackSize 350 | aTrackSize = aTrackSizeIntervaled 351 | } 352 | 353 | if (aTrackSize < this.minSizeStart) { 354 | aTrackSize = this.minSizeStart 355 | } 356 | 357 | if (bTrackSize < this.minSizeEnd) { 358 | bTrackSize = this.minSizeEnd 359 | } 360 | 361 | if (this.trackValues[this.aTrack].type === 'px') { 362 | this.tracks[this.aTrack] = `${aTrackSize}px` 363 | } else if (this.trackValues[this.aTrack].type === 'fr') { 364 | if (this.totalFrs === 1) { 365 | this.tracks[this.aTrack] = '1fr' 366 | } else { 367 | const targetFr = aTrackSize / this.frToPixels 368 | this.tracks[this.aTrack] = `${targetFr}fr` 369 | } 370 | } else if (this.trackValues[this.aTrack].type === '%') { 371 | const targetPercentage = aTrackSize / this.percentageToPixels 372 | this.tracks[this.aTrack] = `${targetPercentage}%` 373 | } 374 | 375 | if (this.trackValues[this.bTrack].type === 'px') { 376 | this.tracks[this.bTrack] = `${bTrackSize}px` 377 | } else if (this.trackValues[this.bTrack].type === 'fr') { 378 | if (this.totalFrs === 1) { 379 | this.tracks[this.bTrack] = '1fr' 380 | } else { 381 | const targetFr = bTrackSize / this.frToPixels 382 | this.tracks[this.bTrack] = `${targetFr}fr` 383 | } 384 | } else if (this.trackValues[this.bTrack].type === '%') { 385 | const targetPercentage = bTrackSize / this.percentageToPixels 386 | this.tracks[this.bTrack] = `${targetPercentage}%` 387 | } 388 | 389 | const style = this.tracks.join(' ') 390 | this.writeStyle(this.grid, this.gridTemplateProp, style) 391 | this.onDrag(this.direction, this.track, style) 392 | } 393 | 394 | cleanup() { 395 | window.removeEventListener('mouseup', this.stopDragging) 396 | window.removeEventListener('touchend', this.stopDragging) 397 | window.removeEventListener('touchcancel', this.stopDragging) 398 | window.removeEventListener('mousemove', this.drag) 399 | window.removeEventListener('touchmove', this.drag) 400 | 401 | if (this.grid) { 402 | this.grid.removeEventListener('selectstart', NOOP) 403 | this.grid.removeEventListener('dragstart', NOOP) 404 | 405 | this.grid.style.userSelect = '' 406 | this.grid.style.webkitUserSelect = '' 407 | this.grid.style.MozUserSelect = '' 408 | this.grid.style.pointerEvents = '' 409 | 410 | this.grid.style.cursor = '' 411 | } 412 | 413 | window.document.body.style.cursor = '' 414 | } 415 | 416 | destroy(immediate = true, cb) { 417 | if (immediate || this.dragging === false) { 418 | this.cleanup() 419 | if (this.element) { 420 | this.element.removeEventListener( 421 | 'mousedown', 422 | this.startDragging, 423 | ) 424 | this.element.removeEventListener( 425 | 'touchstart', 426 | this.startDragging, 427 | ) 428 | } 429 | 430 | if (cb) { 431 | cb() 432 | } 433 | } else { 434 | this.needsDestroy = true 435 | if (cb) { 436 | this.destroyCb = cb 437 | } 438 | } 439 | } 440 | } 441 | 442 | export default Gutter 443 | -------------------------------------------------------------------------------- /packages/split-grid/src/getMatchedCSSRules.js: -------------------------------------------------------------------------------- 1 | export default el => 2 | [] 3 | .concat( 4 | ...Array.from(el.ownerDocument.styleSheets).map(s => { 5 | let rules = [] 6 | 7 | try { 8 | rules = Array.from(s.cssRules || []) 9 | } catch (e) { 10 | // Ignore results on security error 11 | } 12 | 13 | return rules 14 | }), 15 | ) 16 | .filter(r => { 17 | let matches = false 18 | try { 19 | matches = el.matches(r.selectorText) 20 | } catch (e) { 21 | // Ignore matching erros 22 | } 23 | 24 | return matches 25 | }) 26 | -------------------------------------------------------------------------------- /packages/split-grid/src/index.js: -------------------------------------------------------------------------------- 1 | import Gutter from './Gutter' 2 | import { getOption } from './util' 3 | 4 | const getTrackOption = (options, track, defaultValue) => { 5 | if (track in options) { 6 | return options[track] 7 | } 8 | 9 | return defaultValue 10 | } 11 | 12 | const createGutter = (direction, options) => gutterOptions => { 13 | if (gutterOptions.track < 1) { 14 | throw Error( 15 | `Invalid track index: ${gutterOptions.track}. Track must be between two other tracks.`, 16 | ) 17 | } 18 | 19 | const trackMinSizes = 20 | direction === 'column' 21 | ? options.columnMinSizes || {} 22 | : options.rowMinSizes || {} 23 | const trackMaxSizes = 24 | direction === 'column' 25 | ? options.columnMaxSizes || {} 26 | : options.rowMaxSizes || {} 27 | const trackMinSize = direction === 'column' ? 'columnMinSize' : 'rowMinSize' 28 | const trackMaxSize = direction === 'column' ? 'columnMaxSize' : 'rowMaxSize' 29 | 30 | return new Gutter( 31 | direction, 32 | { 33 | minSizeStart: getTrackOption( 34 | trackMinSizes, 35 | gutterOptions.track - 1, 36 | getOption( 37 | options, 38 | trackMinSize, 39 | getOption(options, 'minSize', 0), 40 | ), 41 | ), 42 | minSizeEnd: getTrackOption( 43 | trackMinSizes, 44 | gutterOptions.track + 1, 45 | getOption( 46 | options, 47 | trackMinSize, 48 | getOption(options, 'minSize', 0), 49 | ), 50 | ), 51 | maxSizeStart: getTrackOption( 52 | trackMaxSizes, 53 | gutterOptions.track - 1, 54 | getOption( 55 | options, 56 | trackMaxSize, 57 | getOption(options, 'maxSize', Infinity), 58 | ), 59 | ), 60 | maxSizeEnd: getTrackOption( 61 | trackMaxSizes, 62 | gutterOptions.track + 1, 63 | getOption( 64 | options, 65 | trackMaxSize, 66 | getOption(options, 'maxSize', Infinity), 67 | ), 68 | ), 69 | ...gutterOptions, 70 | }, 71 | options, 72 | ) 73 | } 74 | 75 | class Grid { 76 | constructor(options) { 77 | this.columnGutters = {} 78 | this.rowGutters = {} 79 | 80 | this.options = { 81 | columnGutters: options.columnGutters || [], 82 | rowGutters: options.rowGutters || [], 83 | columnMinSizes: options.columnMinSizes || {}, 84 | rowMinSizes: options.rowMinSizes || {}, 85 | columnMaxSizes: options.columnMaxSizes || {}, 86 | rowMaxSizes: options.rowMaxSizes || {}, 87 | ...options, 88 | } 89 | 90 | this.options.columnGutters.forEach(gutterOptions => { 91 | this.columnGutters[gutterOptions.track] = createGutter( 92 | 'column', 93 | this.options, 94 | )(gutterOptions) 95 | }) 96 | 97 | this.options.rowGutters.forEach(gutterOptions => { 98 | this.rowGutters[gutterOptions.track] = createGutter( 99 | 'row', 100 | this.options, 101 | )(gutterOptions) 102 | }) 103 | } 104 | 105 | addColumnGutter(element, track) { 106 | if (this.columnGutters[track]) { 107 | this.columnGutters[track].destroy() 108 | } 109 | 110 | this.columnGutters[track] = createGutter( 111 | 'column', 112 | this.options, 113 | )({ 114 | element, 115 | track, 116 | }) 117 | } 118 | 119 | addRowGutter(element, track) { 120 | if (this.rowGutters[track]) { 121 | this.rowGutters[track].destroy() 122 | } 123 | 124 | this.rowGutters[track] = createGutter( 125 | 'row', 126 | this.options, 127 | )({ 128 | element, 129 | track, 130 | }) 131 | } 132 | 133 | removeColumnGutter(track, immediate = true) { 134 | if (this.columnGutters[track]) { 135 | this.columnGutters[track].destroy(immediate, () => { 136 | delete this.columnGutters[track] 137 | }) 138 | } 139 | } 140 | 141 | removeRowGutter(track, immediate = true) { 142 | if (this.rowGutters[track]) { 143 | this.rowGutters[track].destroy(immediate, () => { 144 | delete this.rowGutters[track] 145 | }) 146 | } 147 | } 148 | 149 | handleDragStart(e, direction, track) { 150 | if (direction === 'column') { 151 | if (this.columnGutters[track]) { 152 | this.columnGutters[track].destroy() 153 | } 154 | 155 | this.columnGutters[track] = createGutter( 156 | 'column', 157 | this.options, 158 | )({ 159 | track, 160 | }) 161 | this.columnGutters[track].startDragging(e) 162 | } else if (direction === 'row') { 163 | if (this.rowGutters[track]) { 164 | this.rowGutters[track].destroy() 165 | } 166 | 167 | this.rowGutters[track] = createGutter( 168 | 'row', 169 | this.options, 170 | )({ 171 | track, 172 | }) 173 | this.rowGutters[track].startDragging(e) 174 | } 175 | } 176 | 177 | destroy(immediate = true) { 178 | Object.keys(this.columnGutters).forEach(track => 179 | this.columnGutters[track].destroy(immediate, () => { 180 | delete this.columnGutters[track] 181 | }), 182 | ) 183 | Object.keys(this.rowGutters).forEach(track => 184 | this.rowGutters[track].destroy(immediate, () => { 185 | delete this.rowGutters[track] 186 | }), 187 | ) 188 | } 189 | } 190 | 191 | export default options => new Grid(options) 192 | -------------------------------------------------------------------------------- /packages/split-grid/src/util.js: -------------------------------------------------------------------------------- 1 | export const getStyles = (rule, ownRules, matchedRules) => 2 | [...ownRules, ...matchedRules] 3 | .map(r => r.style[rule]) 4 | .filter(style => style !== undefined && style !== '') 5 | 6 | export const getGapValue = (unit, size) => { 7 | if (size.endsWith(unit)) { 8 | return Number(size.slice(0, -1 * unit.length)) 9 | } 10 | return null 11 | } 12 | 13 | export const firstNonZero = tracks => { 14 | // eslint-disable-next-line no-plusplus 15 | for (let i = 0; i < tracks.length; i++) { 16 | if (tracks[i].numeric > 0) { 17 | return i 18 | } 19 | } 20 | return null 21 | } 22 | 23 | export const NOOP = () => false 24 | 25 | export const defaultWriteStyle = (element, gridTemplateProp, style) => { 26 | // eslint-disable-next-line no-param-reassign 27 | element.style[gridTemplateProp] = style 28 | } 29 | 30 | export const getOption = (options, propName, def) => { 31 | const value = options[propName] 32 | if (value !== undefined) { 33 | return value 34 | } 35 | return def 36 | } 37 | -------------------------------------------------------------------------------- /packages/split-grid/src/util.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | 3 | import { getStyles, getGapValue, firstNonZero } from './util' 4 | 5 | const ownStyle = { 'grid-template-columns': '2px 2px 2px' } 6 | 7 | const columns = { 'grid-template-columns': '1px 1px 1px' } 8 | const emptyColumns = { 'grid-template-columns': '' } 9 | const noColumns = {} 10 | 11 | test('getStyles columns', () => { 12 | const res = getStyles( 13 | 'grid-template-columns', 14 | [{ style: {} }], 15 | [{ style: columns }], 16 | ) 17 | expect(res).toEqual(['1px 1px 1px']) 18 | }) 19 | 20 | test('getStyles emptyColumns', () => { 21 | const res = getStyles( 22 | 'grid-template-columns', 23 | [{ style: {} }], 24 | [{ style: emptyColumns }], 25 | ) 26 | expect(res).toEqual([]) 27 | }) 28 | 29 | test('getStyles noColumns', () => { 30 | const res = getStyles( 31 | 'grid-template-columns', 32 | [{ style: {} }], 33 | [{ style: noColumns }], 34 | ) 35 | expect(res).toEqual([]) 36 | }) 37 | 38 | test('getStyles ownStyle', () => { 39 | const res = getStyles( 40 | 'grid-template-columns', 41 | [{ style: ownStyle }], 42 | [{ style: noColumns }], 43 | ) 44 | expect(res).toEqual(['2px 2px 2px']) 45 | }) 46 | 47 | test('getStyles ownStyle no match', () => { 48 | const res = getStyles( 49 | 'grid-template-columns', 50 | [{ style: { other: '1' } }], 51 | [{ style: columns }], 52 | ) 53 | expect(res).toEqual(['1px 1px 1px']) 54 | }) 55 | 56 | const rows = { 'grid-template-rows': '1px 1px 1px' } 57 | const emptyRows = { 'grid-template-rows': '' } 58 | const noRows = {} 59 | 60 | test('getStyles rows', () => { 61 | const res = getStyles( 62 | 'grid-template-rows', 63 | [{ style: {} }], 64 | [{ style: rows }], 65 | ) 66 | expect(res).toEqual(['1px 1px 1px']) 67 | }) 68 | 69 | test('getStyles emptyRows', () => { 70 | const res = getStyles( 71 | 'grid-template-rows', 72 | [{ style: {} }], 73 | [{ style: emptyRows }], 74 | ) 75 | expect(res).toEqual([]) 76 | }) 77 | 78 | test('getStyles noRows', () => { 79 | const res = getStyles( 80 | 'grid-template-rows', 81 | [{ style: {} }], 82 | [{ style: noRows }], 83 | ) 84 | expect(res).toEqual([]) 85 | }) 86 | 87 | test('getGapValue', () => { 88 | expect(getGapValue('px', '10px')).toEqual(10) 89 | }) 90 | 91 | test('firstNonZero first', () => { 92 | expect(firstNonZero([{ numeric: 1 }, { numeric: 0 }])).toEqual(0) 93 | }) 94 | 95 | test('firstNonZero second', () => { 96 | expect(firstNonZero([{ numeric: 0 }, { numeric: 1 }])).toEqual(1) 97 | }) 98 | -------------------------------------------------------------------------------- /packages/splitjs/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Nathan Cahill 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/splitjs/grips/horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathancahill/split/48759432a50510e2ed762109d5a8d12a3aac9e63/packages/splitjs/grips/horizontal.png -------------------------------------------------------------------------------- /packages/splitjs/grips/vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathancahill/split/48759432a50510e2ed762109d5a8d12a3aac9e63/packages/splitjs/grips/vertical.png -------------------------------------------------------------------------------- /packages/splitjs/index.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Split.js 2 | // Project: https://github.com/nathancahill/split/tree/master/packages/splitjs 3 | // Definitions by: Ilia Choly 4 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped 5 | // TypeScript Version: 2.1 6 | 7 | // Global variable outside module loader 8 | export as namespace Split 9 | 10 | // Module loader 11 | export = Split 12 | 13 | declare function Split( 14 | elements: Array, 15 | options?: Split.Options, 16 | ): Split.Instance 17 | 18 | declare namespace Split { 19 | type Partial = { [P in keyof T]?: T[P] } 20 | type CSSStyleDeclarationPartial = Partial 21 | 22 | interface Options { 23 | // Initial sizes of each element in percents or CSS values. 24 | sizes?: number[] 25 | 26 | // Minimum size of each element. 27 | minSize?: number | number[] 28 | 29 | // Maximum size of each element. 30 | maxSize?: number | number[] 31 | 32 | expandToMin?: boolean 33 | 34 | // Gutter size in pixels. 35 | gutterSize?: number 36 | 37 | gutterAlign?: string 38 | 39 | // Snap to minimum size offset in pixels. 40 | snapOffset?: number | number[] 41 | 42 | dragInterval?: number 43 | 44 | // Direction to split: horizontal or vertical. 45 | direction?: 'horizontal' | 'vertical' 46 | 47 | // Cursor to display while dragging. 48 | cursor?: string 49 | 50 | // Callback on drag. 51 | onDrag?(sizes: number[]): void 52 | 53 | // Callback on drag start. 54 | onDragStart?(sizes: number[]): void 55 | 56 | // Callback on drag end. 57 | onDragEnd?(sizes: number[]): void 58 | 59 | // Called to create each gutter element 60 | gutter?( 61 | index: number, 62 | direction: 'horizontal' | 'vertical', 63 | ): HTMLElement 64 | 65 | // Called to set the style of each element. 66 | elementStyle?( 67 | dimension: 'width' | 'height', 68 | elementSize: number, 69 | gutterSize: number, 70 | index: number, 71 | ): CSSStyleDeclarationPartial 72 | 73 | // Called to set the style of the gutter. 74 | gutterStyle?( 75 | dimension: 'width' | 'height', 76 | gutterSize: number, 77 | index: number, 78 | ): CSSStyleDeclarationPartial 79 | } 80 | 81 | interface Instance { 82 | // setSizes behaves the same as the sizes configuration option, passing an array of percents or CSS values. 83 | // It updates the sizes of the elements in the split. 84 | setSizes(sizes: number[]): void 85 | 86 | // getSizes returns an array of percents, suitable for using with setSizes or creation. 87 | getSizes(): number[] 88 | 89 | // collapse changes the size of element at index to 0. 90 | // Every element except the last is collapsed towards the front (left or top). 91 | // The last is collapsed towards the back. 92 | collapse(index: number): void 93 | 94 | // Destroy the instance. It removes the gutter elements, and the size CSS styles Split.js set. 95 | destroy(preserveStyles?: boolean, preserveGutters?: boolean): void 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /packages/splitjs/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = config => { 2 | config.set({ 3 | customLaunchers: { 4 | // latest firefox, chrome, safari 5 | sl_firefox_latest: { 6 | base: 'SauceLabs', 7 | browserName: 'firefox', 8 | platform: 'Windows 10', 9 | version: 'latest', 10 | }, 11 | sl_chrome_latest: { 12 | base: 'SauceLabs', 13 | browserName: 'chrome', 14 | platform: 'Windows 10', 15 | version: 'latest', 16 | }, 17 | sl_safari: { 18 | base: 'SauceLabs', 19 | browserName: 'safari', 20 | platform: 'macOS 10.15', 21 | version: 'latest', 22 | }, 23 | 24 | // latest edge 25 | sl_edge: { 26 | base: 'SauceLabs', 27 | browserName: 'MicrosoftEdge', 28 | platform: 'Windows 10', 29 | version: 'latest', 30 | }, 31 | // ie 11 32 | sl_ie_11: { 33 | base: 'SauceLabs', 34 | browserName: 'internet explorer', 35 | platform: 'Windows 10', 36 | version: 'latest', 37 | }, 38 | // ie 10 39 | sl_ie_10: { 40 | base: 'SauceLabs', 41 | browserName: 'internet explorer', 42 | platform: 'Windows 7', 43 | version: '10.0', 44 | }, 45 | }, 46 | frameworks: ['jasmine'], 47 | browsers: ['FirefoxHeadless', 'ChromeHeadless'], 48 | singleRun: true, 49 | files: ['dist/split.js', 'test/split.spec.js'], 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /packages/splitjs/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/splitjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "split.js", 3 | "version": "1.6.5", 4 | "description": "2kb unopinionated utility for resizeable split views", 5 | "main": "dist/split.js", 6 | "minified:main": "dist/split.min.js", 7 | "module": "dist/split.es.js", 8 | "types": "index.d.ts", 9 | "repository": "https://github.com/nathancahill/split", 10 | "keywords": [ 11 | "css", 12 | "split", 13 | "flexbox", 14 | "tiny", 15 | "split-layout" 16 | ], 17 | "author": "Nathan Cahill ", 18 | "license": "MIT", 19 | "homepage": "https://split.js.org/", 20 | "scripts": { 21 | "test": "karma start", 22 | "prepublish": "rollup -c", 23 | "build": "rollup -c && npm run size", 24 | "watch": "rollup -cw", 25 | "size": "echo \"gzip size: $(gzip-size --raw $npm_package_minified_main) bytes\"", 26 | "saucelabs": "yarn run test --browsers sl_firefox_latest,sl_chrome_latest,sl_safari,sl_edge,sl_ie_11,sl_ie_10" 27 | }, 28 | "files": [ 29 | "index.d.ts", 30 | "dist" 31 | ], 32 | "browserslist": [ 33 | "Chrome >= 22", 34 | "Firefox >= 6", 35 | "Opera >= 15", 36 | "Safari >= 6.2", 37 | "IE >= 9" 38 | ], 39 | "collective": { 40 | "type": "opencollective", 41 | "url": "https://opencollective.com/splitjs" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/splitjs/rollup.config.js: -------------------------------------------------------------------------------- 1 | import buble from '@rollup/plugin-buble' 2 | import { terser } from 'rollup-plugin-terser' 3 | 4 | const pkg = require('./package.json') 5 | 6 | const output = { 7 | format: 'umd', 8 | file: pkg.main, 9 | name: 'Split', 10 | sourcemap: false, 11 | banner: `/*! Split.js - v${pkg.version} */\n`, 12 | } 13 | 14 | export default [ 15 | { 16 | input: 'src/split.js', 17 | output: [ 18 | output, 19 | { 20 | file: pkg.module, 21 | format: 'esm', 22 | sourcemap: false, 23 | }, 24 | ], 25 | plugins: [buble()], 26 | }, 27 | { 28 | input: 'src/split.js', 29 | output: { 30 | ...output, 31 | sourcemap: true, 32 | file: pkg['minified:main'], 33 | }, 34 | plugins: [buble(), terser()], 35 | }, 36 | ] 37 | -------------------------------------------------------------------------------- /packages/splitjs/test/SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Jasmine Spec Runner v3.5.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /packages/splitjs/test/lib/jasmine-3.5.0/boot.js: -------------------------------------------------------------------------------- 1 | /** 2 | Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project. 3 | 4 | If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms. 5 | 6 | The location of `boot.js` can be specified and/or overridden in `jasmine.yml`. 7 | 8 | [jasmine-gem]: http://github.com/pivotal/jasmine-gem 9 | */ 10 | 11 | (function() { 12 | 13 | /** 14 | * ## Require & Instantiate 15 | * 16 | * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference. 17 | */ 18 | window.jasmine = jasmineRequire.core(jasmineRequire); 19 | 20 | /** 21 | * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference. 22 | */ 23 | jasmineRequire.html(jasmine); 24 | 25 | /** 26 | * Create the Jasmine environment. This is used to run all specs in a project. 27 | */ 28 | var env = jasmine.getEnv(); 29 | 30 | /** 31 | * ## The Global Interface 32 | * 33 | * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged. 34 | */ 35 | var jasmineInterface = jasmineRequire.interface(jasmine, env); 36 | 37 | /** 38 | * Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. 39 | */ 40 | extend(window, jasmineInterface); 41 | 42 | /** 43 | * ## Runner Parameters 44 | * 45 | * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface. 46 | */ 47 | 48 | var queryString = new jasmine.QueryString({ 49 | getWindowLocation: function() { return window.location; } 50 | }); 51 | 52 | var filterSpecs = !!queryString.getParam("spec"); 53 | 54 | var config = { 55 | failFast: queryString.getParam("failFast"), 56 | oneFailurePerSpec: queryString.getParam("oneFailurePerSpec"), 57 | hideDisabled: queryString.getParam("hideDisabled") 58 | }; 59 | 60 | var random = queryString.getParam("random"); 61 | 62 | if (random !== undefined && random !== "") { 63 | config.random = random; 64 | } 65 | 66 | var seed = queryString.getParam("seed"); 67 | if (seed) { 68 | config.seed = seed; 69 | } 70 | 71 | /** 72 | * ## Reporters 73 | * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any). 74 | */ 75 | var htmlReporter = new jasmine.HtmlReporter({ 76 | env: env, 77 | navigateWithNewParam: function(key, value) { return queryString.navigateWithNewParam(key, value); }, 78 | addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); }, 79 | getContainer: function() { return document.body; }, 80 | createElement: function() { return document.createElement.apply(document, arguments); }, 81 | createTextNode: function() { return document.createTextNode.apply(document, arguments); }, 82 | timer: new jasmine.Timer(), 83 | filterSpecs: filterSpecs 84 | }); 85 | 86 | /** 87 | * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript. 88 | */ 89 | env.addReporter(jasmineInterface.jsApiReporter); 90 | env.addReporter(htmlReporter); 91 | 92 | /** 93 | * Filter which specs will be run by matching the start of the full name against the `spec` query param. 94 | */ 95 | var specFilter = new jasmine.HtmlSpecFilter({ 96 | filterString: function() { return queryString.getParam("spec"); } 97 | }); 98 | 99 | config.specFilter = function(spec) { 100 | return specFilter.matches(spec.getFullName()); 101 | }; 102 | 103 | env.configure(config); 104 | 105 | /** 106 | * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack. 107 | */ 108 | window.setTimeout = window.setTimeout; 109 | window.setInterval = window.setInterval; 110 | window.clearTimeout = window.clearTimeout; 111 | window.clearInterval = window.clearInterval; 112 | 113 | /** 114 | * ## Execution 115 | * 116 | * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded. 117 | */ 118 | var currentWindowOnload = window.onload; 119 | 120 | window.onload = function() { 121 | if (currentWindowOnload) { 122 | currentWindowOnload(); 123 | } 124 | htmlReporter.initialize(); 125 | env.execute(); 126 | }; 127 | 128 | /** 129 | * Helper function for readability above. 130 | */ 131 | function extend(destination, source) { 132 | for (var property in source) destination[property] = source[property]; 133 | return destination; 134 | } 135 | 136 | }()); 137 | -------------------------------------------------------------------------------- /packages/splitjs/test/lib/jasmine-3.5.0/jasmine.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | body { overflow-y: scroll; } 3 | 4 | .jasmine_html-reporter { width: 100%; background-color: #eee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333; } 5 | 6 | .jasmine_html-reporter a { text-decoration: none; } 7 | 8 | .jasmine_html-reporter a:hover { text-decoration: underline; } 9 | 10 | .jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { margin: 0; line-height: 14px; } 11 | 12 | .jasmine_html-reporter .jasmine-banner, .jasmine_html-reporter .jasmine-symbol-summary, .jasmine_html-reporter .jasmine-summary, .jasmine_html-reporter .jasmine-result-message, .jasmine_html-reporter .jasmine-spec .jasmine-description, .jasmine_html-reporter .jasmine-spec-detail .jasmine-description, .jasmine_html-reporter .jasmine-alert .jasmine-bar, .jasmine_html-reporter .jasmine-stack-trace { padding-left: 9px; padding-right: 9px; } 13 | 14 | .jasmine_html-reporter .jasmine-banner { position: relative; } 15 | 16 | .jasmine_html-reporter .jasmine-banner .jasmine-title { background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==") no-repeat; background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=") no-repeat, none; -moz-background-size: 100%; -o-background-size: 100%; -webkit-background-size: 100%; background-size: 100%; display: block; float: left; width: 90px; height: 25px; } 17 | 18 | .jasmine_html-reporter .jasmine-banner .jasmine-version { margin-left: 14px; position: relative; top: 6px; } 19 | 20 | .jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; } 21 | 22 | .jasmine_html-reporter .jasmine-version { color: #aaa; } 23 | 24 | .jasmine_html-reporter .jasmine-banner { margin-top: 14px; } 25 | 26 | .jasmine_html-reporter .jasmine-duration { color: #fff; float: right; line-height: 28px; padding-right: 9px; } 27 | 28 | .jasmine_html-reporter .jasmine-symbol-summary { overflow: hidden; margin: 14px 0; } 29 | 30 | .jasmine_html-reporter .jasmine-symbol-summary li { display: inline-block; height: 10px; width: 14px; font-size: 16px; } 31 | 32 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed { font-size: 14px; } 33 | 34 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed:before { color: #007069; content: "•"; } 35 | 36 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed { line-height: 9px; } 37 | 38 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed:before { color: #ca3a11; content: "×"; font-weight: bold; margin-left: -1px; } 39 | 40 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded { font-size: 14px; } 41 | 42 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded:before { color: #bababa; content: "•"; } 43 | 44 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded-no-display { font-size: 14px; display: none; } 45 | 46 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending { line-height: 17px; } 47 | 48 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending:before { color: #ba9d37; content: "*"; } 49 | 50 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty { font-size: 14px; } 51 | 52 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty:before { color: #ba9d37; content: "•"; } 53 | 54 | .jasmine_html-reporter .jasmine-run-options { float: right; margin-right: 5px; border: 1px solid #8a4182; color: #8a4182; position: relative; line-height: 20px; } 55 | 56 | .jasmine_html-reporter .jasmine-run-options .jasmine-trigger { cursor: pointer; padding: 8px 16px; } 57 | 58 | .jasmine_html-reporter .jasmine-run-options .jasmine-payload { position: absolute; display: none; right: -1px; border: 1px solid #8a4182; background-color: #eee; white-space: nowrap; padding: 4px 8px; } 59 | 60 | .jasmine_html-reporter .jasmine-run-options .jasmine-payload.jasmine-open { display: block; } 61 | 62 | .jasmine_html-reporter .jasmine-bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } 63 | 64 | .jasmine_html-reporter .jasmine-bar.jasmine-failed, .jasmine_html-reporter .jasmine-bar.jasmine-errored { background-color: #ca3a11; border-bottom: 1px solid #eee; } 65 | 66 | .jasmine_html-reporter .jasmine-bar.jasmine-passed { background-color: #007069; } 67 | 68 | .jasmine_html-reporter .jasmine-bar.jasmine-incomplete { background-color: #bababa; } 69 | 70 | .jasmine_html-reporter .jasmine-bar.jasmine-skipped { background-color: #bababa; } 71 | 72 | .jasmine_html-reporter .jasmine-bar.jasmine-warning { background-color: #ba9d37; color: #333; } 73 | 74 | .jasmine_html-reporter .jasmine-bar.jasmine-menu { background-color: #fff; color: #aaa; } 75 | 76 | .jasmine_html-reporter .jasmine-bar.jasmine-menu a { color: #333; } 77 | 78 | .jasmine_html-reporter .jasmine-bar a { color: white; } 79 | 80 | .jasmine_html-reporter.jasmine-spec-list .jasmine-bar.jasmine-menu.jasmine-failure-list, .jasmine_html-reporter.jasmine-spec-list .jasmine-results .jasmine-failures { display: none; } 81 | 82 | .jasmine_html-reporter.jasmine-failure-list .jasmine-bar.jasmine-menu.jasmine-spec-list, .jasmine_html-reporter.jasmine-failure-list .jasmine-summary { display: none; } 83 | 84 | .jasmine_html-reporter .jasmine-results { margin-top: 14px; } 85 | 86 | .jasmine_html-reporter .jasmine-summary { margin-top: 14px; } 87 | 88 | .jasmine_html-reporter .jasmine-summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; } 89 | 90 | .jasmine_html-reporter .jasmine-summary ul.jasmine-suite { margin-top: 7px; margin-bottom: 7px; } 91 | 92 | .jasmine_html-reporter .jasmine-summary li.jasmine-passed a { color: #007069; } 93 | 94 | .jasmine_html-reporter .jasmine-summary li.jasmine-failed a { color: #ca3a11; } 95 | 96 | .jasmine_html-reporter .jasmine-summary li.jasmine-empty a { color: #ba9d37; } 97 | 98 | .jasmine_html-reporter .jasmine-summary li.jasmine-pending a { color: #ba9d37; } 99 | 100 | .jasmine_html-reporter .jasmine-summary li.jasmine-excluded a { color: #bababa; } 101 | 102 | .jasmine_html-reporter .jasmine-specs li.jasmine-passed a:before { content: "• "; } 103 | 104 | .jasmine_html-reporter .jasmine-specs li.jasmine-failed a:before { content: "× "; } 105 | 106 | .jasmine_html-reporter .jasmine-specs li.jasmine-empty a:before { content: "* "; } 107 | 108 | .jasmine_html-reporter .jasmine-specs li.jasmine-pending a:before { content: "• "; } 109 | 110 | .jasmine_html-reporter .jasmine-specs li.jasmine-excluded a:before { content: "• "; } 111 | 112 | .jasmine_html-reporter .jasmine-description + .jasmine-suite { margin-top: 0; } 113 | 114 | .jasmine_html-reporter .jasmine-suite { margin-top: 14px; } 115 | 116 | .jasmine_html-reporter .jasmine-suite a { color: #333; } 117 | 118 | .jasmine_html-reporter .jasmine-failures .jasmine-spec-detail { margin-bottom: 28px; } 119 | 120 | .jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description { background-color: #ca3a11; color: white; } 121 | 122 | .jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description a { color: white; } 123 | 124 | .jasmine_html-reporter .jasmine-result-message { padding-top: 14px; color: #333; white-space: pre-wrap; } 125 | 126 | .jasmine_html-reporter .jasmine-result-message span.jasmine-result { display: block; } 127 | 128 | .jasmine_html-reporter .jasmine-stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666; border: 1px solid #ddd; background: white; white-space: pre; } 129 | -------------------------------------------------------------------------------- /packages/splitjs/test/lib/jasmine-3.5.0/jasmine_favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathancahill/split/48759432a50510e2ed762109d5a8d12a3aac9e63/packages/splitjs/test/lib/jasmine-3.5.0/jasmine_favicon.png -------------------------------------------------------------------------------- /packages/splitjs/test/split.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jasmine */ 2 | /* global Split */ 3 | /* eslint-disable no-var, func-names, prefer-arrow-callback, object-shorthand, prefer-template */ 4 | 5 | function calcParts(expr) { 6 | var re = /calc\(([\d]*\.?[\d]*?)%\s?-\s?([\d]+)px\)/ 7 | var m = re.exec(expr) 8 | 9 | return { 10 | percentage: parseFloat(m[1]), 11 | pixels: parseInt(m[2], 10), 12 | } 13 | } 14 | 15 | describe('Split', function() { 16 | beforeEach(function() { 17 | document.body.style.width = '800px' 18 | document.body.style.height = '600px' 19 | 20 | this.a = document.createElement('div') 21 | this.b = document.createElement('div') 22 | this.c = document.createElement('div') 23 | 24 | this.a.id = 'a' 25 | this.b.id = 'b' 26 | this.c.id = 'c' 27 | 28 | document.body.appendChild(this.a) 29 | document.body.appendChild(this.b) 30 | document.body.appendChild(this.c) 31 | }) 32 | 33 | afterEach(function() { 34 | document.body.removeChild(this.a) 35 | document.body.removeChild(this.b) 36 | document.body.removeChild(this.c) 37 | }) 38 | 39 | it('splits in two when given two elements', function() { 40 | Split(['#a', '#b']) 41 | 42 | expect(this.a.style.width).toContain('calc(50% - 5px)') 43 | expect(this.b.style.width).toContain('calc(50% - 5px)') 44 | }) 45 | 46 | it('splits in three when given three elements', function() { 47 | Split(['#a', '#b', '#c']) 48 | 49 | expect(calcParts(this.a.style.width).percentage).toBeCloseTo(33.33) 50 | expect(calcParts(this.b.style.width).percentage).toBeCloseTo(33.33) 51 | expect(calcParts(this.c.style.width).percentage).toBeCloseTo(33.33) 52 | 53 | expect(calcParts(this.a.style.width).pixels).toBe(5) 54 | expect(calcParts(this.b.style.width).pixels).toBe(10) 55 | expect(calcParts(this.c.style.width).pixels).toBe(5) 56 | }) 57 | 58 | it('splits vertically when direction is vertical', function() { 59 | Split(['#a', '#b'], { 60 | direction: 'vertical', 61 | }) 62 | 63 | expect(this.a.style.height).toContain('calc(50% - 5px)') 64 | expect(this.b.style.height).toContain('calc(50% - 5px)') 65 | }) 66 | 67 | it('splits in percentages when given sizes', function() { 68 | Split(['#a', '#b'], { 69 | sizes: [25, 75], 70 | }) 71 | 72 | expect(this.a.style.width).toContain('calc(25% - 5px)') 73 | expect(this.b.style.width).toContain('calc(75% - 5px)') 74 | }) 75 | 76 | it('splits in percentages when given sizes', function() { 77 | Split(['#a', '#b'], { 78 | sizes: [25, 75], 79 | }) 80 | 81 | expect(this.a.style.width).toContain('calc(25% - 5px)') 82 | expect(this.b.style.width).toContain('calc(75% - 5px)') 83 | }) 84 | 85 | it('accounts for gutter size', function() { 86 | Split(['#a', '#b'], { 87 | gutterSize: 20, 88 | }) 89 | 90 | expect(this.a.style.width).toContain('calc(50% - 10px)') 91 | expect(this.b.style.width).toContain('calc(50% - 10px)') 92 | }) 93 | 94 | it('accounts for gutter size with more than two elements', function() { 95 | Split(['#a', '#b', '#c'], { 96 | gutterSize: 20, 97 | }) 98 | 99 | expect(calcParts(this.a.style.width).percentage).toBeCloseTo(33.33) 100 | expect(calcParts(this.b.style.width).percentage).toBeCloseTo(33.33) 101 | expect(calcParts(this.c.style.width).percentage).toBeCloseTo(33.33) 102 | 103 | expect(calcParts(this.a.style.width).pixels).toBe(10) 104 | expect(calcParts(this.b.style.width).pixels).toBe(20) 105 | expect(calcParts(this.c.style.width).pixels).toBe(10) 106 | }) 107 | 108 | it('accounts for gutter size when direction is vertical', function() { 109 | Split(['#a', '#b'], { 110 | direction: 'vertical', 111 | gutterSize: 20, 112 | }) 113 | 114 | expect(this.a.style.height).toContain('calc(50% - 10px)') 115 | expect(this.b.style.height).toContain('calc(50% - 10px)') 116 | }) 117 | 118 | it('accounts for gutter size with more than two elements when direction is vertical', function() { 119 | Split(['#a', '#b', '#c'], { 120 | direction: 'vertical', 121 | gutterSize: 20, 122 | }) 123 | 124 | expect(calcParts(this.a.style.height).percentage).toBeCloseTo(33.33) 125 | expect(calcParts(this.b.style.height).percentage).toBeCloseTo(33.33) 126 | expect(calcParts(this.c.style.height).percentage).toBeCloseTo(33.33) 127 | 128 | expect(calcParts(this.a.style.height).pixels).toBe(10) 129 | expect(calcParts(this.b.style.height).pixels).toBe(20) 130 | expect(calcParts(this.c.style.height).pixels).toBe(10) 131 | }) 132 | 133 | it('set size directly when given css values', function() { 134 | Split(['#a', '#b'], { 135 | sizes: ['150px', '640px'], 136 | }) 137 | 138 | expect(this.a.style.width).toBe('150px') 139 | expect(this.b.style.width).toBe('640px') 140 | }) 141 | 142 | it('adjusts sizes using setSizes', function() { 143 | var split = Split(['#a', '#b']) 144 | 145 | split.setSizes([70, 30]) 146 | 147 | expect(this.a.style.width).toContain('calc(70% - 5px)') 148 | expect(this.b.style.width).toContain('calc(30% - 5px)') 149 | }) 150 | 151 | it('collapse splits', function() { 152 | var split = Split(['#a', '#b']) 153 | 154 | split.collapse(0) 155 | 156 | expect(this.a.getBoundingClientRect().width).toBeCloseTo(100, 0) 157 | expect(this.b.getBoundingClientRect().width).toBeCloseTo(800 - 100 - 10, 0) 158 | 159 | split.collapse(1) 160 | 161 | expect(this.a.getBoundingClientRect().width).toBeCloseTo(800 - 100 - 10, 0) 162 | expect(this.b.getBoundingClientRect().width).toBeCloseTo(100, 0) 163 | }) 164 | 165 | it('returns sizes', function() { 166 | var split = Split(['#a', '#b']) 167 | var sizes = split.getSizes() 168 | 169 | expect(sizes).toEqual([50, 50]) 170 | 171 | split.setSizes([70, 30]) 172 | 173 | sizes = split.getSizes() 174 | 175 | expect(sizes).toEqual([70, 30]) 176 | }) 177 | 178 | it('sets element styles using the elementStyle function', function() { 179 | Split(['#a', '#b'], { 180 | elementStyle: function(dimension, size) { 181 | return { 182 | width: size + '%', 183 | } 184 | }, 185 | }) 186 | 187 | expect(this.a.style.width).toBe('50%') 188 | expect(this.b.style.width).toBe('50%') 189 | }) 190 | }) 191 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | trailingComma: 'all', 3 | tabWidth: 4, 4 | semi: false, 5 | singleQuote: true, 6 | arrowParens: 'avoid', 7 | } 8 | --------------------------------------------------------------------------------