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/Layout.vue

<script setup>
import { computed, onBeforeMount, onUpdated, reactive, ref, watch, inject } from 'vue';
import { MultiListSelect } from "vue-search-select"
import axios from "axios";
import LayoutBuilder from "./LayoutBuilder.vue";
import Modal from "./Modal.vue";
import SelectElement from "./SelectElement.vue";
import LayoutGrid from "./LayoutGrid.vue";
const emit = defineEmits(['update:modelValue', 'update:subLayouts', 'update:Preset']);
const props = defineProps({
    modelValue: { type: String, default: '' },
    field: { type: Object, default: null },
    source: { type: String, default: 'root' },
    presetUpdated: { type: Boolean, default: false }
});
const constant  =   inject('constant', {});
const language  =   inject('language', []);

onBeforeMount(()=>{
    layout.value    =   props.field.input.value;
    form_template.value = constant.form_template;
    Object.keys(constant.form_template).forEach(key => {
        if (!constant.form_template[key].info.multiple) {
            system.value[constant.form_template[key].info.type] = true;
        }
    })
    if (typeof layout.value.devices === 'undefined') {
        layout.value.devices = [ 
            { "code": "lg", "icon": "fa-solid fa-computer", "title": "Large Device" }, 
            { "code": "md", "icon": "fa-solid fa-laptop", "title": "Medium Device" }, 
            { "code": "sm", "icon": "fa-solid fa-tablet-screen-button", "title": "On Tablet" }, 
            { "code": "xs", "icon": "fa-solid fa-mobile-screen", "title": "On Mobile" } 
        ];
    }
    activeDevice.value = layout.value.devices[0].code;

    if (props.source === 'article_layouts') {
        let url = constant.site_url+"administrator/index.php?option=com_ajax&astroid=getArticleFormTemplate&id="+constant.template_id+"&ts="+Date.now();
        if (process.env.NODE_ENV === 'development') {
            url = "articleformtemplate_ajax.txt?ts="+Date.now();
        }
        axios.get(url)
        .then(function (response) {
            if (response.data.status === 'success') {
                form_template.value = response.data.data;
            }
        })
        .catch(function (error) {
            // handle error
            console.log(error);
        });
    }
})
onUpdated(()=>{
    if (props.presetUpdated === true) {
        emit('update:Preset', false);
        const tmp = JSON.parse(props.modelValue);
        layout.value.sections = tmp.sections;
        if (typeof tmp.devices !== 'undefined') {
            layout.value.devices = tmp.devices;
            activeDevice.value = tmp.devices[0].code;
        } else {
            layout.value.devices = [
                { "code": "lg", "icon": "fa-solid fa-computer", "title": "Large Device" },
                { "code": "md", "icon": "fa-solid fa-laptop", "title": "Medium Device" },
                { "code": "sm", "icon": "fa-solid fa-tablet-screen-button", "title": "On Tablet" },
                { "code": "xs", "icon": "fa-solid fa-mobile-screen", "title": "On Mobile" }
            ];
            activeDevice.value = layout.value.devices[0].code;
        }
    }
})
const layout = ref([]);
const system = ref({});
const activeDevice = ref('lg');
const responsive = [
    {
        code: 'xxl',
        icon: 'fa-solid fa-tv',
        title: 'Extra Extra Large'
    },
    {
        code: 'xl',
        icon: 'fa-solid fa-desktop',
        title: 'Extra Large'
    },
    {
        code: 'lg',
        icon: 'fa-solid fa-computer',
        title: 'Large Device'
    },
    {
        code: 'md',
        icon: 'fa-solid fa-laptop',
        title: 'Medium Device'
    },
    {
        code: 'sm',
        icon: 'fa-solid fa-tablet-screen-button',
        title: 'On Tablet'
    },
    {
        code: 'xs',
        icon: 'fa-solid fa-mobile-screen',
        title: 'On Mobile'
    },
]

function onSelectDevice(items, lastSelectItem) {
    if (items.length) {
        layout.value.devices = items
        activeDevice.value = layout.value.devices[0].code;
    } else {
        alert('You can not remove all devices!');
    }
}

function updateSystem(addonType, value = false) {
    if (typeof system.value[addonType] !== 'undefined') {
        system.value[addonType] = value;
    }
}

