Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does it have support to Vue 3? #29

Open
danielduqueusma opened this issue Jan 8, 2021 · 1 comment
Open

Does it have support to Vue 3? #29

danielduqueusma opened this issue Jan 8, 2021 · 1 comment

Comments

@danielduqueusma
Copy link

I d like to know if you have considered update the plugin to accept vue 3 implementation?

@danielduqueusma danielduqueusma changed the title Does it have suppor to Vue 3? Does it have support to Vue 3? Jan 8, 2021
@denisoed
Copy link

denisoed commented Jul 11, 2023

Solution for VUE 3

@danielduqueusma Create a local component and paste this code into it.

<script lang="ts">
import {
  ref,
  onMounted,
  onUpdated,
  onBeforeUnmount,
  defineComponent,
  useSlots,
  h,
} from 'vue';

export default defineComponent({
  name: 'MasonryGrid',
  props: {
    tag: {
      type: [String],
      default: 'div',
    },
    cols: {
      type: [Object, Number, String],
      default: 2,
    },
    gutter: {
      type: [Object, Number, String],
      default: 0,
    },
    css: {
      type: [Boolean],
      default: true,
    },
    columnTag: {
      type: [String],
      default: 'div',
    },
    columnClass: {
      type: [String, Array, Object],
      default: () => [],
    },
    columnAttr: {
      type: [Object],
      default: () => ({}),
    },
  },
  setup(props) {
    const slots = useSlots();
    const windowWidth = ref(0);
    const displayColumns = ref(0);
    const displayGutter = ref(0);
    const columns = ref<any[]>([]);

    const breakpointValue = (mixed, windowWidth: number) => {
      const valueAsNum = parseInt(mixed);

      if (valueAsNum > -1) {
        return mixed;
      } else if (typeof mixed !== 'object') {
        return 0;
      }

      let matchedBreakpoint = Infinity;
      let matchedValue = mixed.default || 0;

      for (let k in mixed) {
        const breakpoint = parseInt(k);
        const breakpointValRaw = mixed[breakpoint];
        const breakpointVal = parseInt(breakpointValRaw);

        if (isNaN(breakpoint) || isNaN(breakpointVal)) {
          continue;
        }

        const isNewBreakpoint =
          windowWidth <= breakpoint && breakpoint < matchedBreakpoint;

        if (isNewBreakpoint) {
          matchedBreakpoint = breakpoint;
          matchedValue = breakpointValRaw;
        }
      }

      return matchedValue;
    };

    const getColumns = () => {
      const cols = [];
      const childItems = slots?.default()[0]?.children;

      if (!childItems?.length) return [];

      // Loop through child elements
      for (
        let i = 0, visibleItemI = 0;
        i < childItems.length;
        i++, visibleItemI++
      ) {
        // Get the column index the child item will end up in
        const columnIndex = visibleItemI % displayColumns.value;

        if (!cols[columnIndex]) {
          cols[columnIndex] = [];
        }

        cols[columnIndex].push(childItems[i]);
      }
      return cols;
    };

    const reCalculate = () => {
      const previousWindowWidth = windowWidth.value;
      windowWidth.value = window ? window.innerWidth : null || Infinity;

      if (previousWindowWidth === windowWidth.value) {
        return;
      }

      reCalculateColumnCount(windowWidth.value);
      reCalculateGutterSize(windowWidth.value);
      columns.value = getColumns();
    };

    const reCalculateGutterSize = (windowWidth: number) => {
      displayGutter.value = breakpointValue(props.gutter, windowWidth);
    };

    const reCalculateColumnCount = (windowWidth: number) => {
      let newColumns = breakpointValue(props.cols, windowWidth);
      newColumns = Math.max(1, Number(newColumns) || 0);
      displayColumns.value = newColumns;
    };

    onMounted(() => {
      reCalculate();
      if (window) {
        window.addEventListener('resize', reCalculate);
      }
    });

    onUpdated(() => {
      reCalculate();
    });

    onBeforeUnmount(() => {
      if (window) {
        window.removeEventListener('resize', reCalculate);
      }
    });

    return () => {
      const columnsContainingChildren = getColumns();
      const isGutterSizeUnitless =
        parseInt(displayGutter.value) === displayGutter.value * 1;
      const gutterSizeWithUnit = isGutterSizeUnitless
        ? `${displayGutter.value}px`
        : displayGutter.value;

      const columnStyle = {
        width: `${100 / displayColumns.value}%`,
        display: ['-webkit-box', '-ms-flexbox', 'flex'],
        flexDirection: 'column',
        gap: gutterSizeWithUnit,
      };

      const columns = columnsContainingChildren.map((children, index) => {
        /// Create column element and inject the children
        return h(
          props.columnTag,
          {
            key: index + '-' + columnsContainingChildren.length,
            style: props.css ? columnStyle : null,
            class: props.columnClass,
            attrs: props.columnAttr,
          },
          children
        ); // specify child items here
      });

      const containerStyle = {
        display: ['-webkit-box', '-ms-flexbox', 'flex'],
        gap: gutterSizeWithUnit,
      };

      // Return wrapper with columns
      return h(
        props.tag, // tag name
        props.css ? { style: containerStyle } : null, // element options
        columns // column vue elements
      );
    };
  },
});
</script>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants