2.0.5 • Published 11 months ago

vue3-resize-bounding v2.0.5

Weekly downloads
-
License
MIT
Repository
github
Last release
11 months ago

image Version License: MIT

Vue3 Resize Bounding is a simple, highly customizable Vue3 component that allows you to intuitively resize nested content using draggable border panels.

Demo

image

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 the knob 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.

2.0.5

11 months ago

2.0.4

1 year ago

2.0.3

1 year ago

2.0.2

1 year ago

2.0.1

1 year ago

1.0.5

1 year ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago