├── .browserslistrc
├── .editorconfig
├── .eslintrc-auto-import.json
├── .github
├── CONTRIBUTING.md
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── BUG_REPORT.yaml
│ └── FEATURE_REQUEST.yaml
└── workflows
│ ├── inactive-issues.yaml
│ └── main.yml
├── .gitignore
├── .husky
└── pre-commit
├── .npmignore
├── .npmrc
├── .nvmrc
├── .prettierrc.js
├── .release-it.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE.md
├── README.md
├── dist
├── VResizeDrawer-BeVN9VIZ.mjs
├── VResizeDrawer-CKGjiLW1.js
├── plugin
│ ├── VResizeDrawer.vue.d.ts
│ ├── composables
│ │ ├── classes.d.ts
│ │ ├── colors.d.ts
│ │ ├── helpers.d.ts
│ │ ├── icons.d.ts
│ │ ├── storage.d.ts
│ │ └── styles.d.ts
│ ├── index.d.ts
│ ├── types
│ │ └── index.d.ts
│ └── utils
│ │ ├── globals.d.ts
│ │ └── props.d.ts
├── scss
│ ├── _mixins.scss
│ └── main.scss
├── vuetify-resize-drawer.cjs.js
└── vuetify-resize-drawer.es.js
├── eslint.config.mjs
├── index.html
├── package.json
├── playground
└── index.html
├── pnpm-lock.yaml
├── public
└── vuetify-logo.svg
├── src
├── App.vue
├── assets
│ └── vuetify-logo.svg
├── documentation
│ ├── DocsPage.vue
│ ├── components
│ │ ├── MenuComponent.vue
│ │ ├── PropsTable.vue
│ │ └── VuetifyGridExamples.vue
│ ├── layout
│ │ └── AppBar.vue
│ ├── sections
│ │ ├── DependenciesSection.vue
│ │ ├── DescriptionSection.vue
│ │ ├── EventsSection.vue
│ │ ├── ExampleSection.vue
│ │ ├── GridSection.vue
│ │ ├── LegalSection.vue
│ │ ├── LicenseSection.vue
│ │ ├── PlaygroundSection.vue
│ │ ├── PropsSection.vue
│ │ ├── SassVariablesSection.vue
│ │ ├── SlotsSection.vue
│ │ ├── UsageSection.vue
│ │ └── index.js
│ └── types
│ │ └── docs.d.ts
├── libraries
│ └── fontawesome.ts
├── main.ts
├── playground
│ ├── .gitignore
│ └── configs
│ │ ├── PlaygroundApp.vue
│ │ ├── build.sh
│ │ ├── playground.ts
│ │ └── templates
│ │ └── PlaygroundPage.vue
├── plugin
│ ├── VResizeDrawer.vue
│ ├── __tests__
│ │ └── index.test.ts
│ ├── composables
│ │ ├── __tests__
│ │ │ ├── __snapshots__
│ │ │ │ └── classes.test.ts.snap
│ │ │ ├── classes.test.ts
│ │ │ ├── colors.test.ts
│ │ │ ├── helpers.test.ts
│ │ │ ├── icons.test.ts
│ │ │ ├── storage.test.ts
│ │ │ └── styles.test.ts
│ │ ├── classes.ts
│ │ ├── colors.ts
│ │ ├── helpers.ts
│ │ ├── icons.ts
│ │ ├── storage.ts
│ │ └── styles.ts
│ ├── index.ts
│ ├── styles
│ │ ├── _mixins.scss
│ │ └── main.scss
│ ├── types
│ │ ├── auto-imports.d.ts
│ │ ├── index.ts
│ │ ├── vite-env.d.ts
│ │ └── vue-shim.d.ts
│ └── utils
│ │ ├── globals.ts
│ │ └── props.ts
├── plugins
│ ├── index.ts
│ ├── theme.ts
│ ├── vuetify.ts
│ └── webfontloader.ts
├── stores
│ ├── index.ts
│ ├── menu.ts
│ └── props.ts
└── style.css
├── stylelint.config.js
├── tsconfig.build.json
├── tsconfig.json
├── tsconfig.node.json
├── vite.build.config.mts
├── vite.config.mts
└── vitest.config.mts
/.browserslistrc:
--------------------------------------------------------------------------------
1 | # Browsers that we support
2 |
3 | > 1%
4 | last 2 versions
5 | not dead
6 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 4
7 | indent_style = tab
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | indent_size = 2
13 | indent_style = space
14 | trim_trailing_whitespace = false
15 |
16 | [*.{yml,yaml}]
17 | indent_size = 2
18 |
19 | [*.{js,ts,mts,vue}]
20 | indent_size = 2
21 | indent_style = tab
22 |
23 | [*.{scss,css}]
24 | indent_size = 2
25 | indent_style = space
26 |
--------------------------------------------------------------------------------
/.eslintrc-auto-import.json:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "CSSProperties": true,
4 | "Component": true,
5 | "ComponentPublicInstance": true,
6 | "ComputedRef": true,
7 | "EffectScope": true,
8 | "InjectionKey": true,
9 | "PropType": true,
10 | "Ref": true,
11 | "VNode": true,
12 | "computed": true,
13 | "createApp": true,
14 | "customRef": true,
15 | "defineAsyncComponent": true,
16 | "defineComponent": true,
17 | "effectScope": true,
18 | "getCurrentInstance": true,
19 | "getCurrentScope": true,
20 | "h": true,
21 | "inject": true,
22 | "isProxy": true,
23 | "isReactive": true,
24 | "isReadonly": true,
25 | "isRef": true,
26 | "markRaw": true,
27 | "nextTick": true,
28 | "onActivated": true,
29 | "onBeforeMount": true,
30 | "onBeforeUnmount": true,
31 | "onBeforeUpdate": true,
32 | "onDeactivated": true,
33 | "onErrorCaptured": true,
34 | "onMounted": true,
35 | "onRenderTracked": true,
36 | "onRenderTriggered": true,
37 | "onScopeDispose": true,
38 | "onServerPrefetch": true,
39 | "onUnmounted": true,
40 | "onUpdated": true,
41 | "provide": true,
42 | "reactive": true,
43 | "readonly": true,
44 | "ref": true,
45 | "resolveComponent": true,
46 | "shallowReactive": true,
47 | "shallowReadonly": true,
48 | "shallowRef": true,
49 | "toRaw": true,
50 | "toRef": true,
51 | "toRefs": true,
52 | "triggerRef": true,
53 | "unref": true,
54 | "useAttrs": true,
55 | "useCssModule": true,
56 | "useCssVars": true,
57 | "useSlots": true,
58 | "useTheme": true,
59 | "watch": true,
60 | "watchEffect": true,
61 | "watchPostEffect": true,
62 | "watchSyncEffect": true
63 | }
64 | }
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to this repository
2 |
3 | ## Getting started
4 |
5 | Before you begin:
6 | - Have you read the [code of conduct](CODE_OF_CONDUCT.md)?
7 | - Check out the [existing issues](https://github.com/webdevnerdstuff/vuetify-resize-drawer/issues).
8 |
9 | ### Don't see your issue? Open one
10 |
11 | If you spot something new, open an issue using a [template](https://github.com/webdevnerdstuff/vuetify-resize-drawer/issues/new/choose). We'll use the issue to have a conversation about the problem you want to fix.
12 |
13 | ### Ready to make a change? Fork the repo
14 |
15 | Fork using the command line:
16 |
17 | - [Fork the repo](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#fork-an-example-repository) so that you can make your changes without affecting the original project until you're ready to merge them.
18 |
19 | ### Make your update:
20 |
21 | Make your changes to the file(s) you'd like to update.
22 | Update the CHANGELOG.md with the updates you made, please include the date and Github username.
23 |
24 | ### Open a pull request
25 | When you're done making changes and you'd like to propose them for review, open your PR (pull request).
26 |
27 | ### Submit your PR & get it reviewed
28 | - Once you submit your PR, others from the Vue Unicorn Log community will review it with you.
29 | - After that, we may have questions, check back on your PR to keep up with the conversation.
30 |
31 | ### Your PR is merged!
32 | Congratulations! The whole GitHub community thanks you. :sparkles:
33 |
34 | Once your PR is merged, you will be proudly listed as a contributor in the [contributor chart](https://github.com/webdevnerdstuff/vuetify-resize-drawer/graphs/contributors).
35 |
36 | ### Keep contributing as you use Vue Unicorn Log
37 |
38 | Now that you're a part of the Vue Unicorn Log community, you can keep participating in many ways.
39 |
40 | ## Types of contributions
41 | You can contribute to the Vue Unicorn Log content and site in several ways. This repo is a place to discuss and collaborate on Vue Unicorn Log! Our small, but mighty team is maintaining this repo, to preserve our bandwidth, off topic conversations will be closed.
42 |
43 | ### :mega: Discussions
44 | Discussions are where we have conversations.
45 |
46 | If you'd like help troubleshooting a Vue Unicorn Log PR you're working on, have a great new idea, or want to share something amazing you've learned, join us in [discussions](https://github.com/webdevnerdstuff/vuetify-resize-drawer/discussions).
47 |
48 | ### :beetle: Issues
49 | [Issues](https://docs.github.com/en/github/managing-your-work-on-github/about-issues) are used to track tasks that contributors can help with.
50 |
51 | If you've found something in the content or the website that should be updated, search open issues to see if someone else has reported the same thing. If it's something new, open an issue using a [template](https://github.com/webdevnerdstuff/vuetify-resize-drawer/issues/new/choose). We'll use the issue to have a conversation about the problem you want to fix.
52 |
53 | ### :hammer_and_wrench: Pull requests
54 | A [pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) is a way to suggest changes in our repository.
55 |
56 | To learn more about opening a pull request in this repo, see [Opening a pull request](#opening-a-pull-request) below.
57 |
58 | ### :question: Support
59 | We are a small team working hard to keep up with the documentation demands of a continuously changing product. Unfortunately, we just can't help with support questions in this repository. If you are experiencing a problem with GitHub, unrelated to our documentation, please [contact GitHub Support directly](https://support.github.com/contact). Any issues, discussions, or pull requests opened here requesting support will be given information about how to contact GitHub Support, then closed and locked.
60 |
61 | If you're having trouble with your GitHub account, contact [Support](https://support.github.com/contact).
62 |
63 | ## Starting with an issue
64 | You can browse existing issues to find something that needs help!
65 |
66 | ### Labels
67 | Labels can help you find an issue you'd like to help with.
68 |
69 | - The [`help wanted` label](https://github.com/webdevnerdstuff/vuetify-resize-drawer/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement) is for problems or updates that anyone in the community can start working on.
70 | - The [`documentation` label](https://github.com/webdevnerdstuff/vuetify-resize-drawer/issues?q=is%3Aopen+is%3Aissue+label%3Adocumentation) is for problems or updates relating to the README.MD documentation.
71 | - The [`bug` label](https://github.com/webdevnerdstuff/vuetify-resize-drawer/issues?q=is%3Aopen+is%3Aissue+label%3Abug) is for problems with the code and bugs.
72 | - The [`enhancement` label](https://github.com/webdevnerdstuff/vuetify-resize-drawer/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement) is for
73 | suggestions to improve the code or adding of additional features.
74 |
75 | ## Opening a pull request
76 | You can use the GitHub user interface for some small changes, like fixing a typo or updating a readme. You can also fork the repo and then clone it locally, to view changes and run your tests on your machine.
77 |
78 | ## Working in the Vue Unicorn Log repository
79 | Here's some information that might be helpful while working on a Vue Unicorn Log PR:
80 |
81 |
82 |
83 | ## Reviewing
84 | We (usually the Vue Unicorn Log team) review every single PR. The purpose of reviews is to create the best content we can for people who use GitHub.
85 |
86 | - Reviews are always respectful, acknowledging that everyone did the best possible job with the knowledge they had at the time.
87 | - Reviews discuss content, not the person who created it.
88 | - Reviews are constructive and start conversation around feedback.
89 |
90 | ### Self review
91 | You should always review your own PR first.
92 |
93 |
95 |
96 | ### Suggested changes
97 | We may ask for changes to be made before a PR can be merged, either using [suggested changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request) or pull request comments. You can apply suggested changes directly through the UI. You can make any other changes in your fork, then commit them to your branch.
98 |
99 | As you update your PR and apply changes, mark each conversation as [resolved](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request#resolving-conversations).
100 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | buy_me_a_coffee: webdevnerdstuff
2 | custom: ["paypal.me/webdevnerdstuff"]
3 | patreon: webdevnerdstuff
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/BUG_REPORT.yaml:
--------------------------------------------------------------------------------
1 | name: Bug Report
2 | description: File a bug report
3 | title: "[Bug]: "
4 | labels: ["bug", "triage"]
5 | assignees:
6 | - webdevnerdstuff
7 | body:
8 | - type: markdown
9 | attributes:
10 | value: |
11 | Thanks for taking the time to fill out this bug report!
12 | - type: textarea
13 | id: bug-description
14 | attributes:
15 | label: Bug description
16 | description: What happened?
17 | validations:
18 | required: true
19 | - type: textarea
20 | id: steps
21 | attributes:
22 | label: Steps to reproduce
23 | description: Which steps do we need to take to reproduce this error?
24 | placeholder: "Steps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'"
25 | validations:
26 | required: true
27 | - type: textarea
28 | id: logs
29 | attributes:
30 | label: Relevant log output
31 | description: If applicable, please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
32 | render: shell
33 | - type: textarea
34 | id: additional-context
35 | attributes:
36 | label: Additional context
37 | description: Add any other context about the problem here.
38 | - type: checkboxes
39 | id: terms
40 | attributes:
41 | label: Code of Conduct
42 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/webdevnerdstuff/vuetify3-resize-drawer/blob/main/CODE_OF_CONDUCT.md)
43 | options:
44 | - label: I agree to follow this project's Code of Conduct
45 | required: true
46 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yaml:
--------------------------------------------------------------------------------
1 | name: Feature Request
2 | description: Suggest an idea for this project
3 | title: "[Feature Request]: "
4 | labels: ["feature request"]
5 | assignees:
6 | - webdevnerdstuff
7 | body:
8 | - type: markdown
9 | attributes:
10 | value: |
11 | Thanks for taking the time to fill out this request!
12 | - type: textarea
13 | id: related-to-a-problem
14 | attributes:
15 | label: Is your feature request related to a problem? Please describe.
16 | description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
17 | validations:
18 | required: true
19 | - type: textarea
20 | id: feature-desc
21 | attributes:
22 | label: Describe the solution you'd like
23 | description: A clear and concise description of what you want to happen.
24 | validations:
25 | required: true
26 | - type: textarea
27 | id: alternatives
28 | attributes:
29 | label: Describe alternatives you've considered
30 | description: A clear and concise description of any alternative solutions or features you've considered.
31 | validations:
32 | required: true
33 | - type: textarea
34 | id: additional-context
35 | attributes:
36 | label: Additional context
37 | description: Add any other context about the problem here.
38 | - type: checkboxes
39 | id: terms
40 | attributes:
41 | label: Code of Conduct
42 | description: By submitting this request, you agree to follow our [Code of Conduct](https://github.com/webdevnerdstuff/vuetify3-resize-drawer/blob/main/CODE_OF_CONDUCT.md)
43 | options:
44 | - label: I agree to follow this project's Code of Conduct
45 | required: true
46 |
--------------------------------------------------------------------------------
/.github/workflows/inactive-issues.yaml:
--------------------------------------------------------------------------------
1 | name: Close inactive issues
2 | on:
3 | schedule:
4 | - cron: "30 1 * * *"
5 |
6 | jobs:
7 | close-issues:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/stale@v3
11 | with:
12 | days-before-issue-stale: 30
13 | days-before-issue-close: 14
14 | stale-issue-label: "stale"
15 | stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
16 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
17 | days-before-pr-stale: -1
18 | days-before-pr-close: -1
19 | repo-token: ${{ secrets.GITHUB_TOKEN }}
20 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | # Simple workflow for deploying static content to GitHub Pages
2 | name: Deploy to GitHub Pages
3 |
4 | on:
5 | push:
6 | branches: [ main ]
7 |
8 | permissions:
9 | contents: write
10 | pages: write
11 | id-token: write
12 |
13 | jobs:
14 | # Build job
15 | build:
16 | runs-on: ubuntu-latest
17 | steps:
18 | - name: Checkout
19 | uses: actions/checkout@v4
20 |
21 | - name: Setup pnpm
22 | uses: pnpm/action-setup@v4
23 | with:
24 | version: 9
25 |
26 | - name: Install dependencies
27 | run: pnpm install --g gh-pages && pnpm install
28 |
29 | - name: Build Docs
30 | run: pnpm build:docs
31 |
32 | - name: Display structure of built files
33 | run: ls -R ./docs
34 |
35 | - name: Upload artifact
36 | uses: actions/upload-pages-artifact@v3
37 | with:
38 | name: github-pages
39 | path: ./docs
40 |
41 | # Deploy job
42 | deploy:
43 | environment:
44 | name: github-pages
45 | url: ${{ steps.deployment.outputs.page_url }}
46 | runs-on: ubuntu-latest
47 | needs: build
48 |
49 | steps:
50 | - name: Checkout
51 | uses: actions/checkout@v4
52 |
53 | - name: Download the build folder
54 | uses: actions/download-artifact@v4
55 | with:
56 | name: github-pages
57 | path: ./docs
58 |
59 | - name: Display structure of downloaded files
60 | run: ls -R ./docs
61 |
62 | - name: Deploy to GitHub Pages
63 | id: deployment
64 | uses: actions/deploy-pages@v4
65 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | docs
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | .history/*
18 | !.vscode/extensions.json
19 | .idea
20 | .DS_Store
21 | *.suo
22 | *.ntvs*
23 | *.njsproj
24 | *.sln
25 | *.sw?
26 |
27 | .eslintcache
28 |
29 | src/plugin/**/*.bk.*
30 |
31 |
32 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | npx lint-staged && npm run test:build
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .github
3 | .history
4 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | node-version=22.15.0
2 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 22.15.0
2 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: true,
3 | singleQuote: true,
4 | trailingComma: 'all',
5 | };
6 |
--------------------------------------------------------------------------------
/.release-it.json:
--------------------------------------------------------------------------------
1 | {
2 | "git": {
3 | "commitMessage": "chore: release ${version}",
4 | "tagName": "v${version}"
5 | },
6 | "npm": {
7 | "publish": true
8 | },
9 | "github": {
10 | "release": true,
11 | "releaseName": "v${version}"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to the "vuetify-resize-drawer" plugin will be documented in this file.
3 |
4 | ## v3.2.0
5 | 2024-10-14
6 | [main] (@webdevnerdstuff)
7 | * Adding `bottom` and `top` location support
8 | * Fix issue with handle mouse position
9 | * Change `width-snap-back` prop to `snap-back` (`width-snap-back` is backwards compatible)
10 | * Add `max-height` and `min-height` props
11 | * Update packages
12 |
13 | ## v3.1.6
14 | 2024-03-14
15 | [main] (@webdevnerdstuff)
16 | * Add another null check
17 |
18 | ## v3.1.5
19 | 2024-03-14
20 | [main] (@webdevnerdstuff)
21 | * Add null check
22 |
23 | ## v3.1.4
24 | 2024-03-13
25 | [main] (@webdevnerdstuff)
26 | * Change component to use `defineAsyncComponent`
27 |
28 | ## v3.1.3
29 | 2024-02-26
30 | [main] (@webdevnerdstuff)
31 | * Fix external rollup config
32 | * Update packages
33 |
34 | ## v3.1.2
35 | 2024-01-11
36 | [main] (@webdevnerdstuff)
37 | * Minor style adjustments
38 | * Update packages
39 |
40 | ## v3.1.0
41 | 2024-01-09
42 | [main] (@webdevnerdstuff)
43 | * Fix typescript issues
44 | * Reorganizing
45 | * Add vitest
46 | * Update node version to v20.10.0
47 |
48 | ## v3.0.0
49 | 2023-11-10
50 | [main] (@webdevnerdstuff)
51 | * Merging in vuetify3-resize-drawer
52 | * Changing package to an npm organization package
53 | * Updating release version to be more inline with vue/vuetify 3 versions
54 | * The change from v2.1.1 to v3.0.0 is not a breaking change, only a version change
55 |
56 | ## v2.1.1
57 | 2023-10-02
58 | [main] (@webdevnerdstuff)
59 | * Fix build config to exclude Vuetify styles from being included in the build
60 |
61 | ## v2.1.0
62 | 2023-08-24
63 | [main] (@webdevnerdstuff)
64 | * Add @container classes to provide Vuetify grid class support for the drawer content, enhancing responsiveness without being constrained by screen size.
65 | * Add Grid System section to documentation
66 | * Add touch support for mobile devices
67 | * Add emitted events for `drawer:mouseenter`, `drawer:mouseleave`, `handle:touchend`, `handle:touchmove`, and `handle:touchstart`
68 | * Add `touchless` support to hide handle on mobile devices. This prop does not function the same as the prop which is not supported.
69 | * Fix offsetWidth value that was returning the resized value (possible breaking change if you used emitted events to get the offsetWidth value)
70 | * Fix issue with resized values being negative, returning positive values
71 | * Fix width values that passed min/max width boundaries
72 | * Fix issue with incorrect widths when min/max values set as percentage
73 | * Fix issue with widthSnapBack prop preventing drawer resizing
74 | * Fix `close` event not being emitted when drawer is closed
75 | * Fix `update:modelValue` event not being emitted when model value was updated
76 | * Update Vuetify package to v3.3.14
77 | * Update other packages to latest versions
78 | * Update Playground template to include grid drawer for testing
79 |
80 | ## v2.0.3
81 | 2023-08-01
82 | [main] (@webdevnerdstuff)
83 | * Add check for fasvg
84 | * Add important to fa icon size
85 |
86 | ## v2.0.2
87 | 2023-06-02
88 | [main] (@webdevnerdstuff)
89 | * Update to turn off transition duration when resizing
90 |
91 | ## v2.0.0
92 | 2023-06-02
93 | [main] (@webdevnerdstuff)
94 | * Update `handleColor` prop to a string from an object. Now accepts various color values (variables, theme color names, color names, hex, rgb, rgba, etc)
95 | * Add `maxWidth` and `minWidth` props to allow for a minimum and maximum width for the drawer.
96 | * Add `snapBackWidth` prop to allow for the drawer to snap back to the `maxWidth`/`minWidth` when the drawer is closed.
97 | * Add `location` prop to allow `start` and `end` drawer locations
98 | * Add `v-icon` to be used as handle with a new prop `handleIcon`
99 | * Add better support for `mdi` and `fa` icons
100 | * Remove `top-icon` slot
101 | * Update styles
102 | * Add stronger typings
103 | * Update how typings and props are defined
104 | * Update Vuetify to current version `3.3.6`
105 | * Improve organization of code
106 | * Update documentation
107 | * Add Developer Playground
108 |
109 | ## v1.1.2
110 | 2023-07-27
111 | [main] Initial release (WebDevNerdStuff)
112 | * Fix: Add missing transform to fix drawer right direction
113 |
114 | ## v1.1.1
115 | 2023-07-27
116 | [main] Initial release (WebDevNerdStuff)
117 | * Fix build missing vuetify imports
118 |
119 | ## v1.1.0
120 | 2023-07-26
121 | [main] Initial release (WebDevNerdStuff)
122 | * Update component to be more like v3 version
123 | * Change handle to v-icon
124 | * Change handle positions to be more like v3 version
125 | * Add handle props
126 | * Add min/max width props
127 | * Remove overflow prop
128 | * Update npm packages
129 | * There may be some breaking changes in this release
130 |
131 | ## v1.0.0
132 | 2022-05-23
133 | [main] Initial release (WebDevNerdStuff)
134 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | webdevnerdstuff@gmail.com.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 WebDevNerdStuff
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Vuetify Resize Drawer
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | ## Description
21 |
22 | The `vuetify-resize-drawer` component extends the functionality of the [v-navigation-drawer](https://vuetifyjs.com/en/components/navigation-drawers/) so that it is resizable by the user.
23 |
24 |
25 | ## Installation
26 |
27 | Using [pnpm](https://pnpm.io/):
28 | ```
29 | pnpm add @wdns/vuetify-resize-drawer
30 | ```
31 |
32 | Using npm:
33 | ```
34 | npm i @wdns/vuetify-resize-drawer
35 | ```
36 |
37 | ## Documentation
38 |
39 | [Documentation & Demo](https://webdevnerdstuff.github.io/vuetify-resize-drawer/)
40 |
41 | ## Dependencies
42 |
43 | [Vuetify v3](https://vuetifyjs.com/)
44 | [Vue 3](https://vuejs.org/)
45 |
46 |
47 | ## Change Log
48 |
49 | [CHANGELOG](https://github.com/webdevnerdstuff/vuetify-resize-drawer/blob/master/CHANGELOG.md)
50 |
51 |
52 | ## License
53 |
54 | Copyright (c) 2023 WebDevNerdStuff
55 | Licensed under the [MIT license](https://github.com/webdevnerdstuff/vuetify-resize-drawer/blob/master/LICENSE.md).
56 |
57 |
58 | ## Legal
59 |
60 | Vuetify and the Vuetify logo are trademarks of Vuetify. This component was not created or endorsed by Vuetify.
61 |
--------------------------------------------------------------------------------
/dist/VResizeDrawer-BeVN9VIZ.mjs:
--------------------------------------------------------------------------------
1 | import o from "./vuetify-resize-drawer.es.js";
2 | /**
3 | * @name @wdns/vuetify-resize-drawer
4 | * @version 3.2.1
5 | * @description The vuetify-resize-drawer component extends the functionality of the v-navigation-drawer so that it is resizable by the user.
6 | * @author WebDevNerdStuff & Bunnies... lots and lots of bunnies! (https://webdevnerdstuff.com)
7 | * @copyright Copyright 2025, WebDevNerdStuff
8 | * @homepage https://webdevnerdstuff.github.io/vuetify-resize-drawer/
9 | * @repository https://github.com/webdevnerdstuff/vuetify-resize-drawer
10 | * @license MIT License
11 | */
12 | export {
13 | o as default
14 | };
15 |
--------------------------------------------------------------------------------
/dist/VResizeDrawer-CKGjiLW1.js:
--------------------------------------------------------------------------------
1 | "use strict";/**
2 | * @name @wdns/vuetify-resize-drawer
3 | * @version 3.2.1
4 | * @description The vuetify-resize-drawer component extends the functionality of the v-navigation-drawer so that it is resizable by the user.
5 | * @author WebDevNerdStuff & Bunnies... lots and lots of bunnies! (https://webdevnerdstuff.com)
6 | * @copyright Copyright 2025, WebDevNerdStuff
7 | * @homepage https://webdevnerdstuff.github.io/vuetify-resize-drawer/
8 | * @repository https://github.com/webdevnerdstuff/vuetify-resize-drawer
9 | * @license MIT License
10 | */Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./vuetify-resize-drawer.cjs.js");exports.default=e.default;
11 |
--------------------------------------------------------------------------------
/dist/plugin/composables/classes.d.ts:
--------------------------------------------------------------------------------
1 | import { UseDrawerClasses, UseHandleContainerClasses, UseHandleIconClasses } from '../types';
2 | export declare const useDrawerClasses: UseDrawerClasses;
3 | export declare const useHandleContainerClasses: UseHandleContainerClasses;
4 | export declare const useHandleIconClasses: UseHandleIconClasses;
5 |
--------------------------------------------------------------------------------
/dist/plugin/composables/colors.d.ts:
--------------------------------------------------------------------------------
1 | import { ThemeInstance } from 'vuetify';
2 | /**
3 | * Converts single color
4 | */
5 | export declare const useGetColor: (color: string, theme: ThemeInstance) => string;
6 |
--------------------------------------------------------------------------------
/dist/plugin/composables/helpers.d.ts:
--------------------------------------------------------------------------------
1 | import { Props, UseConvertToUnit } from '../types';
2 | export declare const useConvertToUnit: UseConvertToUnit;
3 | export declare const useConvertToNumber: (val: string | number) => number;
4 | export declare const useUnitToPx: (valueWithUnit: Props["handleIconSize"]) => number;
5 |
--------------------------------------------------------------------------------
/dist/plugin/composables/icons.d.ts:
--------------------------------------------------------------------------------
1 | import { UseGetIcon } from '../types';
2 | export declare const useGetIcon: UseGetIcon;
3 |
--------------------------------------------------------------------------------
/dist/plugin/composables/storage.d.ts:
--------------------------------------------------------------------------------
1 | import { UseSetStorage } from '../types';
2 | export declare function useGetStorage(storageType: string, storageName: string): string | null;
3 | export declare const useSetStorage: UseSetStorage;
4 |
--------------------------------------------------------------------------------
/dist/plugin/composables/styles.d.ts:
--------------------------------------------------------------------------------
1 | import { UseDrawerStyles, UseHandleContainerStyles, UseHandleIconStyles } from '../types';
2 | export declare const iconSizes: {
3 | default: string;
4 | large: string;
5 | small: string;
6 | 'x-large': string;
7 | 'x-small': string;
8 | };
9 | export declare const useDrawerStyles: UseDrawerStyles;
10 | export declare const useHandleContainerStyles: UseHandleContainerStyles;
11 | export declare const useHandleIconStyles: UseHandleIconStyles;
12 |
--------------------------------------------------------------------------------
/dist/plugin/index.d.ts:
--------------------------------------------------------------------------------
1 | import { GlobalOptions } from './types';
2 | import { App } from 'vue';
3 | import { default as VResizeDrawer } from './VResizeDrawer.vue';
4 | export declare const globalOptions: unique symbol;
5 | export declare function createVResizeDrawer(options?: GlobalOptions): {
6 | install: (app: App) => void;
7 | };
8 | export default VResizeDrawer;
9 | export { VResizeDrawer, };
10 |
--------------------------------------------------------------------------------
/dist/plugin/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import { CSSProperties, MaybeRef } from 'vue';
2 | import { IconOptions, ThemeInstance } from 'vuetify';
3 | import { VIcon, VNavigationDrawer } from 'vuetify/components';
4 | import { default as VResizeDrawer } from '../VResizeDrawer.vue';
5 | export * from '../index';
6 | export type Classes = {
7 | [key: string]: boolean | undefined;
8 | };
9 | export type EmitEventNames = 'handle:click' | 'handle:dblclick' | 'handle:drag' | 'handle:mousedown' | 'handle:mouseup' | 'handle:touchend' | 'handle:touchmove' | 'handle:touchstart';
10 | export type StorageType = 'local' | 'session';
11 | export type HandlePositions = 'bottom' | 'border' | 'center' | 'top';
12 | export type DrawerLocations = 'bottom' | 'end' | 'start' | 'left' | 'right' | 'top' | undefined;
13 | type Height = number | string | undefined;
14 | export type HEXColor = string;
15 | export type HSLColor = [number, number, number];
16 | export type RGBColor = [number, number, number];
17 | export interface Props {
18 | absolute?: VNavigationDrawer['absolute'];
19 | expandOnHover?: VNavigationDrawer['expandOnHover'];
20 | floating?: VNavigationDrawer['floating'];
21 | handleBorderWidth?: number | string;
22 | handleColor?: string | undefined;
23 | handleIcon?: string | undefined;
24 | handleIconSize?: VIcon['size'];
25 | handlePosition?: HandlePositions;
26 | height?: Height;
27 | image?: VNavigationDrawer['image'];
28 | location?: DrawerLocations;
29 | maxHeight?: Height;
30 | maxWidth?: VNavigationDrawer['width'];
31 | minHeight?: Height;
32 | minWidth?: VNavigationDrawer['width'];
33 | modelValue?: VNavigationDrawer['modelValue'];
34 | name?: VNavigationDrawer['name'];
35 | rail?: VNavigationDrawer['rail'];
36 | railWidth?: VNavigationDrawer['railWidth'];
37 | resizable?: boolean | undefined;
38 | saveHeight?: boolean | undefined;
39 | saveWidth?: boolean | undefined;
40 | snapBack?: boolean | undefined;
41 | storageName?: string | undefined;
42 | storageType?: StorageType;
43 | tag?: VNavigationDrawer['tag'];
44 | temporary?: VNavigationDrawer['temporary'];
45 | theme?: VNavigationDrawer['theme'];
46 | touchless?: boolean | undefined;
47 | width?: VNavigationDrawer['width'];
48 | widthSnapBack?: boolean | undefined;
49 | }
50 | export interface GlobalOptions extends Props {
51 | }
52 | export interface UseConvertToUnit {
53 | (options: {
54 | unit?: string;
55 | value: string | number;
56 | }): string | void;
57 | }
58 | export interface UseSetStorage {
59 | (options: {
60 | action?: string;
61 | resizedAmount?: MaybeRef;
62 | resizedWidth?: MaybeRef;
63 | storageType?: StorageType;
64 | storageName?: Props['storageName'];
65 | saveAmount?: Props['saveWidth'] | Props['saveHeight'];
66 | rail?: Props['rail'];
67 | }): void;
68 | }
69 | export interface UseDrawerClasses {
70 | (options: {
71 | absolute?: Props['absolute'];
72 | expandOnHover?: Props['expandOnHover'];
73 | floating?: Props['floating'];
74 | isMouseover?: MaybeRef;
75 | location?: Props['location'];
76 | rail?: Props['rail'];
77 | railWidth?: Props['railWidth'];
78 | temporary?: Props['temporary'];
79 | }): object;
80 | }
81 | export interface UseHandleContainerClasses {
82 | (options: {
83 | handlePosition?: Props['handlePosition'];
84 | drawerLocation?: Props['location'];
85 | }): object;
86 | }
87 | export interface UseHandleIconClasses {
88 | (options: {
89 | handlePosition?: Props['handlePosition'];
90 | iconOptions?: IconOptions;
91 | isUserIcon?: boolean;
92 | drawerLocation?: Props['location'];
93 | }): object;
94 | }
95 | export interface UseDrawerStyles {
96 | (options: {
97 | isMouseDown?: MaybeRef;
98 | location?: Props['location'];
99 | maxHeight?: Props['maxHeight'];
100 | minHeight?: Props['minHeight'];
101 | maxWidth?: Props['maxWidth'];
102 | minWidth?: Props['minWidth'];
103 | rail?: Props['rail'];
104 | railWidth?: Props['railWidth'];
105 | resizedAmount: MaybeRef;
106 | snapBack?: Props['snapBack'];
107 | }): CSSProperties;
108 | }
109 | export interface UseHandleContainerStyles {
110 | (options: {
111 | borderWidth?: Props['handleBorderWidth'];
112 | handleColor?: Props['handleColor'];
113 | iconSize?: Props['handleIconSize'];
114 | iconSizeUnit?: number;
115 | location?: Props['location'];
116 | position?: Props['handlePosition'];
117 | theme: ThemeInstance;
118 | }): CSSProperties;
119 | }
120 | export interface UseHandleIconStyles {
121 | (options: {
122 | color?: Props['handleColor'];
123 | theme: ThemeInstance;
124 | }): CSSProperties;
125 | }
126 | export interface UseGetIcon {
127 | (options: {
128 | icon: Props['handleIcon'];
129 | iconOptions: IconOptions | undefined;
130 | name: Props['handlePosition'];
131 | }): Props['handleIcon'];
132 | }
133 | declare module 'vue' {
134 | interface ComponentCustomProperties {
135 | }
136 | interface GlobalComponents {
137 | VResizeDrawer: typeof VResizeDrawer;
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/dist/plugin/utils/globals.d.ts:
--------------------------------------------------------------------------------
1 | declare const defaultWidth = 256;
2 | declare const componentName = "v-resize-drawer";
3 | export { defaultWidth, componentName, };
4 |
--------------------------------------------------------------------------------
/dist/plugin/utils/props.d.ts:
--------------------------------------------------------------------------------
1 | import { Props } from '../types';
2 | export declare const AllProps: Props;
3 |
--------------------------------------------------------------------------------
/dist/scss/_mixins.scss:
--------------------------------------------------------------------------------
1 | @use 'sass:map';
2 | @use 'sass:math';
3 | @use 'vuetify/tools';
4 | @use 'vuetify/settings';
5 |
6 | /*
7 | * @source vuetify/packages/vuetify/src/styles/tools/_functions.sass
8 | */
9 | @function breakpoint-min($name, $breakpoints) {
10 | $min: map.get($breakpoints, $name);
11 | @return if($min !=0, $min, null);
12 | }
13 |
14 | /*
15 | * @source vuetify/packages/vuetify/src/styles/tools/_display.sass
16 | */
17 | @mixin media-breakpoint-up($name, $breakpoints: settings.$grid-breakpoints) {
18 | $min: breakpoint-min($name, $breakpoints);
19 |
20 | @if $min {
21 | @container v-resize-drawer (min-width: #{$min}) {
22 | @content;
23 | }
24 | }
25 | }
26 |
27 | /*
28 | * @source vuetify/packages/vuetify/src/components/VGrid/_mixins.sass
29 | */
30 | @mixin make-col($size, $columns: settings.$grid-columns) {
31 | flex: 0 0 math.percentage(math.div($size, $columns));
32 | max-width: math.percentage(math.div($size, $columns));
33 | }
34 |
35 | @mixin make-grid-columns($columns: settings.$grid-columns, $gutter: settings.$grid-gutter, $breakpoints: settings.$grid-breakpoints) {
36 | @each $breakpoint in map.keys($breakpoints) {
37 | $infix: tools.breakpoint-infix($breakpoint, $breakpoints);
38 |
39 | @include media-breakpoint-up($breakpoint, $breakpoints) {
40 | @for $i from 1 through $columns {
41 | .v-col#{$infix}-#{$i} {
42 | @include make-col($i, $columns);
43 | }
44 | }
45 | }
46 | }
47 | }
48 |
49 | /*
50 | * @source vuetify/packages/vuetify/src/styles/utilities/_display.sass
51 | */
52 | @mixin make-grid-columns-hidden() {
53 | $display: map.get(settings.$utilities, 'display');
54 |
55 | @each $size,
56 | $media_query in settings.$display-breakpoints {
57 | @container v-resize-drawer #{$media_query} {
58 | .hidden-#{$size} {
59 | display: none !important;
60 | }
61 | }
62 |
63 | @container v-resize-drawer #{$media_query} {
64 | @each $val in map.get($display, 'values') {
65 | .#{map.get($display, 'class')}-#{$size}-#{$val} {
66 | display: $val !important;
67 | }
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/dist/scss/main.scss:
--------------------------------------------------------------------------------
1 | @use 'sass:map';
2 | @use './mixins' as *;
3 | @use 'vuetify/settings';
4 |
5 | .v-resize-drawer {
6 | container-name: v-resize-drawer;
7 | container-type: inline-size;
8 |
9 | &--handle {
10 | &-container {
11 | align-items: center;
12 | cursor: grab;
13 | display: flex;
14 | justify-content: center;
15 | position: absolute;
16 | z-index: 1;
17 |
18 | &-icon {
19 | &-parent {
20 | &-end,
21 | &-right {
22 | left: initial;
23 | right: 0;
24 | }
25 | }
26 |
27 | &-center {
28 | &-end,
29 | &-right {
30 | transform: rotate(180deg);
31 | }
32 | }
33 |
34 | &-user-icon {
35 | transform: none;
36 | }
37 |
38 | &-fa {
39 | font-size: .7rem !important;
40 | }
41 | }
42 |
43 | &-parent {
44 | &-left,
45 | &-start,
46 | &-undefined {
47 | right: 0;
48 | }
49 |
50 | &-end,
51 | &-right {
52 | left: 0;
53 | }
54 | }
55 |
56 | &-position {
57 | &-top {
58 | top: 0;
59 | }
60 |
61 | &-center {
62 | top: 50%;
63 | transform: translateY(-50%);
64 | }
65 |
66 | &-bottom {
67 | bottom: 0;
68 | }
69 |
70 | &-border {
71 | cursor: col-resize;
72 | height: 100%;
73 | top: 0;
74 | width: 8px;
75 | }
76 | }
77 | }
78 | }
79 |
80 | &--bottom {
81 | transition: min-height 0.3s;
82 |
83 | .v-resize-drawer--handle-container {
84 | &-position {
85 | &-center {
86 | left: 50%;
87 | top: 0;
88 | transform: translateX(-50%);
89 | }
90 |
91 | &-border {
92 | cursor: row-resize;
93 | left: 0%;
94 | top: 0 !important;
95 | width: 100% !important;
96 | }
97 | }
98 | }
99 | }
100 |
101 | &--top {
102 | top: 0 !important;
103 | transition: min-height 0.3s;
104 |
105 | .v-resize-drawer--handle-container {
106 | &-position {
107 | &-center {
108 | bottom: 1px;
109 | left: 50%;
110 | top: unset;
111 | transform: translateX(-50%);
112 | }
113 |
114 | &-border {
115 | bottom: 0 !important;
116 | cursor: row-resize;
117 | left: 0%;
118 | top: unset;
119 | width: 100% !important;
120 | }
121 | }
122 | }
123 | }
124 |
125 | &--bottom,
126 | &--top {
127 | .v-navigation-drawer__content {
128 | flex: 1 1 auto;
129 | position: relative;
130 | }
131 | }
132 | }
133 |
134 |
135 | @container v-resize-drawer (width > #{map.get(settings.$grid-breakpoints, 'xs')}) and (max-width: #{map.get(settings.$grid-breakpoints, 'sm') - 0.02}) {
136 | .v-col {
137 | &-xs-12 {
138 | flex: 0 0 100% !important;
139 | flex-basis: 0;
140 | flex-grow: 1;
141 | max-width: 100% !important;
142 | }
143 | }
144 | }
145 |
146 | // Grid Columns //
147 | @include make-grid-columns;
148 |
149 | // Hidden & d-#{size}-#{display-value} Columns //
150 | @include make-grid-columns-hidden;
151 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | // eslint.config.js
2 | import eslint from '@eslint/js';
3 | import wdnsConfig from '@wdns/eslint-config-wdns';
4 | import tseslint from 'typescript-eslint';
5 | import AutoImportJson from './.eslintrc-auto-import.json' with { type: 'json' };
6 |
7 |
8 | export default tseslint.config(
9 | eslint.configs.recommended,
10 | ...tseslint.configs.recommended,
11 | ...wdnsConfig,
12 | {
13 | ignores: [
14 | '**/cypress/**',
15 | '**/*.cy.ts',
16 | 'cypress.config.ts',
17 | 'vite.build.config.mts',
18 | 'vite.config.mts',
19 | 'src/playground/configs/templates/PlaygroundPage.vue',
20 | ],
21 | },
22 | {
23 | name: 'app/files-to-lint',
24 | files: ['**/*.{ts,mts,tsx,vue}'],
25 | languageOptions: {
26 | ...AutoImportJson,
27 | ecmaVersion: 'latest',
28 | sourceType: 'module',
29 | parserOptions: {
30 | parser: tseslint.parser,
31 | project: ['./tsconfig.json', './tsconfig.node.json'],
32 | extraFileExtensions: ['.vue'],
33 | sourceType: 'module',
34 | },
35 | },
36 | plugins: {
37 | 'typescript-eslint': tseslint.plugin,
38 | },
39 | settings: {
40 | 'import/resolver': {
41 | typescript: {
42 | alwaysTryTypes: true,
43 | project: './tsconfig.json',
44 | },
45 | },
46 | },
47 | },
48 | );
49 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
17 |
18 |
19 |
24 |
28 | Vuetify 3 Resize Drawer
29 |
30 |
34 |
38 |
42 |
46 |
50 |
54 |
55 |
56 |
57 |
58 |
62 |
63 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@wdns/vuetify-resize-drawer",
3 | "version": "3.2.1",
4 | "description": "The vuetify-resize-drawer component extends the functionality of the v-navigation-drawer so that it is resizable by the user.",
5 | "private": false,
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "main": "dist/vuetify-resize-drawer.cjs.js",
10 | "module": "dist/vuetify-resize-drawer.es.js",
11 | "types": "dist/plugin/types/index.d.ts",
12 | "scripts": {
13 | "dev": "NODE_OPTIONS='--no-warnings' vite",
14 | "watch": "pnpm dev",
15 | "play": "sh src/playground/configs/build.sh && NODE_ENV=playground pnpm dev",
16 | "build": "tsc -p tsconfig.build.json && npm run test:build && vite build --config vite.build.config.mts",
17 | "build:docs": "vite build",
18 | "predeploy": "npm run build",
19 | "deploy": "gh-pages -d docs",
20 | "prepublishOnly": "npm run build",
21 | "lint": "eslint src/**/*.{ts,vue} --max-warnings 20",
22 | "prepare": "husky install",
23 | "test:dev": "NODE_OPTIONS='--no-warnings' vitest",
24 | "test:all": "vitest --run",
25 | "test:build": "vitest --run --bail 1",
26 | "cy:run": "npx cypress run --headless --browser electron",
27 | "cy:run:dev": "cypress run --browser chrome",
28 | "cy:open": "cypress open --browser chrome"
29 | },
30 | "lint-staged": {
31 | "src/**/*.{js,ts,vue}": [
32 | "npm run lint"
33 | ]
34 | },
35 | "author": "WebDevNerdStuff & Bunnies... lots and lots of bunnies! (https://webdevnerdstuff.com)",
36 | "funding": [
37 | {
38 | "type": "paypal",
39 | "url": "https://paypal.me/webdevnerdstuff"
40 | },
41 | {
42 | "type": "patreon",
43 | "url": "https://www.patreon.com/WebDevNerdStuff"
44 | }
45 | ],
46 | "license": "MIT",
47 | "files": [
48 | "dist/*",
49 | "LICENSE.md",
50 | "README.md"
51 | ],
52 | "repository": "https://github.com/webdevnerdstuff/vuetify-resize-drawer",
53 | "bugs": {
54 | "url": "https://github.com/webdevnerdstuff/vuetify-resize-drawer/issues"
55 | },
56 | "homepage": "https://webdevnerdstuff.github.io/vuetify-resize-drawer/",
57 | "keywords": [
58 | "vuetify-resize-drawer",
59 | "vuetifyResizeDrawer",
60 | "v-resize-drawer",
61 | "vResizeDrawer",
62 | "vuetify",
63 | "vuetify3",
64 | "navigation drawer",
65 | "v-navigation-drawer",
66 | "VNavigationDrawer",
67 | "api",
68 | "drawer",
69 | "resize",
70 | "resizable",
71 | "vue",
72 | "vue2",
73 | "component",
74 | "javascript",
75 | "webdevnerdstuff",
76 | "wdns"
77 | ],
78 | "dependencies": {
79 | "vue": "^3.5.12",
80 | "vuetify": "^3.7.4"
81 | },
82 | "devDependencies": {
83 | "@eslint/js": "^9.14.0",
84 | "@fortawesome/fontawesome-svg-core": "^6.6.0",
85 | "@fortawesome/free-brands-svg-icons": "^6.6.0",
86 | "@fortawesome/free-regular-svg-icons": "^6.6.0",
87 | "@fortawesome/free-solid-svg-icons": "^6.6.0",
88 | "@fortawesome/vue-fontawesome": "^3.0.8",
89 | "@mdi/font": "^7.4.47",
90 | "@rollup/plugin-commonjs": "^28.0.1",
91 | "@rollup/plugin-node-resolve": "^15.3.0",
92 | "@rollup/plugin-terser": "^0.4.4",
93 | "@stylistic/stylelint-plugin": "^3.1.1",
94 | "@types/jest": "^29.5.14",
95 | "@types/node": "^22.9.0",
96 | "@typescript-eslint/eslint-plugin": "^8.13.0",
97 | "@typescript-eslint/parser": "^8.13.0",
98 | "@vitejs/plugin-vue": "^5.1.4",
99 | "@vue/cli-service": "^5.0.8",
100 | "@vue/eslint-config-typescript": "^14.1.3",
101 | "@vue/test-utils": "^2.4.6",
102 | "@wdns/eslint-config-wdns": "^1.0.11",
103 | "@wdns/stylelint-config-wdns": "^1.0.0",
104 | "@wdns/vue-code-block": "^2.3.3",
105 | "autoprefixer": "^10.4.20",
106 | "cypress": "^13.15.2",
107 | "eslint": "^9.14.0",
108 | "eslint-config-prettier": "^9.1.0",
109 | "eslint-import-resolver-typescript": "^3.6.3",
110 | "eslint-plugin-vue": "^9.30.0",
111 | "gh-pages": "^6.2.0",
112 | "husky": "^9.1.6",
113 | "jsdom": "^25.0.1",
114 | "lint-staged": "^15.2.10",
115 | "pinia": "^2.2.6",
116 | "postcss": "^8.4.48",
117 | "postcss-html": "^1.7.0",
118 | "postcss-scss": "^4.0.9",
119 | "prettier": "^3.3.3",
120 | "prismjs": "^1.29.0",
121 | "roboto-fontface": "^0.10.0",
122 | "rollup": "^4.25.0",
123 | "rollup-plugin-polyfill-node": "^0.13.0",
124 | "rollup-plugin-postcss": "^4.0.2",
125 | "rollup-plugin-scss": "^4.0.0",
126 | "rollup-plugin-typescript2": "^0.36.0",
127 | "sass": "^1.80.6",
128 | "stylelint": "^16.10.0",
129 | "stylelint-config-standard": "^36.0.1",
130 | "stylelint-order": "^6.0.4",
131 | "stylelint-scss": "^6.8.1",
132 | "typescript": "^5.6.3",
133 | "typescript-eslint": "^8.13.0",
134 | "unplugin-auto-import": "^0.18.3",
135 | "vite": "^5.4.10",
136 | "vite-plugin-css-injected-by-js": "^3.5.2",
137 | "vite-plugin-dts": "^4.3.0",
138 | "vite-plugin-eslint2": "^5.0.2",
139 | "vite-plugin-static-copy": "^2.1.0",
140 | "vite-plugin-stylelint": "^5.3.0",
141 | "vite-plugin-vue-devtools": "^7.6.3",
142 | "vite-plugin-vuetify": "^2.0.4",
143 | "vitest": "^2.1.4",
144 | "vue-tsc": "^2.1.10",
145 | "webfontloader": "^1.6.28"
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/playground/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Vuetify 3 Resize Drawer
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/public/vuetify-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
28 |
32 |
33 |
37 | {{ computedWidth }}
38 |
39 |
40 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
55 |
63 |
64 | .v-col-lg-3
65 | .v-col-md-4
66 | .v-col-sm-6
67 | .v-col-xs-12
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
86 |
89 |
90 |
91 |
92 | Resize Drawer
93 | I'm as free as a bird now
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
119 |
120 |
121 |
122 | Navigation Drawer
123 |
124 | Stuck With You
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
304 |
305 |
383 |
384 |
405 |
--------------------------------------------------------------------------------
/src/assets/vuetify-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/documentation/DocsPage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
14 | Vuetify 3 Resize Drawer
15 |
16 | by WebDevNerdStuff
17 |
22 | v{{ componentVersion }}
23 |
24 |
25 |
26 |
27 |
28 |
29 |
33 |
34 | #
38 | Installation
39 |
40 |
41 |
42 |
43 |
50 |
51 | Using pnpm :
55 |
56 |
57 |
58 |
59 |
64 | Using npm:
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/src/documentation/components/MenuComponent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
71 |
72 |
83 |
--------------------------------------------------------------------------------
/src/documentation/components/PropsTable.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #
9 | {{ sectionTitle }}
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
30 |
31 |
32 |
40 |
41 |
42 |
46 | #
47 | {{ item.name }}
52 |
53 |
54 |
55 |
56 |
57 |
61 |
62 |
63 |
64 |
68 |
69 |
70 |
71 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/src/documentation/layout/AppBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
14 |
15 |
16 |
17 |
23 |
24 |
25 |
26 | Vuetify Resize Drawer
27 |
28 |
29 |
30 |
42 |
43 |
51 |
52 |
53 |
54 |
55 |
64 |
65 |
66 |
67 |
75 |
76 |
77 |
78 |
86 |
87 |
88 |
89 |
96 |
100 |
104 |
105 |
106 |
107 |
108 |
166 |
167 |
168 |
--------------------------------------------------------------------------------
/src/documentation/sections/DependenciesSection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 | #
12 | Dependencies
13 |
14 |
15 |
16 |
17 |
21 | Vuetify v3
22 |
23 |
24 |
28 | Vue 3
29 |
30 |
31 |
32 |
33 |
34 |
40 |
--------------------------------------------------------------------------------
/src/documentation/sections/DescriptionSection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 | #
12 | Description
13 |
14 |
15 |
16 |
17 | The vuetify-resize-drawer
component extends the
18 | functionality of the
19 | v-navigation-drawer
23 | so that it is resizable by the user.
24 |
25 |
26 |
27 |
28 |
35 |
--------------------------------------------------------------------------------
/src/documentation/sections/EventsSection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 | #
12 | Events
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
26 |
27 |
28 |
29 |
33 | #
34 |
39 | {{ item.name }}
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
63 |
64 | @submit
Event Example
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
157 |
--------------------------------------------------------------------------------
/src/documentation/sections/ExampleSection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 | #
12 | Example
13 |
14 |
15 |
16 |
17 |
24 |
25 |
26 |
27 |
28 |
29 |
93 |
--------------------------------------------------------------------------------
/src/documentation/sections/GridSection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 | #
12 | Vuetify Grid System
13 |
14 |
15 |
16 |
17 | The vuetify-resize-drawer
component has the ability to use Vuetify Grid classes that are
18 | responsive to the container width using @container
queries. The @media
queries will still work as intended. For more information about the
20 | Vuetify Grid System, see the
21 | Grid System page.
25 |
26 |
27 |
28 | Open Grid Drawer Example
29 |
30 |
31 |
32 |
33 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/documentation/sections/LegalSection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 | #
12 | Legal
13 |
14 |
15 |
16 |
17 |
18 | Vuetify and the Vuetify logo are trademarks of Vuetify LLC. This
19 | component was not created or endorsed by Vuetify.
20 |
21 |
22 |
23 |
24 |
25 |
28 |
--------------------------------------------------------------------------------
/src/documentation/sections/LicenseSection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 | #
12 | License
13 |
14 |
15 |
16 |
17 | Copyright © {{ new Date().getFullYear() }}
18 | WebDevNerdStuff
22 |
23 | Licensed under the
24 |
28 | MIT License
29 |
30 |
31 |
32 |
33 |
34 |
38 |
--------------------------------------------------------------------------------
/src/documentation/sections/PlaygroundSection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 | #
12 | Developer Playground
13 |
14 |
15 |
16 | In order to gain a comprehensive understanding of utilizing this component, we recommend utilizing the
17 | developer Playground. The Playground serves as a complete exemplification of how to effectively employ this
18 | component, equipped with most of the available properties. It serves as a comprehensive model that can serve as
19 | an ideal launching pad for your own project. You have the flexibility to customize the settings and observe
20 | their direct impact on the table.
21 |
22 |
23 | Setup the Playground
24 |
25 |
33 |
34 | First clone the repository:
38 |
39 |
40 |
41 |
48 |
49 | Next run the following command to setup the Playground:
50 |
51 |
52 |
53 |
54 |
55 | This will create a file for you to adjust and play with.
56 |
57 | /src/playground/PlaygroundPage.vue
58 |
59 |
60 |
61 |
62 | Alternatively you can test out the component on StackBlitz
63 | Drawer Example
69 |
70 | Grid System
76 | Example
77 |
78 |
79 |
80 |
81 |
82 |
91 |
--------------------------------------------------------------------------------
/src/documentation/sections/PropsSection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 | #
12 | Props
13 |
14 |
15 |
16 |
17 | Vuetify Resize Drawer uses Vuetify's
18 | Navigation Drawer
22 | behind the scenes. Most props that work for the
23 | v-navigation-drawer
are supported. For a list of those
24 | props, you can find them
25 | here .
29 |
30 |
31 |
32 |
38 |
39 |
40 |
41 |
46 | Try out some of the props
47 |
48 |
49 |
50 |
51 |
57 |
58 |
59 |
60 |
61 |
65 |
66 |
70 | Try out props
71 |
72 |
73 |
74 |
75 |
80 |
88 |
89 |
90 | resizable:
91 |
92 | {{ options.resizable }}
93 |
94 |
95 |
96 |
97 |
98 |
99 |
104 |
112 |
113 |
114 | location:
115 |
116 | {{ options.location }}
117 |
118 |
119 |
120 |
121 |
122 |
123 |
128 |
135 |
136 |
137 |
142 |
149 |
150 |
151 |
156 |
163 |
164 |
165 |
166 |
171 |
179 |
180 |
181 | theme:
182 |
183 | {{ options.theme }}
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 | Local Storage Options
192 |
193 | Values can be viewed in the browser DevTools Application tab under
194 | Local Storage
195 |
196 |
197 |
198 |
203 |
209 |
210 |
211 | saveWidth:
212 |
213 | {{ options.saveWidth }}
214 |
215 |
216 |
217 |
218 |
219 |
220 |
225 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 | Close
245 | Reset
250 |
251 |
252 |
253 |
254 |
255 |
303 |
304 |
344 |
--------------------------------------------------------------------------------
/src/documentation/sections/SassVariablesSection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 | #
12 | SASS Variables
13 |
14 |
15 |
16 |
17 | Vuetify's
18 | SASS Variables
22 | are supported.
23 |
24 |
25 |
26 |
27 |
34 |
35 |
36 |
37 |
41 | #
42 |
47 | {{ item.name }}
48 |
49 |
50 |
51 |
52 | {{ item.default }}
53 | {{ item.desc }}
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
107 |
--------------------------------------------------------------------------------
/src/documentation/sections/SlotsSection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 | #
12 | Slots
13 |
14 |
15 |
16 |
17 |
18 |
23 |
24 |
25 |
26 |
30 | #
31 |
36 | {{ item.name }}
37 |
38 |
39 |
40 |
41 | {{ item.desc }}
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
89 |
--------------------------------------------------------------------------------
/src/documentation/sections/UsageSection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 | #
12 | Usage
13 |
14 |
15 |
16 |
17 |
24 |
25 | Global Plugin Registration
26 |
27 | Global options have a higher precedence and will override local props
28 |
29 |
30 |
31 |
32 |
33 |
41 |
42 |
43 |
44 |
52 |
53 |
54 |
55 |
56 | `;
93 |
94 |
--------------------------------------------------------------------------------
/src/documentation/sections/index.js:
--------------------------------------------------------------------------------
1 | import DependenciesSection from './DependenciesSection.vue';
2 | import DescriptionSection from './DescriptionSection.vue';
3 | import EventsSection from './EventsSection.vue';
4 | import ExampleSection from './ExampleSection.vue';
5 | import GridSection from './GridSection.vue';
6 | import LegalSection from './LegalSection.vue';
7 | import LicenseSection from './LicenseSection.vue';
8 | import PlaygroundSection from './PlaygroundSection.vue';
9 | import PropsSection from './PropsSection.vue';
10 | import SassVariablesSection from './SassVariablesSection.vue';
11 | import SlotsSection from './SlotsSection.vue';
12 | import UsageSection from './UsageSection.vue';
13 |
14 |
15 | export {
16 | DependenciesSection,
17 | DescriptionSection,
18 | EventsSection,
19 | ExampleSection,
20 | GridSection,
21 | LegalSection,
22 | LicenseSection,
23 | PlaygroundSection,
24 | PropsSection,
25 | SassVariablesSection,
26 | SlotsSection,
27 | UsageSection,
28 | };
29 |
--------------------------------------------------------------------------------
/src/documentation/types/docs.d.ts:
--------------------------------------------------------------------------------
1 | import { Props } from '../../plugin/types';
2 |
3 |
4 | declare namespace Docs {
5 | export interface KeyStringAny {
6 | [key: string]: T;
7 | }
8 |
9 | export interface DrawerOptions extends Props {
10 | absolute: boolean;
11 | color: string;
12 | dark: boolean;
13 | elevation: number;
14 | widthSnapBack?: boolean | undefined;
15 | }
16 |
17 | export interface Links {
18 | changeLog: string;
19 | github: string;
20 | githubProfile: string;
21 | license: string;
22 | npm: string;
23 | pnpm: string;
24 | vResizeDrawer: string;
25 | vue: string;
26 | vuetify: string;
27 | vuetifyGithub: string;
28 | }
29 |
30 | export interface CodeBlockSettings {
31 | plugin: string;
32 | theme: string;
33 | }
34 |
35 | export interface GlobalClasses extends KeyStringAny {
36 | appLink: string;
37 | h2: string;
38 | h3: string;
39 | h4: string;
40 | headerA: string;
41 | }
42 |
43 | export interface MenuItem {
44 | href?: string;
45 | icon?: string;
46 | items?: MenuItem[];
47 | key?: string;
48 | link?: string;
49 | title: string;
50 | topTitle?: string;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/libraries/fontawesome.ts:
--------------------------------------------------------------------------------
1 | import { library } from '@fortawesome/fontawesome-svg-core';
2 | import { fab } from '@fortawesome/free-brands-svg-icons';
3 | import { far } from '@fortawesome/free-regular-svg-icons';
4 | import { fas } from '@fortawesome/free-solid-svg-icons';
5 |
6 | library.add(
7 | fab,
8 | fas,
9 | far,
10 | );
11 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import '@/libraries/fontawesome';
2 | import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
3 | import { createVCodeBlock } from '@wdns/vue-code-block';
4 | import { createPinia } from 'pinia';
5 | import { createApp } from 'vue';
6 | import App from './App.vue';
7 | import { createVResizeDrawer } from './plugin/index';
8 | import { registerPlugins } from './plugins';
9 |
10 |
11 | const app = createApp(App);
12 |
13 | app.use(createVCodeBlock());
14 | app.use(createVResizeDrawer());
15 | app.use(createPinia());
16 |
17 | app.component('font-awesome-icon', FontAwesomeIcon);
18 | app.component('FaIcon', FontAwesomeIcon);
19 |
20 | registerPlugins(app);
21 |
22 | app.mount('#app');
23 |
--------------------------------------------------------------------------------
/src/playground/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 |
3 | !.gitignore
4 |
5 | !configs/
6 | !configs/**/*
7 |
--------------------------------------------------------------------------------
/src/playground/configs/PlaygroundApp.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Vuetify Resize Drawer
16 |
17 |
18 |
19 | Playground v{{ store.pluginVersion }}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Sit aut officia expedita id ullam quia in,
30 | nostrum unde recusandae numquam at vero, autem voluptatibus quis! Quasi odit unde eligendi soluta?
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
42 | Toggle Grid Drawer
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/src/playground/configs/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export WHITE="$(printf '\033[0;37m')"
4 | export BOLD_WHITE="$(printf '\033[1;37m')"
5 | export BOLD_GREEN="$(printf '\033[1;32m')"
6 | export CHECKMARK="$(printf '\e[1;32m\xE2\x9C\x94\e[0m')"
7 |
8 | # Playground path and template #
9 | PLAYGROUND_VUE_DIR=src/playground
10 |
11 | PLAYGROUND_VUE_FILE=PlaygroundPage.vue
12 |
13 |
14 | # Check if Playground.vue file exists before trying to create it #
15 | if [ ! -f "$PLAYGROUND_VUE_DIR/$PLAYGROUND_VUE_FILE" ]; then
16 | cp "$PLAYGROUND_VUE_DIR/configs/templates/$PLAYGROUND_VUE_FILE" "$PLAYGROUND_VUE_DIR/$PLAYGROUND_VUE_FILE"
17 |
18 | echo ""
19 | echo " ${BOLD_GREEN}${CHECKMARK}${BOLD_WHITE} $PLAYGROUND_VUE_FILE file has been created.${WHITE}"
20 | echo ""
21 | fi
22 |
23 |
--------------------------------------------------------------------------------
/src/playground/configs/playground.ts:
--------------------------------------------------------------------------------
1 | import '@/libraries/fontawesome';
2 | import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
3 | import { createPinia } from 'pinia';
4 | import { createApp } from 'vue';
5 | import { createVResizeDrawer } from '../../plugin/index';
6 | import { registerPlugins } from '../../plugins';
7 | import PlaygroundApp from './PlaygroundApp.vue';
8 |
9 |
10 | const app = createApp(PlaygroundApp);
11 |
12 | app.use(createVResizeDrawer());
13 |
14 | app.use(createPinia());
15 | app.component('font-awesome-icon', FontAwesomeIcon);
16 |
17 | app.component('FaIcon', FontAwesomeIcon);
18 |
19 | registerPlugins(app);
20 |
21 | app.mount('#app');
22 |
--------------------------------------------------------------------------------
/src/playground/configs/templates/PlaygroundPage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
22 |
25 |
26 |
27 |
28 | Resize Drawer
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
49 |
50 |
54 |
55 |
59 | {{ computedWidth }}
60 |
61 |
62 |
63 |
64 |
65 |
66 |
74 |
75 | .v-col-lg-3
76 | .v-col-md-4
77 | .v-col-sm-6
78 | .v-col-xs-12
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
248 |
249 |
254 |
--------------------------------------------------------------------------------
/src/plugin/__tests__/index.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from 'vitest';
2 | import { createVResizeDrawer } from '../';
3 |
4 |
5 | describe('Plugin Index', () => {
6 | describe('install', () => {
7 | it('should return install function', () => {
8 | const VResizeDrawer = createVResizeDrawer();
9 |
10 | expect('install' in VResizeDrawer).toBe(true);
11 | });
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/src/plugin/composables/__tests__/__snapshots__/classes.test.ts.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`Classes Composable > useDrawerClasses > should return class object 1`] = `
4 | {
5 | "v-navigation-drawer--absolute": false,
6 | "v-navigation-drawer--custom-rail": true,
7 | "v-navigation-drawer--fixed": true,
8 | "v-navigation-drawer--floating": null,
9 | "v-navigation-drawer--is-mouseover": false,
10 | "v-navigation-drawer--left": true,
11 | "v-navigation-drawer--open-on-hover": false,
12 | "v-navigation-drawer--rail": false,
13 | "v-navigation-drawer--right": false,
14 | "v-navigation-drawer--temporary": false,
15 | "v-resize-drawer": true,
16 | "v-resize-drawer--start": false,
17 | }
18 | `;
19 |
20 | exports[`Classes Composable > useHandleContainerClasses > should return class object 1`] = `
21 | {
22 | "v-resize-drawer--handle-container": true,
23 | "v-resize-drawer--handle-container-parent-start": true,
24 | "v-resize-drawer--handle-container-position-center": true,
25 | }
26 | `;
27 |
28 | exports[`Classes Composable > useHandleContainerClasses > should return class object 2`] = `
29 | {
30 | "v-resize-drawer--handle-container": true,
31 | "v-resize-drawer--handle-container-parent-left": true,
32 | "v-resize-drawer--handle-container-position-top": true,
33 | }
34 | `;
35 |
36 | exports[`Classes Composable > useHandleIconClasses > should return class object with fa classes as false 1`] = `
37 | {
38 | "v-resize-drawer--handle-container-icon": true,
39 | "v-resize-drawer--handle-container-icon-fa": false,
40 | "v-resize-drawer--handle-container-icon-fa-top": false,
41 | "v-resize-drawer--handle-container-icon-top-start": true,
42 | "v-resize-drawer--handle-container-icon-user-icon": false,
43 | }
44 | `;
45 |
46 | exports[`Classes Composable > useHandleIconClasses > should return class object with fa classes as true 1`] = `
47 | {
48 | "v-resize-drawer--handle-container-icon": true,
49 | "v-resize-drawer--handle-container-icon-center-left": true,
50 | "v-resize-drawer--handle-container-icon-fa": true,
51 | "v-resize-drawer--handle-container-icon-fa-center": true,
52 | "v-resize-drawer--handle-container-icon-user-icon": false,
53 | }
54 | `;
55 |
--------------------------------------------------------------------------------
/src/plugin/composables/__tests__/classes.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from 'vitest';
2 | import {
3 | useDrawerClasses,
4 | useHandleContainerClasses,
5 | useHandleIconClasses,
6 | } from '../classes';
7 |
8 |
9 | describe('Classes Composable', () => {
10 | describe('useDrawerClasses', () => {
11 | it('should return class object', () => {
12 | const classes = useDrawerClasses({
13 | absolute: false,
14 | expandOnHover: false,
15 | floating: null,
16 | isMouseover: false,
17 | location: 'start',
18 | rail: false,
19 | railWidth: '8',
20 | temporary: undefined,
21 | });
22 |
23 | expect(classes).toMatchSnapshot();
24 | });
25 | });
26 |
27 | describe('useHandleContainerClasses', () => {
28 | it('should return class object', () => {
29 | const classes = useHandleContainerClasses({
30 | drawerLocation: 'start',
31 | handlePosition: 'center',
32 | });
33 |
34 | expect(classes).toMatchSnapshot();
35 | });
36 |
37 | it('should return class object', () => {
38 | const classes = useHandleContainerClasses({
39 | drawerLocation: 'left',
40 | handlePosition: 'top',
41 | });
42 |
43 | expect(classes).toMatchSnapshot();
44 | });
45 | });
46 |
47 | describe('useHandleIconClasses', () => {
48 | it('should return class object with fa classes as false', () => {
49 | const classes = useHandleIconClasses({
50 | drawerLocation: 'start',
51 | handlePosition: 'top',
52 | iconOptions: {
53 | defaultSet: 'mdi'
54 | },
55 | isUserIcon: false,
56 | });
57 |
58 | expect(classes).toMatchSnapshot();
59 | });
60 |
61 | it('should return class object with fa classes as true', () => {
62 | const classes = useHandleIconClasses({
63 | drawerLocation: 'left',
64 | handlePosition: 'center',
65 | iconOptions: {
66 | defaultSet: 'fa'
67 | },
68 | isUserIcon: false,
69 | });
70 |
71 | expect(classes).toMatchSnapshot();
72 | });
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/src/plugin/composables/__tests__/colors.test.ts:
--------------------------------------------------------------------------------
1 | import {
2 | describe,
3 | it,
4 | expect,
5 | vi,
6 | } from 'vitest';
7 | import {
8 | useGetColor,
9 | } from '../colors';
10 | import vuetify from '../../../plugins/vuetify';
11 |
12 | const theme = vuetify.theme;
13 |
14 |
15 | describe('Colors Composable', () => {
16 | describe('useGetColor', () => {
17 | it('should return color name as HSL', () => {
18 | const color = useGetColor('red', theme);
19 | expect(color).toMatchInlineSnapshot(`"hsl(0 100% 50%)"`);
20 | });
21 |
22 | it('should return hex value as HSL', () => {
23 | const color = useGetColor('#f00', theme);
24 | expect(color).toMatchInlineSnapshot(`"hsl(0 100% 50%)"`);
25 | });
26 |
27 | it('should return RGB value as HSL', () => {
28 | const color = useGetColor('rgb(255,0,0)', theme);
29 | expect(color).toMatchInlineSnapshot(`"hsl(0 100% 50%)"`);
30 | });
31 |
32 | it('should return HSL value as HSL', () => {
33 | const color = useGetColor('hsl(0 100% 50%)', theme);
34 | expect(color).toMatchInlineSnapshot(`"hsl(0 100% 50%)"`);
35 | });
36 |
37 | it('should return theme variable as an RGB', () => {
38 | const color = useGetColor('--v-theme-error', theme);
39 | expect(color).toMatchInlineSnapshot(`"rgb(var(--v-theme-error))"`);
40 | });
41 |
42 | it('should return a non theme color option as default HSL color value', () => {
43 | const color = useGetColor('foobar', theme);
44 |
45 | expect(color).toMatchInlineSnapshot(`"hsl(0 0% 100% / 12%)"`);
46 | });
47 |
48 | it('should return a non theme variable as default HSL color value', () => {
49 | const color = useGetColor('--v-foobar', theme);
50 |
51 | expect(color).toMatchInlineSnapshot(`"hsl(0 0% 100% / 12%)"`);
52 | });
53 |
54 | // console.warn tests //
55 | const logSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
56 |
57 | it('should console warn when color prop "foobar" doesn\'t exist in colors', () => {
58 | logSpy.mockReset();
59 |
60 | useGetColor('foobar', theme);
61 |
62 | expect(logSpy).toHaveBeenCalled();
63 | expect(logSpy).toHaveBeenCalledTimes(1);
64 | });
65 |
66 | it('should console warn when color prop "--v-foobar" doesn\'t exist in colors', () => {
67 | logSpy.mockReset();
68 |
69 | useGetColor('--v-foobar', theme);
70 |
71 | expect(logSpy).toHaveBeenCalled();
72 | expect(logSpy).toHaveBeenCalledTimes(1);
73 | });
74 | });
75 | });
76 |
--------------------------------------------------------------------------------
/src/plugin/composables/__tests__/helpers.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from 'vitest';
2 | import {
3 | useConvertToUnit,
4 | } from '../helpers';
5 |
6 |
7 | describe('Helpers Composable', () => {
8 | describe('useConvertToUnit', () => {
9 | it('should return string with a default px unit', () => {
10 | const unit = useConvertToUnit({ value: '10' });
11 | expect(unit).toBe('10px');
12 | });
13 |
14 | it('should return number with a default px unit', () => {
15 | const unit = useConvertToUnit({ value: 10 });
16 | expect(unit).toBe('10px');
17 | });
18 |
19 | it('should return string with a supplied unit', () => {
20 | const unit = useConvertToUnit({ unit: 'em', value: '10' });
21 | expect(unit).toBe('10em');
22 | });
23 |
24 | it('should return number with a supplied unit', () => {
25 | const unit = useConvertToUnit({ unit: 'em', value: 10 });
26 | expect(unit).toBe('10em');
27 | });
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/src/plugin/composables/__tests__/icons.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from 'vitest';
2 | import { useGetIcon } from '../icons';
3 |
4 |
5 | const iconOptions = {
6 | defaultSet: 'mdi'
7 | };
8 |
9 | describe('Icon Composable', () => {
10 | describe('useGetIcon', () => {
11 | it('should return supplied icon value', () => {
12 |
13 | const unit = useGetIcon({
14 | icon: 'mdi:mdi-cog',
15 | iconOptions,
16 | name: 'top',
17 | });
18 |
19 | expect(unit).toMatchInlineSnapshot(`"mdi:mdi-cog"`);
20 | });
21 |
22 | it('should return icon value using name', () => {
23 | const unit = useGetIcon({
24 | icon: undefined,
25 | iconOptions,
26 | name: 'center',
27 | });
28 |
29 | expect(unit).toMatchInlineSnapshot(`"mdi:mdi-chevron-double-right"`);
30 | });
31 |
32 | it('throws error if vuetify defaultSet is not supplied', () => {
33 | expect(() => useGetIcon({
34 | icon: undefined,
35 | iconOptions: {},
36 | name: 'top',
37 | })).toThrowError('[VResizeDrawer]: No default undefined icon set found.');
38 | });
39 |
40 | it('throws error if supplied name not found', () => {
41 | expect(() => useGetIcon({
42 | icon: undefined,
43 | iconOptions,
44 | name: 'foobar',
45 | })).toThrowError('[VResizeDrawer]: No foobar icon found.');
46 | });
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/src/plugin/composables/__tests__/storage.test.ts:
--------------------------------------------------------------------------------
1 | import {
2 | beforeEach,
3 | describe,
4 | expect,
5 | test,
6 | vi,
7 | } from 'vitest';
8 | import {
9 | useGetStorage,
10 | useSetStorage,
11 | } from '../storage';
12 |
13 |
14 | const storageName = 'vuetify-resize-drawer';
15 |
16 | vi.spyOn(Object.getPrototypeOf(window.localStorage), 'getItem');
17 | vi.spyOn(Object.getPrototypeOf(window.localStorage), 'setItem');
18 | vi.spyOn(Object.getPrototypeOf(window.sessionStorage), 'getItem');
19 | vi.spyOn(Object.getPrototypeOf(window.sessionStorage), 'setItem');
20 |
21 | beforeEach(() => {
22 | window.localStorage.clear();
23 | window.sessionStorage.clear();
24 | });
25 |
26 | describe('Storage Composable', () => {
27 | describe('localStorage', () => {
28 | test('useSetStorage - action update', () => {
29 | useSetStorage({
30 | action: 'update',
31 | rail: false,
32 | resizedWidth: '100px',
33 | saveWidth: true,
34 | storageName,
35 | storageType: 'local',
36 | });
37 |
38 | expect(window.localStorage.setItem).toHaveBeenCalled();
39 | });
40 |
41 | test('useSetStorage - action set', () => {
42 | useSetStorage({
43 | action: 'set',
44 | rail: false,
45 | resizedWidth: '100px',
46 | saveWidth: true,
47 | storageName,
48 | storageType: 'local',
49 | });
50 |
51 | expect(window.localStorage.setItem).toHaveBeenCalled();
52 | });
53 |
54 | test('useSetStorage & useGetStorage - action update', () => {
55 | useSetStorage({
56 | action: 'update',
57 | rail: false,
58 | resizedWidth: '100px',
59 | saveWidth: true,
60 | storageName,
61 | storageType: 'local',
62 | });
63 |
64 | expect(window.localStorage.setItem).toHaveBeenCalled();
65 |
66 | useGetStorage('local', storageName);
67 |
68 | expect(window.localStorage.getItem).toHaveBeenCalled();
69 | expect(window.localStorage.getItem(storageName)).toMatchInlineSnapshot(`"undefined"`);
70 | });
71 |
72 | test('useSetStorage & useGetStorage - action set', () => {
73 | useSetStorage({
74 | action: 'set',
75 | rail: false,
76 | resizedWidth: '100px',
77 | saveWidth: true,
78 | storageName,
79 | storageType: 'local',
80 | });
81 |
82 | expect(window.localStorage.setItem).toHaveBeenCalled();
83 |
84 | useGetStorage('local', storageName);
85 |
86 | expect(window.localStorage.getItem).toHaveBeenCalled();
87 | expect(window.localStorage.getItem(storageName)).toMatchInlineSnapshot(`"undefined"`);
88 | });
89 |
90 | test('useGetStorage that has not been set, and return null value', () => {
91 | useGetStorage('local', storageName);
92 |
93 | expect(window.localStorage.getItem).toHaveBeenCalled();
94 | expect(window.localStorage.getItem(storageName)).toMatchInlineSnapshot(`null`);
95 | });
96 | });
97 |
98 | describe('sessionStorage', () => {
99 | test('useSetStorage - action update', () => {
100 | useSetStorage({
101 | action: 'update',
102 | rail: false,
103 | resizedWidth: '100px',
104 | saveWidth: true,
105 | storageName,
106 | storageType: 'session',
107 | });
108 |
109 | expect(window.sessionStorage.setItem).toHaveBeenCalled();
110 | });
111 |
112 | test('useSetStorage - action set', () => {
113 | useSetStorage({
114 | action: 'set',
115 | rail: false,
116 | resizedWidth: '100px',
117 | saveWidth: true,
118 | storageName,
119 | storageType: 'session',
120 | });
121 |
122 | expect(window.sessionStorage.setItem).toHaveBeenCalled();
123 | });
124 |
125 | test('useSetStorage & useGetStorage - action update', () => {
126 | useSetStorage({
127 | action: 'update',
128 | rail: false,
129 | resizedWidth: '100px',
130 | saveWidth: true,
131 | storageName,
132 | storageType: 'session',
133 | });
134 |
135 | expect(window.sessionStorage.setItem).toHaveBeenCalled();
136 |
137 | useGetStorage('session', storageName);
138 |
139 | expect(window.sessionStorage.getItem).toHaveBeenCalled();
140 | expect(window.sessionStorage.getItem(storageName)).toMatchInlineSnapshot(`"undefined"`);
141 | });
142 |
143 | test('useSetStorage & useGetStorage - action set', () => {
144 | useSetStorage({
145 | action: 'set',
146 | rail: false,
147 | resizedWidth: '100px',
148 | saveWidth: true,
149 | storageName,
150 | storageType: 'session',
151 | });
152 |
153 | expect(window.sessionStorage.setItem).toHaveBeenCalled();
154 |
155 | useGetStorage('session', storageName);
156 |
157 | expect(window.sessionStorage.getItem).toHaveBeenCalled();
158 | expect(window.sessionStorage.getItem(storageName)).toMatchInlineSnapshot(`"undefined"`);
159 | });
160 |
161 | test('useGetStorage that has not been set, and return null value', () => {
162 | useGetStorage('session', storageName);
163 |
164 | expect(window.sessionStorage.getItem).toHaveBeenCalled();
165 | expect(window.sessionStorage.getItem(storageName)).toMatchInlineSnapshot(`null`);
166 | });
167 |
168 | test('useSetStorage to set supplied value, and return supplied value', () => {
169 | useSetStorage({
170 | action: 'set',
171 | rail: false,
172 | resizedWidth: '100px',
173 | saveWidth: true,
174 | storageName,
175 | storageType: 'session',
176 | });
177 |
178 | useGetStorage('session', storageName);
179 |
180 | expect(window.sessionStorage.getItem).toHaveBeenCalled();
181 | expect(window.sessionStorage.getItem(storageName)).toMatchInlineSnapshot(`"undefined"`);
182 | });
183 | });
184 | });
185 |
--------------------------------------------------------------------------------
/src/plugin/composables/__tests__/styles.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from 'vitest';
2 | import {
3 | useDrawerStyles,
4 | useHandleContainerStyles,
5 | useHandleIconStyles
6 | } from '../styles';
7 | import vuetify from '../../../plugins/vuetify';
8 |
9 | const theme = vuetify.theme;
10 |
11 |
12 | describe('Styles Composable', () => {
13 | describe('useDrawerStyles', () => {
14 | it('should return styles with transition if isMouseDown is false.', () => {
15 | const data = useDrawerStyles({
16 | isMouseDown: false,
17 | maxWidth: '50%',
18 | minWidth: '56',
19 | rail: false,
20 | railWidth: '8',
21 | resizedWidth: '300',
22 | widthSnapBack: true,
23 | });
24 | expect(data).toMatchInlineSnapshot(`
25 | {
26 | "transitionDuration": ".2s",
27 | "width": undefined,
28 | }
29 | `);
30 | });
31 |
32 | it('should return styles with no transition if isMouseDown is true', () => {
33 | const data = useDrawerStyles({
34 | isMouseDown: true,
35 | maxWidth: '500px',
36 | minWidth: '56px',
37 | rail: false,
38 | railWidth: '8px',
39 | resizedWidth: '256px',
40 | widthSnapBack: false,
41 | });
42 | expect(data).toMatchInlineSnapshot(`
43 | {
44 | "transitionDuration": "0s",
45 | "width": undefined,
46 | }
47 | `);
48 | });
49 |
50 | it('should return no styles if rail is true', () => {
51 | const data = useDrawerStyles({
52 | isMouseDown: true,
53 | maxWidth: '500px',
54 | minWidth: '56px',
55 | rail: true,
56 | railWidth: '8px',
57 | resizedWidth: '300px',
58 | widthSnapBack: false,
59 | });
60 | expect(data).toMatchInlineSnapshot(`{}`);
61 | });
62 | });
63 |
64 |
65 | describe('useHandleContainerStyles', () => {
66 | it('should return styles with a background color if position is border.', () => {
67 | const data = useHandleContainerStyles({
68 | borderWidth: '8',
69 | handleColor: 'primary',
70 | iconSize: 'x-small',
71 | position: 'border',
72 | theme,
73 | });
74 | expect(data).toMatchInlineSnapshot(`
75 | {
76 | "backgroundColor": "hsl(207 90% 54%)",
77 | "height": "100%",
78 | "width": "8px",
79 | }
80 | `);
81 | });
82 |
83 | it('should return styles with a background color if position is border.', () => {
84 | const data = useHandleContainerStyles({
85 | borderWidth: '8',
86 | handleColor: 'success',
87 | iconSize: 'x-small',
88 | position: 'border',
89 | theme,
90 | });
91 | expect(data).toMatchInlineSnapshot(`
92 | {
93 | "backgroundColor": "hsl(122 39% 49%)",
94 | "height": "100%",
95 | "width": "8px",
96 | }
97 | `);
98 | });
99 |
100 | it('should return styles with background color using theme variable handleColor', () => {
101 | const data = useHandleContainerStyles({
102 | borderWidth: '8',
103 | handleColor: '--v-theme-primary',
104 | iconSize: 'x-small',
105 | position: 'border',
106 | theme,
107 | });
108 | expect(data).toMatchInlineSnapshot(`
109 | {
110 | "backgroundColor": "rgb(var(--v-theme-primary))",
111 | "height": "100%",
112 | "width": "8px",
113 | }
114 | `);
115 | });
116 |
117 | it('should return styles with background color using color name handleColor', () => {
118 | const data = useHandleContainerStyles({
119 | borderWidth: '8',
120 | handleColor: 'blue',
121 | iconSize: 'x-small',
122 | position: 'border',
123 | theme,
124 | });
125 | expect(data).toMatchInlineSnapshot(`
126 | {
127 | "backgroundColor": "hsl(240 100% 50%)",
128 | "height": "100%",
129 | "width": "8px",
130 | }
131 | `);
132 | });
133 |
134 | it('should return styles with a transparent background color if position is not border.', () => {
135 | const data = useHandleContainerStyles({
136 | borderWidth: '8',
137 | handleColor: 'primary',
138 | iconSize: 'x-small',
139 | position: 'center',
140 | theme,
141 | });
142 | expect(data).toMatchInlineSnapshot(`
143 | {
144 | "backgroundColor": "transparent",
145 | "height": "undefinedpx",
146 | "transform": undefined,
147 | "width": "undefinedpx",
148 | }
149 | `);
150 | });
151 | });
152 |
153 | describe('useHandleIconStyles', () => {
154 | it('should return ...', () => {
155 | const data = useHandleIconStyles({
156 | color: 'primary',
157 | theme,
158 | });
159 | expect(data).toMatchInlineSnapshot(`
160 | {
161 | "color": "hsl(207 90% 54%)",
162 | }
163 | `);
164 | });
165 | });
166 | });
167 |
--------------------------------------------------------------------------------
/src/plugin/composables/classes.ts:
--------------------------------------------------------------------------------
1 | import {
2 | UseDrawerClasses,
3 | UseHandleContainerClasses,
4 | UseHandleIconClasses,
5 | } from '@/plugin/types';
6 | import { componentName } from '@utils/globals';
7 |
8 |
9 | export const useDrawerClasses: UseDrawerClasses = (options) => {
10 | const { absolute = false, expandOnHover, floating, isMouseover, location, rail, railWidth, temporary } = options;
11 |
12 | let isLeft = location === 'left' || location === 'start' || typeof location === 'undefined';
13 |
14 | if (location === 'top' || location === 'bottom') {
15 | isLeft = false;
16 | }
17 |
18 | return {
19 | [`${componentName}`]: true,
20 | [`${componentName}--${location}`]: location === 'bottom' || location === 'top',
21 | 'v-navigation-drawer--absolute': absolute ?? false,
22 | 'v-navigation-drawer--custom-rail': Number(railWidth) !== 56,
23 | 'v-navigation-drawer--fixed': !absolute,
24 | 'v-navigation-drawer--floating': floating,
25 | 'v-navigation-drawer--is-mouseover': unref(isMouseover),
26 | 'v-navigation-drawer--left': isLeft,
27 | 'v-navigation-drawer--open-on-hover': expandOnHover,
28 | 'v-navigation-drawer--rail': rail ?? false,
29 | 'v-navigation-drawer--right': location === 'right' || location === 'end',
30 | 'v-navigation-drawer--temporary': temporary || false,
31 | };
32 | };
33 |
34 |
35 | export const useHandleContainerClasses: UseHandleContainerClasses = (options) => {
36 | const { drawerLocation, handlePosition } = options;
37 |
38 | const classes = {
39 | [`${componentName}--handle-container`]: true,
40 | [`${componentName}--handle-container-position-${handlePosition}`]: true,
41 | [`${componentName}--handle-container-parent-${drawerLocation}`]: true,
42 | };
43 |
44 | return classes;
45 | };
46 |
47 | export const useHandleIconClasses: UseHandleIconClasses = (options) => {
48 | const { drawerLocation, handlePosition, iconOptions, isUserIcon } = options;
49 |
50 | const classes = {
51 | [`${componentName}--handle-container-icon`]: true,
52 | [`${componentName}--handle-container-icon-${handlePosition}-${drawerLocation}`]: true,
53 | [`${componentName}--handle-container-icon-user-icon`]: isUserIcon,
54 | [`${componentName}--handle-container-icon-fa`]: iconOptions?.defaultSet === 'fa',
55 | [`${componentName}--handle-container-icon-fa-${handlePosition}`]: iconOptions?.defaultSet === 'fa',
56 | };
57 |
58 | return classes;
59 | };
60 |
--------------------------------------------------------------------------------
/src/plugin/composables/colors.ts:
--------------------------------------------------------------------------------
1 | import { ThemeInstance } from 'vuetify';
2 | import {
3 | HEXColor,
4 | HSLColor,
5 | RGBColor,
6 | } from '@/plugin/types';
7 |
8 |
9 | /**
10 | * Checks
11 | */
12 | function checkDoNotConvert(color: string) {
13 | return color === 'transparent' || color === 'none' || color === 'inherit' || color === 'currentColor' || color === 'initial' || color === 'unset';
14 | }
15 |
16 | function checkIfThemeVarColor(color: string) {
17 | return color.includes('--v-theme');
18 | }
19 |
20 | function checkIfThemeColor(color: string, theme: ThemeInstance) {
21 | const themeColors = theme.global.current.value?.colors ?? {};
22 |
23 | return Object.entries(themeColors).find(([key]) => {
24 | return key === color;
25 | });
26 | }
27 |
28 |
29 | /**
30 | * Converts the color to HSL values
31 | */
32 | function convertToHSL(color: string): string {
33 | let newColor: HEXColor | RGBColor | HSLColor = checkColorNames(color);
34 | let h = 0;
35 | let s = 0;
36 | let l = 0;
37 | let r = 0;
38 | let g = 0;
39 | let b = 0;
40 |
41 | // Convert hex color to RGB if necessary
42 | if (newColor.substring(0, 1) === '#') {
43 | newColor = hexToRGB(newColor);
44 | }
45 | // Convert RGB to array values if necessary
46 | else if (newColor.includes('rgb')) {
47 | newColor = [...newColor.matchAll(/\d+/g)].map(Number);
48 | }
49 | // If HSL is passed in, extract values
50 | else if (newColor.includes('hsl')) {
51 | newColor = [...newColor.matchAll(/\d+/g)].map(Number);
52 |
53 | h = newColor[0];
54 | s = newColor[1];
55 | l = newColor[2];
56 |
57 | // Return HSL values
58 | return `${h} ${s}% ${l}%`;
59 | }
60 |
61 | // Extract RGB values
62 | [r, g, b] = newColor;
63 |
64 | // Convert RGB to HSL
65 | r /= 255;
66 | g /= 255;
67 | b /= 255;
68 | const max = Math.max(r, g, b);
69 | const min = Math.min(r, g, b);
70 |
71 | // Color doesn't exist, return --v-theme-surface //
72 | // eslint-disable-next-line no-constant-binary-expression
73 | if (max === null || !min === null || isNaN(max) || isNaN(min)) {
74 | const defaultColor = '0 0% 100% / 12%';
75 |
76 | console.warn(`[VResizeDrawer]: The "color" prop value using "${newColor}" doesn't exist. Using the value "hsl(${defaultColor})" in it's place.`);
77 | return defaultColor;
78 | }
79 |
80 | h = (max + min) / 2;
81 | s = (max + min) / 2;
82 | l = (max + min) / 2;
83 |
84 | if (max == min) {
85 | h = s = 0; // achromatic
86 | }
87 | else {
88 | const d = max - min;
89 | s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
90 | switch (max) {
91 | case r: h = (g - b) / d + (g < b ? 6 : 0); break;
92 | case g: h = (b - r) / d + 2; break;
93 | case b: h = (r - g) / d + 4; break;
94 | default: break;
95 | }
96 | h /= 6;
97 | }
98 |
99 |
100 | h = Math.round(h * 360);
101 | s = Math.round(s * 100);
102 | l = Math.round(l * 100);
103 |
104 | // Return HSL values
105 | return `${h} ${s}% ${l}%`;
106 | }
107 |
108 |
109 | /**
110 | * Checks if the color is a color name and converts it to a hex value
111 | */
112 | function checkColorNames(color: string): HEXColor {
113 | const colors = {
114 | AliceBlue: '#F0F8FF',
115 | AntiqueWhite: '#FAEBD7',
116 | Aqua: '#00FFFF',
117 | Aquamarine: '#7FFFD4',
118 | Azure: '#F0FFFF',
119 | Beige: '#F5F5DC',
120 | Bisque: '#FFE4C4',
121 | Black: '#000000',
122 | BlanchedAlmond: '#FFEBCD',
123 | Blue: '#0000FF',
124 | BlueViolet: '#8A2BE2',
125 | Brown: '#A52A2A',
126 | BurlyWood: '#DEB887',
127 | CadetBlue: '#5F9EA0',
128 | Chartreuse: '#7FFF00',
129 | Chocolate: '#D2691E',
130 | Coral: '#FF7F50',
131 | CornflowerBlue: '#6495ED',
132 | Cornsilk: '#FFF8DC',
133 | Crimson: '#DC143C',
134 | Cyan: '#00FFFF',
135 | DarkBlue: '#00008B',
136 | DarkCyan: '#008B8B',
137 | DarkGoldenRod: '#B8860B',
138 | DarkGray: '#A9A9A9',
139 | DarkGreen: '#006400',
140 | DarkGrey: '#A9A9A9',
141 | DarkKhaki: '#BDB76B',
142 | DarkMagenta: '#8B008B',
143 | DarkOliveGreen: '#556B2F',
144 | DarkOrange: '#FF8C00',
145 | DarkOrchid: '#9932CC',
146 | DarkRed: '#8B0000',
147 | DarkSalmon: '#E9967A',
148 | DarkSeaGreen: '#8FBC8F',
149 | DarkSlateBlue: '#483D8B',
150 | DarkSlateGray: '#2F4F4F',
151 | DarkSlateGrey: '#2F4F4F',
152 | DarkTurquoise: '#00CED1',
153 | DarkViolet: '#9400D3',
154 | DeepPink: '#FF1493',
155 | DeepSkyBlue: '#00BFFF',
156 | DimGray: '#696969',
157 | DimGrey: '#696969',
158 | DodgerBlue: '#1E90FF',
159 | FireBrick: '#B22222',
160 | FloralWhite: '#FFFAF0',
161 | ForestGreen: '#228B22',
162 | Fuchsia: '#FF00FF',
163 | Gainsboro: '#DCDCDC',
164 | GhostWhite: '#F8F8FF',
165 | Gold: '#FFD700',
166 | GoldenRod: '#DAA520',
167 | Gray: '#808080',
168 | Green: '#008000',
169 | GreenYellow: '#ADFF2F',
170 | Grey: '#808080',
171 | HoneyDew: '#F0FFF0',
172 | HotPink: '#FF69B4',
173 | IndianRed: '#CD5C5C',
174 | Indigo: '#4B0082',
175 | Ivory: '#FFFFF0',
176 | Khaki: '#F0E68C',
177 | Lavender: '#E6E6FA',
178 | LavenderBlush: '#FFF0F5',
179 | LawnGreen: '#7CFC00',
180 | LemonChiffon: '#FFFACD',
181 | LightBlue: '#ADD8E6',
182 | LightCoral: '#F08080',
183 | LightCyan: '#E0FFFF',
184 | LightGoldenRodYellow: '#FAFAD2',
185 | LightGray: '#D3D3D3',
186 | LightGreen: '#90EE90',
187 | LightGrey: '#D3D3D3',
188 | LightPink: '#FFB6C1',
189 | LightSalmon: '#FFA07A',
190 | LightSeaGreen: '#20B2AA',
191 | LightSkyBlue: '#87CEFA',
192 | LightSlateGray: '#778899',
193 | LightSlateGrey: '#778899',
194 | LightSteelBlue: '#B0C4DE',
195 | LightYellow: '#FFFFE0',
196 | Lime: '#00FF00',
197 | LimeGreen: '#32CD32',
198 | Linen: '#FAF0E6',
199 | Magenta: '#FF00FF',
200 | Maroon: '#800000',
201 | MediumAquaMarine: '#66CDAA',
202 | MediumBlue: '#0000CD',
203 | MediumOrchid: '#BA55D3',
204 | MediumPurple: '#9370DB',
205 | MediumSeaGreen: '#3CB371',
206 | MediumSlateBlue: '#7B68EE',
207 | MediumSpringGreen: '#00FA9A',
208 | MediumTurquoise: '#48D1CC',
209 | MediumVioletRed: '#C71585',
210 | MidnightBlue: '#191970',
211 | MintCream: '#F5FFFA',
212 | MistyRose: '#FFE4E1',
213 | Moccasin: '#FFE4B5',
214 | NavajoWhite: '#FFDEAD',
215 | Navy: '#000080',
216 | OldLace: '#FDF5E6',
217 | Olive: '#808000',
218 | OliveDrab: '#6B8E23',
219 | Orange: '#FFA500',
220 | OrangeRed: '#FF4500',
221 | Orchid: '#DA70D6',
222 | PaleGoldenRod: '#EEE8AA',
223 | PaleGreen: '#98FB98',
224 | PaleTurquoise: '#AFEEEE',
225 | PaleVioletRed: '#DB7093',
226 | PapayaWhip: '#FFEFD5',
227 | PeachPuff: '#FFDAB9',
228 | Peru: '#CD853F',
229 | Pink: '#FFC0CB',
230 | Plum: '#DDA0DD',
231 | PowderBlue: '#B0E0E6',
232 | Purple: '#800080',
233 | RebeccaPurple: '#663399',
234 | Red: '#FF0000',
235 | RosyBrown: '#BC8F8F',
236 | RoyalBlue: '#4169E1',
237 | SaddleBrown: '#8B4513',
238 | Salmon: '#FA8072',
239 | SandyBrown: '#F4A460',
240 | SeaGreen: '#2E8B57',
241 | SeaShell: '#FFF5EE',
242 | Sienna: '#A0522D',
243 | Silver: '#C0C0C0',
244 | SkyBlue: '#87CEEB',
245 | SlateBlue: '#6A5ACD',
246 | SlateGray: '#708090',
247 | SlateGrey: '#708090',
248 | Snow: '#FFFAFA',
249 | SpringGreen: '#00FF7F',
250 | SteelBlue: '#4682B4',
251 | Tan: '#D2B48C',
252 | Teal: '#008080',
253 | Thistle: '#D8BFD8',
254 | Tomato: '#FF6347',
255 | Turquoise: '#40E0D0',
256 | Violet: '#EE82EE',
257 | Wheat: '#F5DEB3',
258 | White: '#FFFFFF',
259 | WhiteSmoke: '#F5F5F5',
260 | Yellow: '#FFFF00',
261 | YellowGreen: '#9ACD32',
262 | };
263 | let response = color;
264 |
265 | Object.entries(colors).forEach(([key, value]) => {
266 | if (color.toLowerCase() == key.toLowerCase()) {
267 | response = value;
268 | return;
269 | }
270 | });
271 |
272 | return response;
273 | }
274 |
275 |
276 | /**
277 | * Converts the HEX color to RGB
278 | */
279 | function hexToRGB(hex: string): RGBColor {
280 | let newHex = hex.replace('#', '');
281 |
282 | // Convert 3-digit hex colors to 6-digit hex colors
283 | if (newHex.length === 3) {
284 | newHex = newHex.split('').map(char => char + char).join('');
285 | }
286 |
287 | // Extract the red, green, and blue values from the hex string
288 | const r = parseInt(newHex.substring(0, 2), 16);
289 | const g = parseInt(newHex.substring(2, 4), 16);
290 | const b = parseInt(newHex.substring(4, 6), 16);
291 |
292 | // Return an array of the RGB values
293 | return [r, g, b];
294 | }
295 |
296 |
297 | /**
298 | * Converts single color
299 | */
300 | export const useGetColor = (color: string, theme: ThemeInstance): string => {
301 | if (checkDoNotConvert(color)) {
302 | return color;
303 | }
304 |
305 | if (checkIfThemeVarColor(color)) {
306 | return `rgb(var(${color}))`;
307 | }
308 |
309 | const isThemeColor = checkIfThemeColor(color, theme);
310 |
311 | if (isThemeColor) {
312 | return `hsl(${convertToHSL(isThemeColor[1])})`;
313 | }
314 |
315 | return `hsl(${convertToHSL(color)})`;
316 | };
317 |
--------------------------------------------------------------------------------
/src/plugin/composables/helpers.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Props,
3 | UseConvertToUnit,
4 | } from '@/plugin/types';
5 |
6 |
7 | /*
8 | * Converts a string to a number with a unit.
9 | */
10 | export const useConvertToUnit: UseConvertToUnit = (options) => {
11 | const { unit = 'px', value } = options;
12 |
13 | if (value == null || value === '') {
14 | return undefined;
15 | }
16 |
17 | if (!+value) {
18 | return String(value);
19 | }
20 |
21 | return `${Number(value)}${unit}`;
22 | };
23 |
24 |
25 | /*
26 | * Converts a string | number to a whole number founded.
27 | */
28 | function roundToWhole(input: string | number): string | number {
29 | if (typeof input === 'string') {
30 | const regex = /(\d+(\.\d+)?)(\s*([a-zA-Z]+))?/;
31 | const match = input.match(regex);
32 |
33 | if (!match) {
34 | return input;
35 | }
36 |
37 | const numberPart = parseFloat(match[1] as string);
38 | const unitPart = match[4];
39 |
40 | if (!isNaN(numberPart)) {
41 | const roundedNumber = Math.round(numberPart);
42 | const result = unitPart ? `${roundedNumber} ${unitPart}` : `${roundedNumber}`;
43 | return result;
44 | }
45 |
46 | return input;
47 |
48 | }
49 | else if (typeof input === 'number') {
50 | return Math.round(input);
51 | }
52 |
53 | return input;
54 | }
55 |
56 |
57 | /*
58 | * Converts a string to a number.
59 | */
60 | export const useConvertToNumber = (val: string | number): number => {
61 | const windowsWidth = window.innerWidth;
62 | const drawerWidth = roundToWhole(val);
63 |
64 | if (typeof val === 'string' && val.includes('%')) {
65 | return Number(val.replace('%', '')) / 100 * windowsWidth;
66 | }
67 |
68 | if (typeof drawerWidth === 'string') {
69 | return Number(drawerWidth.replace(/\D/g, ''));
70 | }
71 |
72 | return drawerWidth;
73 | };
74 |
75 |
76 | /*
77 | * Converts a unit to px.
78 | */
79 | export const useUnitToPx = (valueWithUnit: Props['handleIconSize']): number => {
80 | if (!valueWithUnit) return 0;
81 |
82 |
83 | // check if string valueWithUnit contains px
84 | if (typeof valueWithUnit === 'number') {
85 | return valueWithUnit;
86 | }
87 |
88 | // Create a temporary element for calculating the value
89 | const tempElement = document.createElement('div');
90 | tempElement.style.position = 'absolute';
91 | tempElement.style.visibility = 'hidden';
92 | tempElement.style.width = valueWithUnit as string;
93 |
94 | // Append the temporary element to the specified parent or body
95 | const targetParent = document.body;
96 | targetParent.appendChild(tempElement);
97 |
98 | // Get the computed width in px
99 | const computedValue = getComputedStyle(tempElement).width;
100 |
101 | // Remove the temporary element after calculation
102 | targetParent.removeChild(tempElement);
103 |
104 | // Return the numeric px value
105 | return parseFloat(computedValue);
106 | };
107 |
--------------------------------------------------------------------------------
/src/plugin/composables/icons.ts:
--------------------------------------------------------------------------------
1 | import { UseGetIcon } from '@/plugin/types';
2 |
3 | const defaultIcons = {
4 | fa: {
5 | bottom: 'fas fa-grip',
6 | center: 'fas fa-angles-right',
7 | top: 'fas fa-grip',
8 | },
9 | mdi: {
10 | bottom: 'mdi:mdi-dots-grid',
11 | center: 'mdi:mdi-chevron-double-right',
12 | top: 'mdi:mdi-dots-grid',
13 | },
14 | };
15 |
16 | export const useGetIcon: UseGetIcon = (options) => {
17 | const { icon, iconOptions, name } = options;
18 |
19 | if (name === 'border') {
20 | return;
21 | }
22 |
23 | if (icon) {
24 | return icon;
25 | }
26 |
27 | const defaultSet = iconOptions?.defaultSet as string ?? '';
28 | let iconAbbv = defaultSet.toLowerCase();
29 |
30 | iconAbbv = iconAbbv === 'fa' || iconAbbv === 'fasvg' ? 'fa' : iconAbbv;
31 | const iconSet = defaultIcons[iconAbbv];
32 |
33 | if (!iconSet) {
34 | throw new Error(`[VResizeDrawer]: No default ${iconOptions?.defaultSet} icon set found.`);
35 | }
36 |
37 | const newIcon = iconSet[name as string];
38 |
39 | if (!newIcon) {
40 | throw new Error(`[VResizeDrawer]: No ${name} icon found.`);
41 | }
42 |
43 | return newIcon;
44 | };
45 |
--------------------------------------------------------------------------------
/src/plugin/composables/storage.ts:
--------------------------------------------------------------------------------
1 | import { UseSetStorage } from '@/plugin/types';
2 |
3 |
4 | export function useGetStorage(storageType: string, storageName: string): string | null {
5 | if (storageType === 'local') {
6 | return localStorage.getItem(storageName);
7 | }
8 |
9 | if (storageType === 'session') {
10 | return sessionStorage.getItem(storageName);
11 | }
12 |
13 | return '';
14 | }
15 |
16 |
17 | export const useSetStorage: UseSetStorage = (options) => {
18 | const { action = 'update', rail, resizedAmount, saveAmount, storageName, storageType } = options;
19 |
20 | if (rail && !saveAmount) {
21 | return;
22 | }
23 |
24 | let amount = resizedAmount;
25 | amount = amount ?? undefined;
26 |
27 | if (action === 'set') {
28 | amount = useGetStorage(storageType as string, storageName as string) ?? '';
29 | amount = amount || resizedAmount;
30 | }
31 |
32 | if (storageType === 'local') {
33 | localStorage.setItem(storageName as string, String(amount));
34 | }
35 |
36 | if (storageType === 'session') {
37 | sessionStorage.setItem(storageName as string, String(amount));
38 | }
39 |
40 | return;
41 | };
42 |
--------------------------------------------------------------------------------
/src/plugin/composables/styles.ts:
--------------------------------------------------------------------------------
1 | import {
2 | UseDrawerStyles,
3 | UseHandleContainerStyles,
4 | UseHandleIconStyles,
5 | } from '@/plugin/types';
6 | import { useGetColor } from '@composables/colors';
7 | import { useConvertToUnit } from '@composables/helpers';
8 |
9 |
10 | export const iconSizes = {
11 | default: '1.5em',
12 | large: '1.75em',
13 | small: '1.25em',
14 | 'x-large': '2em',
15 | 'x-small': '1em',
16 | };
17 |
18 |
19 | // -------------------------------------------------- Drawer //
20 | export const useDrawerStyles: UseDrawerStyles = (options) => {
21 | const { isMouseDown, location, maxWidth, minWidth, rail, railWidth, resizedAmount, snapBack } = options;
22 |
23 | if (rail) {
24 | return {};
25 | }
26 |
27 | let mountValue = rail ? railWidth : unref(resizedAmount);
28 |
29 | if (!snapBack) {
30 | if (parseInt(mountValue as string) >= parseInt(maxWidth as string)) {
31 | mountValue = parseInt(maxWidth as string);
32 | }
33 |
34 | if (parseInt(mountValue as string) <= parseInt(minWidth as string)) {
35 | mountValue = parseInt(minWidth as string);
36 | }
37 | }
38 |
39 | let response = {};
40 |
41 | if (location === 'top' || location === 'bottom') {
42 | response = {
43 | maxHeight: `${useConvertToUnit({ value: mountValue as string }) as string} !important`,
44 | minHeight: `${useConvertToUnit({ value: mountValue as string }) as string} !important`,
45 | transitionDuration: unref(isMouseDown) ? '0s' : '.2s',
46 | width: '100%',
47 | };
48 | }
49 | else {
50 | response = {
51 | transitionDuration: unref(isMouseDown) ? '0s' : '.2s',
52 | width: useConvertToUnit({ value: mountValue as string }) as string,
53 | };
54 | }
55 |
56 | return response;
57 | };
58 |
59 |
60 | // -------------------------------------------------- Handle //
61 | export const useHandleContainerStyles: UseHandleContainerStyles = (options) => {
62 | const { borderWidth, handleColor, iconSizeUnit, location, position, theme } = options;
63 |
64 | const transform = `translateX(-50%) ${location === 'top' ? 'rotate(90deg)' : 'rotate(-90deg)'}`;
65 | let height = `${iconSizeUnit}px`;
66 | let width = `${iconSizeUnit}px`;
67 |
68 | if (position === 'border') {
69 | if (location === 'bottom' || location === 'top') {
70 | height = useConvertToUnit({ value: borderWidth as string }) as string;
71 | }
72 | else {
73 | height = '100%';
74 | width = useConvertToUnit({ value: borderWidth as string }) as string;
75 | }
76 |
77 | return {
78 | backgroundColor: useGetColor(handleColor as string, theme),
79 | height,
80 | width,
81 | };
82 | }
83 |
84 | return {
85 | backgroundColor: 'transparent',
86 | height: height,
87 | transform: location === 'top' || location === 'bottom' ? transform : undefined,
88 | width,
89 | };
90 | };
91 |
92 |
93 | export const useHandleIconStyles: UseHandleIconStyles = (options) => {
94 | const { color, theme } = options;
95 |
96 | const handleColor = useGetColor(color as string, theme);
97 |
98 | const styles = {
99 | color: handleColor as string,
100 | };
101 |
102 | return styles;
103 | };
104 |
--------------------------------------------------------------------------------
/src/plugin/index.ts:
--------------------------------------------------------------------------------
1 | import type { GlobalOptions } from './types';
2 | import type { App } from 'vue';
3 | import './styles/main.scss';
4 | import VResizeDrawer from './VResizeDrawer.vue';
5 |
6 |
7 | export const globalOptions = Symbol();
8 |
9 | export function createVResizeDrawer(options: GlobalOptions = {}) {
10 | const install = (app: App) => {
11 | app.provide(globalOptions, options);
12 |
13 | app.component('VResizeDrawer', defineAsyncComponent(() => import('./VResizeDrawer.vue')));
14 | };
15 |
16 | return {
17 | install,
18 | };
19 | }
20 |
21 | export default VResizeDrawer;
22 |
23 | export {
24 | VResizeDrawer,
25 | };
26 |
--------------------------------------------------------------------------------
/src/plugin/styles/_mixins.scss:
--------------------------------------------------------------------------------
1 | @use 'sass:map';
2 | @use 'sass:math';
3 | @use 'vuetify/tools';
4 | @use 'vuetify/settings';
5 |
6 | /*
7 | * @source vuetify/packages/vuetify/src/styles/tools/_functions.sass
8 | */
9 | @function breakpoint-min($name, $breakpoints) {
10 | $min: map.get($breakpoints, $name);
11 | @return if($min !=0, $min, null);
12 | }
13 |
14 | /*
15 | * @source vuetify/packages/vuetify/src/styles/tools/_display.sass
16 | */
17 | @mixin media-breakpoint-up($name, $breakpoints: settings.$grid-breakpoints) {
18 | $min: breakpoint-min($name, $breakpoints);
19 |
20 | @if $min {
21 | @container v-resize-drawer (min-width: #{$min}) {
22 | @content;
23 | }
24 | }
25 | }
26 |
27 | /*
28 | * @source vuetify/packages/vuetify/src/components/VGrid/_mixins.sass
29 | */
30 | @mixin make-col($size, $columns: settings.$grid-columns) {
31 | flex: 0 0 math.percentage(math.div($size, $columns));
32 | max-width: math.percentage(math.div($size, $columns));
33 | }
34 |
35 | @mixin make-grid-columns($columns: settings.$grid-columns, $gutter: settings.$grid-gutter, $breakpoints: settings.$grid-breakpoints) {
36 | @each $breakpoint in map.keys($breakpoints) {
37 | $infix: tools.breakpoint-infix($breakpoint, $breakpoints);
38 |
39 | @include media-breakpoint-up($breakpoint, $breakpoints) {
40 | @for $i from 1 through $columns {
41 | .v-col#{$infix}-#{$i} {
42 | @include make-col($i, $columns);
43 | }
44 | }
45 | }
46 | }
47 | }
48 |
49 | /*
50 | * @source vuetify/packages/vuetify/src/styles/utilities/_display.sass
51 | */
52 | @mixin make-grid-columns-hidden() {
53 | $display: map.get(settings.$utilities, 'display');
54 |
55 | @each $size,
56 | $media_query in settings.$display-breakpoints {
57 | @container v-resize-drawer #{$media_query} {
58 | .hidden-#{$size} {
59 | display: none !important;
60 | }
61 | }
62 |
63 | @container v-resize-drawer #{$media_query} {
64 | @each $val in map.get($display, 'values') {
65 | .#{map.get($display, 'class')}-#{$size}-#{$val} {
66 | display: $val !important;
67 | }
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/plugin/styles/main.scss:
--------------------------------------------------------------------------------
1 | @use 'sass:map';
2 | @use './mixins' as *;
3 | @use 'vuetify/settings';
4 |
5 | .v-resize-drawer {
6 | container-name: v-resize-drawer;
7 | container-type: inline-size;
8 |
9 | &--handle {
10 | &-container {
11 | align-items: center;
12 | cursor: grab;
13 | display: flex;
14 | justify-content: center;
15 | position: absolute;
16 | z-index: 1;
17 |
18 | &-icon {
19 | &-parent {
20 | &-end,
21 | &-right {
22 | left: initial;
23 | right: 0;
24 | }
25 | }
26 |
27 | &-center {
28 | &-end,
29 | &-right {
30 | transform: rotate(180deg);
31 | }
32 | }
33 |
34 | &-user-icon {
35 | transform: none;
36 | }
37 |
38 | &-fa {
39 | font-size: .7rem !important;
40 | }
41 | }
42 |
43 | &-parent {
44 | &-left,
45 | &-start,
46 | &-undefined {
47 | right: 0;
48 | }
49 |
50 | &-end,
51 | &-right {
52 | left: 0;
53 | }
54 | }
55 |
56 | &-position {
57 | &-top {
58 | top: 0;
59 | }
60 |
61 | &-center {
62 | top: 50%;
63 | transform: translateY(-50%);
64 | }
65 |
66 | &-bottom {
67 | bottom: 0;
68 | }
69 |
70 | &-border {
71 | cursor: col-resize;
72 | height: 100%;
73 | top: 0;
74 | width: 8px;
75 | }
76 | }
77 | }
78 | }
79 |
80 | &--bottom {
81 | transition: min-height 0.3s;
82 |
83 | .v-resize-drawer--handle-container {
84 | &-position {
85 | &-center {
86 | left: 50%;
87 | top: 0;
88 | transform: translateX(-50%);
89 | }
90 |
91 | &-border {
92 | cursor: row-resize;
93 | left: 0%;
94 | top: 0 !important;
95 | width: 100% !important;
96 | }
97 | }
98 | }
99 | }
100 |
101 | &--top {
102 | top: 0 !important;
103 | transition: min-height 0.3s;
104 |
105 | .v-resize-drawer--handle-container {
106 | &-position {
107 | &-center {
108 | bottom: 1px;
109 | left: 50%;
110 | top: unset;
111 | transform: translateX(-50%);
112 | }
113 |
114 | &-border {
115 | bottom: 0 !important;
116 | cursor: row-resize;
117 | left: 0%;
118 | top: unset;
119 | width: 100% !important;
120 | }
121 | }
122 | }
123 | }
124 |
125 | &--bottom,
126 | &--top {
127 | .v-navigation-drawer__content {
128 | flex: 1 1 auto;
129 | position: relative;
130 | }
131 | }
132 | }
133 |
134 |
135 | @container v-resize-drawer (width > #{map.get(settings.$grid-breakpoints, 'xs')}) and (max-width: #{map.get(settings.$grid-breakpoints, 'sm') - 0.02}) {
136 | .v-col {
137 | &-xs-12 {
138 | flex: 0 0 100% !important;
139 | flex-basis: 0;
140 | flex-grow: 1;
141 | max-width: 100% !important;
142 | }
143 | }
144 | }
145 |
146 | // Grid Columns //
147 | @include make-grid-columns;
148 |
149 | // Hidden & d-#{size}-#{display-value} Columns //
150 | @include make-grid-columns-hidden;
151 |
--------------------------------------------------------------------------------
/src/plugin/types/auto-imports.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /* prettier-ignore */
3 | // @ts-nocheck
4 | // Generated by unplugin-auto-import
5 | export {}
6 | declare global {
7 | const CSSProperties: typeof import('vue')['CSSProperties']
8 | const EffectScope: typeof import('vue')['EffectScope']
9 | const computed: typeof import('vue')['computed']
10 | const createApp: typeof import('vue')['createApp']
11 | const customRef: typeof import('vue')['customRef']
12 | const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
13 | const defineComponent: typeof import('vue')['defineComponent']
14 | const effectScope: typeof import('vue')['effectScope']
15 | const getCurrentInstance: typeof import('vue')['getCurrentInstance']
16 | const getCurrentScope: typeof import('vue')['getCurrentScope']
17 | const h: typeof import('vue')['h']
18 | const inject: typeof import('vue')['inject']
19 | const isProxy: typeof import('vue')['isProxy']
20 | const isReactive: typeof import('vue')['isReactive']
21 | const isReadonly: typeof import('vue')['isReadonly']
22 | const isRef: typeof import('vue')['isRef']
23 | const markRaw: typeof import('vue')['markRaw']
24 | const nextTick: typeof import('vue')['nextTick']
25 | const onActivated: typeof import('vue')['onActivated']
26 | const onBeforeMount: typeof import('vue')['onBeforeMount']
27 | const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
28 | const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
29 | const onDeactivated: typeof import('vue')['onDeactivated']
30 | const onErrorCaptured: typeof import('vue')['onErrorCaptured']
31 | const onMounted: typeof import('vue')['onMounted']
32 | const onRenderTracked: typeof import('vue')['onRenderTracked']
33 | const onRenderTriggered: typeof import('vue')['onRenderTriggered']
34 | const onScopeDispose: typeof import('vue')['onScopeDispose']
35 | const onServerPrefetch: typeof import('vue')['onServerPrefetch']
36 | const onUnmounted: typeof import('vue')['onUnmounted']
37 | const onUpdated: typeof import('vue')['onUpdated']
38 | const provide: typeof import('vue')['provide']
39 | const reactive: typeof import('vue')['reactive']
40 | const readonly: typeof import('vue')['readonly']
41 | const ref: typeof import('vue')['ref']
42 | const resolveComponent: typeof import('vue')['resolveComponent']
43 | const shallowReactive: typeof import('vue')['shallowReactive']
44 | const shallowReadonly: typeof import('vue')['shallowReadonly']
45 | const shallowRef: typeof import('vue')['shallowRef']
46 | const toRaw: typeof import('vue')['toRaw']
47 | const toRef: typeof import('vue')['toRef']
48 | const toRefs: typeof import('vue')['toRefs']
49 | const toValue: typeof import('vue')['toValue']
50 | const triggerRef: typeof import('vue')['triggerRef']
51 | const unref: typeof import('vue')['unref']
52 | const useAttrs: typeof import('vue')['useAttrs']
53 | const useCssModule: typeof import('vue')['useCssModule']
54 | const useCssVars: typeof import('vue')['useCssVars']
55 | const useSlots: typeof import('vue')['useSlots']
56 | const useTheme: typeof import('vuetify')['useTheme']
57 | const watch: typeof import('vue')['watch']
58 | const watchEffect: typeof import('vue')['watchEffect']
59 | const watchPostEffect: typeof import('vue')['watchPostEffect']
60 | const watchSyncEffect: typeof import('vue')['watchSyncEffect']
61 | }
62 | // for type re-export
63 | declare global {
64 | // @ts-ignore
65 | export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue'
66 | }
67 | // for vue template auto import
68 | import { UnwrapRef } from 'vue'
69 | declare module 'vue' {
70 | interface ComponentCustomProperties {
71 | readonly CSSProperties: UnwrapRef
72 | readonly EffectScope: UnwrapRef
73 | readonly computed: UnwrapRef
74 | readonly createApp: UnwrapRef
75 | readonly customRef: UnwrapRef
76 | readonly defineAsyncComponent: UnwrapRef
77 | readonly defineComponent: UnwrapRef
78 | readonly effectScope: UnwrapRef
79 | readonly getCurrentInstance: UnwrapRef
80 | readonly getCurrentScope: UnwrapRef
81 | readonly h: UnwrapRef
82 | readonly inject: UnwrapRef
83 | readonly isProxy: UnwrapRef
84 | readonly isReactive: UnwrapRef
85 | readonly isReadonly: UnwrapRef
86 | readonly isRef: UnwrapRef
87 | readonly markRaw: UnwrapRef
88 | readonly nextTick: UnwrapRef
89 | readonly onActivated: UnwrapRef
90 | readonly onBeforeMount: UnwrapRef
91 | readonly onBeforeUnmount: UnwrapRef
92 | readonly onBeforeUpdate: UnwrapRef
93 | readonly onDeactivated: UnwrapRef
94 | readonly onErrorCaptured: UnwrapRef
95 | readonly onMounted: UnwrapRef
96 | readonly onRenderTracked: UnwrapRef
97 | readonly onRenderTriggered: UnwrapRef
98 | readonly onScopeDispose: UnwrapRef
99 | readonly onServerPrefetch: UnwrapRef
100 | readonly onUnmounted: UnwrapRef
101 | readonly onUpdated: UnwrapRef
102 | readonly provide: UnwrapRef
103 | readonly reactive: UnwrapRef
104 | readonly readonly: UnwrapRef
105 | readonly ref: UnwrapRef
106 | readonly resolveComponent: UnwrapRef
107 | readonly shallowReactive: UnwrapRef
108 | readonly shallowReadonly: UnwrapRef
109 | readonly shallowRef: UnwrapRef
110 | readonly toRaw: UnwrapRef
111 | readonly toRef: UnwrapRef
112 | readonly toRefs: UnwrapRef
113 | readonly toValue: UnwrapRef
114 | readonly triggerRef: UnwrapRef
115 | readonly unref: UnwrapRef
116 | readonly useAttrs: UnwrapRef
117 | readonly useCssModule: UnwrapRef
118 | readonly useCssVars: UnwrapRef
119 | readonly useSlots: UnwrapRef
120 | readonly useTheme: UnwrapRef
121 | readonly watch: UnwrapRef
122 | readonly watchEffect: UnwrapRef
123 | readonly watchPostEffect: UnwrapRef
124 | readonly watchSyncEffect: UnwrapRef
125 | }
126 | }
127 | declare module '@vue/runtime-core' {
128 | interface ComponentCustomProperties {
129 | readonly CSSProperties: UnwrapRef
130 | readonly EffectScope: UnwrapRef
131 | readonly computed: UnwrapRef
132 | readonly createApp: UnwrapRef
133 | readonly customRef: UnwrapRef
134 | readonly defineAsyncComponent: UnwrapRef
135 | readonly defineComponent: UnwrapRef
136 | readonly effectScope: UnwrapRef
137 | readonly getCurrentInstance: UnwrapRef
138 | readonly getCurrentScope: UnwrapRef
139 | readonly h: UnwrapRef
140 | readonly inject: UnwrapRef
141 | readonly isProxy: UnwrapRef
142 | readonly isReactive: UnwrapRef
143 | readonly isReadonly: UnwrapRef
144 | readonly isRef: UnwrapRef
145 | readonly markRaw: UnwrapRef
146 | readonly nextTick: UnwrapRef
147 | readonly onActivated: UnwrapRef
148 | readonly onBeforeMount: UnwrapRef
149 | readonly onBeforeUnmount: UnwrapRef
150 | readonly onBeforeUpdate: UnwrapRef
151 | readonly onDeactivated: UnwrapRef
152 | readonly onErrorCaptured: UnwrapRef
153 | readonly onMounted: UnwrapRef
154 | readonly onRenderTracked: UnwrapRef
155 | readonly onRenderTriggered: UnwrapRef
156 | readonly onScopeDispose: UnwrapRef
157 | readonly onServerPrefetch: UnwrapRef
158 | readonly onUnmounted: UnwrapRef
159 | readonly onUpdated: UnwrapRef
160 | readonly provide: UnwrapRef
161 | readonly reactive: UnwrapRef
162 | readonly readonly: UnwrapRef
163 | readonly ref: UnwrapRef
164 | readonly resolveComponent: UnwrapRef
165 | readonly shallowReactive: UnwrapRef
166 | readonly shallowReadonly: UnwrapRef
167 | readonly shallowRef: UnwrapRef
168 | readonly toRaw: UnwrapRef
169 | readonly toRef: UnwrapRef
170 | readonly toRefs: UnwrapRef
171 | readonly toValue: UnwrapRef
172 | readonly triggerRef: UnwrapRef
173 | readonly unref: UnwrapRef
174 | readonly useAttrs: UnwrapRef
175 | readonly useCssModule: UnwrapRef
176 | readonly useCssVars: UnwrapRef
177 | readonly useSlots: UnwrapRef
178 | readonly useTheme: UnwrapRef
179 | readonly watch: UnwrapRef
180 | readonly watchEffect: UnwrapRef
181 | readonly watchPostEffect: UnwrapRef
182 | readonly watchSyncEffect: UnwrapRef
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/plugin/types/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | CSSProperties,
3 | MaybeRef,
4 | } from 'vue';
5 | import type { IconOptions, ThemeInstance } from 'vuetify';
6 | import type {
7 | VIcon,
8 | VNavigationDrawer,
9 | } from 'vuetify/components';
10 | import VResizeDrawer from '../VResizeDrawer.vue';
11 |
12 | export * from '../index';
13 |
14 |
15 | export type Classes = {
16 | [key: string]: boolean | undefined;
17 | };
18 |
19 | export type EmitEventNames = 'handle:click' | 'handle:dblclick' | 'handle:drag' | 'handle:mousedown' | 'handle:mouseup' | 'handle:touchend' | 'handle:touchmove' | 'handle:touchstart';
20 | export type StorageType = 'local' | 'session';
21 | export type HandlePositions = 'bottom' | 'border' | 'center' | 'top';
22 | export type DrawerLocations = 'bottom' | 'end' | 'start' | 'left' | 'right' | 'top' | undefined;
23 |
24 | type Height = number | string | undefined;
25 |
26 | // -------------------------------------------------- Colors //
27 | export type HEXColor = string;
28 | export type HSLColor = [number, number, number];
29 | export type RGBColor = [number, number, number];
30 |
31 |
32 | // -------------------------------------------------- Props //
33 | export interface Props {
34 | absolute?: VNavigationDrawer['absolute'];
35 | expandOnHover?: VNavigationDrawer['expandOnHover'];
36 | floating?: VNavigationDrawer['floating'];
37 | handleBorderWidth?: number | string;
38 | handleColor?: string | undefined;
39 | handleIcon?: string | undefined;
40 | handleIconSize?: VIcon['size'];
41 | handlePosition?: HandlePositions;
42 | height?: Height;
43 | image?: VNavigationDrawer['image'];
44 | location?: DrawerLocations;
45 | maxHeight?: Height;
46 | maxWidth?: VNavigationDrawer['width'];
47 | minHeight?: Height;
48 | minWidth?: VNavigationDrawer['width'];
49 | modelValue?: VNavigationDrawer['modelValue'];
50 | name?: VNavigationDrawer['name'];
51 | rail?: VNavigationDrawer['rail'];
52 | railWidth?: VNavigationDrawer['railWidth'];
53 | resizable?: boolean | undefined;
54 | saveHeight?: boolean | undefined;
55 | saveWidth?: boolean | undefined;
56 | snapBack?: boolean | undefined;
57 | storageName?: string | undefined;
58 | storageType?: StorageType;
59 | tag?: VNavigationDrawer['tag'];
60 | temporary?: VNavigationDrawer['temporary'];
61 | theme?: VNavigationDrawer['theme'];
62 | touchless?: boolean | undefined;
63 | width?: VNavigationDrawer['width'];
64 | widthSnapBack?: boolean | undefined;
65 | }
66 |
67 | export interface GlobalOptions extends Props { }
68 |
69 |
70 | // -------------------------------------------------- Composables //
71 |
72 | // ------------------------- Helpers //
73 | export interface UseConvertToUnit {
74 | (
75 | options: {
76 | unit?: string,
77 | value: string | number,
78 | }
79 | ): string | void;
80 | }
81 |
82 | // ------------------------- Storage //
83 | export interface UseSetStorage {
84 | (
85 | options: {
86 | action?: string;
87 | resizedAmount?: MaybeRef;
88 | resizedWidth?: MaybeRef;
89 | storageType?: StorageType;
90 | storageName?: Props['storageName'];
91 | saveAmount?: Props['saveWidth'] | Props['saveHeight'];
92 | rail?: Props['rail'];
93 | }
94 | ): void;
95 | }
96 |
97 | // ------------------------- Classes //
98 | export interface UseDrawerClasses {
99 | (
100 | options: {
101 | absolute?: Props['absolute'],
102 | expandOnHover?: Props['expandOnHover'],
103 | floating?: Props['floating'],
104 | isMouseover?: MaybeRef,
105 | location?: Props['location'],
106 | rail?: Props['rail'],
107 | railWidth?: Props['railWidth'],
108 | temporary?: Props['temporary'],
109 | }
110 | ): object;
111 | }
112 |
113 | export interface UseHandleContainerClasses {
114 | (
115 | options: {
116 | handlePosition?: Props['handlePosition'],
117 | drawerLocation?: Props['location'],
118 | }
119 | ): object;
120 | }
121 |
122 | export interface UseHandleIconClasses {
123 | (
124 | options: {
125 | handlePosition?: Props['handlePosition'],
126 | iconOptions?: IconOptions,
127 | isUserIcon?: boolean,
128 | drawerLocation?: Props['location'],
129 | }
130 | ): object;
131 | }
132 |
133 | // ------------------------- Styles //
134 | export interface UseDrawerStyles {
135 | (
136 | options: {
137 | isMouseDown?: MaybeRef,
138 | location?: Props['location'],
139 | maxHeight?: Props['maxHeight'],
140 | minHeight?: Props['minHeight'],
141 | maxWidth?: Props['maxWidth'],
142 | minWidth?: Props['minWidth'],
143 | rail?: Props['rail'],
144 | railWidth?: Props['railWidth'],
145 | resizedAmount: MaybeRef,
146 | snapBack?: Props['snapBack'],
147 | }
148 | ): CSSProperties;
149 | }
150 |
151 | export interface UseHandleContainerStyles {
152 | (
153 | options: {
154 | borderWidth?: Props['handleBorderWidth'],
155 | handleColor?: Props['handleColor'],
156 | iconSize?: Props['handleIconSize'],
157 | iconSizeUnit?: number,
158 | location?: Props['location'],
159 | position?: Props['handlePosition'],
160 | theme: ThemeInstance,
161 | }
162 | ): CSSProperties;
163 | }
164 |
165 | export interface UseHandleIconStyles {
166 | (
167 | options: {
168 | color?: Props['handleColor'],
169 | theme: ThemeInstance,
170 | }
171 | ): CSSProperties;
172 | }
173 |
174 | // ------------------------ Icons //
175 | export interface UseGetIcon {
176 | (
177 | options: {
178 | icon: Props['handleIcon'];
179 | iconOptions: IconOptions | undefined;
180 | name: Props['handlePosition'],
181 | }
182 | ): Props['handleIcon'];
183 | }
184 |
185 |
186 | declare module 'vue' {
187 | interface ComponentCustomProperties { }
188 |
189 | interface GlobalComponents {
190 | VResizeDrawer: typeof VResizeDrawer;
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/src/plugin/types/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/plugin/types/vue-shim.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | declare module '*.vue' {
3 | import type { DefineComponent } from 'vue';
4 | const component: DefineComponent<{}, {}, any>;
5 | export default component;
6 | }
7 |
--------------------------------------------------------------------------------
/src/plugin/utils/globals.ts:
--------------------------------------------------------------------------------
1 | const defaultWidth = 256;
2 | const componentName = 'v-resize-drawer';
3 |
4 |
5 | export {
6 | defaultWidth,
7 | componentName,
8 | };
9 |
--------------------------------------------------------------------------------
/src/plugin/utils/props.ts:
--------------------------------------------------------------------------------
1 | import { Props } from '@/plugin/types';
2 | import { componentName } from './globals';
3 |
4 |
5 | export const AllProps: Props = {
6 | handleBorderWidth: 8,
7 | handleColor: 'primary',
8 | handleIcon: undefined,
9 | handleIconSize: 'x-small' as const,
10 | handlePosition: 'center',
11 | height: 256,
12 | location: 'start',
13 | maxHeight: window.innerHeight,
14 | maxWidth: window.innerWidth,
15 | minHeight: 56,
16 | minWidth: 56,
17 | modelValue: true,
18 | name: undefined,
19 | rail: false,
20 | railWidth: 8,
21 | resizable: true,
22 | saveWidth: true,
23 | snapBack: true,
24 | storageName: `${componentName}-width`,
25 | storageType: 'local',
26 | tag: 'nav',
27 | theme: undefined,
28 | touchless: false,
29 | width: 256,
30 | };
31 |
--------------------------------------------------------------------------------
/src/plugins/index.ts:
--------------------------------------------------------------------------------
1 | import type { App } from 'vue';
2 | import vuetify from '@/plugins/vuetify';
3 | import { loadFonts } from '@/plugins/webfontloader';
4 |
5 |
6 | export function registerPlugins(app: App) {
7 | loadFonts();
8 | app.use(vuetify);
9 | }
10 |
--------------------------------------------------------------------------------
/src/plugins/theme.ts:
--------------------------------------------------------------------------------
1 | import colors from 'vuetify/lib/util/colors.mjs';
2 |
3 | export const dark = {
4 | colors: {
5 | accent: '#d00274',
6 | danger: colors.red.base,
7 | error: colors.red.base,
8 | info: colors.teal.base,
9 | primary: colors.blue.darken2,
10 | secondary: colors.purple.base,
11 | success: colors.green.base,
12 | warning: colors.orange.darken3,
13 | },
14 | dark: true,
15 | };
16 |
17 | export const light = {
18 | colors: {
19 | accent: '#905',
20 | danger: colors.red.base,
21 | error: colors.red.base,
22 | info: colors.teal.darken1,
23 | primary: colors.blue.base,
24 | secondary: colors.purple.lighten1,
25 | success: colors.green.base,
26 | warning: colors.orange.base,
27 | },
28 | dark: false,
29 | };
30 |
31 |
32 | export default {
33 | dark,
34 | light,
35 | };
36 |
--------------------------------------------------------------------------------
/src/plugins/vuetify.ts:
--------------------------------------------------------------------------------
1 | import 'vuetify/styles';
2 | import '@mdi/font/css/materialdesignicons.css';
3 | import { createVuetify } from 'vuetify';
4 | import * as components from 'vuetify/components';
5 | import * as directives from 'vuetify/directives';
6 | import { fa } from 'vuetify/iconsets/fa-svg';
7 | import { aliases, mdi } from 'vuetify/iconsets/mdi';
8 | import defaultThemes from './theme';
9 |
10 |
11 | export default createVuetify({
12 | components,
13 | directives,
14 | icons: {
15 | aliases,
16 | defaultSet: 'mdi',
17 | sets: {
18 | fa,
19 | mdi,
20 | },
21 | },
22 | theme: {
23 | defaultTheme: 'light',
24 | themes: {
25 | ...defaultThemes,
26 | },
27 | },
28 | });
29 |
--------------------------------------------------------------------------------
/src/plugins/webfontloader.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * plugins/webfontloader.ts
3 | *
4 | * webfontloader documentation: https://github.com/typekit/webfontloader
5 | */
6 |
7 | export async function loadFonts() {
8 | const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader');
9 |
10 | webFontLoader.load({
11 | google: {
12 | families: ['Roboto:100,300,400,500,700,900&display=swap'],
13 | },
14 | });
15 | }
16 |
--------------------------------------------------------------------------------
/src/stores/index.ts:
--------------------------------------------------------------------------------
1 | import packageJson from '@root/package.json';
2 | import { defineStore } from 'pinia';
3 |
4 |
5 | export const useCoreStore = defineStore('core', () => {
6 | const scopedPackageName = packageJson.name;
7 | const packageName = scopedPackageName.split('/')[1];
8 |
9 | // Links //
10 | const repoBaseUrl = `https://github.com/webdevnerdstuff/${packageName}`;
11 | const links = {
12 | changeLog: `${repoBaseUrl}/blob/main/CHANGELOG.md`,
13 | demo: 'https://stackblitz.com/edit/vuetify-resize-drawer?file=src%2Fcomponents%2FResizeDrawerExample.vue',
14 | demoGrid: 'https://stackblitz.com/edit/vuetify-resize-drawer?file=src%2Fcomponents%2FGridDrawerExample.vue',
15 | discord: 'https://discord.com/users/979453275369783346',
16 | github: repoBaseUrl,
17 | githubProfile: 'https://github.com/webdevnerdstuff',
18 | license: `${repoBaseUrl}/blob/main/LICENSE.md`,
19 | npm: `https://www.npmjs.com/package/${scopedPackageName}`,
20 | pnpm: 'https://pnpm.io/',
21 | vue: 'https://vuejs.org/',
22 | vueUse: 'https://vueuse.org/',
23 | vuetify: 'https://vuetifyjs.com/',
24 | vuetifyGithub: 'https://github.com/vuetifyjs/vuetify',
25 | };
26 |
27 | const actions = {
28 | setLocalStorage(val: string): string {
29 | const oldValue = localStorage.getItem(packageName);
30 | const newValue = val ?? oldValue;
31 |
32 | localStorage.setItem(packageName, newValue);
33 | return newValue;
34 | },
35 | setTheme(val: string): string {
36 | const themeName = val === 'dark' ? 'light' : 'dark';
37 | const currentTheme = localStorage.getItem(`${packageName}-theme`);
38 | const newTheme = themeName ?? currentTheme;
39 |
40 | localStorage.setItem(`${packageName}-theme`, newTheme);
41 | return newTheme;
42 | },
43 | };
44 |
45 | const getters = {
46 | getLocalStorage: () => (): unknown => {
47 | const value = localStorage.getItem(packageName);
48 | return value;
49 | },
50 | getTheme: () => {
51 | const value = localStorage.getItem(`${packageName}-theme`);
52 | return value;
53 | },
54 | };
55 |
56 | return {
57 | ...actions,
58 | ...getters,
59 | links,
60 | package: packageJson,
61 | packageName,
62 | pluginVersion: packageJson.version,
63 | };
64 | });
65 |
--------------------------------------------------------------------------------
/src/stores/menu.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia';
2 | import { useCoreStore } from './index';
3 |
4 |
5 | export const useMenuStore = defineStore('menu', () => {
6 | const coreStore = useCoreStore();
7 | const links = coreStore.links;
8 |
9 | const componentItems = [
10 | {
11 | href: '#components-v-navigation-drawer',
12 | icon: 'mdi:mdi-menu',
13 | key: 'v-navigation-drawer',
14 | link: `${links.vuetify}en/api/v-navigation-drawer/`,
15 | title: 'VNavigationDrawer',
16 | topTitle: 'VNavigationDrawer',
17 |
18 | },
19 | ];
20 |
21 | const vuetifyLinks = [
22 | {
23 | icon: 'mdi:mdi-github',
24 | key: 'vuetify-github',
25 | link: links.vuetifyGithub,
26 | title: 'Github',
27 | },
28 | {
29 | key: 'vuetify',
30 | link: `${links.vuetify}en/components/all/`,
31 | title: 'Vuetify Components',
32 | },
33 | ];
34 |
35 | const menuItems = [
36 | {
37 | href: '#home',
38 | icon: 'mdi:mdi-home',
39 | title: 'Home',
40 | },
41 | {
42 | href: '#installation',
43 | icon: 'mdi:mdi-plus-thick',
44 | title: 'Installation',
45 | },
46 | {
47 | href: '#description',
48 | icon: 'mdi:mdi-information-outline',
49 | title: 'Description',
50 | },
51 | {
52 | href: '#usage',
53 | icon: 'mdi:mdi-power-plug-outline',
54 | title: 'Usage',
55 | },
56 | {
57 | href: '#props',
58 | icon: 'mdi-cog',
59 | title: 'Props',
60 | },
61 | {
62 | href: '#vuetify-grid-system',
63 | icon: 'mdi-view-grid',
64 | title: 'Grid System',
65 | },
66 | {
67 | href: '#events',
68 | icon: 'mdi-calendar-star',
69 | title: 'Events',
70 | },
71 | {
72 | href: '#slots',
73 | icon: 'mdi-slot-machine',
74 | title: 'Slots',
75 | },
76 | {
77 | href: '#sass-variables',
78 | icon: 'mdi-sass',
79 | title: 'SASS Variables',
80 | },
81 | {
82 | href: '#example',
83 | icon: 'mdi-code-json',
84 | title: 'Example',
85 | },
86 | {
87 | href: '#playground',
88 | icon: 'mdi:mdi-seesaw',
89 | title: 'Playground',
90 | },
91 | {
92 | href: '#dependencies',
93 | icon: 'mdi-asterisk-circle-outline',
94 | title: 'Dependencies',
95 | },
96 | {
97 | href: '#license',
98 | icon: 'mdi-card-account-details-outline',
99 | title: 'License',
100 | },
101 | {
102 | href: '#legal',
103 | icon: 'mdi-scale-balance',
104 | title: 'Legal',
105 | },
106 | ];
107 |
108 | return {
109 | componentItems,
110 | menuItems,
111 | vuetifyLinks,
112 | };
113 | });
114 |
--------------------------------------------------------------------------------
/src/stores/props.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia';
2 | // import { useCoreStore } from './';
3 |
4 |
5 | export const usePropsStore = defineStore('props', () => {
6 | // const { links } = useCoreStore();
7 |
8 | const propsHeaders = [
9 | {
10 | align: 'start',
11 | filterable: true,
12 | key: 'name',
13 | sortable: true,
14 | title: 'Name',
15 | width: '20%',
16 | },
17 | {
18 | align: 'start',
19 | filterable: false,
20 | key: 'type',
21 | sortable: false,
22 | title: 'Type',
23 | width: '20%',
24 | },
25 | {
26 | align: 'start',
27 | filterable: false,
28 | key: 'default',
29 | sortable: false,
30 | title: 'Default',
31 | },
32 | {
33 | align: 'start',
34 | filterable: false,
35 | key: 'desc',
36 | sortable: false,
37 | title: 'Description',
38 | },
39 | ];
40 |
41 | const componentProps = [
42 | {
43 | default: '8',
44 | desc: 'Specifies the width of the handle if the handle position border
is selected.',
45 | name: 'handle-border-width',
46 | type: 'string | number',
47 | },
48 | {
49 | default: 'primary',
50 | desc: 'Determines the color of the handle',
51 | name: 'handle-color',
52 | type: 'string',
53 | },
54 | {
55 | default: 'undefined',
56 | desc: 'Determines the icon of the handle',
57 | name: 'handle-icon',
58 | type: 'string',
59 | },
60 | {
61 | default: 'x-small',
62 | desc: 'Sets the height and width of the icon. Default unit is px. Can also use the following predefined sizes: x-small
, small
, default
, large
, and x-large
',
63 | name: 'handle-icon-size',
64 | type: 'string',
65 | },
66 | {
67 | default: 'center',
68 | desc: 'Specifies the position of the handle. Valid values are border
, center
, top
, bottom
. If location
prop is set to top
or bottom
, the handle position top
and bottom
are not allowed.',
69 | name: 'handle-position',
70 | type: "'bottom' | 'border' | 'center' | 'top'",
71 | },
72 | {
73 | default: 'start',
74 | desc: 'Places the navigation drawer position on the the screen.',
75 | name: 'location',
76 | type: "'bottom' | 'end' | 'start' | 'left' | 'right' | 'top' | undefined",
77 | },
78 | {
79 | default: '100%',
80 | desc: 'The maximum height of the navigation drawer. Accepts: number, px, or %',
81 | name: 'max-height',
82 | type: 'string',
83 | },
84 | {
85 | default: '100%',
86 | desc: 'The maximum width of the navigation drawer. Accepts: number, px, or %',
87 | name: 'max-width',
88 | type: 'string',
89 | },
90 | {
91 | default: '56',
92 | desc: 'The minimum height of the navigation drawer. Accepts: number, px, or %',
93 | name: 'min-height',
94 | type: 'string',
95 | },
96 | {
97 | default: '56',
98 | desc: 'The minimum width of the navigation drawer. Accepts: number, px, or %',
99 | name: 'min-width',
100 | type: 'string',
101 | },
102 | {
103 | default: 'true',
104 | desc: 'Enables resize functionality',
105 | name: 'resizable',
106 | type: 'boolean',
107 | },
108 | {
109 | default: 'true',
110 | desc: 'Determines if the width of the component is saved in local/session storage',
111 | name: 'save-width',
112 | type: 'boolean',
113 | },
114 | {
115 | default: 'true',
116 | desc: 'Determines if the width or height of the navigation drawer should snap back if the min-width
, max-width
, min-height
or max-height
prop values pass their respective thresholds. (previously width-snap-back
. Backwards compatible)',
117 | name: 'snap-back',
118 | type: 'boolean',
119 | },
120 | {
121 | default: 'v-resize-drawer-width',
122 | desc: 'Determines the name of the local/session storage item. The value is appended with the location
prop value.',
123 | name: 'storage-name',
124 | type: 'string',
125 | },
126 | {
127 | default: 'local',
128 | desc: 'Determines the type of storage to use when the save-width
is true
. Valid values are local
and session
',
129 | name: 'storage-type',
130 | type: "'local' | 'session'",
131 | },
132 | {
133 | default: 'false',
134 | desc: 'Hides the resize handle on mobile devices. * The touchless
prop does not function the same as the v-navigation-drawer
prop which is not supported',
135 | name: 'touchless',
136 | type: 'boolean',
137 | },
138 | ];
139 |
140 | const propsNotSupportedHeaders = [
141 | {
142 | align: 'start',
143 | filterable: true,
144 | key: 'name',
145 | sortable: false,
146 | title: 'Name',
147 | width: '15%',
148 | },
149 | {
150 | align: 'start',
151 | filterable: false,
152 | key: 'status',
153 | sortable: false,
154 | title: 'Status',
155 | width: '10%',
156 | },
157 | {
158 | align: 'start',
159 | filterable: false,
160 | key: 'notes',
161 | sortable: false,
162 | title: 'Notes',
163 | },
164 | ];
165 |
166 | const propsNotSupported = [
167 | {
168 | name: 'disable-route-watcher',
169 | notes: 'An environment that uses routes is needed to test',
170 | status: '',
171 | },
172 | {
173 | name: 'expand-on-hover',
174 | notes: 'The expand-on-hover
prop will work, but the resizable functionality is disabled',
175 | status: 'partial support',
176 | },
177 | {
178 | name: 'rail',
179 | notes: 'The rail
prop will work, but the resizable functionality is disabled',
180 | status: 'partial support',
181 | },
182 | {
183 | name: 'rail-width',
184 | notes: 'The rail-width
prop for use with the rail
prop will work, but the resizable functionality is disabled',
185 | status: 'partial support',
186 | },
187 | ];
188 |
189 |
190 | return {
191 | componentProps,
192 | propsHeaders,
193 | propsNotSupported,
194 | propsNotSupportedHeaders,
195 | };
196 | });
197 |
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webdevnerdstuff/vuetify-resize-drawer/0347ff6acf011e01e214915d351e7f89c39c9a86/src/style.css
--------------------------------------------------------------------------------
/stylelint.config.js:
--------------------------------------------------------------------------------
1 | module.exports =
2 | {
3 | extends: ['@wdns/stylelint-config-wdns'],
4 | };
5 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "exclude": [
3 | "playground",
4 | "src/**/*.spec.ts",
5 | "src/**/*.test.ts",
6 | "src/playground/**/*.ts",
7 | "src/playground/**/*.vue",
8 | "src/documentation/**/*.ts",
9 | "src/documentation/**/*.tsx",
10 | "src/documentation/**/*.vue",
11 | "src/libraries/**/*.ts",
12 | "src/plugins/**/*.ts",
13 | "src/stores/**/*.ts",
14 | "src/App.vue",
15 | "src/main.ts",
16 | "node_modules",
17 | ],
18 | "extends": "./tsconfig.json"
19 | }
20 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "baseUrl": "./",
5 | "declaration": true,
6 | "declarationDir": "./dist",
7 | "esModuleInterop": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "importHelpers": true,
10 | "jsx": "preserve",
11 | "lib": [
12 | "ESNext",
13 | "ES2017",
14 | "DOM"
15 | ],
16 | "module": "ESNext",
17 | "moduleResolution": "Node",
18 | "noEmit": true,
19 | "noFallthroughCasesInSwitch": true,
20 | "noImplicitAny": false,
21 | "noUncheckedIndexedAccess": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "outDir": "./dist",
25 | "paths": {
26 | "@/*": [
27 | "./src/*"
28 | ],
29 | "@types/*": [
30 | "./src/plugin/types/*"
31 | ],
32 | "@components/*": [
33 | "./src/plugin/components/*"
34 | ],
35 | "@composables/*": [
36 | "./src/plugin/composables/*"
37 | ],
38 | "@slots/*": [
39 | "./src/plugin/slots/*"
40 | ],
41 | "@utils/*": [
42 | "./src/plugin/utils/*"
43 | ]
44 | },
45 | "resolveJsonModule": true,
46 | "rootDir": "src",
47 | "skipLibCheck": true,
48 | "strict": true,
49 | "target": "ESNext",
50 | "typeRoots": [
51 | "./src/plugin/types",
52 | "./src/documentation/types",
53 | "./node_modules/@types",
54 | "./node_modules/vuetify"
55 | ],
56 | "types": [
57 | "jest",
58 | "node"
59 | ],
60 | "useDefineForClassFields": true
61 | },
62 | "exclude": [
63 | "cypress.config.ts",
64 | "cypress/**/*.ts",
65 | "node_modules",
66 | "playground",
67 | "src/**/*.spec.ts",
68 | "src/**/*.test.ts",
69 | "src/playground/configs/templates/PlaygroundPage.vue",
70 | "src/plugins/**/*.ts",
71 | "src/stores/**/*.ts",
72 | ],
73 | "include": [
74 | "eslint.config.mjs",
75 | "src/App.vue",
76 | "src/main.ts",
77 | "src/playground/**/*.ts",
78 | "src/playground/**/*.vue",
79 | "src/documentation/**/*.ts",
80 | "src/documentation/**/*.tsx",
81 | "src/documentation/**/*.vue",
82 | "src/plugin/**/*.ts",
83 | "src/plugin/**/*.tsx",
84 | "src/plugin/**/*.vue"
85 | ],
86 | "references": [
87 | {
88 | "path": "./tsconfig.node.json"
89 | }
90 | ]
91 | }
92 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": [
10 | "vite.config.mts"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/vite.build.config.mts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import * as path from 'path';
3 | import AutoImport from 'unplugin-auto-import/vite';
4 | import commonjs from '@rollup/plugin-commonjs';
5 | import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
6 | import dts from 'vite-plugin-dts';
7 | import pkg from './package.json';
8 | import terser from '@rollup/plugin-terser';
9 | import typescript from 'rollup-plugin-typescript2';
10 | import vue from '@vitejs/plugin-vue';
11 | import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify';
12 | import { viteStaticCopy } from 'vite-plugin-static-copy';
13 |
14 |
15 | const scopedPackageName = pkg.name;
16 | const packageName = scopedPackageName.split('/')[1];
17 |
18 | const banner = `/**
19 | * @name ${scopedPackageName}
20 | * @version ${pkg.version}
21 | * @description ${pkg.description}
22 | * @author ${pkg.author}
23 | * @copyright Copyright ${new Date().getFullYear()}, WebDevNerdStuff
24 | * @homepage ${pkg.homepage}
25 | * @repository ${pkg.repository}
26 | * @license ${pkg.license} License
27 | */
28 | `;
29 |
30 | export default defineConfig({
31 | publicDir: false,
32 | build: {
33 | lib: {
34 | entry: './src/plugin/index.ts',
35 | name: packageName,
36 | formats: ['es', 'cjs'],
37 | fileName: format => `${packageName}.${format}.js`,
38 | },
39 | rollupOptions: {
40 | input: {
41 | main: path.resolve(__dirname, './src/plugin/index.ts')
42 | },
43 | external: [
44 | ...Object.keys(pkg.dependencies || {}),
45 | /^vuetify($|\/.+)/,
46 | ],
47 | output: {
48 | banner,
49 | exports: 'named',
50 | },
51 | },
52 | },
53 | css: {
54 | preprocessorOptions: {
55 | scss: {
56 | api: 'modern-compiler', // or "modern", "legacy"
57 | importers: [],
58 | },
59 | },
60 | },
61 | plugins: [
62 | commonjs(),
63 | AutoImport({
64 | dts: false,
65 | imports: [
66 | 'vue',
67 | {
68 | vue: ['CSSProperties'],
69 | }
70 | ],
71 | vueTemplate: true,
72 | }),
73 | vue({
74 | template: { transformAssetUrls },
75 | }),
76 | dts({
77 | insertTypesEntry: true,
78 | tsconfigPath: 'tsconfig.build.json',
79 | }),
80 | typescript({
81 | check: true,
82 | include: ['./src/plugin/**/*.vue'],
83 | tsconfig: 'tsconfig.build.json',
84 | }),
85 | vuetify({
86 | autoImport: true,
87 | styles: 'none',
88 | }),
89 | cssInjectedByJsPlugin({ topExecutionPriority: false }),
90 | viteStaticCopy({
91 | targets: [
92 | {
93 | src: 'src/plugin/styles/*',
94 | dest: 'scss',
95 | },
96 | ]
97 | }),
98 | terser({
99 | compress: {
100 | drop_console: ['log'],
101 | },
102 | }),
103 | ],
104 | resolve: {
105 | alias: {
106 | '@': path.resolve(__dirname, './src'),
107 | '@components': path.resolve(__dirname, './src/plugin/components'),
108 | '@composables': path.resolve(__dirname, './src/plugin/composables'),
109 | '@plugin': path.resolve(__dirname, './src/plugin'),
110 | '@root': path.resolve(__dirname, './'),
111 | '@slots': path.resolve(__dirname, './src/plugin/slots'),
112 | '@types': path.resolve(__dirname, './src/plugin/types'),
113 | '@utils': path.resolve(__dirname, './src/plugin/utils'),
114 | },
115 | extensions: [
116 | '.js',
117 | '.json',
118 | '.jsx',
119 | '.mjs',
120 | '.mts',
121 | '.ts',
122 | '.tsx',
123 | '.vue',
124 | ],
125 | },
126 | });
127 |
--------------------------------------------------------------------------------
/vite.config.mts:
--------------------------------------------------------------------------------
1 | import vue from '@vitejs/plugin-vue';
2 | import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify';
3 | import eslint from 'vite-plugin-eslint2';
4 | import stylelint from 'vite-plugin-stylelint';
5 | import { defineConfig } from 'vite';
6 | import { fileURLToPath, URL } from 'node:url';
7 | import AutoImport from 'unplugin-auto-import/vite';
8 | import vueDevTools from 'vite-plugin-vue-devtools';
9 |
10 | const baseUrl = '/vuetify-resize-drawer/';
11 | const playgroundUrl = baseUrl + 'playground/';
12 |
13 |
14 | export default defineConfig({
15 | base: baseUrl,
16 | build: {
17 | outDir: 'docs',
18 | },
19 | css: {
20 | preprocessorOptions: {
21 | scss: {
22 | api: 'modern-compiler',
23 | importers: [],
24 | },
25 | },
26 | },
27 | plugins: [
28 | eslint({
29 | fix: true,
30 | exclude: ['node_modules/**', 'vendor/**'],
31 | include: ['src/**/*.{ts,mts,tsx,vue}'],
32 | }),
33 | stylelint({
34 | cache: false,
35 | fix: true,
36 | include: [
37 | 'src/**/*.{css,scss,sass,vue}',
38 | './src/components/**/*.{css,scss,sass,vue}',
39 | './src/plugin/styles/*.{css,scss,sass}'
40 | ],
41 | }),
42 | AutoImport({
43 | dts: false,
44 | imports: [
45 | 'vue',
46 | {
47 | vue: ['CSSProperties'],
48 | vuetify: ['useTheme']
49 | }
50 | ],
51 | vueTemplate: true,
52 | }),
53 | vue({
54 | template: { transformAssetUrls }
55 | }),
56 | vuetify({
57 | autoImport: true,
58 | }),
59 | ],
60 | resolve: {
61 | alias: {
62 | '@': fileURLToPath(new URL('./src', import.meta.url)),
63 | '@components': fileURLToPath(new URL('./src/plugin/components', import.meta.url)),
64 | '@composables': fileURLToPath(new URL('./src/plugin/composables', import.meta.url)),
65 | '@plugin': fileURLToPath(new URL('./src/plugin', import.meta.url)),
66 | '@root': fileURLToPath(new URL('.', import.meta.url)),
67 | '@slots': fileURLToPath(new URL('./src/plugin/slots', import.meta.url)),
68 | '@types': fileURLToPath(new URL('./src/plugin/types', import.meta.url)),
69 | '@utils': fileURLToPath(new URL('./src/plugin/utils', import.meta.url)),
70 | },
71 | extensions: [
72 | '.js',
73 | '.json',
74 | '.jsx',
75 | '.mjs',
76 | '.mts',
77 | '.ts',
78 | '.tsx',
79 | '.vue',
80 | ],
81 | },
82 | server: {
83 | hmr: {
84 | protocol: 'ws',
85 | },
86 | open: process?.env?.NODE_ENV === 'playground' ? playgroundUrl : false,
87 | },
88 | });
89 |
90 | export const assetAttrsConfig: Record = {
91 | link: ['href'],
92 | video: ['src', 'poster'],
93 | source: ['src', 'srcset'],
94 | img: ['src', 'srcset'],
95 | image: ['xlink:href', 'href'],
96 | use: ['xlink:href', 'href']
97 | };
98 |
--------------------------------------------------------------------------------
/vitest.config.mts:
--------------------------------------------------------------------------------
1 | import { fileURLToPath } from 'node:url';
2 | import { mergeConfig, defineConfig, configDefaults } from 'vitest/config';
3 | import viteConfig from './vite.config.mts';
4 |
5 | export default mergeConfig(
6 | viteConfig,
7 | defineConfig({
8 | test: {
9 | environment: 'jsdom',
10 | exclude: [
11 | ...configDefaults.exclude,
12 | ],
13 | root: fileURLToPath(new URL('./', import.meta.url)),
14 | server: {
15 | deps: {
16 | inline: ['element-plus', 'vuetify-resize-drawer', 'vuetify']
17 | }
18 | },
19 | }
20 | })
21 | );
22 |
--------------------------------------------------------------------------------