Your IP : 216.73.216.84


Current Path : /home/helpink/www/media/astroid/assets/vendor/manager/src/components/helpers/
Upload File :
Current File : /home/helpink/www/media/astroid/assets/vendor/manager/src/components/helpers/LayoutBuilder.vue

<script setup>
import { onBeforeMount, onUpdated, ref } from "vue";
import draggable from "vuedraggable";
import LayoutGrid from "./LayoutGrid.vue";
const emit = defineEmits(['edit:Element', 'select:Element', 'update:System', 'save:Sublayout']);
const props = defineProps(['list', 'group', 'system', 'form', 'device', 'source']);
const layout = ref([]);
const map = {
    'root': 'sections',
    'sections': 'rows',
    'rows': 'cols',
    'cols': 'elements'
}

let elClass = '';
let handle = '';

function initLayout() {
    layout.value = props.list;
    if (props.group === 'root') {
        elClass = 'astroid-sections row row-cols-1 g-3'; 
        handle = '.section-handle';
    } else if (props.group === 'sections') {
        elClass = 'astroid-section';
        handle = '.row-handle';
    } else if (props.group === 'rows') {
        elClass = 'astroid-rows row g-2';
        handle = '.column-handle';
    } else if (props.group === 'cols') {
        elClass = 'astroid-cols';
    } else if (props.group === 'elements') {
        elClass = 'astroid-elements';
    }
    layout.value[map[props.group]].forEach(element => {
        if (typeof props.system[element.type] !== 'undefined') {
            updateSystem(element.type);
        }
        if (element.type === 'column') {
            if (['1','2','3','4','5','6','7','8','9','10','11','12'].includes(element.size+'')) {
                const tmp = element.size;
                element.size = {
                    xxl: tmp,
                    xl: tmp,
                    lg: tmp,
                    md: 12,
                    sm: 12,
                    xs: 12
                }
            }
        }
        if (element.type === 'row') {
            if (typeof element.fill === 'undefined') {
                element.fill = true;
            }
        }
        if (typeof element.state === 'undefined') {
            element.state = 1;
        }
        showGrid.value[element.id] = false;
    });
}

onBeforeMount(()=>{
    initLayout();
})

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

function updateSystem(addonType, value = false) {
    emit('update:System', addonType, value);
}

// Grid layout configure
const showGrid  = ref(new Object());
const _addtype = ref('');

function showGridModal(id, type) {
    _addtype.value = type;
    showGrid.value[id] = true;
}

function addGrid(element, index, grid = []) {
    const sec = Date.now() * 1000 + Math.random() * 1000;
    let tmp_grid = [];
    grid.forEach(col => {
        if (col > 0) {
            tmp_grid.push({
                id: sec.toString(16).replace(/\./g, "").padEnd(14, "0")+Math.trunc(Math.random() * 100000000),
                type: 'column',
                size: {
                    xxl: col,
                    xl: col,
                    lg: col,
                    md: 12,
                    sm: 12,
                    xs: 12
                },
                elements: []
            })
        }
    });
    switch (_addtype.value) {
        case 'section':
            layout.value[map[props.group]].splice(index+1, 0, {
                id: sec.toString(16).replace(/\./g, "").padEnd(14, "0")+Math.trunc(Math.random() * 100000000),
                type: 'section',
                rows: [
                    {
                        id: sec.toString(16).replace(/\./g, "").padEnd(14, "0")+Math.trunc(Math.random() * 100000000),
                        type: 'row',
                        cols: tmp_grid
                    }
                ],
                params: [
                    {name: 'title', value: 'Astroid Section'}
                ]
            });
            break;

        case 'row':
            layout.value[map[props.group]][index].rows.push({
                id: sec.toString(16).replace(/\./g, "").padEnd(14, "0")+Math.trunc(Math.random() * 100000000),
                type: 'row',
                cols: tmp_grid
            });
            break;
    
        default:
            break;
    }
    showGrid.value[element.id] = false;
}

function editGrid(element, grid = []) {
    const sec = Date.now() * 1000 + Math.random() * 1000;
    let col_idx = 0;
    grid.forEach(col => {
        if (col > 0) {
            if (col_idx < element.cols.length) {
                element.cols[col_idx].size[props.device] = col;
            } else {
                element.cols.push({
                    id: sec.toString(16).replace(/\./g, "").padEnd(14, "0")+Math.trunc(Math.random() * 100000000),
                    type: 'column',
                    size: {
                        xxl: col,
                        xl: col,
                        lg: col,
                        md: 12,
                        sm: 12,
                        xs: 12
                    },
                    elements: []
                });
            }
            col_idx ++
        } 
    });
    while (col_idx < element.cols.length) {
        if (col_idx>0 && element.cols[col_idx].elements.length > 0) {
            element.cols[col_idx - 1].elements = [...element.cols[col_idx - 1].elements , ...element.cols[col_idx].elements];
        }
        element.cols.splice(col_idx, 1);
    }
    showGrid.value[element.id] = false;
}

