Skip to content

Commit

Permalink
Add cell metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
anmcgrath committed Jun 3, 2023
1 parent 2e0d684 commit 9c80a89
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 11 deletions.
33 changes: 33 additions & 0 deletions src/BlazorDatasheet/Commands/SetMetaDataCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using BlazorDatasheet.Data;

namespace BlazorDatasheet.Commands;

public class SetMetaDataCommand : IUndoableCommand
{
private readonly int _row;
private readonly int _col;
private readonly string _name;
private readonly object? _value;
private object? _oldValue;

public SetMetaDataCommand(int row, int col, string name, object? value)
{
_row = row;
_col = col;
_name = name;
_value = value;
}

public bool Execute(Sheet sheet)
{
_oldValue = sheet.GetMetaData(_row, _col, _name);
sheet.SetMetaDataImpl(_row, _col, _name, _value);
return true;
}

public bool Undo(Sheet sheet)
{
sheet.SetMetaDataImpl(_row, _col, _name, _oldValue);
return true;
}
}
36 changes: 36 additions & 0 deletions src/BlazorDatasheet/Data/Cell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ public class Cell : IReadOnlyCell, IWriteableCell
/// </summary>
public Type DataType { get; set; }

private Dictionary<string, object?>? _metaData;
public IReadOnlyDictionary<string, object?> MetaData => _metaData ?? new Dictionary<string, object?>();

/// <summary>
/// Represents an individual datasheet cell
/// </summary>
Expand Down Expand Up @@ -151,6 +154,17 @@ public T GetValue<T>()
}
}

public void ClearMetadata()
{
_metaData?.Clear();
}

public void Clear()
{
ClearMetadata();
ClearValue();
}

public void ClearValue()
{
var currentVal = GetValue();
Expand Down Expand Up @@ -199,6 +213,28 @@ public bool TrySetValue<T>(T val)
return TrySetValue(val, typeof(T));
}

internal void SetCellMetaData(string name, object? value)
{
if (_metaData == null)
_metaData = new Dictionary<string, object?>();

if (!_metaData.ContainsKey(name))
_metaData.Add(name, value);
_metaData[name] = value;
}

public object? GetMetaData(string name)
{
if (HasMetaData(name))
return _metaData[name];
return null;
}

public bool HasMetaData(string name)
{
return _metaData != null && _metaData.ContainsKey(name);
}

