docs(kb): add KB articles for Grid, Chart, Upload and Editor (#394)
This commit is contained in:
Родитель
5b1dcd05d1
Коммит
bd55e1b03f
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
title: Add custom line height tool inside the Native Editor.
|
||||
description: An example demonstrating how to implement a custom line height tool inside the Native Editor component.
|
||||
type: how-to
|
||||
page_title: Add custom line height tool in the Native Editor | Kendo UI for Vue Native Editor
|
||||
slug: editor-line-height-custom-tool
|
||||
tags: editor, custom tool, line height, tools, kendovue, native
|
||||
res_type: kb
|
||||
category: knowledge-base
|
||||
---
|
||||
|
||||
## Environment
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Product Version</td>
|
||||
<td>3.10.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Product</td>
|
||||
<td>Progress® Kendo UI for Vue Native</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
The current KB article demonstrates how to add a custom `line-height` tool inside the Native Editor
|
||||
|
||||
|
||||
## Runnable example
|
||||
|
||||
{% meta height: 420 %}
|
||||
{% embed_file editor-line-height-custom-tool/main.vue preview %}
|
||||
{% embed_file editor-line-height-custom-tool/main.js %}
|
||||
{% embed_file editor-line-height-custom-tool/content-basic.js %}
|
||||
{% endmeta %}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
const content = `<p>The Kendo UI for Vue Native Editor allows your users to edit HTML in a familiar, user-friendly way.<br />The Editor provides the core HTML editing engine, which includes text formatting, hyperlinks, and lists. The component <strong>outputs identical HTML</strong> across all major browsers, follows accessibility standards, and provides API for content manipulation.</p>`;
|
||||
|
||||
export default content;
|
|
@ -0,0 +1,4 @@
|
|||
import { createApp } from 'vue'
|
||||
import App from './main.vue'
|
||||
|
||||
createApp(App).mount('#app')
|
|
@ -0,0 +1,54 @@
|
|||
<template>
|
||||
<Editor
|
||||
:tools="tools"
|
||||
:default-content="content"
|
||||
:content-style="{
|
||||
height: '300px',
|
||||
}"
|
||||
>
|
||||
<template v-slot:MyLineHeight="{ props }">
|
||||
<fontsize v-bind="props" />
|
||||
</template>
|
||||
</Editor>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
Editor,
|
||||
InlineFormat,
|
||||
FontName,
|
||||
} from '@progress/kendo-vue-editor';
|
||||
import content from './content-basic';
|
||||
|
||||
const lineHeightSettings = {
|
||||
style: 'line-height',
|
||||
defaultItem: {
|
||||
text: 'No Line Height',
|
||||
value: '',
|
||||
},
|
||||
items: [
|
||||
{ text: '2', value: '2' },
|
||||
{ text: '3', value: '3' },
|
||||
{ text: '4', value: '4' },
|
||||
{ text: '5', value: '5' },
|
||||
{ text: '6', value: '6' },
|
||||
{ text: '7', value: '7' },
|
||||
],
|
||||
commandName: 'LineHeight',
|
||||
};
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Editor,
|
||||
inlineformat: InlineFormat,
|
||||
fontsize: FontName,
|
||||
fontname: FontName,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
content: content,
|
||||
tools: [{ render: 'MyLineHeight', props: lineHeightSettings }],
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,89 @@
|
|||
<template>
|
||||
<td style="padding: 0; position: relative">
|
||||
<span style="position: absolute; top: 25px; left: 140px; z-index: 10000">
|
||||
{{ chartValue }}
|
||||
</span>
|
||||
<Chart style="height: 90px; margin-top: -20px" :transitions="false">
|
||||
<ChartArea
|
||||
:background="'#eee'"
|
||||
:opacity="0"
|
||||
:height="120"
|
||||
:margin="-40"
|
||||
:border="{ width: 0 }"
|
||||
/>
|
||||
<ChartValueAxis>
|
||||
<ChartValueAxisItem
|
||||
:reverse="true"
|
||||
:max="700"
|
||||
:labels="{ visible: false }"
|
||||
:majorGridLines="{ visible: false }"
|
||||
/>
|
||||
</ChartValueAxis>
|
||||
<ChartSeries>
|
||||
<ChartSeriesItem
|
||||
:color="'pink'"
|
||||
:type="'bar'"
|
||||
:spacing="0.25"
|
||||
:data-items="firstSeries"
|
||||
/>
|
||||
</ChartSeries>
|
||||
</Chart>
|
||||
</td>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
Chart,
|
||||
ChartTitle,
|
||||
ChartSeries,
|
||||
ChartSeriesItem,
|
||||
ChartCategoryAxis,
|
||||
ChartCategoryAxisTitle,
|
||||
ChartCategoryAxisItem,
|
||||
ChartValueAxis,
|
||||
ChartValueAxisItem,
|
||||
ChartArea,
|
||||
} from "@progress/kendo-vue-charts";
|
||||
import "hammerjs";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Chart,
|
||||
ChartTitle,
|
||||
ChartSeries,
|
||||
ChartSeriesItem,
|
||||
ChartCategoryAxis,
|
||||
ChartCategoryAxisTitle,
|
||||
ChartCategoryAxisItem,
|
||||
ChartValueAxis,
|
||||
ChartValueAxisItem,
|
||||
ChartArea,
|
||||
},
|
||||
props: {
|
||||
field: String,
|
||||
dataItem: Object,
|
||||
format: String,
|
||||
className: String,
|
||||
columnIndex: Number,
|
||||
columnsCount: Number,
|
||||
rowType: String,
|
||||
level: Number,
|
||||
expanded: Boolean,
|
||||
editor: String,
|
||||
},
|
||||
computed: {
|
||||
chartValue() {
|
||||
return this.$props.dataItem.UnitsInStock > 800
|
||||
? this.$props.dataItem.UnitsInStock - 800
|
||||
: 0;
|
||||
},
|
||||
firstSeries() {
|
||||
return [this.chartValue];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
change(e) {
|
||||
this.$emit("change", e, e.target.value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,92 @@
|
|||
<template>
|
||||
<td style="padding: 0; position: relative">
|
||||
<span style="position: absolute; top: 25px; left: 110px; z-index: 10000">
|
||||
{{ chartValue }}
|
||||
</span>
|
||||
<Chart style="height: 90px; margin-top: -20px" :transitions="false">
|
||||
<ChartArea
|
||||
:background="'#eee'"
|
||||
:opacity="0"
|
||||
:height="120"
|
||||
:margin="-40"
|
||||
:border="{ width: 0 }"
|
||||
/>
|
||||
<ChartValueAxis>
|
||||
<ChartValueAxisItem
|
||||
:reverse="true"
|
||||
:max="800"
|
||||
:labels="{ visible: false }"
|
||||
:majorGridLines="{ visible: false }"
|
||||
/>
|
||||
</ChartValueAxis>
|
||||
<ChartSeries>
|
||||
<ChartSeriesItem
|
||||
:color="'pink'"
|
||||
:type="'bar'"
|
||||
:spacing="0.25"
|
||||
:data-items="firstSeries"
|
||||
/>
|
||||
</ChartSeries>
|
||||
</Chart>
|
||||
</td>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
Chart,
|
||||
ChartTitle,
|
||||
ChartSeries,
|
||||
ChartSeriesItem,
|
||||
ChartCategoryAxis,
|
||||
ChartCategoryAxisTitle,
|
||||
ChartCategoryAxisItem,
|
||||
ChartValueAxis,
|
||||
ChartValueAxisItem,
|
||||
ChartArea,
|
||||
} from "@progress/kendo-vue-charts";
|
||||
import "hammerjs";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Chart,
|
||||
ChartTitle,
|
||||
ChartSeries,
|
||||
ChartSeriesItem,
|
||||
ChartCategoryAxis,
|
||||
ChartCategoryAxisTitle,
|
||||
ChartCategoryAxisItem,
|
||||
ChartValueAxis,
|
||||
ChartValueAxisItem,
|
||||
ChartArea,
|
||||
},
|
||||
props: {
|
||||
field: String,
|
||||
dataItem: Object,
|
||||
format: String,
|
||||
className: String,
|
||||
columnIndex: Number,
|
||||
columnsCount: Number,
|
||||
rowType: String,
|
||||
level: Number,
|
||||
expanded: Boolean,
|
||||
editor: String,
|
||||
},
|
||||
computed: {
|
||||
chartValue() {
|
||||
return this.$props.dataItem.UnitsInStock > 800
|
||||
? 800
|
||||
: this.$props.dataItem.UnitsInStock;
|
||||
},
|
||||
firstSeries() {
|
||||
return [this.chartValue];
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
change(e) {
|
||||
this.$emit("change", e, e.target.value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,82 @@
|
|||
<template>
|
||||
<td style="padding: 0; position: relative">
|
||||
<span style="position: absolute; top: 25px; left: 40px; z-index: 10000">
|
||||
{{ dataItem["UnitsInStock"] }}
|
||||
</span>
|
||||
<Chart style="height: 90px; margin-top: -20px">
|
||||
<ChartArea
|
||||
:background="'#eee'"
|
||||
:opacity="0"
|
||||
:height="120"
|
||||
:margin="-40"
|
||||
:border="{ width: 0 }"
|
||||
/>
|
||||
<ChartValueAxis>
|
||||
<ChartValueAxisItem
|
||||
:max="100"
|
||||
:labels="{ visible: false }"
|
||||
:majorGridLines="{ visible: false }"
|
||||
/>
|
||||
</ChartValueAxis>
|
||||
<ChartSeries>
|
||||
<ChartSeriesItem
|
||||
:type="'bar'"
|
||||
:spacing="0.25"
|
||||
:data-items="firstSeries"
|
||||
/>
|
||||
</ChartSeries>
|
||||
</Chart>
|
||||
</td>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
Chart,
|
||||
ChartTitle,
|
||||
ChartSeries,
|
||||
ChartSeriesItem,
|
||||
ChartCategoryAxis,
|
||||
ChartCategoryAxisTitle,
|
||||
ChartCategoryAxisItem,
|
||||
ChartValueAxis,
|
||||
ChartValueAxisItem,
|
||||
ChartArea,
|
||||
} from "@progress/kendo-vue-charts";
|
||||
import "hammerjs";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Chart,
|
||||
ChartTitle,
|
||||
ChartSeries,
|
||||
ChartSeriesItem,
|
||||
ChartCategoryAxis,
|
||||
ChartCategoryAxisTitle,
|
||||
ChartCategoryAxisItem,
|
||||
ChartValueAxis,
|
||||
ChartValueAxisItem,
|
||||
ChartArea,
|
||||
},
|
||||
props: {
|
||||
field: String,
|
||||
dataItem: Object,
|
||||
format: String,
|
||||
className: String,
|
||||
columnIndex: Number,
|
||||
columnsCount: Number,
|
||||
rowType: String,
|
||||
level: Number,
|
||||
expanded: Boolean,
|
||||
editor: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
firstSeries: [this.$props.dataItem.UnitsInStock],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
change(e) {
|
||||
this.$emit("change", e, e.target.value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,4 @@
|
|||
import { createApp } from 'vue'
|
||||
import App from './main.vue'
|
||||
|
||||
createApp(App).mount('#app')
|
|
@ -0,0 +1,102 @@
|
|||
<template>
|
||||
<Grid
|
||||
ref="grid"
|
||||
:style="{ height: '360px' }"
|
||||
:data-items="gridData"
|
||||
:columns="columns"
|
||||
>
|
||||
<template v-slot:unitsCellTemplate="{ props }">
|
||||
<UnitsCell :data-item="props.dataItem" :field="props.field" />
|
||||
</template>
|
||||
<template v-slot:unitsTemplate="{ props }">
|
||||
<Units :data-item="props.dataItem" :field="props.field" />
|
||||
</template>
|
||||
<template v-slot:unitsCellReverseTemplate="{ props }">
|
||||
<UnitCellReverse :data-item="props.dataItem" :field="props.field" />
|
||||
</template>
|
||||
</Grid>
|
||||
</template>
|
||||
<script>
|
||||
import { Grid } from "@progress/kendo-vue-grid";
|
||||
import { Button } from "@progress/kendo-vue-buttons";
|
||||
import UnitsCell from "./UnitsCell";
|
||||
import Units from "./Units";
|
||||
import UnitCellReverse from "./UnitCellReverse";
|
||||
|
||||
var sampleProducts = [
|
||||
{
|
||||
ProductID: 1,
|
||||
ProductName: "Chai",
|
||||
UnitsInStock: 890,
|
||||
Discontinued: false,
|
||||
FirstOrderedOn: new Date(1996, 8, 20),
|
||||
},
|
||||
{
|
||||
ProductID: 2,
|
||||
ProductName: "Chang",
|
||||
UnitsInStock: 400,
|
||||
Discontinued: false,
|
||||
FirstOrderedOn: new Date(1996, 7, 12),
|
||||
},
|
||||
{
|
||||
ProductID: 3,
|
||||
ProductName: "Aniseed Syrup",
|
||||
UnitsInStock: 1300,
|
||||
Discontinued: false,
|
||||
FirstOrderedOn: new Date(1996, 8, 26),
|
||||
},
|
||||
{
|
||||
ProductID: 4,
|
||||
ProductName: "Chef Anton's Cajun Seasoning",
|
||||
UnitsInStock: 1030,
|
||||
Discontinued: false,
|
||||
FirstOrderedOn: new Date(1996, 9, 19),
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Grid: Grid,
|
||||
UnitsCell,
|
||||
UnitCellReverse,
|
||||
Units,
|
||||
kbutton: Button,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
gridData: sampleProducts.slice(0),
|
||||
updatedData: [],
|
||||
editID: null,
|
||||
group: [{ field: "UnitsInStock" }],
|
||||
expandedItems: [],
|
||||
columns: [
|
||||
{ field: "ProductID", editable: false, title: "ID", width: "80px" },
|
||||
{ field: "ProductName", title: "Name" },
|
||||
{
|
||||
field: "FirstOrderedOn",
|
||||
editor: "date",
|
||||
title: "First Ordered",
|
||||
format: "{0:d}",
|
||||
},
|
||||
{
|
||||
field: "Discontinued",
|
||||
title: "Units right to left",
|
||||
cell: "unitsCellReverseTemplate",
|
||||
width: "200px",
|
||||
},
|
||||
{
|
||||
field: "UnitsInStock",
|
||||
title: "Units",
|
||||
width: "250px",
|
||||
cell: "unitsTemplate",
|
||||
},
|
||||
{
|
||||
field: "Discontinued",
|
||||
title: "Units presented with Chart",
|
||||
cell: "unitsCellTemplate",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,4 @@
|
|||
import { createApp } from 'vue'
|
||||
import App from './main.vue'
|
||||
|
||||
createApp(App).mount('#app')
|
|
@ -0,0 +1,148 @@
|
|||
<template>
|
||||
<Grid
|
||||
:data-items="products"
|
||||
:filterable="true"
|
||||
:filter="filter"
|
||||
@filterchange="filterChange"
|
||||
:columns="columns"
|
||||
:filter-cell-render="filterRender"
|
||||
>
|
||||
<template v-slot:filterSlotTemplate="{ props, methods }">
|
||||
<div>
|
||||
Filter Slot:
|
||||
<KInput
|
||||
:style="{ width: '180px' }"
|
||||
:value="inputValue"
|
||||
@input="onInput"
|
||||
/>
|
||||
<KButton :style="{ 'margin-left': '15px' }" @click="onReset"
|
||||
>Clear</KButton
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</Grid>
|
||||
</template>
|
||||
<script>
|
||||
import { Grid } from '@progress/kendo-vue-grid';
|
||||
import { Input } from '@progress/kendo-vue-inputs';
|
||||
import { Button } from '@progress/kendo-vue-buttons';
|
||||
import { filterBy } from '@progress/kendo-data-query';
|
||||
import { sampleProducts } from './products';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Grid,
|
||||
KInput: Input,
|
||||
KButton: Button,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [],
|
||||
},
|
||||
columns: [
|
||||
{ field: 'ProductID', filterable: false, title: 'ID', width: '50px' },
|
||||
{
|
||||
field: 'ProductName',
|
||||
title: 'Product Name',
|
||||
},
|
||||
{
|
||||
field: 'UnitsInStock',
|
||||
filterCell: 'filterSlotTemplate',
|
||||
width: '350px',
|
||||
title: 'UnitsInStock',
|
||||
},
|
||||
{ field: 'UnitPrice', filter: 'numeric', title: 'UnitPrice' },
|
||||
{ field: 'Discontinued', filter: 'boolean', title: 'Discontinued' },
|
||||
],
|
||||
inputValue: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
products: function () {
|
||||
return filterBy(sampleProducts, this.filter);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
filterChange: function (ev) {
|
||||
this.filter = ev.filter;
|
||||
},
|
||||
filterRender: function (h, defaultRendering, props, change) {
|
||||
return defaultRendering;
|
||||
},
|
||||
onReset() {
|
||||
this.inputValue = '';
|
||||
this.filter = {
|
||||
logic: 'and',
|
||||
filters: [],
|
||||
};
|
||||
},
|
||||
onInput(ev) {
|
||||
this.inputValue = ev.target.value;
|
||||
|
||||
if (this.inputValue.length === 0) {
|
||||
setTimeout(() => {
|
||||
this.filter = {
|
||||
logic: 'and',
|
||||
filters: [],
|
||||
};
|
||||
});
|
||||
}
|
||||
let newFilter = [...this.filter.filters].filter(
|
||||
(filter) => filter.field !== 'UnitsInStock'
|
||||
);
|
||||
|
||||
const commaSeparatedValues = ev.target.value.split(',');
|
||||
|
||||
const equalFilters = [];
|
||||
const rangeFilters = [];
|
||||
commaSeparatedValues.forEach((ev) => {
|
||||
if (Number(ev)) {
|
||||
equalFilters.push({
|
||||
field: 'UnitsInStock',
|
||||
operator: 'eq',
|
||||
value: ev,
|
||||
});
|
||||
} else {
|
||||
const rangeValues = ev.split('-');
|
||||
|
||||
if (rangeValues[0] !== '') {
|
||||
rangeFilters.push({
|
||||
field: 'UnitsInStock',
|
||||
operator: 'gte',
|
||||
value: Number(rangeValues[0]),
|
||||
});
|
||||
rangeFilters.push({
|
||||
field: 'UnitsInStock',
|
||||
operator: 'lte',
|
||||
value: Number(rangeValues[1]),
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const newFilterDefinitions = [];
|
||||
if (rangeFilters.length !== 0) {
|
||||
newFilterDefinitions.push({
|
||||
logic: 'and',
|
||||
filters: [...rangeFilters],
|
||||
});
|
||||
}
|
||||
|
||||
if (equalFilters.length !== 0) {
|
||||
newFilterDefinitions.push(...equalFilters);
|
||||
}
|
||||
|
||||
newFilter.push({
|
||||
field: 'UnitsInStock',
|
||||
logic: 'or',
|
||||
filters: newFilterDefinitions,
|
||||
});
|
||||
|
||||
this.filter.filters = newFilter;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
const sampleProducts = [
|
||||
{
|
||||
ProductID: 1,
|
||||
ProductName: 'Chai',
|
||||
SupplierID: 1,
|
||||
CategoryID: 1,
|
||||
QuantityPerUnit: '10 boxes x 20 bags',
|
||||
UnitPrice: 18,
|
||||
UnitsInStock: 39,
|
||||
UnitsOnOrder: 0,
|
||||
ReorderLevel: 10,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 1,
|
||||
CategoryName: 'Beverages',
|
||||
Description: 'Soft drinks, coffees, teas, beers, and ales',
|
||||
},
|
||||
FirstOrderedOn: new Date(1996, 8, 20),
|
||||
},
|
||||
{
|
||||
ProductID: 2,
|
||||
ProductName: 'Chang',
|
||||
SupplierID: 1,
|
||||
CategoryID: 1,
|
||||
QuantityPerUnit: '24 - 12 oz bottles',
|
||||
UnitPrice: 19,
|
||||
UnitsInStock: 17,
|
||||
UnitsOnOrder: 40,
|
||||
ReorderLevel: 25,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 1,
|
||||
CategoryName: 'Beverages',
|
||||
Description: 'Soft drinks, coffees, teas, beers, and ales',
|
||||
},
|
||||
FirstOrderedOn: new Date(1996, 7, 12),
|
||||
},
|
||||
{
|
||||
ProductID: 3,
|
||||
ProductName: 'Aniseed Syrup',
|
||||
SupplierID: 1,
|
||||
CategoryID: 2,
|
||||
QuantityPerUnit: '12 - 550 ml bottles',
|
||||
UnitPrice: 10,
|
||||
UnitsInStock: 13,
|
||||
UnitsOnOrder: 70,
|
||||
ReorderLevel: 25,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 2,
|
||||
CategoryName: 'Condiments',
|
||||
Description: 'Sweet and savory sauces, relishes, spreads, and seasonings',
|
||||
},
|
||||
FirstOrderedOn: new Date(1996, 8, 26),
|
||||
},
|
||||
{
|
||||
ProductID: 4,
|
||||
ProductName: "Chef Anton's Cajun Seasoning",
|
||||
SupplierID: 2,
|
||||
CategoryID: 2,
|
||||
QuantityPerUnit: '48 - 6 oz jars',
|
||||
UnitPrice: 22,
|
||||
UnitsInStock: 53,
|
||||
UnitsOnOrder: 0,
|
||||
ReorderLevel: 0,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 2,
|
||||
CategoryName: 'Condiments',
|
||||
Description: 'Sweet and savory sauces, relishes, spreads, and seasonings',
|
||||
},
|
||||
FirstOrderedOn: new Date(1996, 9, 19),
|
||||
},
|
||||
{
|
||||
ProductID: 5,
|
||||
ProductName: "Chef Anton's Gumbo Mix",
|
||||
SupplierID: 2,
|
||||
CategoryID: 2,
|
||||
QuantityPerUnit: '36 boxes',
|
||||
UnitPrice: 21.35,
|
||||
UnitsInStock: 20,
|
||||
UnitsOnOrder: 0,
|
||||
ReorderLevel: 0,
|
||||
Discontinued: true,
|
||||
Category: {
|
||||
CategoryID: 2,
|
||||
CategoryName: 'Condiments',
|
||||
Description: 'Sweet and savory sauces, relishes, spreads, and seasonings',
|
||||
},
|
||||
FirstOrderedOn: new Date(1996, 7, 17),
|
||||
},
|
||||
{
|
||||
ProductID: 6,
|
||||
ProductName: "Grandma's Boysenberry Spread",
|
||||
SupplierID: 3,
|
||||
CategoryID: 2,
|
||||
QuantityPerUnit: '12 - 8 oz jars',
|
||||
UnitPrice: 25,
|
||||
UnitsInStock: 120,
|
||||
UnitsOnOrder: 0,
|
||||
ReorderLevel: 25,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 2,
|
||||
CategoryName: 'Condiments',
|
||||
Description: 'Sweet and savory sauces, relishes, spreads, and seasonings',
|
||||
},
|
||||
FirstOrderedOn: new Date(1996, 9, 19),
|
||||
},
|
||||
{
|
||||
ProductID: 7,
|
||||
ProductName: "Uncle Bob's Organic Dried Pears",
|
||||
SupplierID: 3,
|
||||
CategoryID: 7,
|
||||
QuantityPerUnit: '12 - 1 lb pkgs.',
|
||||
UnitPrice: 30,
|
||||
UnitsInStock: 15,
|
||||
UnitsOnOrder: 0,
|
||||
ReorderLevel: 10,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 7,
|
||||
CategoryName: 'Produce',
|
||||
Description: 'Dried fruit and bean curd',
|
||||
},
|
||||
FirstOrderedOn: new Date(1996, 7, 22),
|
||||
},
|
||||
{
|
||||
ProductID: 8,
|
||||
ProductName: 'Northwoods Cranberry Sauce',
|
||||
SupplierID: 3,
|
||||
CategoryID: 2,
|
||||
QuantityPerUnit: '12 - 12 oz jars',
|
||||
UnitPrice: 40,
|
||||
UnitsInStock: 6,
|
||||
UnitsOnOrder: 0,
|
||||
ReorderLevel: 0,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 2,
|
||||
CategoryName: 'Condiments',
|
||||
Description: 'Sweet and savory sauces, relishes, spreads, and seasonings',
|
||||
},
|
||||
FirstOrderedOn: new Date(1996, 11, 1),
|
||||
},
|
||||
{
|
||||
ProductID: 9,
|
||||
ProductName: 'Mishi Kobe Niku',
|
||||
SupplierID: 4,
|
||||
CategoryID: 6,
|
||||
QuantityPerUnit: '18 - 500 g pkgs.',
|
||||
UnitPrice: 97,
|
||||
UnitsInStock: 29,
|
||||
UnitsOnOrder: 0,
|
||||
ReorderLevel: 0,
|
||||
Discontinued: true,
|
||||
Category: {
|
||||
CategoryID: 6,
|
||||
CategoryName: 'Meat/Poultry',
|
||||
Description: 'Prepared meats',
|
||||
},
|
||||
FirstOrderedOn: new Date(1997, 1, 21),
|
||||
},
|
||||
{
|
||||
ProductID: 10,
|
||||
ProductName: 'Ikura',
|
||||
SupplierID: 4,
|
||||
CategoryID: 8,
|
||||
QuantityPerUnit: '12 - 200 ml jars',
|
||||
UnitPrice: 31,
|
||||
UnitsInStock: 31,
|
||||
UnitsOnOrder: 0,
|
||||
ReorderLevel: 0,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 8,
|
||||
CategoryName: 'Seafood',
|
||||
Description: 'Seaweed and fish',
|
||||
},
|
||||
FirstOrderedOn: new Date(1996, 8, 5),
|
||||
},
|
||||
];
|
||||
|
||||
export { sampleProducts };
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
<template>
|
||||
<div>
|
||||
<GridColumnMenuSort
|
||||
:column="column"
|
||||
:sortable="sortable"
|
||||
:sort="sort"
|
||||
@closemenu="closeMenu"
|
||||
@sortchange="sortChange"
|
||||
/>
|
||||
<GridColumnMenuFilter
|
||||
:column="column"
|
||||
:filterable="filterable"
|
||||
:filter="filter"
|
||||
@filterfocus="handleFocus"
|
||||
@closemenu="closeMenu"
|
||||
@expandchange="expandChange"
|
||||
@filterchange="filterChange"
|
||||
/>
|
||||
<GridColumnMenuItemGroup>
|
||||
<GridColumnMenuItem
|
||||
:title="'Columns'"
|
||||
:icon-class="'k-i-columns'"
|
||||
@menuitemclick="onMenuItemClick"
|
||||
/>
|
||||
<GridColumnMenuItemContent :show="columnsExpanded">
|
||||
<div class="k-column-list-wrapper">
|
||||
<form @submit="onSubmit" @reset="onReset">
|
||||
<div class="k-column-list">
|
||||
<div
|
||||
:key="idx"
|
||||
class="k-column-list-item"
|
||||
v-for="(column, idx) in currentColumns"
|
||||
>
|
||||
<span>
|
||||
<input
|
||||
:id="'column-visiblity-show-' + idx"
|
||||
:class="'k-checkbox k-checkbox-md k-rounded-md'"
|
||||
:type="'checkbox'"
|
||||
:readOnly="true"
|
||||
:disabled="!column.hidden && oneVisibleColumn"
|
||||
:checked="!column.hidden"
|
||||
@click="onToggleColumn(idx)"
|
||||
/>
|
||||
<label
|
||||
:for="'column-visiblity-show-' + idx"
|
||||
:class="'k-checkbox-label'"
|
||||
:style="{ userSelect: 'none' }"
|
||||
>
|
||||
{{ column.title }} - {{ idx }}
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="k-columnmenu-actions">
|
||||
<kbutton type="reset">Reset</kbutton>
|
||||
<kbutton :theme-color="'primary'">Save</kbutton>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</GridColumnMenuItemContent>
|
||||
</GridColumnMenuItemGroup>
|
||||
<GridColumnMenuItemGroup>
|
||||
<GridColumnMenuItemContent :show="true">
|
||||
<div class="k-column-list-wrapper">
|
||||
<div class="k-column-list">
|
||||
<div
|
||||
@click="columnLockUnlock(true)"
|
||||
:class="['k-column-list-item', $props.locked ? 'k-disabled' : '']"
|
||||
>
|
||||
<span class="k-icon k-i-lock" /> Lock Column
|
||||
</div>
|
||||
<div
|
||||
@click="columnLockUnlock(false)"
|
||||
:class="[
|
||||
'k-column-list-item',
|
||||
!$props.locked ? 'k-disabled' : '',
|
||||
]"
|
||||
>
|
||||
<span class="k-icon k-i-unlock" /> Unlock Column
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</GridColumnMenuItemContent>
|
||||
</GridColumnMenuItemGroup>
|
||||
<GridColumnMenuItemGroup>
|
||||
<GridColumnMenuItemContent :show="true">
|
||||
<div class="k-column-list-wrapper">
|
||||
<div class="k-column-list">
|
||||
<div
|
||||
@click="columnShowHide(true)"
|
||||
:class="['k-column-list-item', $props.hidden ? 'k-disabled' : '']"
|
||||
>
|
||||
<span class="k-icon k-i-minus" /> Hide Column
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</GridColumnMenuItemContent>
|
||||
</GridColumnMenuItemGroup>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
GridColumnMenuFilter,
|
||||
GridColumnMenuSort,
|
||||
GridColumnMenuItemGroup,
|
||||
GridColumnMenuItemContent,
|
||||
GridColumnMenuItem,
|
||||
} from '@progress/kendo-vue-grid';
|
||||
import { Button } from '@progress/kendo-vue-buttons';
|
||||
import { products } from './products';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
column: Object,
|
||||
sortable: [Boolean, Object],
|
||||
sort: {
|
||||
type: Array,
|
||||
},
|
||||
filter: Object,
|
||||
filterable: Boolean,
|
||||
locked: Boolean,
|
||||
columns: Array,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentColumns: [],
|
||||
columnsExpanded: false,
|
||||
filterExpanded: false,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.$data.currentColumns = this.$props.columns;
|
||||
},
|
||||
components: {
|
||||
GridColumnMenuSort: GridColumnMenuSort,
|
||||
GridColumnMenuFilter: GridColumnMenuFilter,
|
||||
GridColumnMenuItemGroup: GridColumnMenuItemGroup,
|
||||
GridColumnMenuItemContent: GridColumnMenuItemContent,
|
||||
GridColumnMenuItem: GridColumnMenuItem,
|
||||
kbutton: Button,
|
||||
},
|
||||
computed: {
|
||||
oneVisibleColumn() {
|
||||
return this.currentColumns.filter((c) => !c.hidden).length === 1;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleFocus(e) {
|
||||
this.$emit('contentfocus', e);
|
||||
},
|
||||
onToggleColumn(id) {
|
||||
this.currentColumns = this.currentColumns.map((column, idx) => {
|
||||
return idx === id ? { ...column, hidden: !column.hidden } : column;
|
||||
});
|
||||
},
|
||||
onReset(event) {
|
||||
event.preventDefault();
|
||||
const allColumns = this.$props.columns.map((col) => {
|
||||
return {
|
||||
...col,
|
||||
hidden: false,
|
||||
};
|
||||
});
|
||||
|
||||
this.currentColumns = allColumns;
|
||||
this.onSubmit();
|
||||
},
|
||||
onSubmit(event) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
this.$emit('columnssubmit', this.currentColumns);
|
||||
this.$emit('closemenu');
|
||||
},
|
||||
onMenuItemClick() {
|
||||
const value = !this.columnsExpanded;
|
||||
this.columnsExpanded = value;
|
||||
this.filterExpanded = value ? false : this.filterExpanded;
|
||||
},
|
||||
onFilterExpandChange(value) {
|
||||
this.filterExpanded = value;
|
||||
this.columnsExpanded = value ? false : this.columnsExpanded;
|
||||
},
|
||||
expandChange() {
|
||||
this.$emit('expandchange');
|
||||
},
|
||||
closeMenu() {
|
||||
this.$emit('closemenu');
|
||||
},
|
||||
filterChange(newDescriptor, e) {
|
||||
this.$emit('filterchange', newDescriptor);
|
||||
},
|
||||
sortChange(newDescriptor, e) {
|
||||
this.$emit('sortchange', newDescriptor);
|
||||
},
|
||||
columnLockUnlock(state) {
|
||||
this.$emit('lockchange', state);
|
||||
this.$emit('closemenu');
|
||||
},
|
||||
columnShowHide(state) {
|
||||
this.$emit('visibilitychange', state);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
import { createApp } from 'vue'
|
||||
import App from './main.vue'
|
||||
|
||||
createApp(App).mount('#app')
|
|
@ -0,0 +1,184 @@
|
|||
<template>
|
||||
<Grid
|
||||
:data-items="result"
|
||||
:take="dataState.take"
|
||||
:skip="dataState.skip"
|
||||
:sort="dataState.sort"
|
||||
:filter="dataState.filter"
|
||||
@datastatechange="dataStateChange"
|
||||
@columnreorder="handleColumnReorder"
|
||||
:columns="lockedColumns"
|
||||
:sortable="true"
|
||||
:pageable="true"
|
||||
:reorderable="true"
|
||||
:page-size="8"
|
||||
:style="{ width: '500px' }"
|
||||
>
|
||||
<template v-slot:myTemplate="{ props }">
|
||||
<div>
|
||||
<custom
|
||||
:column="props.column"
|
||||
:filterable="props.filterable"
|
||||
:filter="props.filter"
|
||||
:sortable="props.sortable"
|
||||
:sort="props.sort"
|
||||
:columns="columns"
|
||||
:locked="isColumnLocked(props.column.field)"
|
||||
@sortchange="(e) => props.onSortchange(e)"
|
||||
@lockchange="(e) => lockChange(e, props.column)"
|
||||
@visibilitychange="(e) => visibilityChange(e, props.column)"
|
||||
@filterchange="(e) => props.onFilterchange(e)"
|
||||
@closemenu="(e) => props.onClosemenu(e)"
|
||||
@contentfocus="(e) => props.onContentfocus(e)"
|
||||
@columnssubmit="onColumnsSubmit"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Grid>
|
||||
</template>
|
||||
<script>
|
||||
import { Grid } from '@progress/kendo-vue-grid';
|
||||
import { process } from '@progress/kendo-data-query';
|
||||
import ColumnMenu from './ColumnMenu.vue';
|
||||
import { products } from './products.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Grid: Grid,
|
||||
custom: ColumnMenu,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
result: [],
|
||||
dataState: {
|
||||
take: 0,
|
||||
skip: 0,
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
title: 'Product Id',
|
||||
field: 'ProductID',
|
||||
filter: 'numeric',
|
||||
locked: false,
|
||||
columnMenu: 'myTemplate',
|
||||
hidden: false,
|
||||
width: 100,
|
||||
locked: true,
|
||||
},
|
||||
{
|
||||
title: 'Product Name',
|
||||
field: 'ProductName',
|
||||
filter: 'text',
|
||||
locked: false,
|
||||
columnMenu: 'myTemplate',
|
||||
hidden: false,
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'Quantity Per Unit',
|
||||
field: 'QuantityPerUnit',
|
||||
filter: 'numeric',
|
||||
locked: false,
|
||||
columnMenu: 'myTemplate',
|
||||
hidden: false,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'Unit Price',
|
||||
field: 'UnitPrice',
|
||||
filter: 'numeric',
|
||||
locked: false,
|
||||
columnMenu: 'myTemplate',
|
||||
hidden: false,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'Units In Stock',
|
||||
field: 'UnitsInStock',
|
||||
filter: 'numeric',
|
||||
locked: false,
|
||||
columnMenu: 'myTemplate',
|
||||
hidden: false,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'Discontinued',
|
||||
field: 'Discontinued',
|
||||
filter: 'boolean',
|
||||
locked: false,
|
||||
columnMenu: 'myTemplate',
|
||||
hidden: false,
|
||||
width: 100,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
created: function () {
|
||||
this.createDataState({
|
||||
take: 8,
|
||||
skip: 0,
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
lockedColumns() {
|
||||
if (this.columns.filter((ev) => ev.locked)) {
|
||||
const columns = [...this.columns];
|
||||
return columns
|
||||
.sort((a, b) => Number(b.locked) - Number(a.locked))
|
||||
.map((e) => {
|
||||
return { ...e, orderIndex: undefined };
|
||||
});
|
||||
} else {
|
||||
this.columns;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleColumnReorder(event) {
|
||||
const lastLockedindex = this.lockedColumns.findLastIndex((e) => e.locked);
|
||||
|
||||
const updatedColumns = event.columns
|
||||
.sort((a, b) => Number(a.orderIndex) - Number(b.orderIndex))
|
||||
.map((e, index) => {
|
||||
const newLockedState =
|
||||
event.prev > lastLockedindex && event.next <= lastLockedindex
|
||||
? true
|
||||
: event.prev <= lastLockedindex && event.next > lastLockedindex
|
||||
? false
|
||||
: e.locked;
|
||||
return {
|
||||
...e,
|
||||
locked: index === event.next ? newLockedState : e.locked,
|
||||
};
|
||||
});
|
||||
this.columns = updatedColumns;
|
||||
},
|
||||
isColumnLocked(columnName) {
|
||||
return this.columns.filter((ev) => ev.field === columnName)[0].locked;
|
||||
},
|
||||
handleFilterChange: function (filter) {
|
||||
this.result = process(products.slice(0), { filter: filter });
|
||||
},
|
||||
createDataState(dataState) {
|
||||
this.result = process(products.slice(0), dataState);
|
||||
this.dataState = dataState;
|
||||
},
|
||||
dataStateChange(event) {
|
||||
this.createDataState(event.data);
|
||||
},
|
||||
onColumnsSubmit(columnsState) {
|
||||
this.columns = columnsState;
|
||||
},
|
||||
lockChange(state, columnName) {
|
||||
const columnToLock = this.columns.filter(
|
||||
(ev) => ev.field === columnName.field
|
||||
)[0];
|
||||
columnToLock.locked = state;
|
||||
},
|
||||
visibilityChange(state, columnName) {
|
||||
this.columns.filter((ev) => ev.field === columnName.field)[0].hidden =
|
||||
state;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,140 @@
|
|||
const products = [
|
||||
{
|
||||
ProductID: 1,
|
||||
ProductName: 'Chai',
|
||||
SupplierID: 1,
|
||||
CategoryID: 1,
|
||||
QuantityPerUnit: '10 boxes x 20 bags',
|
||||
UnitPrice: 18.0,
|
||||
UnitsInStock: 39,
|
||||
UnitsOnOrder: 0,
|
||||
ReorderLevel: 10,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 1,
|
||||
CategoryName: 'Beverages',
|
||||
Description: 'Soft drinks, coffees, teas, beers, and ales',
|
||||
},
|
||||
},
|
||||
{
|
||||
ProductID: 2,
|
||||
ProductName: 'Chang',
|
||||
SupplierID: 1,
|
||||
CategoryID: 1,
|
||||
QuantityPerUnit: '24 - 12 oz bottles',
|
||||
UnitPrice: 19.0,
|
||||
UnitsInStock: 17,
|
||||
UnitsOnOrder: 40,
|
||||
ReorderLevel: 25,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 1,
|
||||
CategoryName: 'Beverages',
|
||||
Description: 'Soft drinks, coffees, teas, beers, and ales',
|
||||
},
|
||||
},
|
||||
{
|
||||
ProductID: 3,
|
||||
ProductName: 'Aniseed Syrup',
|
||||
SupplierID: 1,
|
||||
CategoryID: 2,
|
||||
QuantityPerUnit: '12 - 550 ml bottles',
|
||||
UnitPrice: 10.0,
|
||||
UnitsInStock: 13,
|
||||
UnitsOnOrder: 70,
|
||||
ReorderLevel: 25,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 2,
|
||||
CategoryName: 'Condiments',
|
||||
Description: 'Sweet and savory sauces, relishes, spreads, and seasonings',
|
||||
},
|
||||
},
|
||||
{
|
||||
ProductID: 4,
|
||||
ProductName: "Chef Anton's Cajun Seasoning",
|
||||
SupplierID: 2,
|
||||
CategoryID: 2,
|
||||
QuantityPerUnit: '48 - 6 oz jars',
|
||||
UnitPrice: 22.0,
|
||||
UnitsInStock: 53,
|
||||
UnitsOnOrder: 0,
|
||||
ReorderLevel: 0,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 2,
|
||||
CategoryName: 'Condiments',
|
||||
Description: 'Sweet and savory sauces, relishes, spreads, and seasonings',
|
||||
},
|
||||
},
|
||||
{
|
||||
ProductID: 5,
|
||||
ProductName: "Chef Anton's Gumbo Mix",
|
||||
SupplierID: 2,
|
||||
CategoryID: 2,
|
||||
QuantityPerUnit: '36 boxes',
|
||||
UnitPrice: 21.35,
|
||||
UnitsInStock: 0,
|
||||
UnitsOnOrder: 0,
|
||||
ReorderLevel: 0,
|
||||
Discontinued: true,
|
||||
Category: {
|
||||
CategoryID: 2,
|
||||
CategoryName: 'Condiments',
|
||||
Description: 'Sweet and savory sauces, relishes, spreads, and seasonings',
|
||||
},
|
||||
},
|
||||
{
|
||||
ProductID: 6,
|
||||
ProductName: "Grandma's Boysenberry Spread",
|
||||
SupplierID: 3,
|
||||
CategoryID: 2,
|
||||
QuantityPerUnit: '12 - 8 oz jars',
|
||||
UnitPrice: 25.0,
|
||||
UnitsInStock: 120,
|
||||
UnitsOnOrder: 0,
|
||||
ReorderLevel: 25,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 2,
|
||||
CategoryName: 'Condiments',
|
||||
Description: 'Sweet and savory sauces, relishes, spreads, and seasonings',
|
||||
},
|
||||
},
|
||||
{
|
||||
ProductID: 7,
|
||||
ProductName: "Uncle Bob's Organic Dried Pears",
|
||||
SupplierID: 3,
|
||||
CategoryID: 7,
|
||||
QuantityPerUnit: '12 - 1 lb pkgs.',
|
||||
UnitPrice: 30.0,
|
||||
UnitsInStock: 15,
|
||||
UnitsOnOrder: 0,
|
||||
ReorderLevel: 10,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 7,
|
||||
CategoryName: 'Produce',
|
||||
Description: 'Dried fruit and bean curd',
|
||||
},
|
||||
},
|
||||
{
|
||||
ProductID: 8,
|
||||
ProductName: 'Northwoods Cranberry Sauce',
|
||||
SupplierID: 3,
|
||||
CategoryID: 2,
|
||||
QuantityPerUnit: '12 - 12 oz jars',
|
||||
UnitPrice: 40.0,
|
||||
UnitsInStock: 6,
|
||||
UnitsOnOrder: 0,
|
||||
ReorderLevel: 0,
|
||||
Discontinued: false,
|
||||
Category: {
|
||||
CategoryID: 2,
|
||||
CategoryName: 'Condiments',
|
||||
Description: 'Sweet and savory sauces, relishes, spreads, and seasonings',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export { products };
|
|
@ -0,0 +1,4 @@
|
|||
import { createApp } from 'vue'
|
||||
import App from './main.vue'
|
||||
|
||||
createApp(App).mount('#app')
|
|
@ -0,0 +1,71 @@
|
|||
<template>
|
||||
<div>
|
||||
<Grid
|
||||
ref="grid"
|
||||
:style="{ height: '520px' }"
|
||||
:data-items="products"
|
||||
:selected-field="selectedField"
|
||||
:columns="columns"
|
||||
@rowclick="onRowClick"
|
||||
>
|
||||
</Grid>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { Grid } from "@progress/kendo-vue-grid";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Grid: Grid,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
selectedField: "selected",
|
||||
selectedID: 1,
|
||||
products: [],
|
||||
columns: [
|
||||
{ field: "ProductID", title: "ID", width: "50px" },
|
||||
{ field: "ProductName", title: "Product Name" },
|
||||
{ field: "UnitPrice", filter: "numeric", title: "Unit Price" },
|
||||
{ field: "UnitsInStock", title: "Units In Stock" },
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onRowClick(event) {
|
||||
if (event.event.metaKey || event.event.ctrlKey || event.event.shiftKey) {
|
||||
event.dataItem[this.selectedField] =
|
||||
!event.dataItem[this.selectedField];
|
||||
}
|
||||
},
|
||||
createRandomData(count) {
|
||||
const productNames = [
|
||||
"Chai",
|
||||
"Chang",
|
||||
"Syrup",
|
||||
"Apple",
|
||||
"Orange",
|
||||
"Banana",
|
||||
"Lemon",
|
||||
"Pineapple",
|
||||
"Tea",
|
||||
"Milk",
|
||||
];
|
||||
const unitPrices = [12.5, 10.1, 5.3, 7, 22.53, 16.22, 20, 50, 100, 120];
|
||||
|
||||
return Array(count)
|
||||
.fill({})
|
||||
.map((_, idx) => ({
|
||||
ProductID: idx + 1,
|
||||
ProductName:
|
||||
productNames[Math.floor(Math.random() * productNames.length)],
|
||||
UnitPrice: unitPrices[Math.floor(Math.random() * unitPrices.length)],
|
||||
UnitsInStock: Math.floor(Math.random() * 100),
|
||||
}));
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.products = this.createRandomData(1000);
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,64 @@
|
|||
<template>
|
||||
<UploadListMultiItem
|
||||
:files="files"
|
||||
:async="async"
|
||||
:disabled="disabled"
|
||||
@cancel="onCancel"
|
||||
@remove="onRemove"
|
||||
@retry="onRetry"
|
||||
/>
|
||||
<Dialog v-if="visibleDialog" :title="'Please confirm'" @close="toggleDialog">
|
||||
<p :style="{ margin: '25px', textAlign: 'center' }">
|
||||
Are you sure you want to remove the file?
|
||||
</p>
|
||||
<DialogActionsBar>
|
||||
<KButton @click="toggleDialog">No</KButton>
|
||||
<KButton :theme-color="'primary'" @click="confirmRemove">Yes</KButton>
|
||||
</DialogActionsBar>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script>
|
||||
import { UploadListMultiItem } from '@progress/kendo-vue-upload';
|
||||
import { Dialog, DialogActionsBar } from '@progress/kendo-vue-dialogs';
|
||||
import { Button as KButton } from '@progress/kendo-vue-buttons';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UploadListMultiItem,
|
||||
KButton,
|
||||
Dialog,
|
||||
DialogActionsBar,
|
||||
},
|
||||
props: {
|
||||
files: Array,
|
||||
disabled: Boolean,
|
||||
async: Object,
|
||||
},
|
||||
emits: ['retry', 'remove', 'cancel'],
|
||||
data() {
|
||||
return {
|
||||
visibleDialog: false,
|
||||
uid: undefined,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleDialog() {
|
||||
this.visibleDialog = false;
|
||||
},
|
||||
onRetry(uid) {
|
||||
this.$emit('retry', uid);
|
||||
},
|
||||
onRemove(uid) {
|
||||
this.visibleDialog = true;
|
||||
this.uid = uid;
|
||||
},
|
||||
confirmRemove() {
|
||||
this.$emit('remove', this.uid);
|
||||
},
|
||||
onCancel(uid) {
|
||||
this.$emit('cancel', uid);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { createApp } from 'vue'
|
||||
import App from './main.vue'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<div>
|
||||
<upload
|
||||
:list="customListItemUI"
|
||||
:default-files="[]"
|
||||
:batch="false"
|
||||
:multiple="true"
|
||||
:with-credentials="false"
|
||||
:save-url="'https://demos.telerik.com/kendo-ui/service-v4/upload/save'"
|
||||
:remove-url="'https://demos.telerik.com/kendo-ui/service-v4/upload/remove'"
|
||||
>
|
||||
<template v-slot:myTemplate="{props}">
|
||||
<custom
|
||||
:files="props.files"
|
||||
:async="props.async"
|
||||
@cancel="props.onCancel"
|
||||
@remove="props.onRemove"
|
||||
@retry="props.onRetry"/>
|
||||
</template>
|
||||
</upload>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { Upload } from '@progress/kendo-vue-upload';
|
||||
import CustomList from './CustomList';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
upload: Upload,
|
||||
'custom': CustomList
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
customListItemUI: 'myTemplate'
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
title: Add Native Chart in multiple Grid columns based on a data item value.
|
||||
description: The current KB article shows how we can display a Native Chart as a background inside multiple Native Grid columns.
|
||||
type: how-to
|
||||
page_title: Add Chart as a background of multiple Native Grid columns based on a data item value.
|
||||
slug: grid-display-chart-in-multiple-grid-columns
|
||||
tags: grid, chart, background, multiple column, kendovue, native
|
||||
res_type: kb
|
||||
category: knowledge-base
|
||||
---
|
||||
|
||||
## Environment
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Product Version</td>
|
||||
<td>3.10.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Product</td>
|
||||
<td>Progress® Kendo UI for Vue Native</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
The current KB article shows how we can display a `Native Chart` as a background inside multiple `Native Grid` columns based on a data item value.
|
||||
|
||||
## Runnable example
|
||||
|
||||
{% meta height: 420 %}
|
||||
{% embed_file grid-display-chart-in-multiple-grid-columns/main.vue preview %}
|
||||
{% embed_file grid-display-chart-in-multiple-grid-columns/main.js %}
|
||||
{% embed_file grid-display-chart-in-multiple-grid-columns/UnitCellReverse.vue %}
|
||||
{% embed_file grid-display-chart-in-multiple-grid-columns/Units.vue %}
|
||||
{% embed_file grid-display-chart-in-multiple-grid-columns/UnitsCell.vue %}
|
||||
{% endmeta %}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
title: Filter the Kendo UI for Vue Native Grid's data using a comma separated values.
|
||||
description: A a sample project that shows how to implement a custom row filter inside the Native Grid that allows data filtering using comma separated values.
|
||||
type: how-to
|
||||
page_title: Filter the Native Grid using comma separated values | Kendo UI for Vue Native Grid
|
||||
slug: grid-filtering-comma-separated-values
|
||||
tags: grid, filter, filtering, row filter, comma separated, kendovue, native
|
||||
res_type: kb
|
||||
category: knowledge-base
|
||||
---
|
||||
|
||||
## Environment
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Product Version</td>
|
||||
<td>3.10.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Product</td>
|
||||
<td>Progress® Kendo UI for Vue Native</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
The current KB article demonstrates how you can implement a custom row filter inside the Native Grid that filter's its data using comma separated values.
|
||||
|
||||
|
||||
## Runnable example
|
||||
|
||||
To test the following example start adding values in the filter input of the `UnitsInStock` column. Ex: "`13,120,17`"
|
||||
|
||||
{% meta id:index height:600 %}
|
||||
{% embed_file grid-filtering-comma-separated-values/main.vue preview %}
|
||||
{% embed_file grid-filtering-comma-separated-values/main.js %}
|
||||
{% embed_file grid-filtering-comma-separated-values/products.js %}
|
||||
{% endmeta %}
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
title: Make a reordered column locked when dropping it over a locked column
|
||||
description: See how to automatically lock a given Grid column when reordering it among locked columns
|
||||
type: how-to
|
||||
page_title: Select Grid rows when Shift, Ctrl or Command(for Mac) buttons are pressed | Kendo UI for Vue Native Grid
|
||||
slug: grid-reordering-locked-columns
|
||||
tags: grid, reorder, locked column, columns, kendovue, native
|
||||
res_type: kb
|
||||
category: knowledge-base
|
||||
---
|
||||
|
||||
## Environment
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Product Version</td>
|
||||
<td>3.10.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Product</td>
|
||||
<td>Progress® Kendo UI for Vue Native</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
This KB is an example that mimics the way the locked columns are internally handled by the Kendo UI for jQuery Grid.
|
||||
|
||||
In the current scenario when we reorder a column and drop it over a locked column or on the left of a locked column, then the column that is being reordered is becoming also a locked column.
|
||||
|
||||
|
||||
## Runnable example
|
||||
|
||||
To test the following example reorder one of the columns different from `Product Id` by moving it before the `Product Id` column. The result will be that the reordered column will also become locked.
|
||||
|
||||
{% meta height: 600 %}
|
||||
{% embed_file grid-reordering-locked-columns/main.vue preview %}
|
||||
{% embed_file grid-reordering-locked-columns/main.js %}
|
||||
{% embed_file grid-reordering-locked-columns/ColumnMenu.vue %}
|
||||
{% embed_file grid-reordering-locked-columns/products.js %}
|
||||
{% endmeta %}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
---
|
||||
title: Select Native Grid Rows When Specific Buttons are Pressed.
|
||||
description: An example demonstrating how the rows' selection inside the Native Grid component can be controlled and based on specific keyboard keys.
|
||||
type: how-to
|
||||
page_title: Select Grid rows when Shift, Ctrl or Command(for Mac) buttons are pressed | Kendo UI for Vue Native Grid
|
||||
slug: grid-select-row-when-shift-ctrl-command-are-pressed
|
||||
tags: grid, selection, specific key, shift, ctrl, command, key, press, kendovue, native
|
||||
res_type: kb
|
||||
category: knowledge-base
|
||||
---
|
||||
|
||||
## Environment
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Product Version</td>
|
||||
<td>3.10.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Product</td>
|
||||
<td>Progress® Kendo UI for Vue Native</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
This KB article demonstrates how you can limit the rows' selection of the Native Grid by pressing specific keyboard keys. In the following example the rows of the Native Grid can be selected or deselected only when the Shift, Ctrl or Command(for Mac) keyboard buttons are pressed.
|
||||
|
||||
**KB sections**
|
||||
|
||||
* [Solution description](#toc-solution-description)
|
||||
* [Runnable example](#toc-runnable-example)
|
||||
|
||||
## Solution description
|
||||
To achieve the following scenario, we use the [onRowClick]({% slug api_grid_gridprops %}#toc-onrowclick) event of the Native Grid and the following code:
|
||||
```
|
||||
onRowClick(event) {
|
||||
if (event.event.metaKey || event.event.ctrlKey || event.event.shiftKey) {
|
||||
event.dataItem[this.selectedField] =
|
||||
!event.dataItem[this.selectedField];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Runnable example
|
||||
|
||||
To test the following example press `Ctrl`, `Shift` or `Command(for Mac)` buttons and click on selected Grid row.
|
||||
|
||||
{% meta id:index height:600 %}
|
||||
{% embed_file grid-select-row-when-shift-ctrl-command-are-pressed/main.vue preview %}
|
||||
{% embed_file grid-select-row-when-shift-ctrl-command-are-pressed/main.js %}
|
||||
{% endmeta %}
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
title: Add confirmation Dialog when removing a file inside the Upload's list.
|
||||
description: A project that shows how you can add a confirmation Dialog when removing an uploaded file.
|
||||
type: how-to
|
||||
page_title: Learn how to add a confirmation Dialog when removing an uploaded file in the Upload component.
|
||||
slug: upload-add-confirmation-dialog-on-remove
|
||||
tags: upload, list, remove file, confirmation, dialog, custom, template, kendovue, native
|
||||
res_type: kb
|
||||
category: knowledge-base
|
||||
---
|
||||
|
||||
## Environment
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Product Version</td>
|
||||
<td>3.10.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Product</td>
|
||||
<td>Progress® Kendo UI for Vue Native Upload</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
This Knowledge base(KB) article demonstrates how we can add a confirmation Dialog when removing a file inside the list of the Upload component.
|
||||
|
||||
|
||||
**KB sections**
|
||||
|
||||
* [Solution description](#toc-solution-description)
|
||||
* [Runnable example](#toc-runnable-example)
|
||||
|
||||
## Solution description
|
||||
|
||||
To achieve the demonstrated behavior, we need to use the [list]({% slug api_upload_uploadprops %}#toc-list) prop of the Upload component and pass a custom template to it. More details about the usage of the list property can be found on our [Upload customization demo]({% slug rendering_upload %}).
|
||||
|
||||
|
||||
### Runnable example
|
||||
|
||||
To test the following example, select files for Upload and click on one of the "X" buttons inside the Upload's list.
|
||||
|
||||
{% meta id:index height:480 %}
|
||||
{% embed_file upload-add-confirmation-dialog-on-remove/main.vue preview %}
|
||||
{% embed_file upload-add-confirmation-dialog-on-remove/CustomList.vue %}
|
||||
{% embed_file upload-add-confirmation-dialog-on-remove/main.js %}
|
||||
{% endmeta %}
|
Загрузка…
Ссылка в новой задаче