// Element Configure
function selectElement(element) {
    emit('select:Element', element);
}

function cloneElement(element) {
    let id = Date.now() * 1000 + Math.random() * 1000;
    id = id.toString(16).replace(/\./g, "").padEnd(14, "0")+Math.trunc(Math.random() * 100000000);
    let tmp = {};
    Object.keys(element).forEach(key => {
        if (key === 'rows' || key === 'cols' || key === 'elements') {
            tmp[key] = [];
            element[key].forEach(el => {
                tmp[key].push(cloneElement(el));
            });
        } else if (key === 'id') {
            tmp.id = id;
        } else {
            tmp[key] = element[key];
        }
    });
    return tmp;
}

function duplicateElement(element, index) {
    layout.value[map[props.group]].splice(index+1, 0, cloneElement(element));
}

function _editElement(element) {
    emit('edit:Element', element);
}

function elementState(element) {
    if (typeof element.state === 'undefined') {
        element.state = 0;
    } else {
        element.state = Math.abs(element.state - 1);
    }
}

function findSystemAddon(element) {
    switch (element.type) {
        case 'section':
            element.rows.forEach(el => {
                findSystemAddon(el);
            })
            break;
        case 'row':
            element.cols.forEach(el => {
                findSystemAddon(el);
            })
            break;
        case 'column':
            element.elements.forEach(el => {
                findSystemAddon(el);
            })
            break;
        default:
            if (typeof props.system[element.type] !== 'undefined') {
                updateSystem(element.type, true);
            }
            break;
    }
}

function deleteElement(element, index) {
    if (confirm('Are you sure?')) {
        findSystemAddon(element);
        layout.value[map[props.group]].splice(index, 1);
    }
}

function getColumnClass(element) {
    let column_class = props.device !== 'xs' ? `col-`+element.size[props.device] : `col-`+element.size.xs;
    let column_order = 0;
    if (typeof element.params !== 'undefined') {
        element.params.every(param => {
            if (param.name === 'column_order_' + props.device) {
                column_order = parseInt(param.value);
                return false;
            }
            return true;
        });
    }
    if (column_order > 0) {
        column_class += ' order-' + column_order;
    }
    return column_class;
}

// Save sublayout
function saveLayout(element) {
    emit('save:Sublayout', {
        sections : [element],
        devices : layout.value.devices
    });
}