const _showModal = ref(false);
const _showElement = ref(false);
const _showGrid = ref(false);
const layout_text = computed(() => {
  return JSON.stringify(layout.value);
})
watch(layout_text, (newText) => {
    if (newText !== props.modelValue) {
        emit('update:modelValue', newText);
    }
})
const element = ref({});
const form_template = ref({});
function editElement(el) {
    element.value = el;
    _showModal.value = true;
}
function saveElement(param) {
    const updateParams = (el) => {
        if (element.value.type === el.type && element.value.id === el.id) {
            el.params = param;
            element.value = {};
            return true;
        }
        return false;
    };

    layout.value.sections.forEach(section => {
        if (updateParams(section)) return;
        section.rows.forEach(row => {
            if (updateParams(row)) return;
            row.cols.forEach(column => {
                if (updateParams(column)) return;
                column.elements.forEach(el => {
                    if (updateParams(el)) return;
                });
            });
        });
    });
}

const select_element_type = ref('');
function selectElement(el, type = '') {
    select_element_type.value = type;
    element.value = el;
    _showElement.value = true;
}
function addElement(addon) {
    if (select_element_type.value === 'loadSublayout') {
        let url = constant.site_url+"administrator/index.php?option=com_ajax&astroid=getlayout&ts="+Date.now();
        let sublayout_data = {};
        const sec = Date.now() * 1000 + Math.random() * 1000;
        if (process.env.NODE_ENV === 'development') {
            url = "editlayout_ajax.txt?ts="+Date.now();
        }
        const formData = new FormData(); // pass data as a form
        formData.append(constant.astroid_admin_token, 1);
        formData.append('name', addon.name);
        formData.append('template', constant.tpl_template_name);
        formData.append('type', 'layouts');
        axios.post(url, formData, {
            headers: {
                "Content-Type": "multipart/form-data",
            },
        }).then((response) => {
            if (response.data.status === 'success') {
                sublayout_data = JSON.parse(response.data.data.data);
                layout.value.sections.every((section, index) => {
                    if (element.value.id === section.id) {
                        sublayout_data.sections.forEach((section, sub_idx) => {
                            layout.value.sections.splice(index+sub_idx+1, 0, {
                                id: sec.toString(16).replace(/\./g, "").padEnd(14, "0")+Math.trunc(Math.random() * 100000000),
                                type: section.type,
                                rows: section.rows,
                                params: section.params,
                                state: 1
                            });
                        });
                        // continue
                        element.value = {};
                        return false;
                    }
                    return true;
                });
            }
        }).catch((err) => {
            console.error(err);
        });
    } else {
        let id = Date.now() * 1000 + Math.random() * 1000;
        id = id.toString(16).replace(/\./g, "").padEnd(14, "0")+Math.trunc(Math.random() * 100000000);
        let params = [
            {name: 'title', value: addon.title}
        ];
        if (addon.type === `sublayout`) {
            params.push({name: 'source', value: addon.name});
            params.push({name: 'desc', value: addon.desc});
            params.push({name: 'thumbnail', value: addon.thumbnail});
        }
        const new_element = {
            id: id,
            type: addon.type,
            state: 1,
            params: params
        }
        layout.value.sections.every(section => {
            section.rows.every(row => {
                row.cols.every(column => {
                    if (element.value.id === column.id) {
                        column.elements.push(new_element);
                        if (typeof system.value[addon.type] !== 'undefined') {
                            system.value[addon.type] = false;
                        }
                        element.value = {};
                        return false;
                    }
                    return true;
                });
                return true;
            });
            return true;
        });
        if (addon.type !== `sublayout`) {
            editElement(new_element);
        }
    }
}
function closeElement() {
    _showModal.value = false;
}

function addGrid(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: []
            })
        }
    });
    layout.value.sections.push({
        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'}
        ]
    });
    _showGrid.value = false;
}

