Demo
Source code
<template> <div class="demo-panel p-4 flex flex-col h-full"> <RevoGrid class="skip-style rounded-lg overflow-hidden color-grid cell-border" :theme="isDark ? 'darkMaterial' : 'material'" :columns="columns" :source="rows" :grouping="grouping" :column-types="COLUMNS_TYPES" :plugins="PLUGINS_COLOR" :filter="true" :hide-columns="hiddenColumns" resize no-header hide-attribution @toggle-hide-column="toggleHideColumn" /> </div></template>
<script setup lang="ts">import { ref, shallowRef } from 'vue';import { currentThemeVue } from '../composables/useRandomData';import RevoGrid, { type ColumnProp, type ColumnRegular,} from '@revolist/vue3-datagrid';import { COLUMNS_COLOR, COLUMNS_TYPES, PLUGINS_COLOR,} from './color.config';import { DATA_COLOR } from './color.data';import { GROUPING_COLOR } from './color.grouping';const { isDark } = currentThemeVue();
// Define columnsconst columns = shallowRef<ColumnRegular[]>([...COLUMNS_COLOR]);const hiddenColumns = shallowRef<ColumnProp[]>([]);
// Sample dataconst rows = ref([...DATA_COLOR]);
// Grouping configurationconst grouping = ref(GROUPING_COLOR);const toggleHideColumn = ({ detail: cols }: CustomEvent<ColumnProp[]>) => { hiddenColumns.value = [...cols];};</script>
<style lang="scss" src="./color.styles.scss"></style>
import React, { useState, useMemo, useRef } from 'react';import { RevoGrid, type DataType } from '@revolist/react-datagrid';import { currentTheme } from '../composables/useRandomData';import { COLUMNS_COLOR, COLUMNS_TYPES, PLUGINS_COLOR } from './color.config';import { DATA_COLOR } from './color.data';import { GROUPING_COLOR } from './color.grouping';
function Color() { const { isDark } = currentTheme(); const gridRef = useRef<HTMLRevoGridElement>(null); const [source] = useState<DataType[]>(DATA_COLOR); const [hiddenColumns, setHiddenColumns] = useState<string[]>([]);
const columns = useMemo(() => [...COLUMNS_COLOR], []); const columnTypes = useMemo(() => ({ ...COLUMNS_TYPES }), []); const grouping = useMemo(() => GROUPING_COLOR, []);
const toggleHideColumn = (cols: string[]) => { setHiddenColumns(cols); };
return ( <div className="flex flex-col gap-4"> <RevoGrid ref={gridRef} className="skip-style rounded-lg overflow-hidden color-grid cell-border" theme={isDark() ? 'darkMaterial' : 'material'} columns={columns} source={source} grouping={grouping} columnTypes={columnTypes} plugins={PLUGINS_COLOR} filter={true} hideColumns={hiddenColumns} style={{ height: 'calc(100vh - 100px)' }} resize noHeader hideAttribution onToggleHideColumn={(e: CustomEvent<string[]>) => toggleHideColumn(e.detail)} /> </div> );}
export default Color;
import { Component, ViewEncapsulation } from '@angular/core';import { RevoGrid } from '@revolist/angular-datagrid';import { COLUMNS_COLOR, COLUMNS_TYPES, PLUGINS_COLOR } from './color.config';import { DATA_COLOR } from './color.data';import { GROUPING_COLOR } from './color.grouping';import { currentTheme } from '../composables/useRandomData';
@Component({ selector: 'color-grid', standalone: true, imports: [RevoGrid], template: ` <div class="flex flex-col gap-4"> <revo-grid class="skip-style rounded-lg overflow-hidden color-grid cell-border" [theme]="theme" [columns]="columns" [source]="source" [grouping]="grouping" [columnTypes]="columnTypes" [plugins]="plugins" [filter]="true" [hideColumns]="hiddenColumns" style="height: calc(100vh - 100px)" [resize]="true" [noHeader]="true" [hideAttribution]="true" (toggleHideColumn)="toggleHideColumn($event)" ></revo-grid> </div> `, encapsulation: ViewEncapsulation.None,})export class ColorGridComponent { theme = currentTheme().isDark() ? 'darkMaterial' : 'material'; columns = [...COLUMNS_COLOR]; source = DATA_COLOR; grouping = GROUPING_COLOR; columnTypes = { ...COLUMNS_TYPES }; plugins = PLUGINS_COLOR; hiddenColumns: string[] = [];
toggleHideColumn(cols: string[]) { this.hiddenColumns = cols; }}
<script lang="ts"> import { RevoGrid } from '@revolist/svelte-datagrid'; import { COLUMNS_COLOR, COLUMNS_TYPES, PLUGINS_COLOR } from './color.config'; import { DATA_COLOR } from './color.data'; import { GROUPING_COLOR } from './color.grouping'; import { currentTheme } from '../composables/useRandomData';
const { isDark } = currentTheme(); let hiddenColumns: string[] = [];
function toggleHideColumn(e: CustomEvent<string[]>) { hiddenColumns = e.detail; }</script>
<div class="flex flex-col gap-4"> <RevoGrid class="skip-style rounded-lg overflow-hidden color-grid cell-border" theme={isDark() ? 'darkMaterial' : 'material'} columns={COLUMNS_COLOR} source={DATA_COLOR} grouping={GROUPING_COLOR} columnTypes={COLUMNS_TYPES} plugins={PLUGINS_COLOR} filter={true} hideColumns={hiddenColumns} style="height: calc(100vh - 100px)" resize noHeader hideAttribution on:toggleHideColumn={toggleHideColumn} /></div>
import { defineCustomElements } from '@revolist/revogrid/loader';import { COLUMNS_COLOR, COLUMNS_TYPES, PLUGINS_COLOR } from './color.config';import { DATA_COLOR } from './color.data';import { GROUPING_COLOR } from './color.grouping';import { currentTheme } from '../composables/useRandomData';
defineCustomElements();
const { isDark } = currentTheme();
export function load(parentSelector: string) { const grid = document.createElement('revo-grid');
grid.className = 'skip-style rounded-lg overflow-hidden color-grid cell-border'; grid.theme = isDark() ? 'darkMaterial' : 'material'; grid.columns = COLUMNS_COLOR; grid.source = DATA_COLOR; grid.grouping = GROUPING_COLOR; grid.columnTypes = COLUMNS_TYPES; grid.plugins = PLUGINS_COLOR; grid.filter = true; grid.style.height = 'calc(100vh - 100px)'; grid.resize = true; grid.noHeader = true; grid.hideAttribution = true;
grid.addEventListener('toggleHideColumn', (e: CustomEvent<string[]>) => { grid.hideColumns = e.detail; });
document.querySelector(parentSelector)?.appendChild(grid);}
import { type ColumnRegular, GROUP_DEPTH, type GroupLabelTemplateFunc, isGrouping, dispatch, type ColumnProp,} from '@revolist/revogrid';
import NumberColumnType from '@revolist/revogrid-column-numeral';
import { AdvanceFilterPlugin, FilterHeaderPlugin, editorTimeline, avatarRenderer, linkRenderer, arrayRenderer, ColumnHidePlugin, extendTemplates, circularProgressRenderer, editorSlider, TooltipPlugin, commonAggregators, getGroupingData, ColumnDropdown, defineDropdown, type DropdownOption,} from '@revolist/revogrid-pro';import { getColorByWeekDiff, getGroupByDate } from './color.utils';import { STATUS, PRIORITY, TAGS } from './color.data';
export const PLUGINS_COLOR = [ AdvanceFilterPlugin, FilterHeaderPlugin, ColumnHidePlugin, TooltipPlugin,];
const currencyFormat = '$0,0.[00]';export const COLUMNS_TYPES = { integer: new NumberColumnType(), percent: new NumberColumnType('0.00%'), currency: new NumberColumnType(currencyFormat), dropdown: ColumnDropdown,};
export const COLUMNS_COLOR: ColumnRegular[] = [ { name: 'Task', prop: 'task', pin: 'colPinStart', filter: ['string'], size: 250, filterPlaceholder: 'Search task', // set progress value based on status and progress fields progress: ({ model }) => { const item1 = model.progress ?? 0; const item2 = model.status === 'Complete' ? 100 : 0; return (item1 + item2) / 2; }, showValue: true, // set thresholds for progress color thresholds: [ { value: 0, className: 'low' }, { value: 50, className: 'medium' }, { value: 75, className: 'high' }, ], // set cell multiple template for task column cellTemplate: extendTemplates( // set color marker side template (h, props) => { const store = props.providers.data; const items = store.get('items'); const source = store.get('source'); const weekDiff = getGroupByDate(props.model.week); const color = getColorByWeekDiff(weekDiff); for (let i = props.rowIndex - 1; i >= 0; i--) { const item = items[i]; const sourceItem = source[item]; if (isGrouping(sourceItem)) { return h('div', { class: 'marker-side', style: { backgroundColor: color, }, }); } } }, // set cell value template (h, { value }) => h('div', { class: 'overflow-hidden grow' }, value), // set progress template (h, schema) => h( 'div', { 'data-tooltip': 'Task progress based on Status and Progress fields', 'tooltip-position': 'bottom', }, circularProgressRenderer(h, schema), ), ), }, { name: 'Owner', prop: 'avatar', readonly: true, size: 100, filter: false, // set column template for owner column with empty name in top header columnTemplate: () => '', // set cell template for owner column with avatar renderer cellTemplate: avatarRenderer, }, { name: 'Priority', prop: 'priority', size: 120, filterPlaceholder: 'Priority', filter: ['selection'], // set column type for priority column with dropdown columnType: 'dropdown', // set dropdown config for priority column dropdown: { // set render selected value for priority column renderSelectedValue: (h, selectedOptions, children) => { return h( 'div', { class: 'px-2 grow flex justify-between', style: { backgroundColor: selectedOptions[0].color, }, }, selectedOptions[0].label, children, ); }, // set render option for priority column renderOption: (h, option) => { return h( 'div', { class: 'px-2 py-1', style: { backgroundColor: option.color, }, }, option.label, ); }, // set dropdown source for priority column source: PRIORITY, }, }, { name: 'Status', prop: 'status', size: 120, filterPlaceholder: 'Status', filter: ['selection'], columnType: 'dropdown', // set dropdown config for status column dropdown: { // set dropdown source for status column source: STATUS, // set render selected value for status column renderSelectedValue: (h, selectedOptions, children) => { return h( 'div', { class: 'px-2 grow flex justify-between', style: { backgroundColor: selectedOptions[0].color, }, }, selectedOptions[0].label, children, ); }, // set render option for status column renderOption: (h, option) => { return h( 'div', { class: 'px-2 py-1', style: { backgroundColor: option.color, }, }, option.label, ); }, }, }, { name: 'Progress', prop: 'progress', size: 150, filterPlaceholder: 'Progress', filter: ['number'], // set cell template for progress column with editor slider cellTemplate: editorSlider, }, // set timeline column with editor timeline { name: 'Timeline', prop: 'timeline', size: 150, filterPlaceholder: 'When?', filter: false, readonly: true, // set column template for timeline column with empty name in top header columnTemplate: () => '', // set cell template for timeline column with editor timeline cellTemplate: editorTimeline, // set progress value based on progress field progress: ({ model }) => model.progress ?? 0, // set thresholds for progress color thresholds: [ { value: 0, className: 'low' }, { value: 50, className: 'medium' }, { value: 75, className: 'high' }, ], }, // set budget column with currency column type { name: 'Budget', prop: 'budget', size: 150, columnType: 'currency', filterPlaceholder: 'How much?', filter: ['number'], // pass column calculatation to grouping groupingAggregatorTemplate: ((h, props, column: ColumnRegular) => { const currentDepth = props.model?.[GROUP_DEPTH]; // get summary for budget column const summary = getGroupingData({ store: props.providers.data, itemIndex: props.itemIndex, currentDepth, columnProp: column.prop, // set aggregator for budget column aggregator: commonAggregators.sum, }).aggregationValue; // set cell template for budget column return h( 'span', { // set tooltip for budget column ['data-tooltip']: 'Total budget spent', class: 'badge flex items-center justify-center ml-2 px-2 py-0.5 text-xs font-medium rounded-md', }, // set cell number format for budget column NumberColumnType.getNumeralInstance()(summary).format(currencyFormat), ); }) as GroupLabelTemplateFunc, }, // set tags column with dropdown column type { name: 'Tags', prop: 'tags', size: 250, filterPlaceholder: 'Tags', columnType: 'dropdown', dropdown: { config: { multiSelect: true, }, source: TAGS, }, }, // set links column with array renderer { name: 'Links', prop: 'links', size: 250, cellTemplate: arrayRenderer(linkRenderer, { separator: ' | ', wrapper: 'div', wrapperClass: 'multi-data-container', }), },];
// set column dropdown options for new columnconst columnDropdownOptions = [...COLUMNS_COLOR].map((col) => ({ value: col.prop, label: col.name,})).filter((col) => !!col.label);
// set column properties for new columnconst ADD_COLUMNS: ColumnRegular = { prop: 'new', size: 50, pin: 'colPinEnd', readonly: true, filter: false, // set column properties for new column columnProperties: () => ({ class: 'flex', }), // set column dropdown options for new column columnDropdownOptions, // set visible columns for new column, we will use this to toggle hide columns in dropdown visibleColumns: columnDropdownOptions.map((col) => col.value), // set column template for new column columnTemplate: (h, props) => { return h('div', { // subscribe to element ref for new column ref: (el?: HTMLButtonElement) => { if (el) { // set dropdown for new column defineDropdown(el, { // set dropdown source for new column source: props.columnDropdownOptions, config: { // set multi select for new column multiSelect: true, // set dropdown menu class name for new column popupClassName: 'dropdown-menu-color', }, value: props.visibleColumns, renderSelectedValue: (h) => h('button', { class: 'insert-column' }), renderOption: (h, option, isSelected) => h('div', { class: 'label px-2 py-1' }, [ h('input', { type: 'checkbox', checked: isSelected, }), h('span', { class: 'toggle-box', }), option.label, ]), // set onChange for new column onChange(value) { const visibleProps = new Set(value); const hiddenColumns = props.columnDropdownOptions.reduce((acc: ColumnProp[], col: DropdownOption) => { if (!visibleProps.has(col.value)) { acc.push(col.value); } return acc; }, []); props.visibleColumns.length = 0; props.visibleColumns.push(...value); dispatch(el, 'toggle-hide-column', hiddenColumns); }, }); } }, }); }};
// set new column to columnsCOLUMNS_COLOR.push(ADD_COLUMNS);
import { type GroupingOptions, getSourceItem,} from '@revolist/revogrid';import { ignoreCellEvents, EXPAND_ICON } from '@revolist/revogrid-pro';import { getColorByWeekDiff, getGroupByDate } from './color.utils';
// set grouping options for color showcaseexport const GROUPING_COLOR: GroupingOptions = { // set grouping by week property props: ['week'], // set expanded all by default expandedAll: true, // set group label template for color showcase groupLabelTemplate: (h, props) => { // get week difference const weekDiff = getGroupByDate(props.name); // get color by week difference const color = getColorByWeekDiff(weekDiff);
// set label based on week difference let label = ''; if (weekDiff === 0) label = 'This Week'; else if (weekDiff === 1) label = 'Next Week'; else if (weekDiff < 0) label = 'Completed'; else label = `${Math.abs(weekDiff)} Weeks ${weekDiff > 0 ? 'Ahead' : 'Ago'}`;
// set title template for color showcase const title = h( 'div', { class: 'flex items-center font-semibold', style: { color, }, }, label, );
// Add expand button to pin start column if (props.providers.colType === 'colPinStart') { const expandButton = h( 'button', { class: 'tree-toggle', expanded: props.expanded, style: { color, }, }, EXPAND_ICON, ); return h( 'div', { class: 'flex items-center font-semibold gap-1 cursor-pointer', style: { color }, }, [h('div', undefined, expandButton), title], ); } if (props.providers.colType === 'colPinEnd' || !props.expanded) { return; }
// Create column headers for grouping const store = props.providers.columns; const columnHeaders = props.columnItems.map((columnProps, i) => { // Get column if visible const column = getSourceItem(store, columnProps.itemIndex); if (!column) return;
return h( 'div', { ...ignoreCellEvents, class: 'flex items-center absolute text-xs subheader', style: { height: '100%', width: `${columnProps.size}px`, top: 0, left: 0, transform: `translateX(${columnProps.start}px)`, }, }, [ column?.name, column.groupingAggregatorTemplate?.(h, props, column), ], ); });
return h('div', undefined, columnHeaders); },};
import { WEEK_DATES } from './color.utils';
// set tags for color showcaseexport const TAGS = [ { label: 'Frontend', value: 'Frontend' }, { label: 'Backend', value: 'Backend' }, { label: 'Design', value: 'Design' }, { label: 'QA', value: 'QA' }, { label: 'React', value: 'React' }, { label: 'Vue', value: 'Vue' }, { label: 'Angular', value: 'Angular' }, { label: 'Node.js', value: 'Node.js' }, { label: 'Python', value: 'Python' }, { label: 'Java', value: 'Java' }, { label: 'C#', value: 'C#' }, { label: 'Ruby', value: 'Ruby' }, { label: 'PHP', value: 'PHP' }, { label: 'Other', value: 'Other' },];
// set priority for color showcaseexport const PRIORITY = [ { label: 'High', value: 'High', color: '#e2435c', }, { label: 'Medium', value: 'Medium', color: '#f7c605', }, { label: 'Low', value: 'Low', color: '#00c874', },];
// set status for color showcaseexport const STATUS = [ { label: 'In Progress', value: 'In Progress', color: '#f7c605' }, { label: 'Complete', value: 'Complete', color: '#00c874' }, { label: 'Not Started', value: 'Not Started', color: '#e2435c' },];
// set data for color showcaseexport const DATA_COLOR = [ { name: 'John Doe', avatar: `https://randomuser.me/api/portraits/men/1.jpg`, priority: 'High', status: 'In Progress', progress: 20, timeline: { from: '2024-03-20', to: '2024-03-27' }, budget: 15000, tags: ['Frontend', 'React', 'Vue'], links: ['https://github.com', 'https://docs.example.com'], week: WEEK_DATES.thisWeek, task: 'Convince AI to write better code than humans', }, { name: 'Jane Smith', avatar: `https://randomuser.me/api/portraits/women/2.jpg`, priority: 'Medium', status: 'Not Started', timeline: { from: '2024-03-28', to: '2024-04-04' }, budget: 8000, tags: ['Backend', 'Node.js'], links: ['https://api.example.com'], progress: 50, week: WEEK_DATES.nextWeek, task: 'Teach rubber duck to debug code', }, { name: 'Bob Johnson', avatar: `https://randomuser.me/api/portraits/men/3.jpg`, priority: 'Medium', status: 'Complete', progress: 40, timeline: { from: '2024-04-05', to: '2024-04-12' }, budget: 5000, tags: ['Design', 'UI/UX'], links: ['https://design.example.com'], todo: 'Completed', week: WEEK_DATES.previousWeek, task: 'Make coffee machine understand TypeScript', }, { name: 'John Doe', avatar: `https://randomuser.me/api/portraits/men/5.jpg`, priority: 'Medium', status: 'Complete', timeline: { from: '2024-03-20', to: '2024-03-27' }, budget: 15000, tags: ['Frontend', 'React', 'Vue'], links: ['https://github.com', 'https://docs.example.com'], week: WEEK_DATES.thisWeek, task: 'Convert office plants to React components', progress: 50, }, { name: 'Jane Smith', avatar: `https://randomuser.me/api/portraits/men/3.jpg`, priority: 'Medium', status: 'Complete', timeline: { from: '2024-03-28', to: '2024-04-04' }, budget: 8000, tags: ['Backend', 'Node.js'], links: ['https://api.example.com'], week: WEEK_DATES.nextWeek, task: 'Train office cat to catch bugs', progress: 30, }, { name: 'Bob Johnson', avatar: `https://randomuser.me/api/portraits/women/4.jpg`, priority: 'High', status: 'Complete', timeline: { from: '2024-04-05', to: '2024-04-12' }, budget: 5000, tags: ['Design', 'UI/UX'], links: ['https://design.example.com'], week: WEEK_DATES.previousWeek, task: 'Make printer understand JSON', progress: 5, }, { name: 'John Doe', avatar: `https://randomuser.me/api/portraits/women/4.jpg`, priority: 'Low', status: 'In Progress', timeline: { from: '2024-03-20', to: '2024-03-27' }, progress: 100, budget: 15000, tags: ['Frontend', 'React', 'Vue'], links: ['https://github.com', 'https://docs.example.com'], week: WEEK_DATES.thisWeek, task: 'Humans: 1, Code: 0!', }, { name: 'Alice Brown', avatar: `https://randomuser.me/api/portraits/women/5.jpg`, priority: 'High', status: 'In Progress', timeline: { from: '2024-04-13', to: '2024-04-20' }, budget: 12000, tags: ['Frontend', 'Vue', 'Design'], links: ['https://portfolio.example.com'], week: WEEK_DATES.thisWeek, task: 'Redesign the company website', progress: 40, }, { name: 'Charlie Davis', avatar: `https://randomuser.me/api/portraits/men/6.jpg`, priority: 'Medium', status: 'Complete', timeline: { from: '2024-04-21', to: '2024-04-28' }, budget: 7000, tags: ['Backend', 'Node.js', 'API'], links: ['https://api.example.com'], week: WEEK_DATES.nextWeek, task: 'Develop new API endpoints', progress: 100, }, { name: 'Eve White', avatar: `https://randomuser.me/api/portraits/women/6.jpg`, priority: 'Low', status: 'In Progress', timeline: { from: '2024-04-29', to: '2024-05-06' }, budget: 5000, tags: ['Testing', 'QA'], links: ['https://testing.example.com'], week: WEEK_DATES.previousWeek, task: 'Conduct user acceptance testing', progress: 20, }, { name: 'David Wilson', avatar: `https://randomuser.me/api/portraits/men/7.jpg`, priority: 'High', status: 'Complete', timeline: { from: '2024-05-07', to: '2024-05-14' }, budget: 15000, tags: ['DevOps', 'Infrastructure'], links: ['https://devops.example.com'], week: WEEK_DATES.thisWeek, task: 'Migrate to cloud infrastructure', progress: 100, }, { name: 'Grace Lee', avatar: `https://randomuser.me/api/portraits/women/7.jpg`, priority: 'Medium', status: 'In Progress', timeline: { from: '2024-05-15', to: '2024-05-22' }, budget: 9000, tags: ['Frontend', 'React', 'UI/UX'], links: ['https://uiux.example.com'], week: WEEK_DATES.nextWeek, task: 'Create a new user dashboard', progress: 60, }, { name: 'Henry Martinez', avatar: `https://randomuser.me/api/portraits/men/8.jpg`, priority: 'Low', status: 'Complete', timeline: { from: '2024-05-23', to: '2024-05-30' }, budget: 3000, tags: ['Documentation'], links: ['https://docs.example.com'], week: WEEK_DATES.previousWeek, task: 'Update project documentation', progress: 100, }, { name: 'Isabella Garcia', avatar: `https://randomuser.me/api/portraits/women/8.jpg`, priority: 'High', status: 'In Progress', timeline: { from: '2024-06-01', to: '2024-06-08' }, budget: 11000, tags: ['Marketing', 'SEO'], links: ['https://marketing.example.com'], week: WEEK_DATES.thisWeek, task: 'Launch new marketing campaign', progress: 30, }, { name: 'Jack Thompson', avatar: `https://randomuser.me/api/portraits/men/9.jpg`, priority: 'Medium', status: 'Complete', timeline: { from: '2024-06-09', to: '2024-06-16' }, budget: 8000, tags: ['Data Science', 'Analytics'], links: ['https://analytics.example.com'], week: WEEK_DATES.nextWeek, task: 'Analyze user data for insights', progress: 100, }, { name: 'Liam Johnson', avatar: `https://randomuser.me/api/portraits/men/10.jpg`, priority: 'Low', status: 'In Progress', timeline: { from: '2024-06-17', to: '2024-06-24' }, budget: 4000, tags: ['Support', 'Customer Service'], links: ['https://support.example.com'], week: WEEK_DATES.previousWeek, task: 'Improve customer support response time', progress: 50, }, { name: 'Mia Robinson', avatar: `https://randomuser.me/api/portraits/women/9.jpg`, priority: 'High', status: 'Complete', timeline: { from: '2024-06-25', to: '2024-07-02' }, budget: 13000, tags: ['Product', 'Management'], links: ['https://product.example.com'], week: WEEK_DATES.thisWeek, task: 'Define product roadmap for next quarter', progress: 100, },];