<template>
    <div>
        <div class="content-section introduction">
            <div class="feature-intro">
                <h1>DataTable <span>InCell Edit</span></h1>
                <p>In cell editing provides a rapid and user friendly way to manipulate the data. The datatable provides a flexible API so that the editing behavior is implemented by the page author whether it utilizes v-model or vuex.</p>
            </div>
            <AppDemoActions />
        </div>

        <div class="content-section implementation p-fluid">
            <div class="card">
                <h5>Cell Editing</h5>
                <p>Validations, dynamic columns and reverting values with the escape key.</p>
                <DataTable :value="products1" editMode="cell" @cell-edit-complete="onCellEditComplete" class="editable-cells-table" responsiveLayout="scroll">
                    <Column v-for="col of columns" :key="col.field" :field="col.field" :header="col.header" style="width: 25%">
                        <template #editor="{ data, field }">
                            <InputText v-model="data[field]" autofocus />
                        </template>
                    </Column>
                </DataTable>
            </div>

            <div class="card">
                <h5>Row Editing</h5>
                <DataTable v-model:editingRows="editingRows" :value="products2" editMode="row" dataKey="id" @row-edit-save="onRowEditSave" responsiveLayout="scroll">
                    <Column field="code" header="Code" style="width: 20%">
                        <template #editor="{ data, field }">
                            <InputText v-model="data[field]" autofocus />
                        </template>
                    </Column>
                    <Column field="name" header="Name" style="width: 20%">
                        <template #editor="{ data, field }">
                            <InputText v-model="data[field]" />
                        </template>
                    </Column>
                    <Column field="inventoryStatus" header="Status" style="width: 20%">
                        <template #editor="{ data, field }">
                            <Dropdown v-model="data[field]" :options="statuses" optionLabel="label" optionValue="value" placeholder="Select a Status">
                                <template #option="slotProps">
                                    <span :class="'product-badge status-' + slotProps.option.value.toLowerCase()">{{ slotProps.option.label }}</span>
                                </template>
                            </Dropdown>
                        </template>
                        <template #body="slotProps">
                            {{ getStatusLabel(slotProps.data.inventoryStatus) }}
                        </template>
                    </Column>
                    <Column field="price" header="Price" style="width: 20%">
                        <template #editor="{ data, field }">
                            <InputText v-model="data[field]" />
                        </template>
                    </Column>
                    <Column :rowEditor="true" style="width: 10%; min-width: 8rem" bodyStyle="text-align:center"></Column>
                </DataTable>
            </div>

            <div class="card">
                <h5>Cell Editing with Sorting and Filter</h5>
                <DataTable v-model:filters="filters" :value="products3" editMode="cell" @cell-edit-complete="onCellEditComplete" class="editable-cells-table" filterDisplay="row" responsiveLayout="scroll">
                    <Column v-for="col of columns" :key="col.field" :field="col.field" :header="col.header" style="width: 25%" sortable filter>
                        <template #filter="{ filterModel, filterCallback }">
                            <InputText v-model="filterModel.value" v-tooltip.top.focus="'Hit enter key to filter'" type="text" @keydown.enter="filterCallback()" class="p-column-filter" />
                        </template>
                        <template #editor="{ data, field }">
                            <InputText v-model="data[field]" autofocus />
                        </template>
                    </Column>
                </DataTable>
            </div>
        </div>

        <AppDoc name="DataTableEditDemo" :sources="sources" :service="['ProductService']" :data="['products-small']" github="datatable/DataTableEditDemo.vue" />
    </div>
</template>

<script>
import { FilterMatchMode } from 'primevue/api';
import ProductService from '../../service/ProductService';