public bool DoTrySetValue(object? val, Type type)
{
try
Expand Down
75 changes: 64 additions & 11 deletions src/BlazorDatasheet/Data/Sheet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ public class Sheet

public event EventHandler<CellsSelectedEventArgs>? CellsSelected;

public event EventHandler<CellMetaDataChangeEventArgs>? MetaDataChanged;

/// <summary>
/// Fired when cells are merged
/// </summary>
Expand Down Expand Up @@ -303,9 +305,12 @@ internal bool RemoveRowAtImpl(int rowIndex)
/// <param name="index">index of inserted row\column</param>
/// <param name="count">count of inserted or removed rows\columns. count > 0 when inserted, count < 0 when reomved</param>
/// <returns>list of affected regions (before operation state) and list of new regions (state after operation)</returns>
internal (IReadOnlyList<CellMerge> mergesPerformed, IReadOnlyList<CellMerge> overridenMergedRegions) RerangeMergedCells(Axis axis, int index, int count)
internal (IReadOnlyList<CellMerge> mergesPerformed, IReadOnlyList<CellMerge> overridenMergedRegions)
RerangeMergedCells(Axis axis, int index, int count)
{
var afterInserted = axis == Axis.Row ? new Region(index, NumRows, 0, NumCols) : new Region(0, NumRows, index, NumCols);
var afterInserted = axis == Axis.Row
? new Region(index, NumRows, 0, NumCols)
: new Region(0, NumRows, index, NumCols);
var envelope = afterInserted.ToEnvelope();
var mergesPerformed = MergedCells.Search(envelope);
var overridenMergedRegions = new List<CellMerge>();
Expand Down Expand Up @@ -343,7 +348,8 @@ internal bool RemoveRowAtImpl(int rowIndex)

MergedCells.Delete(item);

if ((region.Top != region.Bottom && region.Left != region.Right) || region is RowRegion || region is ColumnRegion)
if ((region.Top != region.Bottom && region.Left != region.Right) || region is RowRegion ||
region is ColumnRegion)
{
var merge = new CellMerge(region);
MergedCells.Insert(merge);
Expand All @@ -353,17 +359,20 @@ internal bool RemoveRowAtImpl(int rowIndex)

return (mergesPerformed, overridenMergedRegions.AsReadOnly());
}

/// <summary>
/// Undo rerange operation to restore state before Insert\Remove rows\columns commands
/// </summary>
/// <param name="_mergesPerformed">state to return on</param>
/// <param name="_overridenMergedRegions">state to undo</param>
internal void UndoRerangeMergedCells(IReadOnlyList<CellMerge> _mergesPerformed, IReadOnlyList<CellMerge> _overridenMergedRegions)
internal void UndoRerangeMergedCells(IReadOnlyList<CellMerge> _mergesPerformed,
IReadOnlyList<CellMerge> _overridenMergedRegions)
{
foreach (var item in _overridenMergedRegions)
{
MergedCells.Delete(item);
}

foreach (var item in _mergesPerformed)
{
MergedCells.Insert(item);
Expand Down Expand Up @@ -425,8 +434,8 @@ public BRange Range(IEnumerable<IRegion> regions)
public IEnumerable<IReadOnlyCell> GetCellsInRegion(IRegion region)
{
return (new BRange(this, region))
.Positions
.Select(x => this.GetCell(x.row, x.col));
.Positions
.Select(x => this.GetCell(x.row, x.col));
}

/// <summary>
Expand Down Expand Up @@ -477,9 +486,9 @@ public IReadOnlyCell GetCell(CellPosition position)
internal IEnumerable<(int row, int col)> GetNonEmptyCellPositions(IRegion region)
{
return _cellDataStore.GetNonEmptyPositions(region.TopLeft.Row,
region.BottomRight.Row,
region.TopLeft.Col,
region.BottomRight.Col);
region.BottomRight.Row,
region.TopLeft.Col,
region.BottomRight.Col);
}

#endregion
Expand Down Expand Up @@ -535,6 +544,47 @@ internal bool TrySetCellValueImpl(int row, int col, object? value, bool raiseEve
return setValue;
}

/// <summary>
/// Sets cell metadata, specified by name, for the cell at position row, col
/// </summary>
/// <param name="row"></param>
/// <param name="col"></param>
/// <param name="name"></param>
/// <param name="value"></param>
/// <returns>Whether setting the cell metadata was successful</returns>
public bool SetCellMetaData(int row, int col, string name, object? value)
{
var cmd = new SetMetaDataCommand(row, col, name, value);
return Commands.ExecuteCommand(cmd);
}

internal void SetMetaDataImpl(int row, int col, string name, object? value)
{
var cell = _cellDataStore.Get(row, col);
if (cell == null)
{
cell = new Cell();
_cellDataStore.Set(row, col, cell);
}

var oldMetaData = cell.GetMetaData(name);

cell.SetCellMetaData(name, value);
this.MetaDataChanged?.Invoke(this, new CellMetaDataChangeEventArgs(row, col, name, oldMetaData, value));
}

/// <summary>
/// Returns the metadata with key "name" for the cell at row, col.
/// </summary>
/// <param name="row"></param>
/// <param name="col"></param>
/// <param name="name"></param>
/// <returns></returns>
public object? GetMetaData(int row, int col, string name)
{
return GetCell(row, col)?.GetMetaData(name);
}


internal void SetCell(int row, int col, Cell cell)
{
Expand Down Expand Up @@ -568,6 +618,7 @@ public bool SetCellValues(List<CellChange> changes)
var cmd = new SetCellValuesCommand(changes);
return Commands.ExecuteCommand(cmd);
}

/// <summary>
/// Set read only state for specified cell
/// </summary>
Expand All @@ -591,6 +642,7 @@ internal void SetCellReadOnlyImpl(int row, int col, bool readOnly)
var cell = _cellDataStore.Get(row, col);
cell.IsReadOnly = readOnly;
}

/// <summary>
/// Performs the actual setting of cell values, including raising events for any changes made.
/// </summary>
Expand Down Expand Up @@ -910,7 +962,7 @@ private IEnumerable<CellChangedFormat> SetColumnFormatImpl(CellFormat cellFormat
foreach (var rowInterval in RowFormats.GetAllIntervals())
{
overlappingRegions.Add(new Region(rowInterval.Start, rowInterval.End, region.Start.Col,
region.End.Col));
region.End.Col));
}

foreach (var overlapRegion in overlappingRegions)
Expand Down Expand Up @@ -961,7 +1013,7 @@ private IEnumerable<CellChangedFormat> SetRowFormatImpl(CellFormat cellFormat, R
foreach (var colInterval in ColFormats.GetAllIntervals())
{
overlappingRegions.Add(new Region(region.Start.Row, region.End.Row, colInterval.Start,
colInterval.End));
colInterval.End));
}