function selectLayout(element) {
    emit('select:Element', element, 'loadSublayout');
}
</script>
<template>
    <draggable
        :class="elClass"
        tag="div"
        :list="layout[map[props.group]]"
        :group="{ name: map[props.group] }"
        ghost-class="ghost"
        :handle="handle"
        item-key="id"
    >
        <template #item="{ element, index }">
            <div v-if="props.group === `root`" class="astroid-section-container">
                <nav class="section-toolbar navbar">
                    <span class="navbar-text" href="#"><span class="section-handle handle bg-body-secondary px-1 py-1 rounded me-1"><i class="fa-solid fa-arrows-up-down-left-right"></i></span> {{ element.params.find((param) => param.name === 'title').value }}</span>
                    <ul class="nav">
                        <li class="nav-item">
                            <a class="nav-link px-1" href="#" title="Edit Section" @click.prevent="_editElement(element)"><i class="fas fa-pencil-alt"></i></a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link px-1" href="#" title="Duplicate Section" @click.prevent="duplicateElement(element, index)"><i class="fas fa-copy"></i></a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link px-1" href="#" @click.prevent="deleteElement(element, index)" title="Remove Section"><i class="fas fa-trash-alt"></i></a>
                        </li>
                        <li class="nav-item" v-if="props.source === `root`">
                            <a class="nav-link px-1" href="#" title="Load Section from Sublayout" @click.prevent="selectLayout(element)"><i class="fa-solid fa-cubes"></i></a>
                        </li>
                        <li class="nav-item" v-if="props.source === `root`">
                            <a class="nav-link px-1" href="#" title="Save Section as Sublayout" @click.prevent="saveLayout(element)"><i class="fa-solid fa-floppy-disk"></i></a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link px-1" href="#" @click.prevent="showGridModal(element.id, 'row')"><i class="fas fa-plus"></i> New Row</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link px-1" href="#" @click.prevent="showGridModal(element.id, 'section')"><i class="fas fa-plus"></i> New Section</a>
                        </li>
                    </ul>
                </nav>
                <LayoutBuilder :list="element" :group="map[props.group]" :system="props.system" :form="props.form" :device="props.device" @edit:Element="_editElement" @select:Element="selectElement" @update:System="updateSystem" />
                <Transition name="fade">
                    <LayoutGrid v-if="showGrid[element.id]" :element="element" @update:close-element="(id) => {showGrid[id] = false}" @update:saveElement="(grid) => {addGrid(element, index, grid)}" />
                </Transition>
            </div>
            <div v-else-if="props.group === `sections`" class="astroid-row-container position-relative">
                <div class="row-toolbar position-absolute">
                    <div class="row-handle handle text-dark-emphasis"><i class="fa-solid fa-arrows-up-down-left-right"></i></div>
                    <div><a href="#" title="Edit Columns" class="text-dark-emphasis" @click.prevent="showGridModal(element.id, 'row')"><i class="fa-solid fa-table-columns"></i></a></div>
                    <div><a href="#" @click.prevent="_editElement(element)" title="Edit Row" class="text-dark-emphasis"><i class="fa-solid fa-pencil"></i></a></div>
                    <div><a href="#" @click.prevent="deleteElement(element, index)" title="Remove Row" class="text-dark-emphasis"><i class="fa-solid fa-trash"></i></a></div>
                    <div>
                        <input class="form-check-input" type="checkbox" v-model="element.fill" :id="`fill-row-`+element.id" title="Fill Up Row">
                    </div>
                </div>
                <LayoutBuilder :list="element" :group="map[props.group]" :system="props.system" :form="props.form" :device="props.device" @edit:Element="_editElement" @select:Element="selectElement" @update:System="updateSystem" />
                <Transition name="fade">
                    <LayoutGrid v-if="showGrid[element.id]" :element="element" @update:close-element="(id) => {showGrid[id] = false}" @update:saveElement="(grid) => {editGrid(element, grid)}" />
                </Transition>
            </div>
            <div v-else-if="props.group === `rows`" class="astroid-col-container" :class="getColumnClass(element)">
                <div class="d-flex justify-content-between align-items-center">
                    <div class="column-size mb-2">
                        <select class="form-select form-select-sm" v-model="element.size[props.device]" aria-label="Select column size" :id="`select-column-size-`+element.id">
                            <option v-for="option in [1,2,3,4,5,6,7,8,9,10,11,12]" :value="option" :key="option">{{ 'col'+(props.device !== 'xs' ? '-'+props.device+'-'+option : '-'+option) }}</option>
                        </select>
                    </div>
                    <div class="column-toolbar">
                        <span class="column-handle handle bg-body-secondary px-1 py-1 rounded text-dark-emphasis me-1"><i class="fa-solid fa-arrows-up-down-left-right"></i></span>
                        <a href="#" @click.prevent="_editElement(element)" title="Edit Column"><span class="bg-body-secondary px-1 py-1 rounded text-dark-emphasis me-1"><i class="fas fa-pencil-alt"></i></span></a>
                    </div>
                </div>
                <LayoutBuilder :list="element" :group="map[props.group]" :system="props.system" :form="props.form" :device="props.device" @edit:Element="_editElement" @select:Element="selectElement" @update:System="updateSystem" />
                <div class="add-element d-flex justify-content-center">
                    <a href="#" @click.prevent="selectElement(element)" class="bg-light text-dark border px-2 py-1 rounded-pill"><i class="fas fa-plus"></i><span class="add-element-text ms-1">Add Element</span></a>
                </div>
            </div>
            <div v-else-if="props.group === `cols` && typeof props.form[element.type] !== `undefined`" class="astroid-element card card-default card-body" :class="{'element-disabled' : !element.state}">
                <div class="d-flex justify-content-between">
                    <div class="element-name">
                        <div><i class="text-body-tertiary me-2" :class="props.form[element.type].info.icon"></i>{{ element.params.find((param) => param.name === 'title').value }}<i v-if="element.type === `sublayout`" class="fa-regular fa-circle-question text-body-tertiary ms-1" :title="element.params.find((param) => param.name === 'desc').value"></i></div>
                        <div class="text-body-tertiary form-text">{{ element.type }}</div>
                    </div>
                    <div class="element-toolbar">
                        <ul class="nav">
                            <li class="nav-item">
                                <a class="nav-link py-0 ps-0 pe-1" href="#" title="Enable/Disable Element" @click.prevent="elementState(element)"><i :class="{'fas fa-eye' : element.state, 'fas fa-eye-slash' : !element.state}"></i></a>
                            </li>
                            <li class="nav-item" v-if="element.type !== `sublayout`">
                                <a class="nav-link py-0 px-1" href="#" title="Edit Element" @click.prevent="_editElement(element)"><i class="fas fa-pencil-alt"></i></a>
                            </li>
                            <li class="nav-item">
                                <a class="nav-link py-0 px-1" href="#" title="Duplicate Element" @click.prevent="duplicateElement(element, index)"><i class="fas fa-copy"></i></a>
                            </li>
                            <li class="nav-item">
                                <a class="nav-link py-0 pe-0 ps-1" href="#" @click.prevent="deleteElement(element, index)" title="Remove Element"><i class="fas fa-trash-alt"></i></a>
                            </li>
                        </ul>
                    </div>
                </div>
            </div>
        </template>
    </draggable>
</template>