vue3-resize-bounding
v2.0.5
Published
Vue3 highly customizable component for resizing nested content
Downloads
39
Maintainers
Readme
Vue3 Resize Bounding is a simple, highly customizable Vue3 component that allows you to intuitively resize nested content using draggable border panels.
Interactive Grid (Example):
Installation
npm i vue3-resize-bounding
# or
yarn add vue3-resize-bounding
Usage
<!-- @filename: MyComponent.vue -->
<script setup lang="ts">
import { ref } from "vue";
import ResizeBounding from "vue3-resize-bounding";
const container = ref({ width: 320, height: 480 });
</script>
<template>
<ResizeBounding
:width="container.width"
:height="container.height"
:min-width="240"
:max-width="480"
:min-height="120"
:directions="'hv'"
:options="{
knob: {
show: true
}
}"
:style="{ border: '1px solid gray' }"
@update:width="(width) => (container.width = width)"
@update:height="(height) => (container.height = height)"
>
<!-- CONTENT START -->
<div :style="{ width: '100%', height: '100%' }">My Container</div>
<!-- CONTENT END -->
<!-- KNOB INNER CONTENT START -->
<template #knob>
<div class="some-icon"></div>
</template>
<!-- KNOB INNER CONTENT END -->
</ResizeBounding>
</template>
Register the component globally:
// @filename: main.ts
import App from "@/App";
import { createApp } from "vue";
import ResizeBounding from "vue3-resize-bounding";
const app = createApp(App);
app.use(ResizeBounding, { name: "resize-bounding" });
app.mount("#app");
Properties
Events
Slots
Customization
Overriding:
<template>
<div class="my-class">
<ResizeBounding v-bind="$attrs"
options={{
knob: {
show: true,
},
}}>
<slot />
<template #knob>
<slot name="knob">
</template>
</ResizeBounding>
</div>
</template>
<script setup lang="ts">
import ResizeBounding, { type Props } from "vue3-resize-bounding";
defineProps<Props>();
</script>
Touch Area To increase the touch area, set the value to
options.activeAreaWidth
or use increased height of theknob
Default value is undefined
States styling:
By default, to style the active state (both .focused
or .pressed
), the .actvie
class is used;
So the style definition looks like this:
const styles = {
// Active (focused/pressed) state:
splitter: {
[`.${globalClassNames(prefix).pane}.active &`]: {
background: "cornflowerblue",
},
},
knob: {
[`.${globalClassNames(prefix).pane}.active &`]: {
background: "cornflowerblue",
},
},
};
To separately configure the focused state or the pressed state of a splitter/knob, use the included :options="{ addStateClasses: true }"
flag and the generated state classes:
const styles = {
splitter: {
// Focused state:
[`.${prefix}-pane.focused &`]: {
backgroundColor: "blue",
},
// Pressed state:
[`.${prefix}-pane.pressed &`]: {
backgroundColor: "red",
},
},
knob: {
// Focused state:
[`.${prefix}-pane.focused &`]: {
backgroundColor: "blue",
},
// Pressed state:
[`.${prefix}-pane.pressed &`]: {
backgroundColor: "red",
},
},
};
Using css
(preprocessors)
Use the included :options="{ addStateClasses: true }"
flag to style the .selected
and .pressed
states separately.
<script setup lang="ts">
import { ref } from "vue";
import ResizeBounding from "vue3-resize-bounding";
const container = ref({ width: 320, height: 480 });
</script>
<template>
<ResizeBounding
:width="container.width"
:height="container.height"
:min-width="240"
:max-width="480"
:min-height="120"
:directions="'hv'"
:options="{ addStateClasses: true, knob: { show: true } }"
:style="{ border: '1px solid gray' }"
@update:width="(width) => (container.width = width)"
@update:height="(height) => (container.height = height)"
>
<!-- CONTENT START -->
<div :style="{ width: '100%', height: '100%' }">My Container</div>
<!-- CONTENT END -->
<!-- KNOB INNER CONTENT START -->
<template #knob>
<div class="some-icon"></div>
</template>
<!-- KNOB INNER CONTENT END -->
</ResizeBounding>
</template>
<style lang="scss">
$prefix: "resize-bounding-";
.#{$prefix} {
&-container {
}
&-pane {
/* Normal state */
.#{$prefix}splitter {
&--container {
}
}
.#{$prefix}knob {
}
/* * * Default `options` settings * * */
/* Both selected and pressed states */
&.active {
.#{$prefix}splitter {
}
.#{$prefix}knob {
}
}
/* * * Separate states ({ addStateClasses: true }) * * */
/* Normal state */
&.normal {
.#{$prefix}splitter {
}
.#{$prefix}knob {
}
}
/* Focused state */
&.focused {
.#{$prefix}splitter {
}
.#{$prefix}knob {
}
}
/* Pressed state */
&.pressed {
.#{$prefix}splitter {
}
.#{$prefix}knob {
}
}
}
}
</style>
Default settings (options/styles)
<!-- @filename: MyResizeBoundingComponent.vue -->
<script lang="ts">
import ResizeBounding, { PREFIX } from "vue3-resize-bounding";
/* * * Default styles and classes * * */
const options = {
width: 4,
activeAreaWidth: undefined,
position: "central", // 'central' | 'internal' | 'external'
knob: {
show: true,
normalHidden: true,
},
cursor: {
horizontal: "col-resize",
},
touchActions: true,
};
// Below are all the default styles purely for demonstration purposes
// In reality, you can only override the necessary properties
const styles = (prefix: string): IStyles => ({
container: [
globalClassNames(prefix).container,
{ displayName: globalClassNames(prefix).container, position: "relative" },
],
pane: [
globalClassNames(prefix).pane,
{
displayName: globalClassNames(prefix).pane,
position: "absolute",
display: "block",
zIndex: 9999,
touchAction: "none",
},
],
splitter: [
globalClassNames(prefix).splitter,
{
displayName: globalClassNames(prefix).splitter,
position: "absolute",
zIndex: 9999,
transition: "background 125ms ease-out",
[`.${globalClassNames(prefix).pane}.active &`]: {
background: "cornflowerblue",
},
/*
Focused state:
[`.${globalClassNames(prefix).pane}.focused &`]: {},
Pressed state:
[`.${globalClassNames(prefix).pane}.pressed &`]: {}
*/
},
],
splitterContainer: [
globalClassNames(prefix).splitterContainer,
{
displayName: globalClassNames(prefix).splitterContainer,
position: "relative",
top: "50%",
left: "50%",
width: `0px`,
height: `0px`,
},
],
knob: [
globalClassNames(prefix).knob,
{
displayName: globalClassNames(prefix).knob,
position: "relative",
width: "64px",
height: "6px",
background: "gray",
borderRadius: "3px",
transform: "translate(-50%, -50%)",
transition: "background 125ms ease-out",
[`.${globalClassNames(prefix).pane}.active &`]: {
background: "cornflowerblue",
},
/*
Focused state:
[`.${globalClassNames(prefix).pane}.focused &`]: {},
Pressed state:
[`.${globalClassNames(prefix).pane}.pressed &`]: {}
*/
},
],
});
</script>
Author
Mikhail Grebennikov - yamogoo
This project is licensed under the terms of the MIT license.