foreach (var overlapRegion in overlappingRegions)
Expand Down Expand Up @@ -1048,6 +1100,7 @@ internal CellChangedFormat SetCellFormat(int row, int col, CellFormat cellFormat
strBuilder.Append(value);
}
}

if (col != c1)
strBuilder.Append(tabDelimiter);
}
Expand Down
19 changes: 19 additions & 0 deletions src/BlazorDatasheet/Events/CellMetaDataChangeEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace BlazorDatasheet.Events;

public class CellMetaDataChangeEventArgs
{
public CellMetaDataChangeEventArgs(int row, int col, string name, object? oldValue, object? newValue)
{
Name = name;
OldValue = oldValue;
NewValue = newValue;
Row = row;
Col = col;
}

public int Row { get; }
public int Col { get; }
public string Name { get; }
public object? OldValue { get; }
public object? NewValue { get; }
}
2 changes: 2 additions & 0 deletions src/BlazorDatasheet/Interfaces/IReadOnlyCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ public interface IReadOnlyCell
public int Col { get; }
public bool IsValid { get; }
public object? Data { get; }
object? GetMetaData(string name);
bool HasMetaData(string name);
}
46 changes: 46 additions & 0 deletions test/BlazorDatasheet.Test/MetaDataTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using BlazorDatasheet.Data;
using NUnit.Framework;

namespace BlazorDatasheet.Test;

public class MetaDataTests
{
[Test]
public void Set_Cell_MetaData_And_Undo_Works()
{
var sheet = new Sheet(3, 3);
sheet.SetCellMetaData(1, 1, "test", 7);
Assert.AreEqual(7, sheet.GetMetaData(1, 1, "test"));
sheet.SetCellMetaData(1, 1, "test", 8);
Assert.AreEqual(8, sheet.GetMetaData(1, 1, "test"));
sheet.Commands.Undo();
Assert.AreEqual(7, sheet.GetMetaData(1, 1, "test"));
}

[Test]
public void Set_Cell_MetaData_Fires_Event()
{
var sheet = new Sheet(3, 3);
var fired = false;
var rowFired = -1;
var colFired = -1;
var nameFired = "";
object? newData = null;
sheet.MetaDataChanged += (sender, args) =>
{
fired = true;
rowFired = args.Row;
colFired = args.Col;
newData = args.NewValue;
nameFired = args.Name;
};

sheet.SetCellMetaData(1, 2, "test", "value");

Assert.True(fired);
Assert.AreEqual(1, rowFired);
Assert.AreEqual(2, colFired);
Assert.AreEqual("test", nameFired);
Assert.AreEqual("value", newData);
}
}

0 comments on commit 9c80a89

Please sign in to comment.