diff --git a/COMET.Web.Common/Components/CardView/CardField.cs b/COMET.Web.Common/Components/CardView/CardField.cs index c8296c2a..24238f25 100644 --- a/COMET.Web.Common/Components/CardView/CardField.cs +++ b/COMET.Web.Common/Components/CardView/CardField.cs @@ -1,4 +1,28 @@ - +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 Starion Group S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the Starion Web Application implementation of ECSS-E-TM-10-25 +// Annex A and Annex C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +// -------------------------------------------------------------------------------------------------------------------- + namespace COMET.Web.Common.Components.CardView { using System.Text.RegularExpressions; @@ -8,39 +32,76 @@ namespace COMET.Web.Common.Components.CardView using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Rendering; + /// + /// A component that represents a data field in a 's ItemTemplate + /// + /// public class CardField : ComponentBase { + /// + /// The used to read properties from the instance of . + /// This is a static property on a generic type, so it will have different static values for each used generic type in the application + /// private static TypeAccessor typeAccessor { get; set; } + /// + /// Initializes the static properties of this class + /// static CardField() { typeAccessor = TypeAccessor.Create(typeof(T)); } + /// + /// Gets or sets The parent t + /// [CascadingParameter(Name="CardView")] private CardView CardView { get; set; } + /// + /// The SearchTerm of the used to visually show the SearchTerm in this + /// [CascadingParameter(Name = "SearchTerm")] private string SearchTerm { get; set; } + /// + /// Gets or sets the context of this + /// [Parameter] public T Context { get; set; } + /// + /// Gets or sets the FieldName (propertyname of ) to show in the UI + /// [Parameter] public string FieldName { get; set; } + /// + /// Gets or sets a value indicating that sorting is allowed for this + /// [Parameter] public bool AllowSort { get; set; } = true; + /// + /// Gets or sets a value indicating that searching is allowed for this + /// [Parameter] public bool AllowSearch { get; set; } = true; + /// + /// Method invoked when the component is ready to start, having received its + /// initial parameters from its parent in the render tree. + /// protected override void OnInitialized() { base.OnInitialized(); - this.CardView.RegisterCardField(this); + this.CardView.InitializeCardField(this); } + /// + /// Renders the component to the supplied . + /// + /// A that will receive the render output. protected override void BuildRenderTree(RenderTreeBuilder builder) { base.BuildRenderTree(builder); diff --git a/COMET.Web.Common/Components/CardView/CardView.razor b/COMET.Web.Common/Components/CardView/CardView.razor index 0a31c95b..4f86e930 100644 --- a/COMET.Web.Common/Components/CardView/CardView.razor +++ b/COMET.Web.Common/Components/CardView/CardView.razor @@ -23,24 +23,29 @@ @typeparam T @inherits DisposableComponent -

+

- - +
+ + + + + +

-
+
-
+
@@ -52,6 +51,18 @@ public partial class CardView : DisposableComponent [Parameter] public ICollection Items { get; set; } = new List(); + /// + /// Gets or sets the fixed height of a Card, used to calculate the amout of items to load into the DOM in px + /// + [Parameter] + public float ItemSize { get; set; } = 150; + + /// + /// Gets or sets the minimum width of a Card + /// + [Parameter] + public float MinWidth { get; set; } = 250; + /// /// Gets or sets a collection of propertynames of type to perform search on /// @@ -63,21 +74,21 @@ public partial class CardView : DisposableComponent public SortedSet SortFields { get; set; } = [string.Empty]; /// - /// Gets or sets the fixed height of a Card, used to calculate the amout of items to load into the DOM in px + /// Gets or sets a value indication that sorting is allowed /// - [Parameter] - public float ItemSize { get; set; } = 150; + public bool AllowSort { get; set; } = false; /// - /// Gets or sets the minimum width of a Card + /// Gets or sets a value indication that searching is allowed /// - [Parameter] - public float MinWidth { get; set; } = 250; - - public bool AllowSort { get; set; } = false; - public bool AllowSearch { get; set; } = false; + /// + /// hold a reference to the previously selected Item. + /// Typically used to check for changes in Items collection + /// + private ICollection previousItems = null; + /// /// A reference to the component for loading items /// @@ -99,17 +110,20 @@ public partial class CardView : DisposableComponent private T draggedNode { get; set; } /// - /// Gets or sets the term where to search/filter item of + /// Gets or sets the term where to search/filter items on /// private string searchTerm { get; set; } = string.Empty; - public string SelectedSortField { get; set; } + /// + /// Gets or sets the term where to sort items on + /// + private string selectedSortField { get; set; } = string.Empty; /// /// Gets the class to visually show a Card to be selected or unselected /// /// - /// + /// A string that retrurns the css class for selected Card private string GetSelectedClass(T vm) { return vm.Equals(this.selected) ? "selected" : ""; @@ -118,8 +132,8 @@ private string GetSelectedClass(T vm) /// /// Set the selected item /// - /// - private void selectOption(T item) + /// The item + private void selectItem(T item) { this.selected = item; } @@ -140,18 +154,7 @@ private void OnDragNode(T node) /// A private async Task OnDropNode(T targetNode) { - //TODO: Implement - // var targetFolder = (Folder)targetNode.Thing; - - // switch (this.DraggedNode?.Thing) - // { - // case File file: - // await this.ViewModel.FileHandlerViewModel.MoveFile(file, targetFolder); - // break; - // case Folder folder: - // await this.ViewModel.FolderHandlerViewModel.MoveFolder(folder, targetFolder); - // break; - // } + //not implemented yet } /// @@ -162,16 +165,16 @@ private async Task OnDropNode(T targetNode) private async ValueTask> LoadItems(ItemsProviderRequest request) { // Filter items based on the SearchTerm - var filteredItems = string.IsNullOrWhiteSpace(this.searchTerm) + var filteredItems = !this.AllowSearch || string.IsNullOrWhiteSpace(this.searchTerm) ? this.Items : this.Items.Where(item => this.FilterItem(item, this.searchTerm)).ToList(); // Return paged items for virtualization var items = filteredItems.Skip(request.StartIndex).Take(request.Count); - if (!string.IsNullOrWhiteSpace(this.SelectedSortField)) + if (this.AllowSort && !string.IsNullOrWhiteSpace(this.selectedSortField)) { - items = items.AsQueryable().OrderBy(this.SelectedSortField); + items = items.AsQueryable().OrderBy(this.selectedSortField); } return new ItemsProviderResult(items.ToList(), filteredItems.Count); @@ -185,31 +188,36 @@ private async ValueTask> LoadItems(ItemsProviderRequest r /// True if the item's selected properties contain the value to search for, otherwise false private bool FilterItem(T item, string query) { - var props = this.typeAccessor.GetMembers(); - - return props.Where(x => this.SearchFields.Contains(x.Name)).Any(prop => + if (this.AllowSearch) { - var value = this.typeAccessor[item, prop.Name].ToString(); - return value != null && value.Contains(query, StringComparison.OrdinalIgnoreCase); - }); + var props = this.typeAccessor.GetMembers(); + + return props.Where(x => this.SearchFields.Contains(x.Name)).Any(prop => + { + var value = this.typeAccessor[item, prop.Name].ToString(); + return value != null && value.Contains(query, StringComparison.OrdinalIgnoreCase); + }); + } + + return true; } /// /// A method that is executed when the user changes the search input element /// - /// The from the UI element's event - /// - private async Task OnSearchTextChanged(ChangeEventArgs e) + /// The text from the UI element's event + private void OnSearchTextChanged(string value) { - this.searchTerm = e.Value?.ToString() ?? string.Empty; + this.searchTerm = value ?? string.Empty; - if (this.virtualize != null) - { - await this.virtualize.RefreshDataAsync(); // Tell Virtualize to refresh data - } + this.virtualize?.RefreshDataAsync(); // Tell Virtualize to refresh data } - internal void RegisterCardField(CardField cardField) + /// + /// Initializes the + /// + /// the + internal void InitializeCardField(CardField cardField) { if (cardField.AllowSort) { @@ -230,11 +238,31 @@ internal void RegisterCardField(CardField cardField) } } - private void OnSelectedItemChanged(string arg) + /// + /// Handles the selection of a the Sort item + /// + /// + private void OnSelectedSortItemChanged(string arg) { - this.SelectedSortField = arg ?? string.Empty; + this.selectedSortField = arg ?? string.Empty; this.virtualize?.RefreshDataAsync(); // Tell Virtualize to refresh data } + + /// + /// Raised when a parameter is set + /// + /// An awaitable + protected override async Task OnParametersSetAsync() + { + await base.OnParametersSetAsync(); + + if (!Equals(this.previousItems, this.Items) && this.virtualize != null) + { + await this.virtualize.RefreshDataAsync(); // Tell Virtualize to refresh data + } + + this.previousItems = this.Items; + } } } diff --git a/COMET.Web.Common/wwwroot/css/styles.css b/COMET.Web.Common/wwwroot/css/styles.css index 719b4eed..946a1700 100644 --- a/COMET.Web.Common/wwwroot/css/styles.css +++ b/COMET.Web.Common/wwwroot/css/styles.css @@ -572,3 +572,38 @@ width: 25%; margin: 0; } + +.inline-search-icon { + display: flex; + align-items: center; + position: relative; + padding-left: 20px !important; /* Leave space for the icon */ +} + + .inline-search-icon::before { + content: '\e08f'; /* Unicode for an example icon (change this for your preferred icon) */ + font-family: 'Icons'; + top:3px; + position: absolute; + left: 5px; /* Position of the icon */ + color: #757575; /* Icon color */ + font-size: 16px; /* Icon size */ + } + +.inline-sort-icon { + display: flex; + align-items: center; + position: relative; + padding-left: 20px !important; /* Leave space for the icon */ +} + + .inline-sort-icon::before { + content: '\e0bf'; /* Unicode for an example icon (change this for your preferred icon) */ + font-family: 'Icons'; + vertical-align: middle; + position: absolute; + top: 3px; + left: 5px; /* Position of the icon */ + color: #757575; /* Icon color */ + font-size: 16px; /* Icon size */ + } \ No newline at end of file diff --git a/COMETwebapp/Components/Common/DataItemDetailsComponent.razor.css b/COMETwebapp/Components/Common/DataItemDetailsComponent.razor.css index b737c6d7..013c5918 100644 --- a/COMETwebapp/Components/Common/DataItemDetailsComponent.razor.css +++ b/COMETwebapp/Components/Common/DataItemDetailsComponent.razor.css @@ -1,5 +1,5 @@ .data-item-details-section { - padding: 30px; + padding: 15px; border: 1px dashed; border-radius: 10px; height: 100%; diff --git a/COMETwebapp/Components/ModelEditor/DetailsPanelEditor.razor b/COMETwebapp/Components/ModelEditor/DetailsPanelEditor.razor index 213ad787..8ef86c23 100644 --- a/COMETwebapp/Components/ModelEditor/DetailsPanelEditor.razor +++ b/COMETwebapp/Components/ModelEditor/DetailsPanelEditor.razor @@ -28,7 +28,7 @@ @if (this.ViewModel.SelectedSystemNode != null) { - +
@@ -45,29 +45,21 @@
- : + + + + Actual:
Published:
-
+
+ +
} - -@code -{ - private string[] searchAndSortFields = - [ - nameof(ElementDefinitionDetailsRowViewModel.ParameterTypeName), - nameof(ElementDefinitionDetailsRowViewModel.Owner), - nameof(ElementDefinitionDetailsRowViewModel.ModelCode), - nameof(ElementDefinitionDetailsRowViewModel.ActualValue), - nameof(ElementDefinitionDetailsRowViewModel.PublishedValue), - nameof(ElementDefinitionDetailsRowViewModel.SwitchValue) - ]; -} diff --git a/COMETwebapp/Components/ModelEditor/ElementDefinitionTable.razor b/COMETwebapp/Components/ModelEditor/ElementDefinitionTable.razor index d6fb40b9..b1b9ccdf 100644 --- a/COMETwebapp/Components/ModelEditor/ElementDefinitionTable.razor +++ b/COMETwebapp/Components/ModelEditor/ElementDefinitionTable.razor @@ -75,7 +75,6 @@ Width="100%" CssClass="model-editor-details">
-

Panel Editor

@if (this.ViewModel.SelectedElementDefinition is not null) { diff --git a/COMETwebapp/ViewModels/Components/SystemRepresentation/ElementDefinitionDetailsViewModel.cs b/COMETwebapp/ViewModels/Components/SystemRepresentation/ElementDefinitionDetailsViewModel.cs index cbfb4096..a40c87f6 100644 --- a/COMETwebapp/ViewModels/Components/SystemRepresentation/ElementDefinitionDetailsViewModel.cs +++ b/COMETwebapp/ViewModels/Components/SystemRepresentation/ElementDefinitionDetailsViewModel.cs @@ -53,6 +53,6 @@ public ElementBase SelectedSystemNode /// /// A collection of /// - public IEnumerable Rows { get; set; } + public ICollection Rows { get; set; } } } diff --git a/COMETwebapp/ViewModels/Components/SystemRepresentation/IElementDefinitionDetailsViewModel.cs b/COMETwebapp/ViewModels/Components/SystemRepresentation/IElementDefinitionDetailsViewModel.cs index ba343ca6..f88364b1 100644 --- a/COMETwebapp/ViewModels/Components/SystemRepresentation/IElementDefinitionDetailsViewModel.cs +++ b/COMETwebapp/ViewModels/Components/SystemRepresentation/IElementDefinitionDetailsViewModel.cs @@ -40,6 +40,6 @@ public interface IElementDefinitionDetailsViewModel /// /// A collection of /// - IEnumerable Rows { get; set; } + ICollection Rows { get; set; } } }