export default {
    data() {
        return {
            editingRows: [],
            columns: null,
            products1: null,
            products2: null,
            products3: null,
            statuses: [
                { label: 'In Stock', value: 'INSTOCK' },
                { label: 'Low Stock', value: 'LOWSTOCK' },
                { label: 'Out of Stock', value: 'OUTOFSTOCK' }
            ],
            filters: {
                code: { value: null, matchMode: FilterMatchMode.STARTS_WITH },
                name: { value: null, matchMode: FilterMatchMode.STARTS_WITH },
                quantity: { value: null, matchMode: FilterMatchMode.STARTS_WITH },
                price: { value: null, matchMode: FilterMatchMode.STARTS_WITH }
            },
            sources: {
                'options-api': {
                    tabName: 'Options API Source',
                    content: `
<template>
    <div class="p-fluid">
        <div class="card">
            <h5>Cell Editing</h5>
            <p>Validations, dynamic columns and reverting values with the escape key.</p>
            <DataTable :value="products1" editMode="cell" @cell-edit-complete="onCellEditComplete" class="editable-cells-table" responsiveLayout="scroll">
                <Column v-for="col of columns" :field="col.field" :header="col.header" :key="col.field" style="width:25%">
                    <template #editor="{ data, field }">
                        <InputText v-model="data[field]" autofocus />
                    </template>
                </Column>
            </DataTable>
        </div>

        <div class="card">
            <h5>Row Editing</h5>
            <DataTable :value="products2" editMode="row" dataKey="id" v-model:editingRows="editingRows" @row-edit-save="onRowEditSave" responsiveLayout="scroll">
                <Column field="code" header="Code" style="width:20%">
                    <template #editor="{ data, field }">
                        <InputText v-model="data[field]" autofocus />
                    </template>
                </Column>
                <Column field="name" header="Name" style="width:20%">
                    <template #editor="{ data, field }">
                        <InputText v-model="data[field]" />
                    </template>
                </Column>
                <Column field="inventoryStatus" header="Status" style="width:20%">
                    <template #editor="{ data, field }">
                        <Dropdown v-model="data[field]" :options="statuses" optionLabel="label" optionValue="value" placeholder="Select a Status">
                            <template #option="slotProps">
                                <span :class="'product-badge status-' + slotProps.option.value.toLowerCase()">{{slotProps.option.label}}</span>
                            </template>
                        </Dropdown>
                    </template>
                    <template #body="slotProps">
                        {{getStatusLabel(slotProps.data.inventoryStatus)}}
                    </template>
                </Column>
                <Column field="price" header="Price" style="width:20%">
                    <template #editor="{ data, field }">
                        <InputText v-model="data[field]" />
                    </template>
                </Column>
                <Column :rowEditor="true" style="width:10%; min-width:8rem" bodyStyle="text-align:center"></Column>
            </DataTable>
        </div>

        <div class="card">
            <h5>Cell Editing with Sorting and Filter</h5>
            <DataTable :value="products3" editMode="cell" @cell-edit-complete="onCellEditComplete" class="editable-cells-table" filterDisplay="row" v-model:filters="filters" responsiveLayout="scroll">
                <Column v-for="col of columns" :field="col.field" :header="col.header" :key="col.field" style="width:25%" sortable filter>
                    <template #filter="{filterModel,filterCallback}">
                        <InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" v-tooltip.top.focus="'Hit enter key to filter'"/>
                    </template>
                    <template #editor="{ data, field }">
                        <InputText v-model="data[field]" autofocus />
                    </template>
                </Column>
            </DataTable>
        </div>
    </div>
</template>

<script>
import ProductService from './service/ProductService';
import {FilterMatchMode} from 'primevue/api';

export default {
    data() {
        return {
            editingRows: [],
            columns: null,
            products1: null,
            products2: null,
            products3: null,
            statuses: [
                {label: 'In Stock', value: 'INSTOCK'},
                {label: 'Low Stock', value: 'LOWSTOCK'},
                {label: 'Out of Stock', value: 'OUTOFSTOCK'}
            ],
            filters: {
                'code': {value: null, matchMode: FilterMatchMode.STARTS_WITH},
                'name': {value: null, matchMode: FilterMatchMode.STARTS_WITH},
                'quantity': {value: null, matchMode: FilterMatchMode.STARTS_WITH},
                'price': {value: null, matchMode: FilterMatchMode.STARTS_WITH}
            }
        }
    },
    productService: null,
    created() {
        this.productService = new ProductService();

        this.columns = [
            {field: 'code', header: 'Code'},
            {field: 'name', header: 'Name'},
            {field: 'quantity', header: 'Quantity'},
            {field: 'price', header: 'Price'}
        ];
    },
    methods: {
        onCellEditComplete(event) {
            let { data, newValue, field } = event;

            switch (field) {
                case 'quantity':
                case 'price':
                    if (this.isPositiveInteger(newValue))
                        data[field] = newValue;
                    else
                        event.preventDefault();
                break;

                default:
                    if (newValue.trim().length > 0)
                        data[field] = newValue;
                    else
                        event.preventDefault();
                break;
            }
        },
        isPositiveInteger(val) {
            let str = String(val);
            str = str.trim();
            if (!str) {
                return false;
            }
            str = str.replace(/^0+/, "") || "0";
            var n = Math.floor(Number(str));
            return n !== Infinity && String(n) === str && n >= 0;
        },
        onRowEditSave(event) {
            let { newData, index } = event;

            this.products2[index] = newData;
        },
        getStatusLabel(status) {
            switch(status) {
                case 'INSTOCK':
                    return 'In Stock';

                case 'LOWSTOCK':
                    return 'Low Stock';

                case 'OUTOFSTOCK':
                    return 'Out of Stock';

                default:
                    return 'NA';
            }
        }
    },
    mounted() {
        this.productService.getProductsSmall().then(data => this.products1 = data);
        this.productService.getProductsSmall().then(data => this.products2 = data);
        this.productService.getProductsSmall().then(data => this.products3 = data);
    }
}
<\\/script>

<style lang="scss" scoped>
::v-deep(.editable-cells-table td.p-cell-editing) {
    padding-top: 0;
    padding-bottom: 0;
}
</style>`
                },
                'composition-api': {
                    tabName: 'Composition API Source',
                    content: `
<template>
    <div class="p-fluid">
        <div class="card">
            <h5>Cell Editing</h5>
            <p>Validations, dynamic columns and reverting values with the escape key.</p>
            <DataTable :value="products1" editMode="cell" @cell-edit-complete="onCellEditComplete" class="editable-cells-table" responsiveLayout="scroll">
                <Column v-for="col of columns" :field="col.field" :header="col.header" :key="col.field" style="width:25%">
                    <template #editor="{ data, field }">
                        <InputText v-model="data[field]" autofocus />
                    </template>
                </Column>
            </DataTable>
        </div>

        <div class="card">
            <h5>Row Editing</h5>
            <DataTable :value="products2" editMode="row" dataKey="id" v-model:editingRows="editingRows" @row-edit-save="onRowEditSave" responsiveLayout="scroll">
                <Column field="code" header="Code" style="width:20%">
                    <template #editor="{ data, field }">
                        <InputText v-model="data[field]" autofocus />
                    </template>
                </Column>
                <Column field="name" header="Name" style="width:20%">
                    <template #editor="{ data, field }">
                        <InputText v-model="data[field]" />
                    </template>
                </Column>
                <Column field="inventoryStatus" header="Status" style="width:20%">
                    <template #editor="{ data, field }">
                        <Dropdown v-model="data[field]" :options="statuses" optionLabel="label" optionValue="value" placeholder="Select a Status">
                            <template #option="slotProps">
                                <span :class="'product-badge status-' + slotProps.option.value.toLowerCase()">{{slotProps.option.label}}</span>
                            </template>
                        </Dropdown>
                    </template>
                    <template #body="slotProps">
                        {{getStatusLabel(slotProps.data.inventoryStatus)}}
                    </template>
                </Column>
                <Column field="price" header="Price" style="width:20%">
                    <template #editor="{ data, field }">
                        <InputText v-model="data[field]" />
                    </template>
                </Column>
                <Column :rowEditor="true" style="width:10%; min-width:8rem" bodyStyle="text-align:center"></Column>
            </DataTable>
        </div>

        <div class="card">
            <h5>Cell Editing with Sorting and Filter</h5>
            <DataTable :value="products3" editMode="cell" @cell-edit-complete="onCellEditComplete" class="editable-cells-table" filterDisplay="row" v-model:filters="filters" responsiveLayout="scroll">
                <Column v-for="col of columns" :field="col.field" :header="col.header" :key="col.field" style="width:25%" sortable filter>
                    <template #filter="{filterModel,filterCallback}">
                        <InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" v-tooltip.top.focus="'Hit enter key to filter'"/>
                    </template>
                    <template #editor="{ data, field }">
                        <InputText v-model="data[field]" autofocus />
                    </template>
                </Column>
            </DataTable>
        </div>
    </div>
</template>

<script>
import { ref, onMounted } from 'vue';
import ProductService from './service/ProductService';
import {FilterMatchMode} from 'primevue/api';

export default {
    setup() {
        onMounted(() => {
            productService.value.getProductsSmall().then(data => products1.value = data);
            productService.value.getProductsSmall().then(data => products2.value = data);
            productService.value.getProductsSmall().then(data => products3.value = data);
        });

        const productService = ref(new ProductService());
        const editingRows = ref([]);
        const columns = ref([
            {field: 'code', header: 'Code'},
            {field: 'name', header: 'Name'},
            {field: 'quantity', header: 'Quantity'},
            {field: 'price', header: 'Price'}
        ]);
        const products1 = ref(null);
        const products2 = ref(null);
        const products3 = ref(null);
        const statuses = ref([
            {label: 'In Stock', value: 'INSTOCK'},
            {label: 'Low Stock', value: 'LOWSTOCK'},
            {label: 'Out of Stock', value: 'OUTOFSTOCK'}
        ]);
        const filters = ref({
            'code': {value: null, matchMode: FilterMatchMode.STARTS_WITH},
            'name': {value: null, matchMode: FilterMatchMode.STARTS_WITH},
            'quantity': {value: null, matchMode: FilterMatchMode.STARTS_WITH},
            'price': {value: null, matchMode: FilterMatchMode.STARTS_WITH}
        });

        const onCellEditComplete = (event) => {
            let { data, newValue, field } = event;

            switch (field) {
                case 'quantity':
                case 'price':
                    if (isPositiveInteger(newValue))
                        data[field] = newValue;
                    else
                        event.preventDefault();
                break;

                default:
                    if (newValue.trim().length > 0)
                        data[field] = newValue;
                    else
                        event.preventDefault();
                break;
            }
        };
        const isPositiveInteger = (val) => {
            let str = String(val);
            str = str.trim();
            if (!str) {
                return false;
            }
            str = str.replace(/^0+/, "") || "0";
            var n = Math.floor(Number(str));
            return n !== Infinity && String(n) === str && n >= 0;
        };
        const onRowEditSave = (event) => {
            let { newData, index } = event;

            products2.value[index] = newData;
        };
        const getStatusLabel = (status) => {
            switch(status) {
                case 'INSTOCK':
                    return 'In Stock';

                case 'LOWSTOCK':
                    return 'Low Stock';

                case 'OUTOFSTOCK':
                    return 'Out of Stock';

                default:
                    return 'NA';
            }
        };

        return { productService, editingRows, columns, products1, products2, products3, statuses, filters, onCellEditComplete,
            isPositiveInteger, onRowEditSave, getStatusLabel }
    }
}
<\\/script>

<style lang="scss" scoped>
::v-deep(.editable-cells-table td.p-cell-editing) {
    padding-top: 0;
    padding-bottom: 0;
}
</style>`
                },
                'browser-source': {
                    tabName: 'Browser Source',
                    imports: `<script src="https://unpkg.com/primevue@^3/datatable/datatable.min.js"><\\/script>
        <script src="https://unpkg.com/primevue@^3/column/column.min.js"><\\/script>
        <script src="https://unpkg.com/primevue@^3/dropdown/dropdown.min.js"><\\/script>
        <script src="./ProductService.js"><\\/script>`,
                    content: `<div id="app" class="p-fluid card">
            <div class="card">
                <h5>Cell Editing</h5>
                <p>Validations, dynamic columns and reverting values with the escape key.</p>
                <p-datatable :value="products1" edit-mode="cell" @cell-edit-complete="onCellEditComplete" class="editable-cells-table" responsive-layout="scroll">
                    <p-column v-for="col of columns" :field="col.field" :header="col.header" :key="col.field" style="width:25%">
                        <template #editor="{ data, field }">
                            <p-inputtext v-model="data[field]" autofocus></p-inputtext>
                        </template>
                    </p-column>
                </p-datatable>
            </div>

            <div class="card">
                <h5>Row Editing</h5>
                <p-datatable :value="products2" edit-mode="row" dataKey="id" v-model:editing-rows="editingRows" @row-edit-save="onRowEditSave" responsive-layout="scroll">
                    <p-column field="code" header="Code" style="width:20%">
                        <template #editor="{ data, field }">
                            <p-inputtext v-model="data[field]" autofocus></p-inputtext>
                        </template>
                    </p-column>
                    <p-column field="name" header="Name" style="width:20%">
                        <template #editor="{ data, field }">
                            <p-inputtext v-model="data[field]"></p-inputtext>
                        </template>
                    </p-column>
                    <p-column field="inventoryStatus" header="Status" style="width:20%">
                        <template #editor="{ data, field }">
                            <p-dropdown v-model="data[field]" :options="statuses" option-label="label" option-value="value" placeholder="Select a Status">
                                <template #option="slotProps">
                                    <span :class="'product-badge status-' + slotProps.option.value.toLowerCase()">{{slotProps.option.label}}</span>
                                </template>
                            </p-dropdown>
                        </template>
                        <template #body="slotProps">
                            {{getStatusLabel(slotProps.data.inventoryStatus)}}
                        </template>
                    </p-column>
                    <p-column field="price" header="Price" style="width:20%">
                        <template #editor="{ data, field }">
                            <p-inputtext v-model="data[field]"></p-inputtext>
                        </template>
                    </p-column>
                    <p-column :row-editor="true" style="width:10%; min-width:8rem" bodyStyle="text-align:center"></p-column>
                </p-datatable>
            </div>

            <div class="card">
                <h5>Cell Editing with Sorting and Filter</h5>
                <p-datatable :value="products3" editMode="cell" @cell-edit-complete="onCellEditComplete" class="editable-cells-table" filterDisplay="row" v-model:filters="filters" responsiveLayout="scroll">
                    <p-column v-for="col of columns" :field="col.field" :header="col.header" :key="col.field" style="width:25%" sortable filter>
                        <template #filter="{filterModel,filterCallback}">
                            <InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" v-tooltip.top.focus="'Hit enter key to filter'"/>
                        </template>
                        <template #editor="{ data, field }">
                            <InputText v-model="data[field]" autofocus />
                        </template>
                    </Column>
                </p-datatable>
            </div>
        </div>

        <script type="module">
        const { createApp, ref, onMounted } = Vue;
        const { FilterMatchMode } = primevue.api;

        const App = {
            setup() {
                onMounted(() => {
                    productService.value.getProductsSmall().then(data => products1.value = data);
                    productService.value.getProductsSmall().then(data => products2.value = data);
                    productService.value.getProductsSmall().then(data => products3.value = data);
                });

                const productService = ref(new ProductService());
                const editingRows = ref([]);
                const columns = ref([
                    {field: 'code', header: 'Code'},
                    {field: 'name', header: 'Name'},
                    {field: 'quantity', header: 'Quantity'},
                    {field: 'price', header: 'Price'}
                ]);
                const products1 = ref(null);
                const products2 = ref(null);
                const products3 = ref(null);
                const statuses = ref([
                    {label: 'In Stock', value: 'INSTOCK'},
                    {label: 'Low Stock', value: 'LOWSTOCK'},
                    {label: 'Out of Stock', value: 'OUTOFSTOCK'}
                ]);
                const filters = ref({
                    'code': {value: null, matchMode: FilterMatchMode.STARTS_WITH},
                    'name': {value: null, matchMode: FilterMatchMode.STARTS_WITH},
                    'quantity': {value: null, matchMode: FilterMatchMode.STARTS_WITH},
                    'price': {value: null, matchMode: FilterMatchMode.STARTS_WITH}
                });

                const onCellEditComplete = (event) => {
                    let { data, newValue, field } = event;

                    switch (field) {
                        case 'quantity':
                        case 'price':
                            if (isPositiveInteger(newValue))
                                data[field] = newValue;
                            else
                                event.preventDefault();
                        break;

                        default:
                            if (newValue.trim().length > 0)
                                data[field] = newValue;
                            else
                                event.preventDefault();
                        break;
                    }
                };
                const isPositiveInteger = (val) => {
                    let str = String(val);
                    str = str.trim();
                    if (!str) {
                        return false;
                    }
                    str = str.replace(/^0+/, "") || "0";
                    var n = Math.floor(Number(str));
                    return n !== Infinity && String(n) === str && n >= 0;
                };
                const onRowEditSave = (event) => {
                    let { newData, index } = event;

                    products2.value[index] = newData;
                };
                const getStatusLabel = (status) => {
                    switch(status) {
                        case 'INSTOCK':
                            return 'In Stock';

                        case 'LOWSTOCK':
                            return 'Low Stock';

                        case 'OUTOFSTOCK':
                            return 'Out of Stock';

                        default:
                            return 'NA';
                    }
                };

                return { productService, editingRows, columns, products1, products2, products3, statuses, filters, onCellEditComplete,
                    isPositiveInteger, onRowEditSave, getStatusLabel }
            },
            components: {
                "p-datatable": primevue.datatable,
                "p-column": primevue.column,
                "p-dropdown": primevue.dropdown,
                "p-inputtext": primevue.inputtext
            }
        };

        createApp(App)
            .use(primevue.config.default)
            .mount("#app");
        <\\/script>

<style>
.editable-cells-table td.p-cell-editing {
    padding-top: 0;
    padding-bottom: 0;
}
</style>`
                }
            }
        };
    },
    productService: null,
    created() {
        this.productService = new ProductService();

        this.columns = [
            { field: 'code', header: 'Code' },
            { field: 'name', header: 'Name' },
            { field: 'quantity', header: 'Quantity' },
            { field: 'price', header: 'Price' }
        ];
    },
    mounted() {
        this.productService.getProductsSmall().then((data) => (this.products1 = data));
        this.productService.getProductsSmall().then((data) => (this.products2 = data));
        this.productService.getProductsSmall().then((data) => (this.products3 = data));
    },
    methods: {
        onCellEditComplete(event) {
            let { data, newValue, field } = event;

            switch (field) {
                case 'quantity':
                case 'price':
                    if (this.isPositiveInteger(newValue)) data[field] = newValue;
                    else event.preventDefault();
                    break;

                default:
                    if (newValue.trim().length > 0) data[field] = newValue;
                    else event.preventDefault();
                    break;
            }
        },
        isPositiveInteger(val) {
            let str = String(val);

            str = str.trim();

            if (!str) {
                return false;
            }

            str = str.replace(/^0+/, '') || '0';
            var n = Math.floor(Number(str));

            return n !== Infinity && String(n) === str && n >= 0;
        },
        onRowEditSave(event) {
            let { newData, index } = event;

            this.products2[index] = newData;
        },
        getStatusLabel(status) {
            switch (status) {
                case 'INSTOCK':
                    return 'In Stock';

                case 'LOWSTOCK':
                    return 'Low Stock';

                case 'OUTOFSTOCK':
                    return 'Out of Stock';

                default:
                    return 'NA';
            }
        }
    }
};
</script>

<style lang="scss" scoped>
::v-deep(.editable-cells-table td.p-cell-editing) {
    padding-top: 0;
    padding-bottom: 0;
}
</style>