// Sublayout
const formInfo = reactive({
    title: '',
    desc: '',
    thumbnail: '',
    name: ''
});
const toast_msg = reactive({
    header: '',
    body:'',
    icon: '',
    color:'darkviolet'
});
const files = ref(null);
const save_disabled = ref(false);
const sublayout =   ref('{"sections":[]}');
const _showSublayoutModal = ref(false);
const _subFormTitle = ref(null);
function openSaveLayout(data) {
    _showSublayoutModal.value = true;
    sublayout.value = JSON.stringify(data);
}
function onFileChange(e) {
    files.value = e.target.files || e.dataTransfer.files;
}
function saveSublayout() {
    let url = constant.site_url+"administrator/index.php?option=com_ajax&astroid=savelayout&ts="+Date.now();
    if (!formInfo.title) {
        alert('You have to input the Title')
        _subFormTitle.value.focus();
        return false;
    } 
    const formData = new FormData(); // pass data as a form
    const toastAstroidMsg = document.getElementById(props.field.input.id+`_saveSectionToast`);
    const toastBootstrap = Toast.getOrCreateInstance(toastAstroidMsg);
    formData.append(constant.astroid_admin_token, 1);
    formData.append('title', formInfo.title);
    formData.append('desc', formInfo.desc);
    formData.append('data', sublayout.value);
    formData.append('thumbnail_old', formInfo.thumbnail);
    if (files.value !== null && files.value.length) {
        formData.append('thumbnail', files.value[0]);
    }
    if (formInfo.name !== ``) {
        formData.append('name', formInfo.name);
    }
    formData.append('template', constant.tpl_template_name);
    save_disabled.value = true;
            
    axios.post(url, formData, {
        headers: {
            "Content-Type": "multipart/form-data",
        },
    })
    .then((response) => {
        if (response.data.status === 'success') {
            toast_msg.icon = 'fa-solid fa-rocket';
            toast_msg.header = 'Sub-Layout '+formInfo.title+' is saved.';
            toast_msg.body = 'You can use it to contribute to your layout builder.';
            toast_msg.color = 'green';
            save_disabled.value = false;
            formInfo.title = '';
            formInfo.desc = '';
            formInfo.name = '';
            formInfo.thumbnail = '';
            files.value = null;
            document.getElementById(props.field.input.id+`_saveLayout_form`).reset();
            sublayout.value = '{"sections":[]}';
            emit('update:subLayouts');
            _showSublayoutModal.value = false;
        } else {
            toast_msg.icon = 'fa-regular fa-face-sad-tear';
            toast_msg.header = 'Sub-layout '+formInfo.title+' is not saved.';
            toast_msg.body = response.data.message;
            toast_msg.color = 'red';
        }
        toastBootstrap.show();
    })
    .catch((err) => {
        console.error(err);
    });
}
</script>
<template>
    <div class="astroid-btn-group responsive-devices text-center" role="group" aria-label="Responsive Devices">
        <span v-for="(option, idx) in layout.devices" :key="idx">
            <input type="radio" class="btn-check" v-model="activeDevice" :id="props.field.input.id+`responsive-device-`+option.code" :value="option.code" autocomplete="off">
            <label class="btn btn-sm btn-as btn-outline-secondary" data-bs-toggle="tooltip" :data-bs-title="option.title" :for="props.field.input.id+`responsive-device-`+option.code"><i class="fa-xl" :class="option.icon"></i></label>
        </span>
        <span>
            <button class="layout-config btn btn-sm btn-as btn-outline-secondary" @click.prevent="" data-bs-toggle="modal" :data-bs-target="`#`+props.field.input.id+`_selectDevices`"><i class="fas fa-cog"></i></button>
        </span>
    </div>
    <div class="modal fade" :id="props.field.input.id+`_selectDevices`" tabindex="-1" aria-labelledby="selectDevicesLabel" aria-hidden="true">
        <div class="modal-dialog modal-dialog-centered">
            <div class="modal-content">
                <div class="modal-header">
                    <h3 class="modal-title fs-5" id="selectDevicesLabel">Layout Configurations</h3>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <div>
                        <label :for="props.field.id+`-responsive-device-select`" class="form-label">Select your devices</label>
                        <multi-list-select
                            :list="responsive"
                            option-value="code"
                            option-text="title"
                            :id="props.field.id+`-responsive-device-select`"
                            :selected-items="layout.devices"
                            placeholder="Select a device"
                            @select="onSelectDevice"
                        >
                        </multi-list-select>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div v-if="(typeof layout.sections === 'undefined' || layout.sections.length === 0)" class="text-center">
        <button class="btn btn-lg btn-as btn-as-primary mt-4" :class="{'btn-primary' : props.source === 'joomla_module'}" @click.prevent="_showGrid = true"><i class="fa-solid fa-plus me-2"></i>Add Section</button>
        <Transition name="fade">
            <LayoutGrid v-if="_showGrid" @update:close-element="_showGrid = false" @update:saveElement="addGrid" />
        </Transition>
    </div>
    <LayoutBuilder :list="layout" 
        group="root" :system="system" 
        :form="form_template" 
        :device="activeDevice" 
        :source="props.source"
        @edit:Element="editElement" 
        @select:Element="selectElement" 
        @update:System="updateSystem" 
        @save:Sublayout="openSaveLayout"
        />
    <Transition name="fade">
        <Modal v-if="_showModal" :element="element" :form="form_template[element.type]" @update:saveElement="saveElement" @update:close-element="closeElement" />
    </Transition>
    <Transition name="fade">
        <SelectElement v-if="_showElement" :form="form_template" :type="select_element_type" :system="system" :source="props.source" @update:close-element="_showElement = false" @update:selectElement="addElement" />
    </Transition>
    <form :id="props.field.input.id+`_saveLayout_form`" v-if="props.source === `root` && _showSublayoutModal">
        <div class="astroid-modal modal d-block" :id="props.field.input.id+`_saveLayout`" tabindex="-1" :aria-labelledby="props.field.input.id+`saveLayoutLabel`" aria-hidden="true">
            <div class="modal-dialog modal-dialog-centered">
                <div class="modal-content">
                    <div class="modal-header">
                        <h3 class="modal-title fs-5" :id="props.field.input.id+`_saveLayoutLabel`">Layout Information</h3>
                        <button type="button" class="btn-close" aria-label="Close" @click="_showSublayoutModal = false"></button>
                    </div>
                    <div class="modal-body">
                        <div>
                            <div class="mb-3">
                                <label :for="props.field.input.id+`_saveLayout_title`" class="form-label">Title</label>
                                <input type="text" v-model="formInfo.title" class="form-control" :id="props.field.input.id+`_saveLayout_title`" placeholder="Title" ref="_subFormTitle" required>
                            </div>
                            <div class="mb-3">
                                <label :for="props.field.input.id+`_saveLayout_desc`" class="form-label">Description</label>
                                <textarea class="form-control" v-model="formInfo.desc" :id="props.field.input.id+`_saveLayout_desc`" rows="3"></textarea>
                            </div>
                            <div v-if="formInfo.thumbnail !== ``" class="mb-3">
                                <img class="img-thumbnail" :src="constant.site_url + `/media/templates/site/` + constant.tpl_template_name + `/images/layouts/` + formInfo.thumbnail" :alt="formInfo.title">
                            </div>
                            <div class="mb-3">
                                <label :for="props.field.input.id+`_saveLayout_thumbnail`" class="form-label">Select your thumbnail</label>
                                <input class="form-control" type="file" @change="onFileChange" :id="props.field.input.id+`_saveLayout_thumbnail`">
                            </div>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-sm btn-as btn-as-light" aria-label="Close" :disabled="save_disabled" @click="_showSublayoutModal = false">{{ language.ASTROID_BACK }}</button>
                        <button type="button" class="btn btn-sm btn-as btn-primary btn-as-primary" @click.prevent="saveSublayout()" :disabled="save_disabled" v-html="language.JSAVE"></button>
                    </div>
                </div>
            </div>
        </div>
    </form>
    <div class="toast-container position-fixed bottom-0 end-0 p-3">
        <div :id="props.field.input.id+`_saveSectionToast`" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
            <div class="toast-header">
                <i class="me-2" :class="toast_msg.icon" :style="{color: toast_msg.color}"></i>
                <strong class="me-auto">{{ toast_msg.header }}</strong>
                <small>1 second ago</small>
                <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
            </div>
            <div class="toast-body">
                {{ toast_msg.body }}
            </div>
        </div>
    </div>
    <input
        :id="props.field.input.id"
        :name="props.source === 'joomla_module' ? 'astroid_module_layout' : props.field.input.name"
        :value="modelValue"
        type="hidden"
    />
</template>