├── .browserslistrc ├── .eslintrc.cjs ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── build-test.yml │ ├── vitepress-deploy.yml │ └── vuepress-deploy.yml ├── .gitignore ├── LICENSE ├── README.md ├── dist ├── index.css ├── vue-grid-layout-v3.cjs ├── vue-grid-layout-v3.iife.js ├── vue-grid-layout-v3.js └── vue-grid-layout-v3.umd.js ├── favicon.ico ├── index.html ├── jest.config.js ├── package-lock.json ├── package.json ├── src ├── app.vue ├── assets │ └── logo.png ├── components │ ├── grid-item.vue │ ├── grid-layout.vue │ └── index.js ├── custom-drag-element.vue ├── helpers │ ├── dom.js │ ├── draggable-utils.js │ ├── responsive-utils.js │ └── utils.js ├── main.js └── test-element.vue ├── test ├── interact-test.html ├── interact-test.js └── unit │ ├── GridItem.spec.js │ └── utils.spec.js ├── vite.config.js └── website ├── .editorconfig ├── .eslintrc.cjs ├── .gitignore ├── .vitepress ├── config.mjs └── theme │ ├── custom.css │ ├── index.js │ └── style.css ├── favicon.ico ├── index.html ├── package-lock.json ├── package.json ├── src ├── app.vue ├── example │ ├── 01-basic.vue │ ├── 02-events.vue │ ├── 03-multiple-grids.vue │ ├── 04-allow-ignore.vue │ ├── 05-mirrored.vue │ ├── 06-responsive.vue │ ├── 07-prevent-collision.vue │ ├── 08-responsive-predefined-layouts.vue │ ├── 09-dynamic-add-remove.vue │ ├── 10-drag-from-outside.vue │ ├── 11-bounded.vue │ ├── dom.js │ ├── styling-grid-lines.vue │ ├── styling-placeholder.vue │ ├── test-element.vue │ └── usage.vue ├── guide │ ├── 01-basic.md │ ├── 02-events.md │ ├── 03-multiple-grids.md │ ├── 04-allow-ignore.md │ ├── 05-mirrored.md │ ├── 06-responsive.md │ ├── 07-prevent-collision.md │ ├── 08-responsive-predefined-layouts.md │ ├── 09-dynamic-add-remove.md │ ├── 10-drag-from-outside.md │ ├── 11-bounded.md │ ├── auto-size.md │ ├── events.md │ ├── index.md │ ├── properties.md │ ├── readme.md │ ├── styling.md │ └── usage.md ├── index.md ├── main.js ├── public │ ├── favicon.ico │ └── logo.png ├── readme.md └── zh │ ├── README.md │ └── guide │ ├── 01-basic.md │ ├── 02-events.md │ ├── 03-multiple-grids.md │ ├── 04-allow-ignore.md │ ├── 05-mirrored.md │ ├── 06-responsive.md │ ├── 07-prevent-collision.md │ ├── 08-responsive-predefined-layouts.md │ ├── 09-dynamic-add-remove.md │ ├── 10-drag-from-outside.md │ ├── 11-bounded.md │ ├── README.md │ ├── auto-size.md │ ├── events.md │ ├── examples.md │ ├── properties.md │ ├── styling.md │ └── usage.md └── vite.config.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 8 4 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | const config = { 3 | root: true, 4 | extends: [ 5 | 'plugin:vue/vue3-essential', 6 | 'eslint:recommended', 7 | 'airbnb-base', 8 | ], 9 | // env: { 10 | // // 'vue/setup-compiler-macros': true, 11 | // es6: true, 12 | // }, 13 | // parserOptions: { 14 | // }, 15 | parser: 'vue-eslint-parser', 16 | parserOptions: { 17 | ecmaVersion: 'latest', 18 | // parser: '@babel/eslint-parser', 19 | ecmaFeatures: { 20 | jsx: true, 21 | }, 22 | }, 23 | rules: { 24 | // 修改的规则 25 | 'no-multi-spaces': [2, { ignoreEOLComments: true }], 26 | 27 | // 关闭vue的规则 28 | 'vue/multi-word-component-names': 0, 29 | 'vue/require-component-is': 0, 30 | 'vue/no-v-for-template-key-on-child': 0, 31 | 'vue/no-v-text-v-html-on-component': 0, 32 | 33 | // 关闭import规则 34 | 'import/extensions': 0, 35 | 'import/no-extraneous-dependencies': 0, 36 | 'import/no-dynamic-require': 0, 37 | 'import/no-unresolved': 0, 38 | 'import/prefer-default-export': 0, 39 | 40 | // 关闭airbnb规则 41 | 'arrow-parens': 0, 42 | 'arrow-body-style': 0, 43 | 'consistent-return': 0, 44 | 'default-param-last': 0, 45 | 'default-case': 0, 46 | 'global-require': 0, 47 | 'object-curly-newline': 0, 48 | 'prefer-destructuring': 0, 49 | radix: 0, 50 | 'max-len': 0, 51 | 'newline-per-chained-call': 0, 52 | 'no-bitwise': 0, 53 | 'no-console': 0, 54 | 'no-control-regex': 0, 55 | 'no-cond-assign': 0, 56 | 'no-continue': 0, 57 | 'no-lonely-if': 0, 58 | 'no-return-assign': 0, 59 | 'no-restricted-syntax': 0, 60 | 'no-param-reassign': 0, 61 | 'no-plusplus': 0, 62 | 'no-use-before-define': 0, 63 | 'no-multiple-empty-lines': 0, 64 | 'no-shadow': 0, 65 | 66 | // 关闭其他规则 67 | 'prettier/prettier': 0, 68 | camelcase: 0, 69 | 70 | // 增加的规则 71 | 'no-debugger': 2, 72 | }, 73 | ignorePatterns: [ 74 | 'coverage/**', 75 | 'public/**', 76 | 'dist/**', 77 | 'website/**', 78 | 'test/**/*', 79 | ], 80 | }; 81 | 82 | // 开发测试阶段降低规则要求 83 | if (process.env.NODE_ENV !== 'production') { 84 | Object.assign(config.rules, { 85 | 'vue/no-unused-vars': 1, 86 | 'vue/valid-template-root': 1, 87 | 'vue/return-in-computed-property': 1, 88 | 'vue/valid-v-for': 1, 89 | 90 | 'import/newline-after-import': 1, 91 | 92 | 'array-bracket-spacing': 1, 93 | 'brace-style': 1, 94 | 'block-spacing': 1, 95 | 'comma-dangle': 1, 96 | 'comma-spacing': 1, 97 | indent: [1, 2, { SwitchCase: 1 }], 98 | 'key-spacing': 1, 99 | 'keyword-spacing': 1, 100 | 'operator-linebreak': 1, 101 | 'prefer-const': 1, 102 | 'padded-blocks': 1, 103 | 'quote-props': 1, 104 | semi: 1, 105 | 'spaced-comment': 1, 106 | 'space-before-blocks': 1, 107 | 'space-before-function-paren': 1, 108 | 'space-infix-ops': 1, 109 | 'space-in-parens': 1, 110 | 'no-debugger': 1, 111 | 'no-empty': 1, 112 | 'no-empty-function': 1, 113 | 'no-unused-vars': 1, 114 | 'no-multi-spaces': [1, { ignoreEOLComments: true }], 115 | 'no-unreachable': 1, 116 | }); 117 | } 118 | 119 | module.exports = config; 120 | 121 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: merfais 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Software version (please complete the following information):** 11 | - Browser [e.g. chrome, safari] 12 | - Vue Version [e.g. 2.5.7] 13 | - vue-grid-layout Version: [e.g. 2.3.3] 14 | 15 | **Describe the bug** 16 | A clear and concise description of what the bug is. 17 | 18 | Please use the [CodeSandbox Template](https://codesandbox.io/s/5wy3rz5z1x?module=%2Fsrc%2FShowcaseLayout.js) to demonstrate your bug. It is much easier for us to help you if you do. 19 | 20 | 21 | **To Reproduce** 22 | Steps to reproduce the behavior: 23 | 1. Go to '...' 24 | 2. Click on '....' 25 | 3. Scroll down to '....' 26 | 4. See error 27 | 28 | **Expected behavior** 29 | A clear and concise description of what you expected to happen. 30 | 31 | **Screenshots** 32 | If applicable, add screenshots to help explain your problem. 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## If you have a feature request, please try to implement it before requesting it.
This is free software and the author is busy with other projects. 11 | 12 | **Is your feature request related to a problem? Please describe.** 13 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 14 | 15 | **Describe the solution you'd like** 16 | A clear and concise description of what you want to happen. 17 | 18 | **Describe alternatives you've considered** 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | **Additional context** 22 | Add any other context or screenshots about the feature request here. 23 | -------------------------------------------------------------------------------- /.github/workflows/build-test.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [18.x, 20.x, 22.x] 17 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Use Node.js ${{ matrix.node-version }} 22 | uses: actions/setup-node@v1 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | - name: Install dependency 26 | run: npm i 27 | - name: Lint check 28 | run: npm run lint 29 | - name: Build 30 | run: npm run build 31 | # - name: Unit test 32 | # run: yarn test:unit 33 | -------------------------------------------------------------------------------- /.github/workflows/vitepress-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy VitePress site to Pages 2 | on: 3 | push: 4 | branches: 5 | - master 6 | # 允许你从 Actions 选项卡手动运行此工作流程 7 | workflow_dispatch: 8 | 9 | # 设置 GITHUB_TOKEN 的权限,以允许部署到 GitHub Pages 10 | permissions: 11 | contents: read 12 | pages: write 13 | id-token: write 14 | 15 | # 只允许同时进行一次部署,跳过正在运行和最新队列之间的运行队列 16 | # 但是,不要取消正在进行的运行,因为我们希望允许这些生产部署完成 17 | concurrency: 18 | group: pages 19 | cancel-in-progress: false 20 | 21 | jobs: 22 | # 构建工作 23 | build: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v4 28 | with: 29 | fetch-depth: 0 # 如果未启用 lastUpdated,则不需要 30 | # - uses: pnpm/action-setup@v3 # 如果使用 pnpm,请取消此区域注释 31 | # with: 32 | # version: 9 33 | # - uses: oven-sh/setup-bun@v1 # 如果使用 Bun,请取消注释 34 | - name: Setup Node 35 | uses: actions/setup-node@v4 36 | with: 37 | node-version: 20 38 | cache: npm # 或 pnpm / yarn 39 | - name: Setup Pages 40 | uses: actions/configure-pages@v4 41 | - name: Install dependencies 42 | run: cd website && npm install # 或 pnpm install / yarn install / bun install 43 | - name: Build with VitePress 44 | run: cd website && npm run docs:build # 或 pnpm docs:build / yarn docs:build / bun run docs:build 45 | - name: Upload artifact 46 | uses: actions/upload-pages-artifact@v3 47 | with: 48 | path: website/.vitepress/dist 49 | 50 | # 部署工作 51 | deploy: 52 | environment: 53 | name: github-pages 54 | url: ${{ steps.deployment.outputs.page_url }} 55 | needs: build 56 | runs-on: ubuntu-latest 57 | name: Deploy 58 | steps: 59 | - name: Deploy to GitHub Pages 60 | id: deployment 61 | uses: actions/deploy-pages@v4 62 | 63 | -------------------------------------------------------------------------------- /.github/workflows/vuepress-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy vuepress website 2 | on: 3 | # push: 4 | # branches: 5 | # - master 6 | # 允许你从 Actions 选项卡手动运行此工作流程 7 | workflow_dispatch: 8 | jobs: 9 | build-and-deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@master 14 | 15 | - uses: actions/setup-node@v2 16 | with: 17 | node-version: '16' 18 | 19 | - name: vuepress-deploy 20 | uses: jenkey2011/vuepress-deploy@master 21 | env: 22 | TARGET_REPO: merfais/vue-grid-layout-v3 23 | TARGET_BRANCH: gh-pages 24 | BUILD_SCRIPT: cd website && yarn && yarn build 25 | BUILD_DIR: public 26 | with: 27 | ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | #/dist 4 | dist/demo.html 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw* 23 | 24 | yarn-error.log 25 | cache/** 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 greyby 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Vue Grid Layout 4 | 5 |

6 | 7 |

vue-grid-layout-v3

8 | 9 |

10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 |

21 |

22 | Documentation Website 23 |

24 | 25 | ## What is Vue Grid Layout? 26 | 27 | vue-grid-layout is a grid layout system, like [Gridster](http://dsmorse.github.io/gridster.js/), for Vue.js. **Heavily inspired by [React-Grid-Layout](https://github.com/STRML/react-grid-layout)** 28 | 29 | ## Features 30 | 31 | * Draggable widgets 32 | * Resizable widgets 33 | * Static widgets 34 | * Bounds checking for dragging and resizing 35 | * Widgets may be added or removed without rebuilding grid 36 | * Layout can be serialized and restored 37 | * Automatic RTL support (resizing not working with RTL on 2.2.0) 38 | * Responsive 39 | 40 | ## **Current version:** 3.1.0 (Supports Vue 3.2+) 41 | 42 | #### **upgrading from version 3.0 to version 3.1 has some break change** 43 | + GridLayout expose changed: `{ placeholderRef, emitter }` -> `{ el, placeholderEl, emitter, placeholder }` 44 | + GridItem expose changed: `{ calcXY, domRef }` -> `{ calcXY, el }` 45 | 46 | #### **For legacy browsers**, like IE11, use version [2.3.12-legacy](https://github.com/jbaysolutions/vue-grid-layout/tree/legacy) 47 | #### **For Vue 2.1.10+ use version [2.4.0](https://github.com/jbaysolutions/vue-grid-layout/tree/2.4.0)** 48 | #### **For Vue 2.1.10 and below use version [2.1.3](https://github.com/jbaysolutions/vue-grid-layout/tree/2.1.3)** 49 | #### **For Vue 1 use version [1.0.3](https://github.com/jbaysolutions/vue-grid-layout/tree/1.0.3)** 50 | 51 | ## Documentation 52 | 53 | Check out the Documentation Website 54 | 55 | 58 | 59 | ## Contribute 60 | 61 | If you have a feature request, please add it as an issue or make a pull request. 62 | 63 | 64 | 3.0 version Developed by CoffeeBi 65 | 66 | Developed by JBay Solutions 67 | -------------------------------------------------------------------------------- /dist/index.css: -------------------------------------------------------------------------------- 1 | .vue-grid-item[data-v-99aff433]{transition:all .2s ease;transition-property:left,top,right}.vue-grid-item.no-touch[data-v-99aff433]{touch-action:none}.vue-grid-item.cssTransforms[data-v-99aff433]{transition-property:transform;left:0;right:auto}.vue-grid-item.cssTransforms.render-rtl[data-v-99aff433]{left:auto;right:0}.vue-grid-item.resizing[data-v-99aff433]{opacity:.6;z-index:3}.vue-grid-item.vue-draggable-dragging[data-v-99aff433]{transition:none;z-index:3}.vue-grid-item.vue-grid-placeholder[data-v-99aff433]{background:red;opacity:.2;transition-duration:.1s;z-index:2;-webkit-user-select:none;user-select:none}.vue-grid-item>.vue-resizable-handle[data-v-99aff433]{position:absolute;width:20px;height:20px;bottom:0;right:0;background:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg08IS0tIEdlbmVyYXRvcjogQWRvYmUgRmlyZXdvcmtzIENTNiwgRXhwb3J0IFNWRyBFeHRlbnNpb24gYnkgQWFyb24gQmVhbGwgKGh0dHA6Ly9maXJld29ya3MuYWJlYWxsLmNvbSkgLiBWZXJzaW9uOiAwLjYuMSAgLS0+DTwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DTxzdmcgaWQ9IlVudGl0bGVkLVBhZ2UlMjAxIiB2aWV3Qm94PSIwIDAgNiA2IiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojZmZmZmZmMDAiIHZlcnNpb249IjEuMSINCXhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiDQl4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjZweCIgaGVpZ2h0PSI2cHgiDT4NCTxnIG9wYWNpdHk9IjAuMzAyIj4NCQk8cGF0aCBkPSJNIDYgNiBMIDAgNiBMIDAgNC4yIEwgNCA0LjIgTCA0LjIgNC4yIEwgNC4yIDAgTCA2IDAgTCA2IDYgTCA2IDYgWiIgZmlsbD0iIzAwMDAwMCIvPg0JPC9nPg08L3N2Zz4=);background-position:bottom right;padding:0 3px 3px 0;background-repeat:no-repeat;background-origin:content-box;box-sizing:border-box;cursor:se-resize}.vue-grid-item>.vue-rtl-resizable-handle[data-v-99aff433]{bottom:0;left:0;background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAuMDAwMDAwMDAwMDAwMDAyIiBoZWlnaHQ9IjEwLjAwMDAwMDAwMDAwMDAwMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KIDwhLS0gQ3JlYXRlZCB3aXRoIE1ldGhvZCBEcmF3IC0gaHR0cDovL2dpdGh1Yi5jb20vZHVvcGl4ZWwvTWV0aG9kLURyYXcvIC0tPgogPGc+CiAgPHRpdGxlPmJhY2tncm91bmQ8L3RpdGxlPgogIDxyZWN0IGZpbGw9Im5vbmUiIGlkPSJjYW52YXNfYmFja2dyb3VuZCIgaGVpZ2h0PSIxMiIgd2lkdGg9IjEyIiB5PSItMSIgeD0iLTEiLz4KICA8ZyBkaXNwbGF5PSJub25lIiBvdmVyZmxvdz0idmlzaWJsZSIgeT0iMCIgeD0iMCIgaGVpZ2h0PSIxMDAlIiB3aWR0aD0iMTAwJSIgaWQ9ImNhbnZhc0dyaWQiPgogICA8cmVjdCBmaWxsPSJ1cmwoI2dyaWRwYXR0ZXJuKSIgc3Ryb2tlLXdpZHRoPSIwIiB5PSIwIiB4PSIwIiBoZWlnaHQ9IjEwMCUiIHdpZHRoPSIxMDAlIi8+CiAgPC9nPgogPC9nPgogPGc+CiAgPHRpdGxlPkxheWVyIDE8L3RpdGxlPgogIDxsaW5lIGNhbnZhcz0iI2ZmZmZmZiIgY2FudmFzLW9wYWNpdHk9IjEiIHN0cm9rZS1saW5lY2FwPSJ1bmRlZmluZWQiIHN0cm9rZS1saW5lam9pbj0idW5kZWZpbmVkIiBpZD0ic3ZnXzEiIHkyPSItNzAuMTc4NDA3IiB4Mj0iMTI0LjQ2NDE3NSIgeTE9Ii0zOC4zOTI3MzciIHgxPSIxNDQuODIxMjg5IiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlPSIjMDAwIiBmaWxsPSJub25lIi8+CiAgPGxpbmUgc3Ryb2tlPSIjNjY2NjY2IiBzdHJva2UtbGluZWNhcD0idW5kZWZpbmVkIiBzdHJva2UtbGluZWpvaW49InVuZGVmaW5lZCIgaWQ9InN2Z181IiB5Mj0iOS4xMDY5NTciIHgyPSIwLjk0NzI0NyIgeTE9Ii0wLjAxODEyOCIgeDE9IjAuOTQ3MjQ3IiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9Im5vbmUiLz4KICA8bGluZSBzdHJva2UtbGluZWNhcD0idW5kZWZpbmVkIiBzdHJva2UtbGluZWpvaW49InVuZGVmaW5lZCIgaWQ9InN2Z183IiB5Mj0iOSIgeDI9IjEwLjA3MzUyOSIgeTE9IjkiIHgxPSItMC42NTU2NCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiM2NjY2NjYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+);background-position:bottom left;padding-left:3px;background-repeat:no-repeat;background-origin:content-box;cursor:sw-resize;right:auto}.vue-grid-item.disable-userselect[data-v-99aff433]{-webkit-user-select:none;user-select:none}.vue-grid-layout[data-v-a10aee95]{position:relative;transition:height .2s ease} 2 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merfais/vue-grid-layout-v3/a669942fa85c80211767b89acddbe6211319ffe2/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vue Grid Layout 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: [ 3 | 'js', 4 | 'json', 5 | 'vue', 6 | ], 7 | moduleNameMapper: { 8 | '^@/(.*)$': '/src/$1', 9 | }, 10 | transformIgnorePatterns: [ 11 | '/node_modules/(?!@babel|@interactjs)', 12 | ], 13 | transform: { 14 | '^.+\\.js$': '/node_modules/babel-jest', 15 | '.*\\.(vue)$': '/node_modules/vue-jest', 16 | }, 17 | coverageDirectory: '/test/unit/coverage', 18 | collectCoverageFrom: [ 19 | 'src/**/*.{js,vue}', 20 | '!src/main.js', 21 | '!src/router/index.js', 22 | '!**/node_modules/**', 23 | ], 24 | }; 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-grid-layout-v3", 3 | "version": "3.1.2", 4 | "description": "A draggable and resizable grid layout, as a Vue component.", 5 | "keywords": [ 6 | "grid", 7 | "vuejs", 8 | "drag", 9 | "draggable", 10 | "resize", 11 | "resizable", 12 | "fluid", 13 | "responsive" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/merfais/vue-grid-layout-v3.git" 18 | }, 19 | "author": "coffeebi ", 20 | "homepage": "https://github.com/merfais/vue-grid-layout-v3", 21 | "type": "module", 22 | "main": "./dist/vue-grid-layout-v3.umd.js", 23 | "module": "./dist/vue-grid-layout-v3.js", 24 | "unpkg": "./dist/vue-grid-layout-v3.umd.js", 25 | "exports": { 26 | "import": "./dist/vue-grid-layout-v3.js", 27 | "require": "./dist/vue-grid-layout-v3.cjs" 28 | }, 29 | "scripts": { 30 | "dev": "vite", 31 | "build": "vite build", 32 | "preview": "vite preview", 33 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs", 34 | "lint:fix": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix" 35 | }, 36 | "dependencies": { 37 | "@interactjs/actions": "^1.10.27", 38 | "@interactjs/auto-scroll": "^1.10.27", 39 | "@interactjs/auto-start": "^1.10.27", 40 | "@interactjs/dev-tools": "^1.10.27", 41 | "@interactjs/interact": "^1.10.27", 42 | "@interactjs/modifiers": "^1.10.27", 43 | "element-resize-detector": "^1.2.4", 44 | "mitt": "^3.0.1" 45 | }, 46 | "devDependencies": { 47 | "@rollup/plugin-eslint": "^9.0.5", 48 | "@vitejs/plugin-vue": "^5.0.5", 49 | "@vitejs/plugin-vue-jsx": "^4.0.0", 50 | "autoprefixer": "^10.4.20", 51 | "eslint": "^8.57.0", 52 | "eslint-config-airbnb-base": "^15.0.0", 53 | "eslint-plugin-vue": "^9.23.0", 54 | "vite": "^5.3.1", 55 | "vite-plugin-lib-inject-css": "^2.1.1", 56 | "vue": "^3.4.29", 57 | "vue-eslint-parser": "^9.4.3" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/app.vue: -------------------------------------------------------------------------------- 1 | 182 | 271 | 309 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merfais/vue-grid-layout-v3/a669942fa85c80211767b89acddbe6211319ffe2/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/grid-layout.vue: -------------------------------------------------------------------------------- 1 | 523 | 537 | 543 | 544 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | import GridItem from './grid-item.vue'; 2 | import GridLayout from './grid-layout.vue'; 3 | 4 | export default function install(app) { 5 | if (install.installed) return; 6 | install.installed = true; 7 | app.component('GridLayout', GridLayout); 8 | app.component('GridItem', GridItem); 9 | } 10 | 11 | export { GridLayout, GridItem, install }; 12 | 13 | -------------------------------------------------------------------------------- /src/custom-drag-element.vue: -------------------------------------------------------------------------------- 1 | 8 | 26 | 42 | -------------------------------------------------------------------------------- /src/helpers/dom.js: -------------------------------------------------------------------------------- 1 | // let currentDir: "ltr" | "rtl" | "auto" = "auto"; 2 | let currentDir = 'auto'; 3 | 4 | function hasDocument() { 5 | return (typeof document !== 'undefined'); 6 | } 7 | 8 | function hasWindow() { 9 | return (typeof window !== 'undefined'); 10 | } 11 | 12 | export function getDocumentDir() { 13 | if (!hasDocument()) { 14 | return currentDir; 15 | } 16 | const direction = (typeof document.dir !== 'undefined') 17 | ? document.dir 18 | : document.getElementsByTagName('html')[0].getAttribute('dir'); 19 | return direction; 20 | } 21 | 22 | // export function setDocumentDir(dir: "ltr" | "rtl" | "auto"){ 23 | export function setDocumentDir(dir) { 24 | if (!hasDocument) { 25 | currentDir = dir; 26 | return; 27 | } 28 | 29 | const html = document.getElementsByTagName('html')[0]; 30 | html.setAttribute('dir', dir); 31 | } 32 | 33 | const cb = () => {}; 34 | // export function addWindowEventListener(event:string, callback: () => mixed){ 35 | export function addWindowEventListener(event, callback = cb) { 36 | if (!hasWindow) { 37 | callback(); 38 | return; 39 | } 40 | window.addEventListener(event, callback); 41 | } 42 | 43 | export function removeWindowEventListener(event, callback = cb) { 44 | if (!hasWindow) { 45 | return; 46 | } 47 | window.removeEventListener(event, callback); 48 | } 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/helpers/draggable-utils.js: -------------------------------------------------------------------------------- 1 | // Get {x, y} positions from event. 2 | export function getControlPosition(e) { 3 | return offsetXYFromParentOf(e); 4 | } 5 | 6 | 7 | // Get from offsetParent 8 | export function offsetXYFromParentOf(evt) { 9 | const offsetParent = evt.target.offsetParent || document.body; 10 | const offsetParentRect = evt.offsetParent === document.body ? { left: 0, top: 0 } : offsetParent.getBoundingClientRect(); 11 | 12 | const x = evt.clientX + offsetParent.scrollLeft - offsetParentRect.left; 13 | const y = evt.clientY + offsetParent.scrollTop - offsetParentRect.top; 14 | 15 | /* const x = Math.round(evt.clientX + offsetParent.scrollLeft - offsetParentRect.left); 16 | const y = Math.round(evt.clientY + offsetParent.scrollTop - offsetParentRect.top); */ 17 | 18 | 19 | return { x, y }; 20 | } 21 | 22 | 23 | // Create an data object exposed by 's events 24 | export function createCoreData(lastX, lastY, x, y) { 25 | // State changes are often (but not always!) async. We want the latest value. 26 | const isStart = !isNum(lastX); 27 | 28 | if (isStart) { 29 | // If this is our first move, use the x and y as last coords. 30 | return { 31 | deltaX: 0, 32 | deltaY: 0, 33 | lastX: x, 34 | lastY: y, 35 | x, 36 | y, 37 | }; 38 | } 39 | // Otherwise calculate proper values. 40 | return { 41 | deltaX: x - lastX, 42 | deltaY: y - lastY, 43 | lastX, 44 | lastY, 45 | x, 46 | y, 47 | }; 48 | } 49 | 50 | 51 | function isNum(num) { 52 | return typeof num === 'number' && !Number.isNaN(num); 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/helpers/responsive-utils.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { cloneLayout, compact, correctBounds } from './utils'; 4 | 5 | // import type {Layout} from './utils'; 6 | // export type ResponsiveLayout = {lg?: Layout, md?: Layout, sm?: Layout, xs?: Layout, xxs?: Layout}; 7 | // type Breakpoint = string; 8 | // type Breakpoints = {lg?: number, md?: number, sm?: number, xs?: number, xxs?: number}; 9 | 10 | /** 11 | * Given a width, find the highest breakpoint that matches is valid for it (width > breakpoint). 12 | * 13 | * @param {Object} breakpoints Breakpoints object (e.g. {lg: 1200, md: 960, ...}) 14 | * @param {Number} width Screen width. 15 | * @return {String} Highest breakpoint that is less than width. 16 | */ 17 | // export function getBreakpointFromWidth(breakpoints: Breakpoints, width: number): Breakpoint { 18 | export function getBreakpointFromWidth(breakpoints, width) { 19 | const sorted = sortBreakpoints(breakpoints); 20 | let matching = sorted[0]; 21 | for (let i = 1, len = sorted.length; i < len; i++) { 22 | const breakpointName = sorted[i]; 23 | if (width > breakpoints[breakpointName]) matching = breakpointName; 24 | } 25 | return matching; 26 | } 27 | 28 | 29 | /** 30 | * Given a breakpoint, get the # of cols set for it. 31 | * @param {String} breakpoint Breakpoint name. 32 | * @param {Object} cols Map of breakpoints to cols. 33 | * @return {Number} Number of cols. 34 | */ 35 | // export function getColsFromBreakpoint(breakpoint: Breakpoint, cols: Breakpoints): number { 36 | export function getColsFromBreakpoint(breakpoint, cols) { 37 | if (!cols[breakpoint]) { 38 | throw new Error(`ResponsiveGridLayout: \`cols\` entry for breakpoint ${breakpoint} is missing!`); 39 | } 40 | return cols[breakpoint]; 41 | } 42 | 43 | /** 44 | * Given existing layouts and a new breakpoint, find or generate a new layout. 45 | * 46 | * This finds the layout above the new one and generates from it, if it exists. 47 | * 48 | * @param {Array} orgLayout Original layout. 49 | * @param {Object} layouts Existing layouts. 50 | * @param {Array} breakpoints All breakpoints. 51 | * @param {String} breakpoint New breakpoint. 52 | * @param {String} breakpoint Last breakpoint (for fallback). 53 | * @param {Number} cols Column count at new breakpoint. 54 | * @param {Boolean} verticalCompact Whether or not to compact the layout 55 | * vertically. 56 | * @return {Array} New layout. 57 | */ 58 | // export function findOrGenerateResponsiveLayout(orgLayout: Layout, layouts: ResponsiveLayout, breakpoints: Breakpoints, 59 | // breakpoint: Breakpoint, lastBreakpoint: Breakpoint, 60 | // cols: number, verticalCompact: boolean): Layout { 61 | export function findOrGenerateResponsiveLayout( 62 | orgLayout, 63 | layouts, 64 | breakpoints, 65 | breakpoint, 66 | cols, 67 | verticalCompact, 68 | ) { 69 | // If it already exists, just return it. 70 | if (layouts[breakpoint]) return cloneLayout(layouts[breakpoint]); 71 | // Find or generate the next layout 72 | let layout = orgLayout; 73 | 74 | const breakpointsSorted = sortBreakpoints(breakpoints); 75 | const breakpointsAbove = breakpointsSorted.slice(breakpointsSorted.indexOf(breakpoint)); 76 | for (let i = 0, len = breakpointsAbove.length; i < len; i++) { 77 | const b = breakpointsAbove[i]; 78 | if (layouts[b]) { 79 | layout = layouts[b]; 80 | break; 81 | } 82 | } 83 | layout = cloneLayout(layout || []); // clone layout so we don't modify existing items 84 | return compact(correctBounds(layout, { cols }), verticalCompact); 85 | } 86 | 87 | // // export function generateResponsiveLayout(layout: Layout, breakpoints: Breakpoints, 88 | // // breakpoint: Breakpoint, lastBreakpoint: Breakpoint, 89 | // // cols: number, verticalCompact: boolean): Layout { 90 | // export function generateResponsiveLayout( 91 | // layout, 92 | // breakpoints, 93 | // breakpoint, 94 | // lastBreakpoint, 95 | // cols, 96 | // verticalCompact, 97 | // ) { 98 | // // If it already exists, just return it. 99 | // /* if (layouts[breakpoint]) return cloneLayout(layouts[breakpoint]); 100 | // // Find or generate the next layout 101 | // let layout = layouts[lastBreakpoint]; */ 102 | // /* const breakpointsSorted = sortBreakpoints(breakpoints); 103 | // const breakpointsAbove = breakpointsSorted.slice(breakpointsSorted.indexOf(breakpoint)); 104 | // for (let i = 0, len = breakpointsAbove.length; i < len; i++) { 105 | // const b = breakpointsAbove[i]; 106 | // if (layouts[b]) { 107 | // layout = layouts[b]; 108 | // break; 109 | // } 110 | // } */ 111 | // layout = cloneLayout(layout || []); // clone layout so we don't modify existing items 112 | // return compact(correctBounds(layout, { cols }), verticalCompact); 113 | // } 114 | 115 | /** 116 | * Given breakpoints, return an array of breakpoints sorted by width. This is usually 117 | * e.g. ['xxs', 'xs', 'sm', ...] 118 | * 119 | * @param {Object} breakpoints Key/value pair of breakpoint names to widths. 120 | * @return {Array} Sorted breakpoints. 121 | */ 122 | // export function sortBreakpoints(breakpoints: Breakpoints): Array { 123 | export function sortBreakpoints(breakpoints) { 124 | // const keys: Array = Object.keys(breakpoints); 125 | const keys = Object.keys(breakpoints); 126 | return keys.sort((a, b) => { 127 | return breakpoints[a] - breakpoints[b]; 128 | }); 129 | } 130 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import App from './app.vue'; 3 | 4 | createApp(App).mount('#app'); 5 | 6 | -------------------------------------------------------------------------------- /src/test-element.vue: -------------------------------------------------------------------------------- 1 | 20 | 28 | 36 | 37 | -------------------------------------------------------------------------------- /test/interact-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Interact TEST 6 | 7 | 30 | 31 | 32 |
33 |
34 | Resize from any edge or corner 35 |
36 |
37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /test/interact-test.js: -------------------------------------------------------------------------------- 1 | function dragMoveListener (event) { 2 | var target = event.target, 3 | // keep the dragged position in the data-x/data-y attributes 4 | x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx, 5 | y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy; 6 | 7 | // translate the element 8 | target.style.webkitTransform = 9 | target.style.transform = 10 | 'translate(' + x + 'px, ' + y + 'px)'; 11 | 12 | // update the posiion attributes 13 | target.setAttribute('data-x', x); 14 | target.setAttribute('data-y', y); 15 | } 16 | 17 | // this is used later in the resizing and gesture demos 18 | window.dragMoveListener = dragMoveListener; 19 | 20 | 21 | interact('.resize-drag') 22 | .draggable({ 23 | onmove: window.dragMoveListener, 24 | restrict: { 25 | restriction: 'parent', 26 | elementRect: { top: 0, left: 0, bottom: 1, right: 1 } 27 | }, 28 | }) 29 | .resizable({ 30 | // resize from all edges and corners 31 | edges: { left: true, right: true, bottom: true, top: true }, 32 | 33 | // keep the edges inside the parent 34 | restrictEdges: { 35 | outer: 'parent', 36 | endOnly: true, 37 | }, 38 | 39 | // minimum size 40 | restrictSize: { 41 | min: { width: 100, height: 50 }, 42 | }, 43 | 44 | inertia: true, 45 | }) 46 | .on('resizemove', function (event) { 47 | var target = event.target, 48 | x = (parseFloat(target.getAttribute('data-x')) || 0), 49 | y = (parseFloat(target.getAttribute('data-y')) || 0); 50 | 51 | // update the element's style 52 | target.style.width = event.rect.width + 'px'; 53 | target.style.height = event.rect.height + 'px'; 54 | 55 | // translate when resizing from top or left edges 56 | x += event.deltaRect.left; 57 | y += event.deltaRect.top; 58 | 59 | target.style.webkitTransform = target.style.transform = 60 | 'translate(' + x + 'px,' + y + 'px)'; 61 | 62 | target.setAttribute('data-x', x); 63 | target.setAttribute('data-y', y); 64 | target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height); 65 | }); -------------------------------------------------------------------------------- /test/unit/GridItem.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import GridLayout from '../../src/components/GridLayout.vue' 3 | 4 | let layout 5 | 6 | describe('GridLayout test', () => { 7 | beforeAll(()=>{ 8 | let testLayout = [{"x":0,"y":0,"w":2,"h":2,"i":"0", resizable: true, draggable: true, static: false, minY: 0, maxY: 2}]; 9 | layout = JSON.parse(JSON.stringify(testLayout)) 10 | }) 11 | 12 | describe('Interface test', () => { 13 | it('should render correct contents', () => { 14 | const wrapper = shallowMount(GridLayout, { 15 | propsData: { 16 | layout: layout 17 | } 18 | }) 19 | const grid = wrapper.findAll('.vue-grid-layout'); 20 | expect(grid.selector).toEqual('.vue-grid-layout'); 21 | }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /test/unit/utils.spec.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-env jest */ 3 | 4 | import { 5 | bottom, 6 | collides, 7 | compact, 8 | moveElement, 9 | sortLayoutItemsByRowCol, 10 | validateLayout 11 | } from "../../src/helpers/utils"; 12 | 13 | describe("bottom", () => { 14 | it("Handles an empty layout as input", () => { 15 | expect(bottom([])).toEqual(0); 16 | }); 17 | 18 | it("Returns the bottom coordinate of the layout", () => { 19 | expect( 20 | bottom([ 21 | { i: "1", x: 0, y: 1, w: 1, h: 1 }, 22 | { i: "2", x: 1, y: 2, w: 1, h: 1 } 23 | ]) 24 | ).toEqual(3); 25 | }); 26 | }); 27 | 28 | describe("sortLayoutItemsByRowCol", () => { 29 | it("should sort by top to bottom right", () => { 30 | const layout = [ 31 | { x: 1, y: 1, w: 1, h: 1, i: "2" }, 32 | { x: 1, y: 0, w: 1, h: 1, i: "1" }, 33 | { x: 0, y: 1, w: 2, h: 2, i: "3" } 34 | ]; 35 | expect(sortLayoutItemsByRowCol(layout)).toEqual([ 36 | { x: 1, y: 0, w: 1, h: 1, i: "1" }, 37 | { x: 0, y: 1, w: 2, h: 2, i: "3" }, 38 | { x: 1, y: 1, w: 1, h: 1, i: "2" } 39 | ]); 40 | }); 41 | }); 42 | 43 | describe("collides", () => { 44 | it("Returns whether the layout items collide", () => { 45 | expect( 46 | collides( 47 | { i: "1", x: 0, y: 1, w: 1, h: 1 }, 48 | { i: "2", x: 1, y: 2, w: 1, h: 1 } 49 | ) 50 | ).toEqual(false); 51 | expect( 52 | collides( 53 | { i: "1", x: 0, y: 1, w: 1, h: 1 }, 54 | { i: "2", x: 0, y: 1, w: 1, h: 1 } 55 | ) 56 | ).toEqual(true); 57 | }); 58 | }); 59 | 60 | describe("validateLayout", () => { 61 | it("Validates an empty layout", () => { 62 | validateLayout([]); 63 | }); 64 | it("Validates a populated layout", () => { 65 | validateLayout([ 66 | { i: "1", x: 0, y: 1, w: 1, h: 1 }, 67 | { i: "2", x: 1, y: 2, w: 1, h: 1 } 68 | ]); 69 | }); 70 | it("Throws errors on invalid input", () => { 71 | expect(() => { 72 | validateLayout([ 73 | { i: "1", x: 0, y: 1, w: 1, h: 1 }, 74 | { i: "2", x: 1, y: 2, w: 1 } 75 | ]); 76 | }).toThrowError(/layout\[1\]\.h must be a number!/i); 77 | }); 78 | }); 79 | 80 | describe("moveElement", () => { 81 | function compactAndMove( 82 | layout, 83 | layoutItem, 84 | x, 85 | y, 86 | isUserAction, 87 | preventCollision 88 | ) { 89 | return compact( 90 | moveElement( 91 | layout, 92 | layoutItem, 93 | x, 94 | y, 95 | isUserAction, 96 | preventCollision 97 | ) 98 | ); 99 | } 100 | 101 | it("Does not change layout when colliding on no rearrangement mode", () => { 102 | const layout = [ 103 | { i: "1", x: 0, y: 1, w: 1, h: 1, moved: false }, 104 | { i: "2", x: 1, y: 2, w: 1, h: 1, moved: false } 105 | ]; 106 | const layoutItem = layout[0]; 107 | expect( 108 | moveElement( 109 | layout, 110 | layoutItem, 111 | 1, 112 | 2, // x, y 113 | true, 114 | true // isUserAction, preventCollision 115 | ) 116 | ).toEqual([ 117 | { i: "1", x: 0, y: 1, w: 1, h: 1, moved: false }, 118 | { i: "2", x: 1, y: 2, w: 1, h: 1, moved: false } 119 | ]); 120 | }); 121 | 122 | it("Does change layout when colliding in rearrangement mode", () => { 123 | const layout = [ 124 | { i: "1", x: 0, y: 0, w: 1, h: 1, moved: false }, 125 | { i: "2", x: 1, y: 0, w: 1, h: 1, moved: false } 126 | ]; 127 | const layoutItem = layout[0]; 128 | expect( 129 | moveElement( 130 | layout, 131 | layoutItem, 132 | 1, 133 | 0, // x, y 134 | true, 135 | false // isUserAction, preventCollision 136 | ) 137 | ).toEqual([ 138 | { i: "1", x: 1, y: 0, w: 1, h: 1, moved: true }, 139 | { i: "2", x: 1, y: 1, w: 1, h: 1, moved: true } 140 | ]); 141 | }); 142 | 143 | it("Moves elements out of the way without causing panel jumps when compaction is vertical", () => { 144 | const layout = [ 145 | { x: 0, y: 0, w: 1, h: 10, i: "A" }, 146 | { x: 0, y: 10, w: 1, h: 1, i: "B" }, 147 | { x: 0, y: 11, w: 1, h: 1, i: "C" } 148 | ]; 149 | // move A down slightly so it collides with C; can cause C to jump above B. 150 | // We instead want B to jump above A (it has the room) 151 | const itemA = layout[0]; 152 | expect( 153 | compactAndMove( 154 | layout, 155 | itemA, 156 | 0, 157 | 1, // x, y 158 | true, 159 | false // isUserAction, preventCollision 160 | ) 161 | ).toEqual([ 162 | expect.objectContaining({ x: 0, y: 1, w: 1, h: 10, i: "A" }), 163 | expect.objectContaining({ x: 0, y: 0, w: 1, h: 1, i: "B" }), 164 | expect.objectContaining({ x: 0, y: 11, w: 1, h: 1, i: "C" }) 165 | ]); 166 | }); 167 | 168 | it("Calculates the correct collision when moving large object far", () => { 169 | const layout = [ 170 | { x: 0, y: 0, w: 1, h: 10, i: "A" }, 171 | { x: 0, y: 10, w: 1, h: 1, i: "B" }, 172 | { x: 0, y: 11, w: 1, h: 1, i: "C" } 173 | ]; 174 | // Move A down by 2. This should move B above, but since we don't compact in between, 175 | // C should move below. 176 | const itemA = layout[0]; 177 | expect( 178 | moveElement( 179 | layout, 180 | itemA, 181 | 0, 182 | 2, // x, y 183 | true, 184 | false // isUserAction, preventCollision 185 | ) 186 | ).toEqual([ 187 | expect.objectContaining({ x: 0, y: 2, w: 1, h: 10, i: "A" }), 188 | expect.objectContaining({ x: 0, y: 1, w: 1, h: 1, i: "B" }), 189 | expect.objectContaining({ x: 0, y: 12, w: 1, h: 1, i: "C" }) 190 | ]); 191 | }); 192 | 193 | it("Moves elements out of the way without causing panel jumps when compaction is vertical", () => { 194 | const layout = [ 195 | { x: 0, y: 0, w: 1, h: 1, i: "A" }, 196 | { x: 1, y: 0, w: 1, h: 1, i: "B" }, 197 | { x: 0, y: 1, w: 2, h: 2, i: "C" } 198 | ]; 199 | // move A over slightly so it collides with B; can cause C to jump above B 200 | // this test will check that that does not happen 201 | const itemA = layout[0]; 202 | expect( 203 | moveElement( 204 | layout, 205 | itemA, 206 | 1, 207 | 0, // x, y 208 | true, 209 | false // isUserAction, preventCollision 210 | ) 211 | ).toEqual([ 212 | { x: 1, y: 0, w: 1, h: 1, i: "A", moved: true }, 213 | { x: 1, y: 1, w: 1, h: 1, i: "B", moved: true }, 214 | { x: 0, y: 2, w: 2, h: 2, i: "C", moved: true } 215 | ]); 216 | }); 217 | 218 | it("Moves one element to another should cause moving down panels, vert compact", () => { 219 | // | A | B | 220 | // |C| D | 221 | const layout = [ 222 | { x: 0, y: 0, w: 2, h: 1, i: "A" }, 223 | { x: 2, y: 0, w: 2, h: 1, i: "B" }, 224 | { x: 0, y: 1, w: 1, h: 1, i: "C" }, 225 | { x: 1, y: 1, w: 3, h: 1, i: "D" } 226 | ]; 227 | // move B left slightly so it collides with A; can cause C to jump above A 228 | // this test will check that that does not happen 229 | const itemB = layout[1]; 230 | expect( 231 | compactAndMove( 232 | layout, 233 | itemB, 234 | 1, 235 | 0, // x, y 236 | true, 237 | false // isUserAction, preventCollision 238 | ) 239 | ).toEqual([ 240 | expect.objectContaining({ x: 0, y: 1, w: 2, h: 1, i: "A" }), 241 | expect.objectContaining({ x: 1, y: 0, w: 2, h: 1, i: "B" }), 242 | expect.objectContaining({ x: 0, y: 2, w: 1, h: 1, i: "C" }), 243 | expect.objectContaining({ x: 1, y: 2, w: 3, h: 1, i: "D" }) 244 | ]); 245 | }); 246 | 247 | it("Moves one element to another should cause moving down panels, vert compact", () => { 248 | // | A | 249 | // |B|C| 250 | // | | 251 | // 252 | // Moving C above A should not move B above A 253 | const layout = [ 254 | { x: 0, y: 0, w: 2, h: 1, i: "A" }, 255 | { x: 0, y: 1, w: 1, h: 1, i: "B" }, 256 | { x: 1, y: 1, w: 1, h: 2, i: "C" } 257 | ]; 258 | // Move C up. 259 | const itemB = layout[2]; 260 | expect( 261 | compactAndMove( 262 | layout, 263 | itemB, 264 | 1, 265 | 0, // x, y 266 | true, 267 | false // isUserAction, preventCollision 268 | ) 269 | ).toEqual([ 270 | expect.objectContaining({ x: 0, y: 2, w: 2, h: 1, i: "A" }), 271 | expect.objectContaining({ x: 0, y: 3, w: 1, h: 1, i: "B" }), 272 | expect.objectContaining({ x: 1, y: 0, w: 1, h: 2, i: "C" }) 273 | ]); 274 | }); 275 | }); 276 | 277 | describe("compact vertical", () => { 278 | it("Removes empty vertical space above item", () => { 279 | const layout = [{ i: "1", x: 0, y: 1, w: 1, h: 1 }]; 280 | expect(compact(layout, true)).toEqual([ 281 | { i: "1", x: 0, y: 0, w: 1, h: 1, moved: false } 282 | ]); 283 | }); 284 | 285 | it("Resolve collision by moving item further down in array", () => { 286 | const layout = [ 287 | { x: 0, y: 0, w: 1, h: 5, i: "1" }, 288 | { x: 0, y: 1, w: 1, h: 1, i: "2" } 289 | ]; 290 | expect(compact(layout, true)).toEqual([ 291 | { x: 0, y: 0, w: 1, h: 5, i: "1", moved: false }, 292 | { x: 0, y: 5, w: 1, h: 1, i: "2", moved: false } 293 | ]); 294 | }); 295 | 296 | it("Handles recursive collision by moving new collisions out of the way before moving item down", () => { 297 | const layout = [ 298 | { x: 0, y: 0, w: 2, h: 5, i: "1" }, 299 | { x: 0, y: 0, w: 10, h: 1, i: "2" }, 300 | { x: 5, y: 1, w: 1, h: 1, i: "3" }, 301 | { x: 5, y: 2, w: 1, h: 1, i: "4" }, 302 | { x: 5, y: 3, w: 1, h: 1, i: "5", static: true } 303 | ]; 304 | 305 | expect(compact(layout, true)).toEqual([ 306 | { x: 0, y: 0, w: 2, h: 5, i: "1", moved: false }, 307 | { x: 0, y: 5, w: 10, h: 1, i: "2", moved: false }, 308 | { x: 5, y: 0, w: 1, h: 1, i: "3", moved: false }, 309 | { x: 5, y: 1, w: 1, h: 1, i: "4", moved: false }, 310 | { x: 5, y: 3, w: 1, h: 1, i: "5", moved: false, static: true } 311 | ]); 312 | }); 313 | }); 314 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url'; 2 | import { resolve } from 'path'; 3 | import { defineConfig } from 'vite'; 4 | import vue from '@vitejs/plugin-vue'; 5 | import vueJsx from '@vitejs/plugin-vue-jsx'; 6 | import { libInjectCss } from 'vite-plugin-lib-inject-css'; 7 | import eslint from '@rollup/plugin-eslint'; 8 | import pkg from './package.json'; 9 | 10 | function banner() { 11 | return { 12 | name: 'banner', 13 | enforce: 'post', 14 | generateBundle(options, bundle) { 15 | const banner = `/**\n * ${pkg.name} ${pkg.version}\n` 16 | + ` * ${pkg.author}\n * ${pkg.homepage}\n */\n`; 17 | 18 | for (const module of Object.values(bundle)) { 19 | if (module.type === 'chunk') { 20 | module.code = banner + module.code; 21 | } 22 | } 23 | }, 24 | }; 25 | } 26 | 27 | // https://vitejs.dev/config/ 28 | export default defineConfig(() => ({ 29 | plugins: [ 30 | vue(), 31 | vueJsx(), 32 | eslint({ 33 | include: ['./src/**/*.js', './src/**/*.vue'], 34 | }), 35 | libInjectCss(), 36 | banner(), 37 | ], 38 | resolve: { 39 | alias: { 40 | '@': fileURLToPath(new URL('./src', import.meta.url)), 41 | }, 42 | }, 43 | build: { 44 | lib: { 45 | // Could also be a dictionary or array of multiple entry points 46 | entry: resolve(__dirname, 'src/components/index.js'), 47 | name: 'VueGridLayout', 48 | // the proper extensions will be added 49 | fileName: 'vue-grid-layout-v3', 50 | formats: ['iife', 'es', 'umd', 'cjs'], 51 | }, 52 | rollupOptions: { 53 | // 确保外部化处理那些你不想打包进库的依赖 54 | external: ['vue'], 55 | output: [{ 56 | format: 'es', 57 | // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量 58 | // globals: { vue: 'Vue' }, 59 | }, { 60 | name: 'VueGridLayout', 61 | format: 'iife', 62 | exports: 'named', 63 | // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量 64 | globals: { vue: 'Vue' }, 65 | }, { 66 | name: 'VueGridLayout', 67 | entryFileNames: 'vue-grid-layout-v3.umd.js', 68 | format: 'umd', 69 | exports: 'named', 70 | // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量 71 | globals: { vue: 'Vue' }, 72 | }, { 73 | format: 'cjs', 74 | exports: 'named', 75 | // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量 76 | globals: { vue: 'Vue' }, 77 | }], 78 | }, 79 | }, 80 | })); 81 | -------------------------------------------------------------------------------- /website/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 4 4 | end_of_line = lf 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | max_line_length = 100 8 | -------------------------------------------------------------------------------- /website/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | const config = { 3 | root: true, 4 | extends: [ 5 | 'plugin:vue/vue3-essential', 6 | 'eslint:recommended', 7 | 'airbnb-base', 8 | ], 9 | // env: { 10 | // // 'vue/setup-compiler-macros': true, 11 | // es6: true, 12 | // }, 13 | // parserOptions: { 14 | // }, 15 | parser: 'vue-eslint-parser', 16 | parserOptions: { 17 | ecmaVersion: 'latest', 18 | // parser: '@babel/eslint-parser', 19 | ecmaFeatures: { 20 | jsx: true, 21 | }, 22 | }, 23 | rules: { 24 | // 修改的规则 25 | 'no-multi-spaces': [2, { ignoreEOLComments: true }], 26 | indent: [2, 4, { SwitchCase: 1 }], 27 | 'vue/html-indent': [2, 4], 28 | 29 | // 关闭vue的规则 30 | 'vue/multi-word-component-names': 0, 31 | 'vue/require-component-is': 0, 32 | 'vue/no-v-for-template-key-on-child': 0, 33 | 'vue/no-v-text-v-html-on-component': 0, 34 | 35 | // 关闭import规则 36 | 'import/extensions': 0, 37 | 'import/no-extraneous-dependencies': 0, 38 | 'import/no-dynamic-require': 0, 39 | 'import/no-unresolved': 0, 40 | 'import/prefer-default-export': 0, 41 | 42 | // 关闭airbnb规则 43 | 'arrow-parens': 0, 44 | 'arrow-body-style': 0, 45 | 'consistent-return': 0, 46 | 'default-param-last': 0, 47 | 'default-case': 0, 48 | 'global-require': 0, 49 | 'object-curly-newline': 0, 50 | 'prefer-destructuring': 0, 51 | radix: 0, 52 | 'max-len': 0, 53 | 'newline-per-chained-call': 0, 54 | 'no-bitwise': 0, 55 | 'no-console': 0, 56 | 'no-control-regex': 0, 57 | 'no-cond-assign': 0, 58 | 'no-continue': 0, 59 | 'no-lonely-if': 0, 60 | 'no-return-assign': 0, 61 | 'no-restricted-syntax': 0, 62 | 'no-param-reassign': 0, 63 | 'no-plusplus': 0, 64 | 'no-use-before-define': 0, 65 | 'no-multiple-empty-lines': 0, 66 | 'no-shadow': 0, 67 | 68 | // 关闭其他规则 69 | 'prettier/prettier': 0, 70 | camelcase: 0, 71 | 72 | // 增加的规则 73 | 'no-debugger': 2, 74 | }, 75 | ignorePatterns: [ 76 | 'public/**', 77 | 'dist/**', 78 | ], 79 | }; 80 | 81 | // 开发测试阶段降低规则要求 82 | if (process.env.NODE_ENV !== 'production') { 83 | Object.assign(config.rules, { 84 | 'vue/no-unused-vars': 1, 85 | 'vue/valid-template-root': 1, 86 | 'vue/return-in-computed-property': 1, 87 | 'vue/valid-v-for': 1, 88 | 89 | 'import/newline-after-import': 1, 90 | 91 | 'array-bracket-spacing': 1, 92 | 'brace-style': 1, 93 | 'block-spacing': 1, 94 | 'comma-dangle': 1, 95 | 'comma-spacing': 1, 96 | indent: [1, 4, { SwitchCase: 1 }], 97 | 'key-spacing': 1, 98 | 'keyword-spacing': 1, 99 | 'operator-linebreak': 1, 100 | 'prefer-const': 1, 101 | 'padded-blocks': 1, 102 | 'quote-props': 1, 103 | semi: 1, 104 | 'spaced-comment': 1, 105 | 'space-before-blocks': 1, 106 | 'space-before-function-paren': 1, 107 | 'space-infix-ops': 1, 108 | 'space-in-parens': 1, 109 | 'no-debugger': 1, 110 | 'no-empty': 1, 111 | 'no-empty-function': 1, 112 | 'no-unused-vars': 1, 113 | 'no-multi-spaces': [1, { ignoreEOLComments: true }], 114 | 'no-unreachable': 1, 115 | }); 116 | } 117 | 118 | module.exports = config; 119 | 120 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | #/dist 4 | dist/ 5 | cache/ 6 | 7 | # local env files 8 | .env.local 9 | .env.*.local 10 | 11 | # Log files 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw* 24 | 25 | yarn-error.log 26 | 27 | .vitepress/cache/ 28 | -------------------------------------------------------------------------------- /website/.vitepress/config.mjs: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url'; 2 | import { defineConfig } from 'vitepress' 3 | 4 | // https://vitepress.dev/reference/site-config 5 | export default defineConfig({ 6 | srcDir: 'src', 7 | title: "Vue3 Grid Layout", 8 | description: "A draggable and resizable grid layout, as a Vue component.", 9 | markdown: { 10 | theme: 'monokai', 11 | lineNumbers: true, 12 | }, 13 | cleanUrls: true, 14 | base: "/vue-grid-layout-v3/", 15 | vite: { 16 | resolve: { 17 | alias: { 18 | '@': fileURLToPath(new URL('../src/', import.meta.url)), 19 | }, 20 | }, 21 | ssr: { 22 | noExternal: ['vue-grid-layout-v3'], 23 | }, 24 | }, 25 | themeConfig: { 26 | // https://vitepress.dev/reference/default-theme-config 27 | logo: '/logo.png', 28 | repo: 'merfais/vue-grid-layout-v3', 29 | head: [ 30 | ['link', { rel: 'icon', href: '/favicon.ico' }], 31 | ], 32 | nav: [ 33 | { text: 'Home', link: '/' }, 34 | { text: 'Guide', link: '/guide/' } 35 | ], 36 | sidebar: [ 37 | { 38 | text: 'Guide', 39 | items: [ 40 | { text: 'Installation', link: '/guide/' }, 41 | { text: 'Usage', link: '/guide/usage' }, 42 | { text: 'Properties', link: '/guide/properties' }, 43 | { text: 'Events', link: '/guide/events' }, 44 | { text: 'Styling', link: '/guide/styling' }, 45 | ] 46 | }, 47 | { 48 | text: 'Examples', 49 | items: [ 50 | { text: '01 - Basic', link: '/guide/01-basic' }, 51 | { text: '02 - Move and resize events', link: '/guide/02-events' }, 52 | { text: '03 - Multiple grids', link: '/guide/03-multiple-grids' }, 53 | { text: '04 - Drag allow/ignore elements', link: '/guide/04-allow-ignore' }, 54 | { text: '05 - Mirrored grid layout', link: '/guide/05-mirrored' }, 55 | { text: '06 - Responsive', link: '/guide/06-responsive' }, 56 | { text: '07 - Prevent Collision', link: '/guide/07-prevent-collision' }, 57 | { text: '08 - Responsive with predefined layouts', link: '/guide/08-responsive-predefined-layouts' }, 58 | { text: '09 - Dynamic Add/Remove', link: '/guide/09-dynamic-add-remove' }, 59 | { text: '10 - Drag From Outside', link: '/guide/10-drag-from-outside' }, 60 | { text: '11 - Dragging grid items bounded to grid container', link: '/guide/11-bounded' }, 61 | ] 62 | } 63 | ], 64 | aside: false, 65 | socialLinks: [ 66 | { icon: 'github', link: 'https://github.com/merfais/vue-grid-layout-v3' } 67 | ], 68 | search: { 69 | provider: 'local', 70 | // options: { 71 | // appId: 'vue_grid_layout', 72 | // apiKey: '2f143d1edd24605564065dd02bf0a22b', 73 | // indexName: 'vue_grid_layout' 74 | // } 75 | }, 76 | editLink: { 77 | pattern: 'https://github.com/merfais/vue-grid-layout-v3/tree/master/website/src/:path', 78 | text: 'Edit this page on GitHub' 79 | }, 80 | lastUpdated: { 81 | text: 'Updated at', 82 | formatOptions: { 83 | dateStyle: 'full', 84 | timeStyle: 'medium' 85 | } 86 | }, 87 | externalLinkIcon: true, 88 | } 89 | }) 90 | -------------------------------------------------------------------------------- /website/.vitepress/theme/custom.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --vp-button-brand-bg: #3eaf7c; 3 | } 4 | -------------------------------------------------------------------------------- /website/.vitepress/theme/index.js: -------------------------------------------------------------------------------- 1 | // https://vitepress.dev/guide/custom-theme 2 | import DefaultTheme from 'vitepress/theme' 3 | import './style.css' 4 | 5 | /** @type {import('vitepress').Theme} */ 6 | export default { 7 | extends: DefaultTheme, 8 | // Layout: () => { 9 | // return h(DefaultTheme.Layout, null, { 10 | // // https://vitepress.dev/guide/extending-default-theme#layout-slots 11 | // }) 12 | // }, 13 | enhanceApp({ app, router, siteData }) { 14 | // ... 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /website/.vitepress/theme/style.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Customize default theme styling by overriding CSS variables: 3 | * https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css 4 | */ 5 | 6 | /** 7 | * Colors 8 | * 9 | * Each colors have exact same color scale system with 3 levels of solid 10 | * colors with different brightness, and 1 soft color. 11 | * 12 | * - `XXX-1`: The most solid color used mainly for colored text. It must 13 | * satisfy the contrast ratio against when used on top of `XXX-soft`. 14 | * 15 | * - `XXX-2`: The color used mainly for hover state of the button. 16 | * 17 | * - `XXX-3`: The color for solid background, such as bg color of the button. 18 | * It must satisfy the contrast ratio with pure white (#ffffff) text on 19 | * top of it. 20 | * 21 | * - `XXX-soft`: The color used for subtle background such as custom container 22 | * or badges. It must satisfy the contrast ratio when putting `XXX-1` colors 23 | * on top of it. 24 | * 25 | * The soft color must be semi transparent alpha channel. This is crucial 26 | * because it allows adding multiple "soft" colors on top of each other 27 | * to create a accent, such as when having inline code block inside 28 | * custom containers. 29 | * 30 | * - `default`: The color used purely for subtle indication without any 31 | * special meanings attched to it such as bg color for menu hover state. 32 | * 33 | * - `brand`: Used for primary brand colors, such as link text, button with 34 | * brand theme, etc. 35 | * 36 | * - `tip`: Used to indicate useful information. The default theme uses the 37 | * brand color for this by default. 38 | * 39 | * - `warning`: Used to indicate warning to the users. Used in custom 40 | * container, badges, etc. 41 | * 42 | * - `danger`: Used to show error, or dangerous message to the users. Used 43 | * in custom container, badges, etc. 44 | * -------------------------------------------------------------------------- */ 45 | 46 | :root { 47 | --vp-c-brand-1: var(--vp-c-green-1); 48 | --vp-c-brand-2: var(--vp-c-green-2); 49 | --vp-c-brand-3: var(--vp-c-green-3); 50 | --vp-c-brand-soft: var(--vp-c-green-soft); 51 | 52 | --vp-code-block-color: rgba(255, 255, 245, 0.86); 53 | --vp-code-block-bg: rgb(39, 40, 34); 54 | --vp-code-block-divider-color: #000000; 55 | 56 | --vp-code-lang-color: rgba(235, 235, 245, 0.38); 57 | 58 | --vp-code-line-highlight-color: #000000; 59 | --vp-code-line-number-color: rgba(235, 235, 245, 0.38); 60 | 61 | --vp-code-copy-code-border-color: rgba(82, 82, 89, 0.32); 62 | --vp-code-copy-code-bg: #252529; 63 | --vp-code-copy-code-hover-border-color: rgba(82, 82, 89, 0.32); 64 | --vp-code-copy-code-hover-bg: #323238; 65 | --vp-code-copy-code-active-text: rgba(255, 255, 245, 0.86); 66 | 67 | --vp-code-tab-text-color: rgba(235, 235, 245, 0.6); 68 | --vp-code-tab-hover-text-color: rgba(255, 255, 245, 0.86); 69 | --vp-code-tab-active-text-color: rgba(255, 255, 245, 0.86); 70 | } 71 | 72 | /** 73 | * Component: Button 74 | 75 | :root { 76 | --vp-button-brand-border: transparent; 77 | --vp-button-brand-text: var(--vp-c-white); 78 | --vp-button-brand-bg: var(--vp-c-brand-3); 79 | --vp-button-brand-hover-border: transparent; 80 | --vp-button-brand-hover-text: var(--vp-c-white); 81 | --vp-button-brand-hover-bg: var(--vp-c-brand-2); 82 | --vp-button-brand-active-border: transparent; 83 | --vp-button-brand-active-text: var(--vp-c-white); 84 | --vp-button-brand-active-bg: var(--vp-c-brand-1); 85 | } 86 | * -------------------------------------------------------------------------- */ 87 | 88 | /** 89 | * Component: Home 90 | * -------------------------------------------------------------------------- */ 91 | :root { 92 | --vp-home-hero-name-color: transparent; 93 | --vp-home-hero-name-background: linear-gradient( 94 | 120deg, 95 | #3eaf7c 30%, 96 | #41d1ff 97 | ); 98 | 99 | --vp-home-hero-image-background-image: linear-gradient( 100 | 45deg, 101 | #bd34fe 50%, 102 | #47caff 50% 103 | ); 104 | --vp-home-hero-image-filter: blur(44px); 105 | 106 | } 107 | 108 | @media (min-width: 640px) { 109 | :root { 110 | --vp-home-hero-image-filter: blur(56px); 111 | } 112 | } 113 | 114 | @media (min-width: 960px) { 115 | :root { 116 | --vp-home-hero-image-filter: blur(68px); 117 | } 118 | } 119 | 120 | 121 | -------------------------------------------------------------------------------- /website/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merfais/vue-grid-layout-v3/a669942fa85c80211767b89acddbe6211319ffe2/website/favicon.ico -------------------------------------------------------------------------------- /website/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vue Grid Layout 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-grid-layout-website", 3 | "version": "1.0.0", 4 | "description": "vue-grid-layout website", 5 | "author": "coffeebi (merfais.bwq@163.com)", 6 | "license": "MIT", 7 | "private": false, 8 | "scripts": { 9 | "dev": "vite", 10 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs", 11 | "lint:fix": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix", 12 | "docs:dev": "vitepress dev", 13 | "docs:build": "vitepress build", 14 | "docs:preview": "vitepress preview" 15 | }, 16 | "devDependencies": { 17 | "@rollup/plugin-eslint": "^9.0.5", 18 | "eslint": "^8.57.0", 19 | "eslint-config-airbnb-base": "^15.0.0", 20 | "eslint-plugin-vue": "^9.23.0", 21 | "vite": "^5.3.1", 22 | "vitepress": "^1.3.4", 23 | "vitepress-plugin-back-to-top": "^1.0.1", 24 | "vitepress-plugin-google-analytics": "^1.0.2", 25 | "vue-eslint-parser": "^9.4.3" 26 | }, 27 | "resolutions": {}, 28 | "dependencies": { 29 | "vue": "^3.5.11", 30 | "vue-grid-layout-v3": "^3.1.2-rc.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /website/src/app.vue: -------------------------------------------------------------------------------- 1 | 28 | 39 | 60 | -------------------------------------------------------------------------------- /website/src/example/01-basic.vue: -------------------------------------------------------------------------------- 1 | 41 | 65 | 93 | -------------------------------------------------------------------------------- /website/src/example/02-events.vue: -------------------------------------------------------------------------------- 1 | 101 | 143 | 179 | -------------------------------------------------------------------------------- /website/src/example/03-multiple-grids.vue: -------------------------------------------------------------------------------- 1 | 18 | 70 | 94 | -------------------------------------------------------------------------------- /website/src/example/04-allow-ignore.vue: -------------------------------------------------------------------------------- 1 | 30 | 64 | 114 | -------------------------------------------------------------------------------- /website/src/example/05-mirrored.vue: -------------------------------------------------------------------------------- 1 | 33 | 64 | 92 | -------------------------------------------------------------------------------- /website/src/example/06-responsive.vue: -------------------------------------------------------------------------------- 1 | 33 | 75 | 110 | -------------------------------------------------------------------------------- /website/src/example/07-prevent-collision.vue: -------------------------------------------------------------------------------- 1 | 30 | 58 | 86 | -------------------------------------------------------------------------------- /website/src/example/08-responsive-predefined-layouts.vue: -------------------------------------------------------------------------------- 1 | 63 | 64 | 92 | 116 | -------------------------------------------------------------------------------- /website/src/example/09-dynamic-add-remove.vue: -------------------------------------------------------------------------------- 1 | 37 | 77 | 133 | -------------------------------------------------------------------------------- /website/src/example/10-drag-from-outside.vue: -------------------------------------------------------------------------------- 1 | 101 | 143 | 189 | -------------------------------------------------------------------------------- /website/src/example/11-bounded.vue: -------------------------------------------------------------------------------- 1 | 33 | 75 | 110 | -------------------------------------------------------------------------------- /website/src/example/dom.js: -------------------------------------------------------------------------------- 1 | // let currentDir: "ltr" | "rtl" | "auto" = "auto"; 2 | let currentDir = 'auto'; 3 | 4 | function hasDocument() { 5 | return (typeof document !== 'undefined'); 6 | } 7 | 8 | function hasWindow() { 9 | return (typeof window !== 'undefined'); 10 | } 11 | 12 | export function getDocumentDir() { 13 | if (!hasDocument()) { 14 | return currentDir; 15 | } 16 | const direction = (typeof document.dir !== 'undefined') 17 | ? document.dir 18 | : document.getElementsByTagName('html')[0].getAttribute('dir'); 19 | return direction; 20 | } 21 | 22 | // export function setDocumentDir(dir: "ltr" | "rtl" | "auto"){ 23 | export function setDocumentDir(dir) { 24 | if (!hasDocument) { 25 | currentDir = dir; 26 | return; 27 | } 28 | 29 | const html = document.getElementsByTagName('html')[0]; 30 | html.setAttribute('dir', dir); 31 | } 32 | 33 | const cb = () => {}; 34 | // export function addWindowEventListener(event:string, callback: () => mixed){ 35 | export function addWindowEventListener(event, callback = cb) { 36 | if (!hasWindow) { 37 | callback(); 38 | return; 39 | } 40 | window.addEventListener(event, callback); 41 | } 42 | 43 | export function removeWindowEventListener(event, callback = cb) { 44 | if (!hasWindow) { 45 | return; 46 | } 47 | window.removeEventListener(event, callback); 48 | } 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /website/src/example/styling-grid-lines.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 71 | 114 | -------------------------------------------------------------------------------- /website/src/example/styling-placeholder.vue: -------------------------------------------------------------------------------- 1 | 40 | 66 | 96 | -------------------------------------------------------------------------------- /website/src/example/test-element.vue: -------------------------------------------------------------------------------- 1 | 20 | 28 | 36 | 37 | -------------------------------------------------------------------------------- /website/src/example/usage.vue: -------------------------------------------------------------------------------- 1 | 180 | 263 | 328 | -------------------------------------------------------------------------------- /website/src/guide/01-basic.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 01 - Basic 6 | 7 | 8 | 9 | 10 | 11 | <<< @/example/01-basic.vue 12 | 13 | [View source](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/src/example/01-basic.vue) 14 | 15 | 16 | -------------------------------------------------------------------------------- /website/src/guide/02-events.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 02 - Move and resize events 6 | 7 | 8 | 9 | 10 | 11 | <<< @/example/02-events.vue 12 | 13 | [View source](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/src/example/02-events.vue) 14 | 15 | -------------------------------------------------------------------------------- /website/src/guide/03-multiple-grids.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 03 - Multiple grids 6 | 7 | 8 | 9 | 10 | 11 | <<< @/example/03-multiple-grids.vue 12 | 13 | [View source](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/src/example/03-multiple-grids.vue) 14 | 15 | -------------------------------------------------------------------------------- /website/src/guide/04-allow-ignore.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 04 - Drag allow/ignore elements 6 | 7 | Ignore drag on certain elements and allow on others. 8 | 9 | Click and drag the dots on the corner of each item to reposition 10 | 11 | 12 | 13 | 14 | 15 | <<< @/example/04-allow-ignore.vue 16 | 17 | [View source](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/src/example/04-allow-ignore.vue) 18 | 19 | -------------------------------------------------------------------------------- /website/src/guide/05-mirrored.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 05 - Mirrored grid layout 6 | 7 | 8 | 9 | 10 | 11 | <<< @/example/05-mirrored.vue 12 | 13 | [View source](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/src/example/05-mirrored.vue) 14 | 15 | -------------------------------------------------------------------------------- /website/src/guide/06-responsive.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 06 - Responsive 6 | 7 | 8 | 9 | 10 | 11 | <<< @/example/06-responsive.vue 12 | 13 | [View source](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/src/06-responsive.vue) 14 | 15 | -------------------------------------------------------------------------------- /website/src/guide/07-prevent-collision.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 07 - Prevent Collision 6 | 7 | 8 | 9 | 10 | 11 | <<< @/example/07-prevent-collision.vue 12 | 13 | [View source](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/src/example/07-prevent-collision.vue) 14 | -------------------------------------------------------------------------------- /website/src/guide/08-responsive-predefined-layouts.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 08 - Responsive with predefined layouts 6 | 7 | 8 | 9 | 10 | 11 | <<< @/example/08-responsive-predefined-layouts.vue 12 | 13 | [View source](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/src/example/08-responsive-predefined-layouts.vue) 14 | 15 | -------------------------------------------------------------------------------- /website/src/guide/09-dynamic-add-remove.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 09 - Dynamic Add/Remove 6 | 7 | 8 | 9 | 10 | 11 | <<< @/example/09-dynamic-add-remove.vue 12 | 13 | [View source](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/src/example/09-dynamic-add-remove.vue) 14 | 15 | -------------------------------------------------------------------------------- /website/src/guide/10-drag-from-outside.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 10 - Drag From Outside 6 | 7 | This demo shows what happens when an item is added from outside of the grid. 8 |
9 | Once you drop the item within the grid you'll get its coordinates/properties and can perform actions with it accordingly. 10 | 11 | 12 | 13 | 14 | 15 | <<< @/example/10-drag-from-outside.vue 16 | 17 | [View source](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/src/example/10-drag-from-outside.vue) 18 | 19 | -------------------------------------------------------------------------------- /website/src/guide/11-bounded.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # 11 - Dragging grid items bounded to grid container 6 | 7 | 8 | 9 | 10 | 11 | <<< @/example/11-bounded.vue 12 | 13 | [View source](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/src/example/11-bounded.vue) 14 | 15 | -------------------------------------------------------------------------------- /website/src/guide/auto-size.md: -------------------------------------------------------------------------------- 1 | # Auto Sizing Grid Items 2 | 3 | TODO: https://github.com/merfais/vue-grid-layout-v3/issues/351 4 | -------------------------------------------------------------------------------- /website/src/guide/events.md: -------------------------------------------------------------------------------- 1 | --- 2 | aside: true 3 | --- 4 | # Events 5 | 6 | Move and resize event listeners can be added to each grid-item, so that the parent Vue can be notified when a grid element is being moved or resized. 7 | Moved and resized event listeners can be added, if the only notification needed is when an item is finished moving or resizing. 8 | 9 | Working example [here](../guide/02-events.md) 10 | 11 | ````html 12 | 26 | 40 | {{item.i}} 41 | 42 | 43 | ```` 44 | 45 | ## GridLayout 46 | 47 | ### update:layout 48 | 49 | sync layout by v-model:layout. same as layout-updated 50 | 51 | ```html 52 | 55 | 56 | 57 | ``` 58 | ### layout-created 59 | 60 | Layout created event 61 | 62 | Emited on the component created lifecycle hook 63 | 64 | ```javascript 65 | function layoutCreatedEvent(newLayout){ 66 | console.log("Created layout: ", newLayout) 67 | } 68 | ``` 69 | 70 | ### layout-before-mount 71 | 72 | Layout beforeMount event 73 | 74 | Emited on the component beforeMount lifecycle hook 75 | 76 | ```javascript 77 | function layoutBeforeMountEvent(newLayout){ 78 | console.log("beforeMount layout: ", newLayout) 79 | } 80 | ``` 81 | 82 | ### layout-mounted 83 | 84 | Layout mounted event 85 | 86 | Emited on the component mounted lifecycle hook 87 | 88 | ```javascript 89 | function layoutMountedEvent(newLayout){ 90 | console.log("Mounted layout: ", newLayout) 91 | } 92 | ``` 93 | 94 | ### layout-ready 95 | 96 | Layout ready event 97 | 98 | Emited when all the operations on the mount hook finish 99 | 100 | ```javascript 101 | function layoutReadyEvent(newLayout){ 102 | console.log("Ready layout: ", newLayout) 103 | } 104 | ``` 105 | 106 | ### layout-updated 107 | 108 | Layout updated event 109 | 110 | Every time the layout has finished updating and positions of all grid-items are recalculated 111 | 112 | ```javascript 113 | function layoutUpdatedEvent(newLayout){ 114 | console.log("Updated layout: ", newLayout) 115 | } 116 | ``` 117 | 118 | 119 | ### breakpoint-changed 120 | 121 | Breakpoint Changed event 122 | 123 | Every time the breakpoint value changes due to window resize 124 | 125 | ```javascript 126 | /** 127 | * 128 | * @param newBreakpoint the breakpoint name 129 | * @param newLayout the chosen layout for the breakpoint 130 | * 131 | */ 132 | function breakpointChangedEvent(newBreakpoint, newLayout){ 133 | console.log("BREAKPOINT CHANGED breakpoint=", newBreakpoint, ", layout: ", newLayout ); 134 | }, 135 | ``` 136 | 137 | 138 | ## GridItem 139 | 140 | ### move 141 | 142 | Move event 143 | 144 | Every time an item is being moved and changes position 145 | 146 | ```javascript 147 | function moveEvent(i, newX, newY){ 148 | console.log("MOVE i=" + i + ", X=" + newX + ", Y=" + newY); 149 | }, 150 | ``` 151 | 152 | ### moved 153 | 154 | Moved event 155 | 156 | Every time an item is finished being moved and changes position 157 | 158 | ```javascript 159 | function movedEvent(i, newX, newY){ 160 | console.log("MOVED i=" + i + ", X=" + newX + ", Y=" + newY); 161 | }, 162 | ``` 163 | 164 | ### resize 165 | 166 | Resize event 167 | 168 | Every time an item is being resized and changes size 169 | 170 | ```javascript 171 | function resizeEvent(i, newH, newW, newHPx, newWPx){ 172 | console.log("RESIZE i=" + i + ", H=" + newH + ", W=" + newW + ", H(px)=" + newHPx + ", W(px)=" + newWPx); 173 | }, 174 | ``` 175 | 176 | ### resized 177 | 178 | Resized event 179 | 180 | Every time an item is finished being resized and changes size 181 | 182 | ```javascript 183 | /** 184 | * 185 | * @param i the item id/index 186 | * @param newH new height in grid rows 187 | * @param newW new width in grid columns 188 | * @param newHPx new height in pixels 189 | * @param newWPx new width in pixels 190 | * 191 | */ 192 | function resizedEvent(i, newH, newW, newHPx, newWPx){ 193 | console.log("RESIZED i=" + i + ", H=" + newH + ", W=" + newW + ", H(px)=" + newHPx + ", W(px)=" + newWPx); 194 | }, 195 | ``` 196 | 197 | ### container-resized 198 | 199 | Container Resized event 200 | 201 | Every time the grid item/layout container changes size (browser window or other) 202 | 203 | ```javascript 204 | /** 205 | * 206 | * @param i the item id/index 207 | * @param newH new height in grid rows 208 | * @param newW new width in grid columns 209 | * @param newHPx new height in pixels 210 | * @param newWPx new width in pixels 211 | * 212 | */ 213 | function containerResizedEvent(i, newH, newW, newHPx, newWPx){ 214 | console.log("CONTAINER RESIZED i=" + i + ", H=" + newH + ", W=" + newW + ", H(px)=" + newHPx + ", W(px)=" + newWPx); 215 | }, 216 | ``` 217 | 218 | -------------------------------------------------------------------------------- /website/src/guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | aside: true 3 | --- 4 | # Installation 5 | 6 | ## NPM 7 | 8 | ```shell 9 | npm install vue-grid-layout-v3 --save 10 | ``` 11 | 12 | ## Yarn 13 | 14 | ```shell 15 | yarn add vue-grid-layout-v3 16 | ``` 17 | 18 | 19 | # Import the library 20 | 21 | ## Global Registration 22 | 23 | ```javascript 24 | import { createApp } from 'vue' 25 | import VueGridLayout from 'vue-grid-layout-v3'; 26 | 27 | const app = createApp({ 28 | // ... 29 | }) 30 | // register GridLayout and GridItem component 31 | app.use(VueGridLayout) 32 | app.mount('#app') 33 | 34 | /** 35 | * // custom component name 36 | * import { createApp } from 'vue' 37 | * import { GridLayout, GridItem } from 'vue-grid-layout-v3'; 38 | * 39 | * const app = createApp({ 40 | * // ... 41 | * }) 42 | * app.component('MyGridLayout', GridLayout) 43 | * app.component('MyGridItem', GridLayout) 44 | * app.mount('#app') 45 | */ 46 | 47 | ``` 48 | 49 | ## Local Registration 50 | When using SFC with ` 56 | 57 | 62 | ``` 63 | In non-` 87 | 88 | 89 |
90 | 91 | 101 | {{itemTitle(item)}} 102 | 103 | 104 |
105 | 106 | 156 | ``` 157 | 158 | -------------------------------------------------------------------------------- /website/src/guide/properties.md: -------------------------------------------------------------------------------- 1 | --- 2 | aside: true 3 | --- 4 | # Properties 5 | 6 | ## GridLayout 7 | 8 | ### layout 9 | 10 | 11 | * type: `Array` 12 | * required: `true` 13 | 14 | This is the initial layout of the grid. 15 | 16 | The value must be an `Array` of `Object` items. Each item must have `i`, `x`, `y`, `w` and `h` properties. Please refer to the documentation for `GridItem` below for more information. 17 | 18 | ### responsiveLayouts 19 | 20 | * type: `Object` 21 | * required: `false` 22 | * default : `{}` 23 | 24 | This is the initial layouts of the grid per breakpoint if `responsive` is set to `true`. 25 | The keys of the `Object` are breakpoint names and each value is an `Array` of `Object` items as defined by `layout` prop. eg:{ lg:[layout items], md:[layout items] }. 26 | Setting the prop after the creation of the GridLayout has no effect. 27 | 28 | See also [responsive](#responsive), [breakpoints](#breakpoints) and [cols](#cols) 29 | 30 | ### colNum 31 | 32 | * type: `Number` 33 | * required: `false` 34 | * default: `12` 35 | 36 | Says how many columns the grid has. 37 | 38 | The value should be a _natural number_. 39 | 40 | ### rowHeight 41 | 42 | * type: `Number` 43 | * required: `false` 44 | * default: `150` 45 | 46 | Says what is a height of a single row in pixels. 47 | 48 | ### maxRows 49 | 50 | * type: `Number` 51 | * required: `false` 52 | * default: `Infinity` 53 | 54 | Says what is a maximal number of rows in the grid. 55 | 56 | ### margin 57 | 58 | * type: `Array` 59 | * required: `false` 60 | * default: `[10, 10]` 61 | 62 | Says what are the margins of elements inside the grid. 63 | 64 | The value must be a two-element `Array` of `Number`. Each value is expressed in pixels. The first element is a margin horizontally, the second element is a vertical margin. 65 | 66 | ### isDraggable 67 | 68 | * type: `Boolean` 69 | * required: `false` 70 | * default: `true` 71 | 72 | Says if the grids items are draggable. 73 | 74 | ### isResizable 75 | 76 | * type: `Boolean` 77 | * required: `false` 78 | * default: `true` 79 | 80 | Says if the grids items are resizable. 81 | 82 | ### isMirrored 83 | 84 | * type: `Boolean` 85 | * required: `false` 86 | * default: `false` 87 | 88 | Says if the RTL/LTR should be reversed. 89 | 90 | ### isBounded 91 | 92 | * type: `Boolean` 93 | * required: `false` 94 | * default: `false` 95 | 96 | Says if the grid items are bounded to the container when dragging 97 | 98 | ### autoSize 99 | 100 | * type: `Boolean` 101 | * required: `false` 102 | * default: `true` 103 | 104 | Says if the container height should swells and contracts to fit contents. 105 | 106 | ### verticalCompact 107 | 108 | * type: `Boolean` 109 | * required: `false` 110 | * default: `true` 111 | 112 | Says if the layout should be compact vertically. 113 | 114 | ### restoreOnDrag 115 | 116 | * type: `Boolean` 117 | * required: `false` 118 | * default: `false` 119 | 120 | Says if the moved grid items should be restored after an item has been dragged over. 121 | 122 | ### preventCollision 123 | 124 | * type: `Boolean` 125 | * required: `false` 126 | * default: `false` 127 | 128 | Says if grid items will move when being dragged over. 129 | 130 | ### useCssTransforms 131 | 132 | * type: `Boolean` 133 | * required: `false` 134 | * default: `true` 135 | 136 | Says if the CSS `transition-property: transform;` should be used. 137 | 138 | ### responsive 139 | 140 | * type: `Boolean` 141 | * required: `false` 142 | * default: `false` 143 | 144 | Says if the layout should be responsive to window width 145 | 146 | See also [responsiveLayouts](#responsivelayouts), [breakpoints](#breakpoints) and [cols](#cols) 147 | 148 | 149 | ### breakpoints 150 | 151 | * type: `Object` 152 | * required: `false` 153 | * default: `{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }` 154 | 155 | Breakpoints defined for responsive layout, the parameter represents the width of different devices:lg(large), md(medium), sm(small), xs(extra small). Sets widths on wich column number changes 156 | 157 | See also [responsiveLayouts](#responsivelayouts) and [cols](#cols) 158 | 159 | ### cols 160 | 161 | * type: `Object` 162 | * required: `false` 163 | * default: `{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }` 164 | 165 | Defines number of columns for each breakpoint 166 | 167 | ### useStyleCursor 168 | 169 | * type: `Boolean` 170 | * required: `false` 171 | * default: `true` 172 | 173 | Says if set the `styleCursor` option to true. When dragging freezes, setting this value to `false` may alleviate problems. 174 | **This property is not reactive** 175 | 176 | 177 | ### transformScale 178 | 179 | * type: `Number` 180 | * required: `false` 181 | * default: 1 182 | 183 | Sets a scaling factor to the size of the grid items, 1 is 100% 184 | 185 | ## GridItem 186 | 187 | ### i 188 | 189 | * type: `String` 190 | * required: `true` 191 | 192 | This is the unique identifier of the item. 193 | 194 | ### x 195 | 196 | * type: `Number` 197 | * required: `true` 198 | 199 | Says what is a initial horizontal position of the item (in which column it should be placed). 200 | 201 | The value must be a _whole number_. 202 | 203 | ### y 204 | 205 | * type: `Number` 206 | * required: `true` 207 | 208 | Says what is a initial vertical position of the item (in which row it should be placed). 209 | 210 | The value must be a _whole number_. 211 | 212 | ### w 213 | 214 | * type: `Number` 215 | * required: `true` 216 | 217 | Says what is a initial width of the item. 218 | 219 | The value is a number that is multiplied by `colWidth`. 220 | 221 | ### h 222 | 223 | * type: `Number` 224 | * required: `true` 225 | 226 | Says what is a initial height of the item. 227 | 228 | The value is a number that is multiplied by `rowHeight`. 229 | 230 | ### minW 231 | 232 | * type: `Number` 233 | * required: `false` 234 | * default: `1` 235 | 236 | Says what is a minimal width of the item. If `w` will be smaller then `minW` then `w` will be set to `minW`. 237 | 238 | The value is a number that is multiplied by `colWidth`. 239 | 240 | ### minH 241 | 242 | * type: `Number` 243 | * required: `false` 244 | * default: `1` 245 | 246 | Says what is a minimal hieght of the item. If `h` will be smaller then `minH` then `h` will be set to `minH`. 247 | 248 | The value is a number that is multiplied by `rowHeight`. 249 | 250 | ### maxW 251 | 252 | * type: `Number` 253 | * required: `false` 254 | * default: `Infinity` 255 | 256 | Says what is a maximal width of the item. If `w` will be bigger then `maxW` then `w` will be set to `maxW`. 257 | 258 | The value is a number that is multiplied by `colWidth`. 259 | 260 | ### maxH 261 | 262 | * type: `Number` 263 | * required: `false` 264 | * default: `Infinity` 265 | 266 | Says what is a maximal height of the item. If `h` will be bigger then `maxH` then `h` will be set to `maxH`. 267 | 268 | The value is a number that is multiplied by `rowHeight` 269 | 270 | ### isDraggable 271 | 272 | * type: `Boolean` 273 | * required: `false` 274 | * default: `null` 275 | 276 | Says if item is draggable. 277 | 278 | If default value is `null` then it's inherited from parent. 279 | 280 | ### isResizable 281 | 282 | * type: `Boolean` 283 | * required: `false` 284 | * default: `null` 285 | 286 | Says if item is resizable. 287 | 288 | If default value is `null` then it's inherited from parent. 289 | 290 | ### isBounded 291 | 292 | * type: `Boolean` 293 | * required: `false` 294 | * default: `null` 295 | 296 | Says if the item is bounded to the container when dragging. 297 | 298 | If default value is `null` then it's inherited from parent. 299 | 300 | ### static 301 | 302 | * type: `Boolean` 303 | * required: `false` 304 | * default: `false` 305 | 306 | Says if item is static (won't be draggable, resizable or moved by other items). 307 | 308 | 309 | ### dragIgnoreFrom 310 | 311 | * type: `String` 312 | * required: `false` 313 | * default: `'a, button'` 314 | 315 | Says which elements of the item shouldn't trigger drag event of the item. 316 | 317 | The value is `css-like` selector string. 318 | 319 | For more info please refer to `ignoreFrom` in [interact.js docs](http://interactjs.io/docs/#ignorable-selectors). 320 | 321 | ### dragAllowFrom 322 | 323 | * type: `String` 324 | * required: `false` 325 | * default: `null` 326 | 327 | Says which elements of the item should trigger drag event of the item. 328 | 329 | The value is `css-like` selector string. 330 | 331 | If `null` then one can drag by any (excluding `dragIgnoreFrom`) element of the item. 332 | 333 | For more info please refer to `allowFrom` in [interact.js docs](http://interactjs.io/docs/#ignorable-selectors). 334 | 335 | ### resizeIgnoreFrom 336 | 337 | * type: `String` 338 | * required: `false` 339 | * default: `'a, button'` 340 | 341 | Says which elements of the item shouldn't trigger resize event of the item. 342 | 343 | The value is `css-like` selector string. 344 | 345 | For more info please refer to `ignoreFrom` in [interact.js docs](http://interactjs.io/docs/#ignorable-selectors). 346 | 347 | 348 | ### preserveAspectRatio 349 | 350 | * type: `Boolean` 351 | * required: `false` 352 | * default: `false` 353 | 354 | If 'true', forces the GridItem to preserve its aspect ratio when resizing. 355 | 356 | 357 | ### dragOption 358 | 359 | * type: `Object` 360 | * required: `false` 361 | * default: `{}` 362 | 363 | Passthrough object for the grid item [interact.js draggable configuration](https://interactjs.io/docs/draggable/) 364 | 365 | ### resizeOption 366 | 367 | * type: `Object` 368 | * required: `false` 369 | * default: `{}` 370 | 371 | Passthrough object for the grid item [interact.js resizable configuration](https://interactjs.io/docs/resizable/) 372 | 373 | -------------------------------------------------------------------------------- /website/src/guide/readme.md: -------------------------------------------------------------------------------- 1 | index.md -------------------------------------------------------------------------------- /website/src/guide/styling.md: -------------------------------------------------------------------------------- 1 | --- 2 | aside: true 3 | --- 4 | 8 | 9 | # Styling 10 | 11 | Grid styling can be customized to fit your needs. Below is a list of the classes you can override. 12 | 13 | ## Placeholder 14 | 15 | The default css for the placeholder is: 16 | 17 | ````css 18 | .vue-grid-item.vue-grid-placeholder { 19 | background: red; 20 | opacity: 0.2; 21 | transition-duration: 100ms; 22 | z-index: 2; 23 | -webkit-user-select: none; 24 | -moz-user-select: none; 25 | -ms-user-select: none; 26 | -o-user-select: none; 27 | user-select: none; 28 | } 29 | ```` 30 | 31 | You can override the properties using the !important rule: 32 | 33 | ````css 34 | .vue-grid-item.vue-grid-placeholder { 35 | background: green !important; 36 | } 37 | ```` 38 | 39 | Or by wrapping your grid with a more [specific](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity) class: 40 | 41 | ````css 42 | .container .vue-grid-item.vue-grid-placeholder { 43 | background: green; 44 | } 45 | ```` 46 | 47 | in scoped 48 | 49 | ````css 50 | :deep(.vue-grid-item.vue-grid-placeholder) { 51 | background: green; 52 | } 53 | ```` 54 | 55 | 56 | In this example we change the placeholder background color to green: 57 | 58 | 59 | 60 | 61 | 62 | <<< @/example/styling-placeholder.vue 63 | 64 | [View source](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/src/example/styling-placeholder.vue) 65 | 66 | 67 | ## Grid lines 68 | 69 | To add grid lines to the layout, add the ``grid`` class to the grid-layout element and use the css: 70 | 71 | ````css 72 | .grid::before { 73 | content: ''; 74 | background-size: calc(calc(100% - 5px) / 12) 40px; 75 | background-image: linear-gradient( 76 | to right, 77 | lightgrey 1px, 78 | transparent 1px 79 | ), 80 | linear-gradient(to bottom, lightgrey 1px, transparent 1px); 81 | height: calc(100% - 5px); 82 | width: calc(100% - 5px); 83 | position: absolute; 84 | background-repeat: repeat; 85 | margin:5px; 86 | } 87 | ```` 88 | 89 | CSS calculations for grid lines: 90 | 91 | * background size = calc(calc(100% - (margin/2)) / colNum) rowHeight + margin; 92 | * height: calc(100% - (margin/2)) 93 | * width: calc(100% - (margin/2)) 94 | * margin: margin / 2 95 | 96 | 97 | 98 | 99 | 100 | <<< @/example/styling-grid-lines.vue 101 | 102 | [View source](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/src/example/styling-grid-lines.vue) 103 | 104 | 105 | -------------------------------------------------------------------------------- /website/src/guide/usage.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Usage 6 | [View source](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/src/example/usage.vue) 7 | 8 | <<< @/example/usage.vue 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /website/src/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: "Vue3 Grid Layout" 7 | text: "A draggable and resizable grid layout, as a Vue component." 8 | image: /logo.png 9 | tagline: A grid layout system for Vue.js, like 10 | Gridster, 11 | for Vue.js. Heavily inspired by 12 | React-Grid-Layout 13 | actions: 14 | - theme: brand 15 | text: Get Started → 16 | link: /guide 17 | 18 | features: 19 | - title: Draggable 20 | - title: Resizable 21 | - title: Responsive 22 | --- 23 | 24 | -------------------------------------------------------------------------------- /website/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import App from './app.vue'; 3 | 4 | createApp(App).mount('#app'); 5 | 6 | -------------------------------------------------------------------------------- /website/src/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merfais/vue-grid-layout-v3/a669942fa85c80211767b89acddbe6211319ffe2/website/src/public/favicon.ico -------------------------------------------------------------------------------- /website/src/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merfais/vue-grid-layout-v3/a669942fa85c80211767b89acddbe6211319ffe2/website/src/public/logo.png -------------------------------------------------------------------------------- /website/src/readme.md: -------------------------------------------------------------------------------- 1 | index.md -------------------------------------------------------------------------------- /website/src/zh/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: HomeLayout 3 | home: true 4 | heroImage: /assets/img/logo.png 5 | heroText: Vue Grid Layout 6 | tagline: 一个类似于Gridster的栅格布局系统, 适用于Vue.js。灵感源自于React-Grid-Layout 7 | actionText: 开始 → 8 | actionLink: /zh/guide/ 9 | features: 10 | - title: ✥ 可拖拽 11 | details: 12 | - title: ⇲ 可调整大小 13 | details: 14 | - title: 静态部件(不可拖拽、调整大小) 15 | details: 16 | - title: 拖拽和调整大小时进行边界检查 17 | details: 18 | - title: 增减部件时避免重建栅格 19 | details: 20 | - title: 可序列化和还原的布局 21 | details: 22 | - title: 自动化 RTL 支持 23 | details: 24 | - title: 响应式 25 | details: 26 | - title: Min/max w/h per item 27 | details: 28 | --- 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /website/src/zh/guide/01-basic.md: -------------------------------------------------------------------------------- 1 | # 01 - 基本 2 | 3 | [查看资料](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/docs/.vuepress/components/Example01Basic.vue) 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /website/src/zh/guide/02-events.md: -------------------------------------------------------------------------------- 1 | # 02 - 移动事件并调整大小 2 | 3 | [查看资料](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/docs/.vuepress/components/Example02Events.vue) 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /website/src/zh/guide/03-multiple-grids.md: -------------------------------------------------------------------------------- 1 | # 03 - 多个栅格 2 | 3 | [查看资料](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/docs/.vuepress/components/Example03MultipleGrids.vue) 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /website/src/zh/guide/04-allow-ignore.md: -------------------------------------------------------------------------------- 1 | # 04 - 拖动允许/忽略元素 2 | 3 | 忽略对某些元素的拖动而对其他元素的允许。 4 | 5 | 单击并拖动每个项目角上的点以重新定位 6 | 7 | [查看资料](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/docs/.vuepress/components/Example04AllowIgnore.vue) 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /website/src/zh/guide/05-mirrored.md: -------------------------------------------------------------------------------- 1 | # 05 - 镜像反转栅格布局 2 | 3 | [查看资料](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/docs/.vuepress/components/Example05Mirrored.vue) 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /website/src/zh/guide/06-responsive.md: -------------------------------------------------------------------------------- 1 | # 06 - 响应式 2 | 3 | [查看资料](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/docs/.vuepress/components/Example06Responsive.vue) 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /website/src/zh/guide/07-prevent-collision.md: -------------------------------------------------------------------------------- 1 | # 07 - 防止碰撞 2 | 3 | [查看资料](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/docs/.vuepress/components/Example07PreventCollision.vue) 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /website/src/zh/guide/08-responsive-predefined-layouts.md: -------------------------------------------------------------------------------- 1 | # 08 - 响应预定义的布局 2 | 3 | [查看资料](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/docs/.vuepress/components/Example08ResponsivePredefinedLayouts.vue) 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /website/src/zh/guide/09-dynamic-add-remove.md: -------------------------------------------------------------------------------- 1 | # 09 - 动态添加/删除 2 | 3 | [查看资料](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/docs/.vuepress/components/Example09DynamicAddRemove.vue) 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /website/src/zh/guide/10-drag-from-outside.md: -------------------------------------------------------------------------------- 1 | # 10 - 从外面拖动 2 | 3 | 该演示演示了从栅格外部添加部件时发生的情况。 4 |
5 | 将部件放到栅格中后,您将获得其坐标/属性,并可以据此执行操作。 6 | 7 | [查看资料](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/docs/.vuepress/components/Example10DragFromOutside.vue) 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /website/src/zh/guide/11-bounded.md: -------------------------------------------------------------------------------- 1 | # 11 - 拖动栅格元素绑定到容器 2 | 3 | 可以获得栅格元素的坐标与容器间的绑定关系。 4 | 5 | [查看资料](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/docs/.vuepress/components/Example11Bounded.vue) 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /website/src/zh/guide/README.md: -------------------------------------------------------------------------------- 1 | # 安装 2 | 3 | ## NPM 4 | 5 | npm install vue-grid-layout-v3 --save 6 | 7 | ## Yarn 8 | 9 | yarn add vue-grid-layout-v3 10 | 11 | 12 | 导入库 13 | 14 | ```javascript 15 | import VueGridLayout from 'vue-grid-layout-v3'; 16 | ``` 17 | 18 | 添加到其他Vue组件 19 | 20 | ```javascript 21 | export default { 22 | components: { 23 | GridLayout: VueGridLayout.GridLayout, 24 | GridItem: VueGridLayout.GridItem 25 | }, 26 | // ... data, methods, mounted (), etc. 27 | } 28 | 29 | ``` 30 | 31 | ## 浏览器 32 | 33 | 包括可用于浏览器的软件包([从发布版本](https://github.com/merfais/vue-grid-layout-v3/releases)下载)。组件将自动可用。 34 | 35 | ```html 36 | 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /website/src/zh/guide/auto-size.md: -------------------------------------------------------------------------------- 1 | # 自动调整栅格元素 2 | 3 | 待办: https://github.com/merfais/vue-grid-layout-v3/issues/351 4 | -------------------------------------------------------------------------------- /website/src/zh/guide/events.md: -------------------------------------------------------------------------------- 1 | # 事件 2 | 3 | 每一个栅格元素grid-item上都可以添加监听器,用于监听移动和调整大小事件,这样父级Vue对象就可以收到通知。 4 | 5 | 示例 [点击这里](../guide/02-events.md) 6 | 7 | ````html 8 | 9 | 25 | 26 | 38 | {{item.i}} 39 | 40 | 41 | ```` 42 | 43 | ## GridLayout 44 | 45 | ### layoutCreatedEvent 46 | 47 | 对应Vue生命周期的created 48 | 49 | ```javascript 50 | layoutCreatedEvent: function(newLayout){ 51 | console.log("Created layout: ", newLayout) 52 | } 53 | ``` 54 | 55 | ### layoutBeforeMountEvent 56 | 57 | 对应Vue生命周期的beforeMount 58 | 59 | ```javascript 60 | layoutBeforeMountEvent: function(newLayout){ 61 | console.log("beforeMount layout: ", newLayout) 62 | } 63 | ``` 64 | 65 | ### layoutMountedEvent 66 | 67 | 对应Vue生命周期的mounted 68 | 69 | ```javascript 70 | layoutMountedEvent: function(newLayout){ 71 | console.log("Mounted layout: ", newLayout) 72 | } 73 | ``` 74 | 75 | ### layoutReadyEvent 76 | 77 | 当完成mount中的所有操作时生成的事件 78 | 79 | ```javascript 80 | layoutReadyEvent: function(newLayout){ 81 | console.log("Ready layout: ", newLayout) 82 | } 83 | ``` 84 | 85 | ### layoutUpdatedEvent 86 | 87 | 布局updated事件 88 | 89 | 更新事件(布局更新或栅格元素的位置重新计算) 90 | 91 | ```javascript 92 | layoutUpdatedEvent: function(newLayout){ 93 | console.log("Updated layout: ", newLayout) 94 | } 95 | ``` 96 | 97 | 98 | ### breakpointChangedEvent 99 | 100 | 断点更改事件 101 | 102 | 每次断点值由于窗口调整大小而改变 103 | 104 | ```javascript 105 | /** 106 | * 107 | * @param newBreakpoint the breakpoint name 108 | * @param newLayout the chosen layout for the breakpoint 109 | * 110 | */ 111 | breakpointChangedEvent: function(newBreakpoint, newLayout){ 112 | console.log("BREAKPOINT CHANGED breakpoint=", newBreakpoint, ", layout: ", newLayout ); 113 | }, 114 | ``` 115 | 116 | 117 | ## GridItem 118 | 119 | ### moveEvent 120 | 121 | 移动时的事件 122 | 123 | ```javascript 124 | moveEvent: function(i, newX, newY){ 125 | console.log("MOVE i=" + i + ", X=" + newX + ", Y=" + newY); 126 | }, 127 | ``` 128 | 129 | ### resizeEvent 130 | 131 | 调整大小时的事件 132 | 133 | ```javascript 134 | resizeEvent: function(i, newH, newW, newHPx, newWPx){ 135 | console.log("RESIZE i=" + i + ", H=" + newH + ", W=" + newW + ", H(px)=" + newHPx + ", W(px)=" + newWPx); 136 | }, 137 | ``` 138 | 139 | ### movedEvent 140 | 141 | 移动后的事件 142 | 143 | ```javascript 144 | movedEvent: function(i, newX, newY){ 145 | console.log("MOVED i=" + i + ", X=" + newX + ", Y=" + newY); 146 | }, 147 | ``` 148 | 149 | ### resizedEvent 150 | 151 | 调整大小后的事件 152 | 153 | ```javascript 154 | /** 155 | * 156 | * @param i the item id/index 157 | * @param newH new height in grid rows 158 | * @param newW new width in grid columns 159 | * @param newHPx new height in pixels 160 | * @param newWPx new width in pixels 161 | * 162 | */ 163 | resizedEvent: function(i, newH, newW, newHPx, newWPx){ 164 | console.log("RESIZED i=" + i + ", H=" + newH + ", W=" + newW + ", H(px)=" + newHPx + ", W(px)=" + newWPx); 165 | }, 166 | ``` 167 | 168 | ### containerResizedEvent 169 | 170 | 栅格元素/栅格容器更改大小的事件(浏览器窗口或其他) 171 | 172 | 173 | ```javascript 174 | /** 175 | * 176 | * @param i the item id/index 177 | * @param newH new height in grid rows 178 | * @param newW new width in grid columns 179 | * @param newHPx new height in pixels 180 | * @param newWPx new width in pixels 181 | * 182 | */ 183 | containerResizedEvent: function(i, newH, newW, newHPx, newWPx){ 184 | console.log("CONTAINER RESIZED i=" + i + ", H=" + newH + ", W=" + newW + ", H(px)=" + newHPx + ", W(px)=" + newWPx); 185 | }, 186 | ``` 187 | 188 | -------------------------------------------------------------------------------- /website/src/zh/guide/examples.md: -------------------------------------------------------------------------------- 1 | # 例子 2 | 3 | ## 基本 4 | 5 | 6 | 7 | ## 事件 8 | 9 | 10 | 11 | ## 多个网格 12 | 13 | 14 | 15 | ## 拖动允许/忽略元素 16 | 17 | 忽略对某些元素的拖动,而对其他元素允许。 18 | 19 | 单击并拖动每个栅格角上的点以重新定位 20 | 21 | 22 | -------------------------------------------------------------------------------- /website/src/zh/guide/properties.md: -------------------------------------------------------------------------------- 1 | # 属性 2 | 3 | ## GridLayout 4 | 5 | ### layout 6 | 7 | 8 | * type: `Array` 9 | * required: `true` 10 | 11 | 这是栅格的初始布局。 12 | 13 | 数据源。值必须为 `Array`,其数据项为 `Object`。 每条数据项必须有 `i, x, y, w 和 h` 属性。 请参考下面的 `GridItem`。 14 | 15 | ### responsiveLayouts 16 | 17 | * type: `Object` 18 | * required: `false` 19 | * default : `{}` 20 | 21 | 如果 `responsive` 设置为 `true`,该配置将作为栅格中每个断点的初始布局。键值是断点名称,每项的值都是类似 `layout` 属性定义的数据结构,值必须为 `Array`,其数据项为 `Object`。例如: `{lg: [layout items], md: [layout items]}`。需要注意的是,在创建栅格布局后设置该属性无效。 22 | 在创建GridLayout之后设置prop无效。 23 | 24 | 可以查看 [responsive](#responsive), [breakpoints](#breakpoints) 和 [cols](#cols) 25 | 26 | ### colNum 27 | 28 | * type: `Number` 29 | * required: `false` 30 | * default: `12` 31 | 32 | 定义栅格系统的列数,其值需为自然数。 33 | 34 | ### rowHeight 35 | 36 | * type: `Number` 37 | * required: `false` 38 | * default: `150` 39 | 40 | 每行的高度,单位像素。 41 | 42 | ### maxRows 43 | 44 | * type: `Number` 45 | * required: `false` 46 | * default: `Infinity` 47 | 48 | 定义最大行数。 49 | 50 | ### margin 51 | 52 | * type: `Array` 53 | * required: `false` 54 | * default: `[10, 10]` 55 | 56 | 定义栅格中的元素边距。 57 | 58 | 值必须是包含两个 `Number`的数组,数组中第一个元素表示水平边距,第二个表示垂直边距,单位为像素。 59 | 60 | ### isDraggable 61 | 62 | * type: `Boolean` 63 | * required: `false` 64 | * default: `true` 65 | 66 | 标识栅格中的元素是否可拖拽。 67 | 68 | ### isResizable 69 | 70 | * type: `Boolean` 71 | * required: `false` 72 | * default: `true` 73 | 74 | 标识栅格中的元素是否可调整大小。 75 | 76 | ### isMirrored 77 | 78 | * type: `Boolean` 79 | * required: `false` 80 | * default: `false` 81 | 82 | 标识栅格中的元素是否可镜像反转。 83 | 84 | ### autoSize 85 | 86 | * type: `Boolean` 87 | * required: `false` 88 | * default: `true` 89 | 90 | 标识容器是否自动调整大小。 91 | 92 | ### verticalCompact 93 | 94 | * type: `Boolean` 95 | * required: `false` 96 | * default: `true` 97 | 98 | 标识布局是否垂直压缩。 99 | 100 | ### preventCollision 101 | 102 | * type: `Boolean` 103 | * required: `false` 104 | * default: `false` 105 | 106 | 防止碰撞属性,值设置为`ture`时,栅格只能拖动至空白处。 107 | 108 | ### useCssTransforms 109 | 110 | * type: `Boolean` 111 | * required: `false` 112 | * default: `true` 113 | 114 | 标识是否使用CSS属性 `transition-property: transform;`。 115 | 116 | ### responsive 117 | 118 | * type: `Boolean` 119 | * required: `false` 120 | * default: `false` 121 | 122 | 标识布局是否为响应式。 123 | 124 | 可以查看 [responsiveLayouts](#responsivelayouts)、[breakpoints](#breakpoints)和 [cols](#cols) 125 | 126 | 127 | ### breakpoints 128 | 129 | * type: `Object` 130 | * required: `false` 131 | * default: { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 } 132 | 133 | 为响应式布局设置断点。 134 | 135 | 可以查看 [responsiveLayouts](#responsivelayouts) 和 [cols](#cols) 136 | 137 | ### cols 138 | 139 | * type: `Object` 140 | * required: `false` 141 | * default: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 } 142 | 143 | 设置每个断点对应的列数。 144 | 145 | ### useStyleCursor 146 | 147 | * type: `Boolean` 148 | * required: `false` 149 | * default: `true` 150 | 151 | 标识是否使用动态鼠标指针样式。当拖动出现卡顿时,将此值设为 `false`也许可以缓解布局问题。 152 | **此属性无效** 153 | 154 | ## GridItem 155 | 156 | ### i 157 | 158 | * type: `String` 159 | * required: `true` 160 | 161 | 栅格中元素的ID。 162 | 163 | ### x 164 | 165 | * type: `Number` 166 | * required: `true` 167 | 168 | 标识栅格元素位于第几列,需为自然数。 169 | 170 | ### y 171 | 172 | * type: `Number` 173 | * required: `true` 174 | 175 | 标识栅格元素位于第几行,需为自然数。 176 | 177 | ### w 178 | 179 | * type: `Number` 180 | * required: `true` 181 | 182 | 标识栅格元素的初始宽度,值为`colWidth`的倍数。 183 | 184 | ### h 185 | 186 | * type: `Number` 187 | * required: `true` 188 | 189 | 标识栅格元素的初始高度,值为`rowHeight`的倍数。 190 | 191 | ### minW 192 | 193 | * type: `Number` 194 | * required: `false` 195 | * default: `1` 196 | 197 | 栅格元素的最小宽度,值为`colWidth`的倍数。 198 | 199 | 如果`w`小于`minW`,则`minW`的值会被`w`覆盖。 200 | 201 | ### minH 202 | 203 | * type: `Number` 204 | * required: `false` 205 | * default: `1` 206 | 207 | 栅格元素的最小高度,值为`rowHeight`的倍数。 208 | 209 | 如果`h`小于`minH`,则`minH`的值会被h覆盖。 210 | 211 | ### maxW 212 | 213 | * type: `Number` 214 | * required: `false` 215 | * default: `Infinity` 216 | 217 | 栅格元素的最大宽度,值为`colWidth`的倍数。 218 | 219 | 如果`w`大于`maxW`,则`maxW`的值会被`w`覆盖。 220 | 221 | ### maxH 222 | 223 | * type: `Number` 224 | * required: `false` 225 | * default: `Infinity` 226 | 227 | 栅格元素的最大高度,值为`rowHeight`的倍数。 228 | 229 | 如果`h`大于`maxH`,则`maxH`的值会被`h`覆盖。 230 | 231 | ### isDraggable 232 | 233 | * type: `Boolean` 234 | * required: `false` 235 | * default: `null` 236 | 237 | 标识栅格元素是否可拖拽。如果值为`null`则取决于父容器。 238 | 239 | ### isResizable 240 | 241 | * type: `Boolean` 242 | * required: `false` 243 | * default: `null` 244 | 245 | 标识栅格元素是否可调整大小。如果值为`null`则取决于父容器。 246 | 247 | ### static 248 | 249 | * type: `Boolean` 250 | * required: `false` 251 | * default: `false` 252 | 253 | 标识栅格元素是否为静态的(无法拖拽、调整大小或被其他元素移动)。 254 | 255 | ### dragIgnoreFrom 256 | 257 | * type: `String` 258 | * required: `false` 259 | * default: `'a, button'` 260 | 261 | 标识栅格元素中哪些子元素无法触发拖拽事件,值为`css-like`选择器。 262 | 263 | 请参考[interact.js docs](http://interactjs.io/docs/#ignorable-selectors)中的`ignoreFrom`。 264 | 265 | ### dragAllowFrom 266 | 267 | * type: `String` 268 | * required: `false` 269 | * default: `null` 270 | 271 | 标识栅格元素中哪些子元素可以触发拖拽事件,值为`css-like`选择器。 272 | 273 | 如果值为`null`则表示所有子元素(`dragIgnoreFrom`的除外)。 274 | 275 | 请参考[interact.js docs](http://interactjs.io/docs/#ignorable-selectors)中的`allowFrom`。 276 | 277 | ### resizeIgnoreFrom 278 | 279 | * type: `String` 280 | * required: `false` 281 | * default: `'a, button'` 282 | 283 | 标识栅格元素中哪些子元素无法触发调整大小的事件,值为`css-like`选择器。 284 | 285 | 请参考[interact.js docs](http://interactjs.io/docs/#ignorable-selectors)中的`ignoreFrom`。 286 | -------------------------------------------------------------------------------- /website/src/zh/guide/styling.md: -------------------------------------------------------------------------------- 1 | # 样式 2 | 3 | 可以定制栅格样式以适合您的需求。以下是可以覆盖的类的列表。 4 | 5 | ## 占位符 6 | 7 | 占位符的默认css为: 8 | 9 | ````css 10 | .vue-grid-item.vue-grid-placeholder { 11 | background: red; 12 | opacity: 0.2; 13 | transition-duration: 100ms; 14 | z-index: 2; 15 | -webkit-user-select: none; 16 | -moz-user-select: none; 17 | -ms-user-select: none; 18 | -o-user-select: none; 19 | user-select: none; 20 | } 21 | ```` 22 | 23 | 您可以使用`!important`规则覆盖属性: 24 | 25 | ````css 26 | .vue-grid-item.vue-grid-placeholder { 27 | background: green !important; 28 | } 29 | ```` 30 | 31 | 或者通过用更[具体的](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity)方式包装栅格类: 32 | 33 | ````css 34 | .container .vue-grid-item.vue-grid-placeholder { 35 | background: green; 36 | } 37 | ```` 38 | 39 | 在此示例中,我们将占位符的背景色更改为绿色: 40 | 41 | [查看资料](https://github.com/merfais/vue-grid-layout-v3/blob/master/website/docs/.vuepress/components/ExampleStylingPlaceholder.vue) 42 | 43 | 44 | 45 | 46 | 47 | 48 | 工作正在进行中... 49 | -------------------------------------------------------------------------------- /website/src/zh/guide/usage.md: -------------------------------------------------------------------------------- 1 | # 用法 2 | 3 | ```javascript 4 | new Vue({ 5 | el: '#app', 6 | data: { 7 | layout: [ 8 | {"x":0,"y":0,"w":2,"h":2,"i":"0"}, 9 | {"x":2,"y":0,"w":2,"h":4,"i":"1"}, 10 | {"x":4,"y":0,"w":2,"h":5,"i":"2"}, 11 | {"x":6,"y":0,"w":2,"h":3,"i":"3"}, 12 | {"x":8,"y":0,"w":2,"h":3,"i":"4"}, 13 | {"x":10,"y":0,"w":2,"h":3,"i":"5"}, 14 | {"x":0,"y":5,"w":2,"h":5,"i":"6"}, 15 | {"x":2,"y":5,"w":2,"h":5,"i":"7"}, 16 | {"x":4,"y":5,"w":2,"h":5,"i":"8"}, 17 | {"x":6,"y":3,"w":2,"h":4,"i":"9"}, 18 | {"x":8,"y":4,"w":2,"h":4,"i":"10"}, 19 | {"x":10,"y":4,"w":2,"h":4,"i":"11"}, 20 | {"x":0,"y":10,"w":2,"h":5,"i":"12"}, 21 | {"x":2,"y":10,"w":2,"h":5,"i":"13"}, 22 | {"x":4,"y":8,"w":2,"h":4,"i":"14"}, 23 | {"x":6,"y":8,"w":2,"h":4,"i":"15"}, 24 | {"x":8,"y":10,"w":2,"h":5,"i":"16"}, 25 | {"x":10,"y":4,"w":2,"h":2,"i":"17"}, 26 | {"x":0,"y":9,"w":2,"h":3,"i":"18"}, 27 | {"x":2,"y":6,"w":2,"h":2,"i":"19"} 28 | ], 29 | }, 30 | }); 31 | ``` 32 | 33 | 34 | ```html 35 | 36 | 47 | 48 | 55 | {{item.i}} 56 | 57 | 58 | ``` 59 | -------------------------------------------------------------------------------- /website/vite.config.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url'; 2 | import { defineConfig } from 'vite'; 3 | import vue from '@vitejs/plugin-vue'; 4 | import eslint from '@rollup/plugin-eslint'; 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig(() => ({ 8 | plugins: [ 9 | vue(), 10 | eslint({ 11 | include: ['**/*.js', '**/*.vue'], 12 | }), 13 | ], 14 | resolve: { 15 | alias: { 16 | '@': fileURLToPath(new URL('./src', import.meta.url)), 17 | }, 18 | }, 19 | })); 20 | --------------------------------------------------------------------------------