diff --git a/ASP.NET Core/Controllers/orig_HomeController.cs b/ASP.NET Core/Controllers/orig_HomeController.cs deleted file mode 100644 index 90668d1..0000000 --- a/ASP.NET Core/Controllers/orig_HomeController.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; - -namespace ASP_NET_Core.Controllers -{ - public class HomeController : Controller - { - public IActionResult Index() - { - return View(); - } - - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - public IActionResult Error() { - return View(); - } - } -} diff --git a/ASP.NET Core/Controllers/orig_SampleDataController.cs b/ASP.NET Core/Controllers/orig_SampleDataController.cs deleted file mode 100644 index 9e55a9a..0000000 --- a/ASP.NET Core/Controllers/orig_SampleDataController.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using ASP_NET_Core.Models; -using DevExtreme.AspNet.Data; -using DevExtreme.AspNet.Mvc; -using Microsoft.AspNetCore.Mvc; - -namespace ASP_NET_Core.Controllers { - - [Route("api/[controller]")] - public class SampleDataController : Controller { - - [HttpGet] - public object Get(DataSourceLoadOptions loadOptions) { - return DataSourceLoader.Load(SampleData.Orders, loadOptions); - } - - } -} \ No newline at end of file diff --git a/ASP.NET Core/Models/orig_SampleData.cs b/ASP.NET Core/Models/orig_SampleData.cs deleted file mode 100644 index b49f5b9..0000000 --- a/ASP.NET Core/Models/orig_SampleData.cs +++ /dev/null @@ -1,362 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ASP_NET_Core.Models { - static class SampleData { - public static List Orders = new List() { - new SampleOrder { - OrderID = 10248, - OrderDate = new DateTime(1996, 7, 4), - ShipCountry = "France", - ShipCity = "Reims", - CustomerName = "Paul Henriot" - }, - new SampleOrder { - OrderID = 10249, - OrderDate = new DateTime(1996, 7, 5), - ShipCountry = "Germany", - ShipCity = "Münster", - CustomerName = "Karin Josephs" - }, - new SampleOrder { - OrderID = 10250, - OrderDate = new DateTime(1996, 7, 8), - ShipCountry = "Brazil", - ShipCity = "Rio de Janeiro", - CustomerName = "Mario Pontes" - }, - new SampleOrder { - OrderID = 10251, - OrderDate = new DateTime(1996, 7, 8), - ShipCountry = "France", - ShipCity = "Lyon", - CustomerName = "Mary Saveley" - }, - new SampleOrder { - OrderID = 10252, - OrderDate = new DateTime(1996, 7, 9), - ShipCountry = "Belgium", - ShipCity = "Charleroi", - CustomerName = "Pascale Cartrain" - }, - new SampleOrder { - OrderID = 10253, - OrderDate = new DateTime(1996, 7, 10), - ShipCountry = "Brazil", - ShipCity = "Rio de Janeiro", - CustomerName = "Mario Pontes" - }, - new SampleOrder { - OrderID = 10254, - OrderDate = new DateTime(1996, 7, 11), - ShipCountry = "Switzerland", - ShipCity = "Bern", - CustomerName = "Yang Wang" - }, - new SampleOrder { - OrderID = 10255, - OrderDate = new DateTime(1996, 7, 12), - ShipCountry = "Switzerland", - ShipCity = "Genève", - CustomerName = "Michael Holz" - }, - new SampleOrder { - OrderID = 10256, - OrderDate = new DateTime(1996, 7, 15), - ShipCountry = "Brazil", - ShipCity = "Resende", - CustomerName = "Paula Parente" - }, - new SampleOrder { - OrderID = 10257, - OrderDate = new DateTime(1996, 7, 16), - ShipCountry = "Venezuela", - ShipCity = "San Cristóbal", - CustomerName = "Carlos Hernández" - }, - new SampleOrder { - OrderID = 10258, - OrderDate = new DateTime(1996, 7, 17), - ShipCountry = "Austria", - ShipCity = "Graz", - CustomerName = "Roland Mendel" - }, - new SampleOrder { - OrderID = 10259, - OrderDate = new DateTime(1996, 7, 18), - ShipCountry = "Mexico", - ShipCity = "México D.F.", - CustomerName = "Francisco Chang" - }, - new SampleOrder { - OrderID = 10260, - OrderDate = new DateTime(1996, 7, 19), - ShipCountry = "Germany", - ShipCity = "Köln", - CustomerName = "Henriette Pfalzheim" - }, - new SampleOrder { - OrderID = 10261, - OrderDate = new DateTime(1996, 7, 19), - ShipCountry = "Brazil", - ShipCity = "Rio de Janeiro", - CustomerName = "Bernardo Batista" - }, - new SampleOrder { - OrderID = 10262, - OrderDate = new DateTime(1996, 7, 22), - ShipCountry = "USA", - ShipCity = "Albuquerque", - CustomerName = "Paula Wilson" - }, - new SampleOrder { - OrderID = 10263, - OrderDate = new DateTime(1996, 7, 23), - ShipCountry = "Austria", - ShipCity = "Graz", - CustomerName = "Roland Mendel" - }, - new SampleOrder { - OrderID = 10264, - OrderDate = new DateTime(1996, 7, 24), - ShipCountry = "Sweden", - ShipCity = "Bräcke", - CustomerName = "Maria Larsson" - }, - new SampleOrder { - OrderID = 10265, - OrderDate = new DateTime(1996, 7, 25), - ShipCountry = "France", - ShipCity = "Strasbourg", - CustomerName = "Frédérique Citeaux" - }, - new SampleOrder { - OrderID = 10266, - OrderDate = new DateTime(1996, 7, 26), - ShipCountry = "Finland", - ShipCity = "Oulu", - CustomerName = "Pirkko Koskitalo" - }, - new SampleOrder { - OrderID = 10267, - OrderDate = new DateTime(1996, 7, 29), - ShipCountry = "Germany", - ShipCity = "München", - CustomerName = "Peter Franken" - }, - new SampleOrder { - OrderID = 10268, - OrderDate = new DateTime(1996, 7, 30), - ShipCountry = "Venezuela", - ShipCity = "Caracas", - CustomerName = "Manuel Pereira" - }, - new SampleOrder { - OrderID = 10269, - OrderDate = new DateTime(1996, 7, 31), - ShipCountry = "USA", - ShipCity = "Seattle", - CustomerName = "Karl Jablonski" - }, - new SampleOrder { - OrderID = 10270, - OrderDate = new DateTime(1996, 8, 1), - ShipCountry = "Finland", - ShipCity = "Oulu", - CustomerName = "Pirkko Koskitalo" - }, - new SampleOrder { - OrderID = 10271, - OrderDate = new DateTime(1996, 8, 1), - ShipCountry = "USA", - ShipCity = "Lander", - CustomerName = "Art Braunschweiger" - }, - new SampleOrder { - OrderID = 10272, - OrderDate = new DateTime(1996, 8, 2), - ShipCountry = "USA", - ShipCity = "Albuquerque", - CustomerName = "Paula Wilson" - }, - new SampleOrder { - OrderID = 10273, - OrderDate = new DateTime(1996, 8, 5), - ShipCountry = "Germany", - ShipCity = "Cunewalde", - CustomerName = "Horst Kloss" - }, - new SampleOrder { - OrderID = 10274, - OrderDate = new DateTime(1996, 8, 6), - ShipCountry = "France", - ShipCity = "Reims", - CustomerName = "Paul Henriot" - }, - new SampleOrder { - OrderID = 10275, - OrderDate = new DateTime(1996, 8, 7), - ShipCountry = "Italy", - ShipCity = "Bergamo", - CustomerName = "Giovanni Rovelli" - }, - new SampleOrder { - OrderID = 10276, - OrderDate = new DateTime(1996, 8, 8), - ShipCountry = "Mexico", - ShipCity = "México D.F.", - CustomerName = "Miguel Angel Paolino" - }, - new SampleOrder { - OrderID = 10277, - OrderDate = new DateTime(1996, 8, 9), - ShipCountry = "Germany", - ShipCity = "Leipzig", - CustomerName = "Alexander Feuer" - }, - new SampleOrder { - OrderID = 10278, - OrderDate = new DateTime(1996, 8, 12), - ShipCountry = "Sweden", - ShipCity = "Luleå", - CustomerName = "Christina Berglund" - }, - new SampleOrder { - OrderID = 10279, - OrderDate = new DateTime(1996, 8, 13), - ShipCountry = "Germany", - ShipCity = "Frankfurt a.M.", - CustomerName = "Renate Messner" - }, - new SampleOrder { - OrderID = 10280, - OrderDate = new DateTime(1996, 8, 14), - ShipCountry = "Sweden", - ShipCity = "Luleå", - CustomerName = "Christina Berglund" - }, - new SampleOrder { - OrderID = 10281, - OrderDate = new DateTime(1996, 8, 14), - ShipCountry = "Spain", - ShipCity = "Madrid", - CustomerName = "Alejandra Camino" - }, - new SampleOrder { - OrderID = 10282, - OrderDate = new DateTime(1996, 8, 15), - ShipCountry = "Spain", - ShipCity = "Madrid", - CustomerName = "Alejandra Camino" - }, - new SampleOrder { - OrderID = 10283, - OrderDate = new DateTime(1996, 8, 16), - ShipCountry = "Venezuela", - ShipCity = "Barquisimeto", - CustomerName = "Carlos González" - }, - new SampleOrder { - OrderID = 10284, - OrderDate = new DateTime(1996, 8, 19), - ShipCountry = "Germany", - ShipCity = "Frankfurt a.M.", - CustomerName = "Renate Messner" - }, - new SampleOrder { - OrderID = 10285, - OrderDate = new DateTime(1996, 8, 20), - ShipCountry = "Germany", - ShipCity = "Cunewalde", - CustomerName = "Horst Kloss" - }, - new SampleOrder { - OrderID = 10286, - OrderDate = new DateTime(1996, 8, 21), - ShipCountry = "Germany", - ShipCity = "Cunewalde", - CustomerName = "Horst Kloss" - }, - new SampleOrder { - OrderID = 10287, - OrderDate = new DateTime(1996, 8, 22), - ShipCountry = "Brazil", - ShipCity = "Rio de Janeiro", - CustomerName = "Janete Limeira" - }, - new SampleOrder { - OrderID = 10288, - OrderDate = new DateTime(1996, 8, 23), - ShipCountry = "Italy", - ShipCity = "Reggio Emilia", - CustomerName = "Maurizio Moroni" - }, - new SampleOrder { - OrderID = 10289, - OrderDate = new DateTime(1996, 8, 26), - ShipCountry = "UK", - ShipCity = "London", - CustomerName = "Victoria Ashworth" - }, - new SampleOrder { - OrderID = 10290, - OrderDate = new DateTime(1996, 8, 27), - ShipCountry = "Brazil", - ShipCity = "Sao Paulo", - CustomerName = "Pedro Afonso" - }, - new SampleOrder { - OrderID = 10291, - OrderDate = new DateTime(1996, 8, 27), - ShipCountry = "Brazil", - ShipCity = "Rio de Janeiro", - CustomerName = "Bernardo Batista" - }, - new SampleOrder { - OrderID = 10292, - OrderDate = new DateTime(1996, 8, 28), - ShipCountry = "Brazil", - ShipCity = "Sao Paulo", - CustomerName = "Anabela Domingues" - }, - new SampleOrder { - OrderID = 10293, - OrderDate = new DateTime(1996, 8, 29), - ShipCountry = "Mexico", - ShipCity = "México D.F.", - CustomerName = "Miguel Angel Paolino" - }, - new SampleOrder { - OrderID = 10294, - OrderDate = new DateTime(1996, 8, 30), - ShipCountry = "USA", - ShipCity = "Albuquerque", - CustomerName = "Paula Wilson" - }, - new SampleOrder { - OrderID = 10295, - OrderDate = new DateTime(1996, 9, 2), - ShipCountry = "France", - ShipCity = "Reims", - CustomerName = "Paul Henriot" - }, - new SampleOrder { - OrderID = 10296, - OrderDate = new DateTime(1996, 9, 3), - ShipCountry = "Venezuela", - ShipCity = "Barquisimeto", - CustomerName = "Carlos González" - }, - new SampleOrder { - OrderID = 10297, - OrderDate = new DateTime(1996, 9, 4), - ShipCountry = "France", - ShipCity = "Strasbourg", - CustomerName = "Frédérique Citeaux" - } - }; - } -} diff --git a/ASP.NET Core/Models/orig_SampleOrder.cs b/ASP.NET Core/Models/orig_SampleOrder.cs deleted file mode 100644 index 7f10198..0000000 --- a/ASP.NET Core/Models/orig_SampleOrder.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ASP_NET_Core.Models { - public class SampleOrder { - public int OrderID { get; set; } - public DateTime OrderDate { get; set; } - public string CustomerID { get; set; } - public string CustomerName { get; set; } - public string ShipCountry { get; set; } - public string ShipCity { get; set; } - } -} diff --git a/ASP.NET Core/Views/Home/Index.cshtml b/ASP.NET Core/Views/Home/Index.cshtml index 672571d..211b112 100644 --- a/ASP.NET Core/Views/Home/Index.cshtml +++ b/ASP.NET Core/Views/Home/Index.cshtml @@ -1,29 +1,25 @@ @using ASP_NET_Core.Models -

Home

- -@(Html.DevExtreme().DataGrid() - .ShowBorders(true) - .DataSource(d => d.Mvc().Controller("SampleData").LoadAction("Get").Key("OrderID")) - .Columns(columns => { - columns.AddFor(m => m.OrderID); - columns.AddFor(m => m.OrderDate); - columns.AddFor(m => m.CustomerName); - columns.AddFor(m => m.ShipCountry); - columns.AddFor(m => m.ShipCity); +
+

DataGrid - Select multiple items and drag'n'drop

+
+ Clear selection after drop + @(Html.DevExtreme().Switch().ID("clear-after-drop-switch")) +
+
+@(Html.DevExtreme().TabPanel() + .Items(tabs => { + tabs.Add().Title("Local Data") + .Template(@ +
+ @(await Html.PartialAsync("../PartialViews/DataGridLocal")) +
+
); + tabs.Add().Title("Remote Data") + .Template(@ +
+ @(await Html.PartialAsync("../PartialViews/DataGridRemote")) +
+
); }) - .Paging(p => p.PageSize(10)) - .FilterRow(f => f.Visible(true)) - .HeaderFilter(f => f.Visible(true)) - .GroupPanel(p => p.Visible(true)) - .Grouping(g => g.AutoExpandAll(false)) - .RemoteOperations(true) - .Summary(s => s - .TotalItems(totalItems => { - totalItems.AddFor(m => m.ShipCity).SummaryType(SummaryType.Count); - }) - .GroupItems(groupItems => { - groupItems.Add().SummaryType(SummaryType.Count); - }) - ) ) \ No newline at end of file diff --git a/ASP.NET Core/Views/Home/orig_Index.cshtml b/ASP.NET Core/Views/Home/orig_Index.cshtml deleted file mode 100644 index 83917fc..0000000 --- a/ASP.NET Core/Views/Home/orig_Index.cshtml +++ /dev/null @@ -1,23 +0,0 @@ -
-

DataGrid - Select multiple items and drag'n'drop

-
- Clear selection after drop - @(Html.DevExtreme().Switch().ID("clearAfterDropSwitch")) -
-
-@(Html.DevExtreme().TabPanel() - .Items(tabs => { - tabs.Add().Title("Plain Data") - .Template(@ -
- @(await Html.PartialAsync("../PartialViews/DataGridLocal")) -
-
); - tabs.Add().Title("Hierarchical Data") - .Template(@ -
- @(await Html.PartialAsync("../PartialViews/DataGridRemote")) -
-
); - }) -) \ No newline at end of file diff --git a/ASP.NET Core/Views/PartialViews/orig_DataGridLocal.cshtml b/ASP.NET Core/Views/PartialViews/DataGridLocal.cshtml similarity index 98% rename from ASP.NET Core/Views/PartialViews/orig_DataGridLocal.cshtml rename to ASP.NET Core/Views/PartialViews/DataGridLocal.cshtml index cddf97b..5a00646 100644 --- a/ASP.NET Core/Views/PartialViews/orig_DataGridLocal.cshtml +++ b/ASP.NET Core/Views/PartialViews/DataGridLocal.cshtml @@ -1,4 +1,4 @@ -@( +@( Html.DevExtreme().DataGrid() .DataSource(new JS("customers")) .KeyExpr("ID") @@ -18,4 +18,4 @@ Html.DevExtreme().DataGrid() col.Add().DataField("City"); col.Add().DataField("State"); }) -) \ No newline at end of file +) diff --git a/ASP.NET Core/Views/PartialViews/orig_DataGridRemote.cshtml b/ASP.NET Core/Views/PartialViews/DataGridRemote.cshtml similarity index 75% rename from ASP.NET Core/Views/PartialViews/orig_DataGridRemote.cshtml rename to ASP.NET Core/Views/PartialViews/DataGridRemote.cshtml index 4487a77..5b2ab01 100644 --- a/ASP.NET Core/Views/PartialViews/orig_DataGridRemote.cshtml +++ b/ASP.NET Core/Views/PartialViews/DataGridRemote.cshtml @@ -1,8 +1,8 @@ -@( +@( Html.DevExtreme().DataGrid() .DataSource(d => d.RemoteController() - .LoadUrl("https://js.devexpress.com/Demos/Mvc/api/RowReordering/Tasks") - .UpdateUrl("https://js.devexpress.com/Demos/Mvc/api/RowReordering/UpdateTask") + .LoadUrl("https://js.devexpress.com/Demos/NetCore/api/DataGridRowReordering/Tasks") + .UpdateUrl("https://js.devexpress.com/Demos/NetCore/api/DataGridRowReordering/UpdateTask") .OnBeforeSend("RemoteGrid.beforeSend") .Key("ID")) .RemoteOperations(true) @@ -21,7 +21,7 @@ Html.DevExtreme().DataGrid() columns.Add().DataField("ID").Width(55); columns.Add().DataField("Owner").Lookup(lookup => lookup .DataSource(d => d.RemoteController() - .LoadUrl("https://js.devexpress.com/Demos/Mvc/api/RowReordering/Employees") + .LoadUrl("https://js.devexpress.com/Demos/NetCore/api/DataGridRowReordering/Employees") .Key("ID")) .ValueExpr("ID") .DisplayExpr("FullName") @@ -31,11 +31,11 @@ Html.DevExtreme().DataGrid() .Caption("Assignee") .Lookup(lookup => lookup .DataSource(d => d.RemoteController() - .LoadUrl("https://js.devexpress.com/Demos/Mvc/api/RowReordering/Employees") + .LoadUrl("https://js.devexpress.com/Demos/NetCore/api/DataGridRowReordering/Employees") .Key("ID")) .ValueExpr("ID") .DisplayExpr("FullName") ); columns.Add().DataField("Subject"); }) -) \ No newline at end of file +) diff --git a/ASP.NET Core/Views/Shared/_Layout.cshtml b/ASP.NET Core/Views/Shared/_Layout.cshtml index 0c3c2bc..8d11b5f 100644 --- a/ASP.NET Core/Views/Shared/_Layout.cshtml +++ b/ASP.NET Core/Views/Shared/_Layout.cshtml @@ -17,6 +17,9 @@ + + + diff --git a/ASP.NET Core/Views/Shared/orig__Layout.cshtml b/ASP.NET Core/Views/Shared/orig__Layout.cshtml deleted file mode 100644 index 8d11b5f..0000000 --- a/ASP.NET Core/Views/Shared/orig__Layout.cshtml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - ASP_NET_Core - - - @* Uncomment to use the HtmlEditor control *@ - @* *@ - - - - - - - - - - -
- @RenderBody() -
- - - \ No newline at end of file diff --git a/ASP.NET Core/orig_Program.cs b/ASP.NET Core/orig_Program.cs deleted file mode 100644 index c0566ce..0000000 --- a/ASP.NET Core/orig_Program.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace ASP_NET_Core -{ - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => { - webBuilder.UseStartup(); - }); - } -} \ No newline at end of file diff --git a/ASP.NET Core/orig_Startup.cs b/ASP.NET Core/orig_Startup.cs deleted file mode 100644 index ec87864..0000000 --- a/ASP.NET Core/orig_Startup.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace ASP_NET_Core -{ - public class Startup - { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - // Add framework services. - services - .AddControllersWithViews() - .AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Home/Error"); - } - app.UseStaticFiles(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => { - endpoints.MapControllerRoute( - name: "default", - pattern: "{controller=Home}/{action=Index}/{id?}"); - }); - } - } -} diff --git a/ASP.NET Core/wwwroot/css/Site.css b/ASP.NET Core/wwwroot/css/Site.css index 242203b..92597a1 100644 --- a/ASP.NET Core/wwwroot/css/Site.css +++ b/ASP.NET Core/wwwroot/css/Site.css @@ -5,3 +5,31 @@ body { margin: 8px; } +.tab-item-content { + margin: auto; +} + +.demo-header { + display: flex; + justify-content: space-between; +} + +#toggle-container { + padding-top: 20px; +} + +#clear-after-drop-switch { + vertical-align: text-bottom; +} + +#toggle-container span { + padding-right: 10px; +} + +.drag-container { + padding: 10px; +} + +.drag-container td { + padding: 0px 10px 0px 10px; +} \ No newline at end of file diff --git a/Angular/angular.json b/Angular/angular.json index 2af6cdc..6979f00 100644 --- a/Angular/angular.json +++ b/Angular/angular.json @@ -19,6 +19,7 @@ "options": { "allowedCommonJsDependencies": [ "devextreme-quill", + "devextreme-aspnet-data-nojquery", "jszip", "devexpress-diagram", "devexpress-gantt" diff --git a/Angular/package-lock.json b/Angular/package-lock.json index cca31f2..5ac34ca 100644 --- a/Angular/package-lock.json +++ b/Angular/package-lock.json @@ -18,6 +18,7 @@ "@angular/router": "^18.0.3", "devextreme": "25.1.3", "devextreme-angular": "25.1.3", + "devextreme-aspnet-data-nojquery": "^5.1.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.7" @@ -6243,6 +6244,15 @@ "yarn": ">= 1.13.0" } }, + "node_modules/devextreme-aspnet-data-nojquery": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/devextreme-aspnet-data-nojquery/-/devextreme-aspnet-data-nojquery-5.1.0.tgz", + "integrity": "sha512-YJ7HxOLJTzz6bmpp1uOjXdhUL71THR6IidVONjJRF1R1loTyNVesa6s86gU+mUeNN044UnJKQhtCADc0+TmARQ==", + "license": "MIT", + "peerDependencies": { + "devextreme": ">=18.1.0" + } + }, "node_modules/devextreme-quill": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/devextreme-quill/-/devextreme-quill-1.7.3.tgz", diff --git a/Angular/package.json b/Angular/package.json index 5549641..bbbfa64 100644 --- a/Angular/package.json +++ b/Angular/package.json @@ -24,6 +24,7 @@ "@angular/router": "^18.0.3", "devextreme": "25.1.3", "devextreme-angular": "25.1.3", + "devextreme-aspnet-data-nojquery": "^5.1.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.7" diff --git a/Angular/src/app/app.component.html b/Angular/src/app/app.component.html index 0f59cbc..9aada4e 100644 --- a/Angular/src/app/app.component.html +++ b/Angular/src/app/app.component.html @@ -1,3 +1,28 @@
- +
+

DataGrid - Select multiple items and drag'n'drop

+
+ Clear selection after drop + +
+
+ + +
+ +
+
+ +
+ +
+
+
diff --git a/Angular/src/app/app.component.scss b/Angular/src/app/app.component.scss index 194ccc0..6539d0b 100644 --- a/Angular/src/app/app.component.scss +++ b/Angular/src/app/app.component.scss @@ -1,4 +1,26 @@ .default-style { margin: 50px; - width: 90vw; + width: 90vh; } + +.demo-header { + display: flex; + justify-content: space-between; +} + +.tab-item-content { + margin: auto; +} + +#toggle-container { + padding-top: 20px; +} + +#clear-after-drop-switch { + vertical-align: text-bottom; +} + +#toggle-container span { + padding-right: 10px; +} + diff --git a/Angular/src/app/app.component.ts b/Angular/src/app/app.component.ts index 9e09ad6..a82cc75 100644 --- a/Angular/src/app/app.component.ts +++ b/Angular/src/app/app.component.ts @@ -1,20 +1,11 @@ import { Component } from '@angular/core'; -import { ClickEvent } from 'devextreme/ui/button'; @Component({ selector: 'app-root', + standalone: false, templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], }) export class AppComponent { - title = 'Angular'; - - counter = 0; - - buttonText = 'Click count: 0'; - - onClick(e: ClickEvent): void { - this.counter++; - this.buttonText = `Click count: ${this.counter}`; - } + clearSelectionAfterDrop = false; } diff --git a/Angular/src/app/app.module.ts b/Angular/src/app/app.module.ts index b9e30aa..2c52e14 100644 --- a/Angular/src/app/app.module.ts +++ b/Angular/src/app/app.module.ts @@ -1,17 +1,21 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; -import { DxButtonModule } from 'devextreme-angular/ui/button'; +import { DxDataGridModule, DxTabPanelModule, DxSwitchModule } from 'devextreme-angular'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; +import { DataGridLocalDataComponent } from './components/data-grid-local-data/data-grid-local-data.component'; +import { DataGridRemoteDataComponent } from './components/data-grid-remote-data/data-grid-remote-data.component'; @NgModule({ declarations: [ AppComponent, + DataGridLocalDataComponent, + DataGridRemoteDataComponent, ], imports: [ BrowserModule, AppRoutingModule, - DxButtonModule, + DxDataGridModule, DxTabPanelModule, DxSwitchModule, ], providers: [], bootstrap: [AppComponent], diff --git a/Angular/src/app/components/data-grid-local-data/data-grid-local-data.component.html b/Angular/src/app/components/data-grid-local-data/data-grid-local-data.component.html new file mode 100644 index 0000000..3488e82 --- /dev/null +++ b/Angular/src/app/components/data-grid-local-data/data-grid-local-data.component.html @@ -0,0 +1,25 @@ + + +
+
+ + + {{ cellValue.value }} + + +
+
+ + + + + + + +
diff --git a/Angular/src/app/components/data-grid-local-data/data-grid-local-data.component.ts b/Angular/src/app/components/data-grid-local-data/data-grid-local-data.component.ts new file mode 100644 index 0000000..c994976 --- /dev/null +++ b/Angular/src/app/components/data-grid-local-data/data-grid-local-data.component.ts @@ -0,0 +1,72 @@ +import { Component, Input } from '@angular/core'; +import { KeyValue } from '@angular/common'; +import { Customer, GridDataService } from 'src/app/services/grid-data.service'; +import type { + DxDataGridTypes, +} from 'devextreme-angular/ui/data-grid'; +import { getVisibleRowValues } from 'src/app/utils'; +import notify from 'devextreme/ui/notify'; + +@Component({ + selector: 'grid-local-data', + standalone: false, + templateUrl: './data-grid-local-data.component.html', +}) +export class DataGridLocalDataComponent { + @Input() shouldClearSelection = false; + + customers: Customer[]; + + keyExpr: keyof Customer = 'ID'; + + constructor(dataService: GridDataService) { + this.customers = dataService.getCustomers(); + this.dragStart = this.dragStart.bind(this); + this.dragChange = this.dragChange.bind(this); + this.reorder = this.reorder.bind(this); + } + + dragStart(e: DxDataGridTypes.RowDraggingStartEvent): void { + const selectedData: Customer[] = e.component.getSelectedRowsData(); + e.itemData = getVisibleRowValues(selectedData, e.component); + e.cancel = !this.canDrag(e); + } + + dragChange(e: DxDataGridTypes.RowDraggingChangeEvent): void { + e.cancel = !this.canDrop(e); + } + + reorder(e: DxDataGridTypes.RowDraggingReorderEvent): void { + const fullDataToInsert: Customer[] = []; + e.itemData?.forEach((rowData: Customer) => { + const indexToRemove = this.customers.findIndex((item: Customer) => item[this.keyExpr] === rowData[this.keyExpr]); + fullDataToInsert.push(this.customers[indexToRemove]); + this.customers.splice(indexToRemove, 1); + }); + const toIndex = this.calculateToIndex(this.customers, e); + this.customers.splice(toIndex, 0, ...fullDataToInsert); + if (this.shouldClearSelection) { + e.component.clearSelection(); + } + } + + canDrag(e: DxDataGridTypes.RowDraggingStartEvent): boolean { + const visibleRows = e.component.getVisibleRows(); + return visibleRows.some((r) => r.isSelected && r.rowIndex === e.fromIndex); + } + + canDrop(e: DxDataGridTypes.RowDraggingChangeEvent): boolean { + const visibleRows = e.component.getVisibleRows(); + return !visibleRows.some((r) => r.isSelected && r.rowIndex === e.toIndex); + } + + calculateToIndex(dataArray: Customer[], e: DxDataGridTypes.RowDraggingReorderEvent): number { + const visibleRows = e.component.getVisibleRows(); + const toIndex = dataArray.findIndex((item) => item[this.keyExpr] === visibleRows[e.toIndex].data[this.keyExpr]); + return e.fromIndex >= e.toIndex ? toIndex : toIndex + 1; + } + + originalOrder(a: KeyValue, b: KeyValue): number { + return 0; + } +} diff --git a/Angular/src/app/components/data-grid-local-data/orig_data-grid-local-data.component.html b/Angular/src/app/components/data-grid-local-data/orig_data-grid-local-data.component.html deleted file mode 100644 index 6b9bdf3..0000000 --- a/Angular/src/app/components/data-grid-local-data/orig_data-grid-local-data.component.html +++ /dev/null @@ -1,28 +0,0 @@ - - -
-
- - - {{cellValue.value}} - - -
-
- - - - - - - -
\ No newline at end of file diff --git a/Angular/src/app/components/data-grid-local-data/orig_data-grid-local-data.component.ts b/Angular/src/app/components/data-grid-local-data/orig_data-grid-local-data.component.ts deleted file mode 100644 index c0098a5..0000000 --- a/Angular/src/app/components/data-grid-local-data/orig_data-grid-local-data.component.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { KeyValue } from '@angular/common'; -import { Customer, GridDataService } from 'src/app/services/grid-data.service'; -import dxDataGrid, { RowDraggingStartEvent, RowDraggingChangeEvent, RowDraggingReorderEvent, Column } from 'devextreme/ui/data_grid'; - -@Component({ - selector: 'grid-local-data', - templateUrl: './data-grid-local-data.component.html', -}) -export class DataGridLocalDataComponent { - @Input() shouldClearSelection: boolean = false; - customers: Customer[]; - keyExpr: keyof Customer = "ID"; - - constructor(dataService: GridDataService) { - this.customers = dataService.getCustomers(); - this.dragStart = this.dragStart.bind(this); - this.dragChange = this.dragChange.bind(this); - this.reorder = this.reorder.bind(this); - } - dragStart(e: RowDraggingStartEvent) { - const selectedData: Customer[] = e.component.getSelectedRowsData(); - e.itemData = this.getVisibleRowValues(selectedData, e.component); - e.cancel = !this.canDrag(e); - } - dragChange(e: RowDraggingChangeEvent) { - e.cancel = !this.canDrop(e); - } - reorder(e: RowDraggingReorderEvent) { - const fullDataToInsert: Customer[] = []; - e.itemData.forEach((rowData: any) => { - const indexToRemove = this.customers.findIndex((item: Customer) => item[this.keyExpr] === rowData[this.keyExpr]); - fullDataToInsert.push(this.customers[indexToRemove]); - this.customers.splice(indexToRemove, 1); - }); - const toIndex = this.calculateToIndex(this.customers, e); - this.customers.splice(toIndex, 0, ...fullDataToInsert); - e.component.refresh(); - if (this.shouldClearSelection) - e.component.clearSelection(); - } - - canDrag(e: RowDraggingStartEvent) { - const visibleRows = e.component.getVisibleRows(); - return visibleRows.some(r => r.isSelected && r.rowIndex === e.fromIndex); - } - canDrop(e: RowDraggingChangeEvent) { - const visibleRows = e.component.getVisibleRows(); - return !visibleRows.some(r => r.isSelected && r.rowIndex === e.toIndex); - } - calculateToIndex(dataArray: Customer[], e: RowDraggingReorderEvent) { - const visibleRows = e.component.getVisibleRows(); - const toIndex = dataArray.findIndex((item) => item[this.keyExpr] === visibleRows[e.toIndex].data[this.keyExpr]); - return e.fromIndex >= e.toIndex ? toIndex : toIndex + 1; - } - getVisibleRowValues(rowsData: Customer[], grid: dxDataGrid) { - const visbileColumns: Column[] = grid.getVisibleColumns(); - const selectedData = rowsData.map(rowData => { - const visibleValues: any = {}; - visbileColumns.forEach((column: Column) => { - if (column.dataField) - visibleValues[column.dataField] = this.getVisibleCellValue(column, rowData); - }); - return visibleValues; - }); - return selectedData; - } - getVisibleCellValue(column: Column, rowData: Customer) { - if (column.dataField) { - const propKey = column.dataField as (keyof Customer); - const cellValue = rowData[propKey]; - return column.lookup && column.lookup.calculateCellValue ? column.lookup.calculateCellValue(cellValue) : cellValue; - } - } - originalOrder(a: KeyValue, b: KeyValue): number { - return 0 - } -} diff --git a/Angular/src/app/components/data-grid-remote-data/data-grid-remote-data.component.html b/Angular/src/app/components/data-grid-remote-data/data-grid-remote-data.component.html new file mode 100644 index 0000000..a4a9ea0 --- /dev/null +++ b/Angular/src/app/components/data-grid-remote-data/data-grid-remote-data.component.html @@ -0,0 +1,41 @@ + + +
+
+ + + {{ cellValue.value }} + + +
+
+ + + + + + + + + + + +
diff --git a/Angular/src/app/components/data-grid-remote-data/data-grid-remote-data.component.ts b/Angular/src/app/components/data-grid-remote-data/data-grid-remote-data.component.ts new file mode 100644 index 0000000..b8e5adc --- /dev/null +++ b/Angular/src/app/components/data-grid-remote-data/data-grid-remote-data.component.ts @@ -0,0 +1,102 @@ +import { Component, Input } from '@angular/core'; +import { KeyValue } from '@angular/common'; +import type { + DxDataGridTypes, +} from 'devextreme-angular/ui/data-grid'; +import type CustomStore from 'devextreme/data/custom_store'; +import { createStore } from 'devextreme-aspnet-data-nojquery'; +import { Task } from 'src/app/services/grid-data.service'; +import notify from 'devextreme/ui/notify'; +import { getVisibleRowValues } from 'src/app/utils'; + +@Component({ + selector: 'grid-remote-data', + standalone: false, + templateUrl: './data-grid-remote-data.component.html', +}) +export class DataGridRemoteDataComponent { + @Input() shouldClearSelection = false; + + updateInProgress = false; + + keyExpr: keyof Task = 'ID'; + + tasksStore: CustomStore; + + employeesStore: CustomStore; + + constructor() { + const url = 'https://js.devexpress.com/Demos/NetCore/api/DataGridRowReordering'; + this.tasksStore = createStore({ + key: 'ID', + loadUrl: `${url}/Tasks`, + updateUrl: `${url}/UpdateTask`, + onBeforeSend(_method, ajaxOptions) { + ajaxOptions.xhrFields = { withCredentials: true }; + }, + }); + + this.employeesStore = createStore({ + key: 'ID', + loadUrl: `${url}/Employees`, + onBeforeSend(_method, ajaxOptions) { + ajaxOptions.xhrFields = { withCredentials: true }; + }, + }); + + this.dragStart = this.dragStart.bind(this); + this.dragChange = this.dragChange.bind(this); + this.reorder = this.reorder.bind(this); + } + + dragStart(e: DxDataGridTypes.RowDraggingStartEvent): void { + const selectedData: Task[] = e.component.getSelectedRowsData(); + e.itemData = getVisibleRowValues(selectedData, e.component); + e.cancel = !this.canDrag(e); + } + + dragChange(e: DxDataGridTypes.RowDraggingChangeEvent): void { + e.cancel = !this.canDrop(e); + } + + reorder(e: DxDataGridTypes.RowDraggingReorderEvent): void { + e.promise = this.updateOrderIndex(e); + if (this.shouldClearSelection) { e.component.clearSelection(); } + } + + async updateOrderIndex(e: DxDataGridTypes.RowDraggingReorderEvent): Promise { + const visibleRows = e.component.getVisibleRows(); + const newOrderIndex = visibleRows[e.toIndex].data.OrderIndex; + const store = e.component.getDataSource().store(); + try { + this.updateInProgress = true; + e.component.beginCustomLoading('Loading...'); + const promises = []; + for (const itemData of e.itemData) { + promises.push(store.update(itemData[this.keyExpr], { OrderIndex: newOrderIndex })); + } + await Promise.all(promises); + await e.component.refresh(); + } catch (error: unknown) { + notify(error, 'error', 1000); + } finally { + this.updateInProgress = false; + e.component.endCustomLoading(); + } + } + + canDrag(e: DxDataGridTypes.RowDraggingStartEvent): boolean { + if (this.updateInProgress) return false; + const visibleRows = e.component.getVisibleRows(); + return visibleRows.some((r) => r.isSelected && r.rowIndex === e.fromIndex); + } + + canDrop(e: DxDataGridTypes.RowDraggingChangeEvent): boolean { + const visibleRows = e.component.getVisibleRows(); + return !visibleRows.some((r) => r.isSelected && r.rowIndex === e.toIndex); + } + + originalOrder(a: KeyValue, b: KeyValue): number { + return 0; + } +} diff --git a/Angular/src/app/components/data-grid-remote-data/orig_data-grid-remote-data.component.html b/Angular/src/app/components/data-grid-remote-data/orig_data-grid-remote-data.component.html deleted file mode 100644 index 90a0f57..0000000 --- a/Angular/src/app/components/data-grid-remote-data/orig_data-grid-remote-data.component.html +++ /dev/null @@ -1,41 +0,0 @@ - - -
-
- - - {{cellValue.value}} - - -
-
- - - - - - - - - - - -
\ No newline at end of file diff --git a/Angular/src/app/components/data-grid-remote-data/orig_data-grid-remote-data.component.ts b/Angular/src/app/components/data-grid-remote-data/orig_data-grid-remote-data.component.ts deleted file mode 100644 index 3953c0f..0000000 --- a/Angular/src/app/components/data-grid-remote-data/orig_data-grid-remote-data.component.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { KeyValue } from '@angular/common'; -import dxDataGrid, { RowDraggingStartEvent, RowDraggingChangeEvent, RowDraggingReorderEvent, Column } from 'devextreme/ui/data_grid'; -import CustomStore from 'devextreme/data/custom_store'; -import { createStore } from 'devextreme-aspnet-data-nojquery'; -import { Task } from 'src/app/services/grid-data.service'; - -@Component({ - selector: 'grid-remote-data', - templateUrl: './data-grid-remote-data.component.html', -}) -export class DataGridRemoteDataComponent { - @Input() shouldClearSelection: boolean = false; - updateInProgress: boolean = false; - keyExpr: keyof Task = "ID"; - tasksStore: CustomStore; - employeesStore: CustomStore; - - constructor() { - const url = 'https://js.devexpress.com/Demos/Mvc/api/RowReordering'; - this.tasksStore = createStore({ - key: 'ID', - loadUrl: `${url}/Tasks`, - updateUrl: `${url}/UpdateTask`, - onBeforeSend(method, ajaxOptions) { - ajaxOptions.xhrFields = { withCredentials: true }; - }, - }); - - this.employeesStore = createStore({ - key: 'ID', - loadUrl: `${url}/Employees`, - onBeforeSend(method, ajaxOptions) { - ajaxOptions.xhrFields = { withCredentials: true }; - }, - }); - - this.dragStart = this.dragStart.bind(this); - this.dragChange = this.dragChange.bind(this); - this.reorder = this.reorder.bind(this); - } - - dragStart(e: RowDraggingStartEvent) { - const selectedData: Task[] = e.component.getSelectedRowsData(); - e.itemData = this.getVisibleRowValues(selectedData, e.component); - e.cancel = !this.canDrag(e); - } - dragChange(e: RowDraggingChangeEvent) { - e.cancel = !this.canDrop(e); - } - reorder(e: RowDraggingReorderEvent) { - e.promise = this.updateOrderIndex(e); - if (this.shouldClearSelection) - e.component.clearSelection(); - } - - async updateOrderIndex(e: RowDraggingReorderEvent) { - const visibleRows = e.component.getVisibleRows(); - const newOrderIndex = visibleRows[e.toIndex].data.OrderIndex; - const store = e.component.getDataSource().store(); - this.updateInProgress = true; - e.component.beginCustomLoading("Loading..."); - for (let i = 0; i < e.itemData.length; i++) { - await store.update(e.itemData[i][this.keyExpr], { OrderIndex: newOrderIndex }); - } - e.component.refresh().then(() => { - e.component.endCustomLoading(); - this.updateInProgress = false; - }); - } - canDrag(e: RowDraggingStartEvent) { - if (this.updateInProgress) return false; - const visibleRows = e.component.getVisibleRows(); - return visibleRows.some(r => r.isSelected && r.rowIndex === e.fromIndex); - } - canDrop(e: RowDraggingChangeEvent) { - const visibleRows = e.component.getVisibleRows(); - return !visibleRows.some(r => r.isSelected && r.rowIndex === e.toIndex); - } - getVisibleRowValues(rowsData: Task[], grid: dxDataGrid) { - const visbileColumns = grid.getVisibleColumns(); - const selectedData = rowsData.map((rowData: Task) => { - const visibleValues: any = {}; - visbileColumns.forEach((column: Column) => { - if (column.dataField) - visibleValues[column.dataField] = this.getVisibleCellValue(column, rowData); - }); - return visibleValues; - }); - return selectedData; - } - getVisibleCellValue(column: Column, rowData: Task) { - if (column.dataField) { - const propKey = column.dataField as (keyof Task); - const cellValue = rowData[propKey]; - return column.lookup && column.lookup.calculateCellValue ? column.lookup.calculateCellValue(cellValue) : cellValue; - } - } - originalOrder(a: KeyValue, b: KeyValue): number { - return 0 - } -} diff --git a/Angular/src/app/orig_app-routing.module.ts b/Angular/src/app/orig_app-routing.module.ts deleted file mode 100644 index 0297262..0000000 --- a/Angular/src/app/orig_app-routing.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; - -const routes: Routes = []; - -@NgModule({ - imports: [RouterModule.forRoot(routes)], - exports: [RouterModule] -}) -export class AppRoutingModule { } diff --git a/Angular/src/app/orig_app.component.html b/Angular/src/app/orig_app.component.html deleted file mode 100644 index e3c5a8c..0000000 --- a/Angular/src/app/orig_app.component.html +++ /dev/null @@ -1,21 +0,0 @@ -
-
-

DataGrid - Select multiple items and drag'n'drop

-
- Clear selection after drop - -
-
- - -
- -
-
- -
- -
-
-
-
\ No newline at end of file diff --git a/Angular/src/app/orig_app.component.scss b/Angular/src/app/orig_app.component.scss deleted file mode 100644 index bb4a845..0000000 --- a/Angular/src/app/orig_app.component.scss +++ /dev/null @@ -1,20 +0,0 @@ -.default-style { - margin: 50px 50px; - width: 90vh; -} -.demo-header { - display: flex; - justify-content: space-between; -} -.tab-item-content { - margin: auto; -} -#toggle-container { - padding-top: 20px; -} -#clearAfterDropSwitch { - vertical-align: text-bottom; -} -#toggle-container span { - padding-right: 10px; -} \ No newline at end of file diff --git a/Angular/src/app/orig_app.component.spec.ts b/Angular/src/app/orig_app.component.spec.ts deleted file mode 100644 index d556240..0000000 --- a/Angular/src/app/orig_app.component.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { RouterTestingModule } from '@angular/router/testing'; -import { AppComponent } from './app.component'; - -describe('AppComponent', () => { - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - RouterTestingModule - ], - declarations: [ - AppComponent - ], - }).compileComponents(); - }); - - it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); - }); - - it(`should have as title 'angular-test'`, () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app.title).toEqual('angular-test'); - }); - - it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('.content span')?.textContent).toContain('angular-test app is running!'); - }); -}); diff --git a/Angular/src/app/orig_app.component.ts b/Angular/src/app/orig_app.component.ts deleted file mode 100644 index 09fc231..0000000 --- a/Angular/src/app/orig_app.component.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.scss'] -}) -export class AppComponent { - clearSelectionAfterDrop: boolean = false; -} diff --git a/Angular/src/app/orig_app.module.ts b/Angular/src/app/orig_app.module.ts deleted file mode 100644 index 1ecf32a..0000000 --- a/Angular/src/app/orig_app.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { DxDataGridModule, DxTabPanelModule, DxSwitchModule } from "devextreme-angular"; -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; -import { DataGridLocalDataComponent } from './components/data-grid-local-data/data-grid-local-data.component'; -import { DataGridRemoteDataComponent } from './components/data-grid-remote-data/data-grid-remote-data.component'; - -@NgModule({ - declarations: [ - AppComponent, - DataGridLocalDataComponent, - DataGridRemoteDataComponent - ], - imports: [ - BrowserModule, - AppRoutingModule, - DxDataGridModule, DxTabPanelModule, DxSwitchModule - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } diff --git a/Angular/src/app/services/orig_grid-data.service.ts b/Angular/src/app/services/grid-data.service.ts similarity index 83% rename from Angular/src/app/services/orig_grid-data.service.ts rename to Angular/src/app/services/grid-data.service.ts index 629f568..8bae708 100644 --- a/Angular/src/app/services/orig_grid-data.service.ts +++ b/Angular/src/app/services/grid-data.service.ts @@ -1,22 +1,22 @@ import { Injectable } from '@angular/core'; -export class Task { - ID: number = 0; - AssignedEmployee: number = 0; - OrderIndex: number = 0; - Owner: number = 0; - Priority: number = 0; - Status: number = 0; - Subject: string = ""; +export interface Task { + ID: number; + AssignedEmployee: number; + OrderIndex: number; + Owner: number; + Priority: number; + Status: number; + Subject: string; } -export class Customer { - ID: number = 0; - CompanyName: string = ""; - Address: string = ""; - City: string = ""; - State: string = ""; - Website: string = ""; +export interface Customer { + ID: number; + CompanyName: string; + Address: string; + City: string; + State: string; + Website: string; } const customers: Customer[] = [{ ID: 1, @@ -41,7 +41,7 @@ const customers: Customer[] = [{ Website: 'http://www.nowebsitemusic.com', }, { ID: 4, - CompanyName: "Tom's Club", + CompanyName: 'Tom\'s Club', Address: '999 Lake Drive', City: 'Issaquah', State: 'Washington', @@ -105,10 +105,10 @@ const customers: Customer[] = [{ }]; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class GridDataService { - getCustomers() : Customer[] { + getCustomers(): Customer[] { return customers; } } diff --git a/Angular/src/app/services/orig_grid-data.service.spec.ts b/Angular/src/app/services/orig_grid-data.service.spec.ts deleted file mode 100644 index 86c2bec..0000000 --- a/Angular/src/app/services/orig_grid-data.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { GridDataService } from '../services/grid-data.service'; - -describe('GridDataService', () => { - let service: GridDataService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(GridDataService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/Angular/src/app/utils.ts b/Angular/src/app/utils.ts new file mode 100644 index 0000000..cc8132b --- /dev/null +++ b/Angular/src/app/utils.ts @@ -0,0 +1,32 @@ +import type { DxDataGridTypes } from 'devextreme-angular/ui/data-grid'; +import type dxDataGrid from 'devextreme/ui/data_grid'; + +export type CellValue = number | string | undefined; + +export function getVisibleCellValue(column: DxDataGridTypes.Column, rowData: T): CellValue { + if (column.dataField) { + const propKey = column.dataField as keyof T; + const cellValue = rowData[propKey]; + return column?.lookup?.calculateCellValue + ? column.lookup.calculateCellValue(cellValue) as CellValue + : cellValue as CellValue; + } + return undefined; +} + +export function getVisibleRowValues( + rowsData: T[], + grid: dxDataGrid, +): Record[] { + const visibleColumns: DxDataGridTypes.Column[] = grid.getVisibleColumns(); + const selectedData = rowsData.map((rowData) => { + const visibleValues: Record = {}; + visibleColumns.forEach((column: DxDataGridTypes.Column) => { + if (column.dataField) { + visibleValues[column.dataField] = getVisibleCellValue(column, rowData); + } + }); + return visibleValues; + }); + return selectedData; +} diff --git a/Angular/src/orig_index.html b/Angular/src/orig_index.html deleted file mode 100644 index c9e576b..0000000 --- a/Angular/src/orig_index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Angular - - - - - - - - diff --git a/Angular/src/orig_main.ts b/Angular/src/orig_main.ts deleted file mode 100644 index c58dc05..0000000 --- a/Angular/src/orig_main.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - -import { AppModule } from './app/app.module'; - - -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); diff --git a/Angular/src/orig_styles.scss b/Angular/src/orig_styles.scss deleted file mode 100644 index 775a4d4..0000000 --- a/Angular/src/orig_styles.scss +++ /dev/null @@ -1,6 +0,0 @@ -.drag-container { - padding: 10px; -} -.drag-container td { - padding: 0px 10px 0px 10px; -} \ No newline at end of file diff --git a/Angular/src/styles.scss b/Angular/src/styles.scss index 90d4ee0..a5717c0 100644 --- a/Angular/src/styles.scss +++ b/Angular/src/styles.scss @@ -1 +1,7 @@ -/* You can add global styles to this file, and also import other style files */ +.drag-container { + padding: 10px; +} + +.drag-container td { + padding: 0 10px; +} \ No newline at end of file diff --git a/README.md b/README.md index d3215cb..43b5edd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -![](https://img.shields.io/endpoint?url=https://codecentral.devexpress.com/api/v1/VersionRange/617017018/25.1.3%2B) [![](https://img.shields.io/badge/Open_in_DevExpress_Support_Center-FF7200?style=flat-square&logo=DevExpress&logoColor=white)](https://supportcenter.devexpress.com/ticket/details/T1155013) [![](https://img.shields.io/badge/📖_How_to_use_DevExpress_Examples-e9f6fc?style=flat-square)](https://docs.devexpress.com/GeneralInformation/403183) [![](https://img.shields.io/badge/💬_Leave_Feedback-feecdd?style=flat-square)](#does-this-example-address-your-development-requirementsobjectives) diff --git a/React/package-lock.json b/React/package-lock.json index bd6f0e8..e719013 100644 --- a/React/package-lock.json +++ b/React/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "devextreme": "25.1.3", + "devextreme-aspnet-data-nojquery": "^5.1.0", "devextreme-react": "25.1.3", "react": "^18.2.0", "react-dom": "^18.2.0" @@ -4478,6 +4479,15 @@ "devextreme-bundler-init": "bin/bundler-init.js" } }, + "node_modules/devextreme-aspnet-data-nojquery": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/devextreme-aspnet-data-nojquery/-/devextreme-aspnet-data-nojquery-5.1.0.tgz", + "integrity": "sha512-YJ7HxOLJTzz6bmpp1uOjXdhUL71THR6IidVONjJRF1R1loTyNVesa6s86gU+mUeNN044UnJKQhtCADc0+TmARQ==", + "license": "MIT", + "peerDependencies": { + "devextreme": ">=18.1.0" + } + }, "node_modules/devextreme-quill": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/devextreme-quill/-/devextreme-quill-1.7.3.tgz", diff --git a/React/package.json b/React/package.json index 4e67360..f2cd04c 100644 --- a/React/package.json +++ b/React/package.json @@ -15,11 +15,13 @@ }, "dependencies": { "devextreme": "25.1.3", + "devextreme-aspnet-data-nojquery": "^5.1.0", "devextreme-react": "25.1.3", "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^14.1.2", "@testing-library/user-event": "^14.4.3", @@ -27,7 +29,6 @@ "@types/react-dom": "^18.2.17", "@vitejs/plugin-react": "^4.4.1", "@vitest/coverage-v8": "^1.5.0", - "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "eslint": "^8.35.0", "eslint-config-devextreme": "^1.1.4", "eslint-plugin-no-only-tests": "2.6.0", @@ -39,10 +40,10 @@ "npm-run-all": "^4.1.5", "stylelint": "^15.6.1", "stylelint-config-standard": "^33.0.0", + "ts-node": "10.9.2", "typescript": "~5.8.2", "typescript-eslint": "^8.18.2", "vite": "^6.3.5", - "ts-node": "10.9.2", "vitest": "^1.5.0" } -} \ No newline at end of file +} diff --git a/React/src/App.css b/React/src/App.css index 438a219..e72384c 100644 --- a/React/src/App.css +++ b/React/src/App.css @@ -1,4 +1,33 @@ .main { margin: 50px; width: 90vw; +} + +.tab-item-content { + margin: auto; +} + +.demo-header { + display: flex; + justify-content: space-between; +} + +#toggle-container { + padding-top: 20px; +} + +#clear-after-drop-switch { + vertical-align: text-bottom; +} + +#toggle-container span { + padding-right: 10px; +} + +.drag-container { + padding: 10px; +} + +.drag-container td { + padding: 0 10px; } \ No newline at end of file diff --git a/React/src/App.tsx b/React/src/App.tsx index c61a5cb..2896262 100644 --- a/React/src/App.tsx +++ b/React/src/App.tsx @@ -1,18 +1,46 @@ import { useCallback, useState } from 'react'; import './App.css'; import 'devextreme/dist/css/dx.material.blue.light.compact.css'; -import Button from 'devextreme-react/button'; +import TabPanel, { Item } from 'devextreme-react/tab-panel'; +import Switch from 'devextreme-react/switch'; +import type { ValueChangedEvent } from 'devextreme/ui/switch'; +import DataGridLocalData from './DataGridLocalData'; +import DataGridRemoteData from './DataGridRemoteData'; function App(): JSX.Element { - var [count, setCount] = useState(0); - const clickHandler = useCallback(() => { - setCount((prev) => prev + 1); - }, [setCount]); + const [shouldClearSelection, setShouldClearSelection] = useState(false); + + const switchValueChanged = useCallback((e: ValueChangedEvent) => { + setShouldClearSelection(e.value); + }, []); + + const dataGridLocalRender = useCallback(() =>
+ +
, [shouldClearSelection]); + + const dataGridRemoteRender = useCallback(() =>
+ +
, [shouldClearSelection]); + return (
-
); } +export interface GridDemoComponentProps { + shouldClearSelection: boolean; +} + export default App; diff --git a/React/src/DataGridLocalData.tsx b/React/src/DataGridLocalData.tsx new file mode 100644 index 0000000..6283bcc --- /dev/null +++ b/React/src/DataGridLocalData.tsx @@ -0,0 +1,83 @@ +import { useCallback } from 'react'; +import DataGrid, { + Column, RowDragging, Sorting, Selection, type DataGridTypes, +} from 'devextreme-react/data-grid'; +import type { DragTemplateData } from 'devextreme/ui/draggable'; +import notify from 'devextreme/ui/notify'; +import type { Customer } from './data'; +import { customers } from './data'; +import type { GridDemoComponentProps } from './App'; +import { getVisibleRowValues } from './utils'; + +const keyExpr: keyof Customer = 'ID'; + +function draggedItemsRender(data: DragTemplateData): JSX.Element { + const draggedItems = data.itemData.map((item: Customer) => { + const cellValues = (Object.keys(item) as (keyof Customer)[]).map((key: keyof Customer) => {item[key]}); + return ({cellValues}); + }); + return ( + {draggedItems} +
); +} + +function dragStart(e: DataGridTypes.RowDraggingStartEvent): void { + const selectedData: Customer[] = e.component.getSelectedRowsData(); + e.itemData = getVisibleRowValues(selectedData, e.component); + e.cancel = !canDrag(e); +} +function dragChange(e: DataGridTypes.RowDraggingChangeEvent): void { + e.cancel = !canDrop(e); +} +function canDrag(e: DataGridTypes.RowDraggingStartEvent): boolean { + const visibleRows = e.component.getVisibleRows(); + return visibleRows.some((r) => r.isSelected && r.rowIndex === e.fromIndex); +} +function canDrop(e: DataGridTypes.RowDraggingChangeEvent): boolean { + const visibleRows = e.component.getVisibleRows(); + return !visibleRows.some((r) => r.isSelected && r.rowIndex === e.toIndex); +} +function calculateToIndex(dataArray: Customer[], e: DataGridTypes.RowDraggingChangeEvent): number { + const visibleRows = e.component.getVisibleRows(); + const toIndex = dataArray.findIndex((item) => item[keyExpr] === visibleRows[e.toIndex].data[keyExpr]); + return e.fromIndex >= e.toIndex ? toIndex : toIndex + 1; +} + +function DataGridLocalData(props: GridDemoComponentProps): JSX.Element { + const reorder = useCallback((e: DataGridTypes.RowDraggingReorderEvent): void => { + const fullDataToInsert: Customer[] = []; + e.itemData?.forEach((rowData: Customer) => { + const indexToRemove = customers.findIndex((item: Customer) => item[keyExpr] === rowData[keyExpr]); + fullDataToInsert.push(customers[indexToRemove]); + customers.splice(indexToRemove, 1); + }); + const toIndex = calculateToIndex(customers, e); + customers.splice(toIndex, 0, ...fullDataToInsert); + e.component.refresh().catch((error: unknown) => notify(error, 'error', 1000)); + if (props.shouldClearSelection) { e.component.clearSelection(); } + }, [props.shouldClearSelection]); + + return ( + + void} + onDragChange={dragChange as () => void} + onDragStart={dragStart as () => void} + /> + + + + + + + + + ); +} + +export default DataGridLocalData; diff --git a/React/src/DataGridRemoteData.tsx b/React/src/DataGridRemoteData.tsx new file mode 100644 index 0000000..e915246 --- /dev/null +++ b/React/src/DataGridRemoteData.tsx @@ -0,0 +1,119 @@ +import { useCallback, useState } from 'react'; +import DataGrid, { + Column, RowDragging, Sorting, Selection, Lookup, Scrolling, type DataGridTypes, +} from 'devextreme-react/data-grid'; +import type { DragTemplateData } from 'devextreme/ui/draggable'; +import { createStore } from 'devextreme-aspnet-data-nojquery'; +import notify from 'devextreme/ui/notify'; +import type CustomStore from 'devextreme/data/custom_store'; +import type { Task } from './data'; +import type { GridDemoComponentProps } from './App'; +import { getVisibleRowValues } from './utils'; + +const keyExpr: keyof Task = 'ID'; +const url = 'https://js.devexpress.com/Demos/NetCore/api/DataGridRowReordering'; + +const tasksStore: CustomStore = createStore({ + key: 'ID', + loadUrl: `${url}/Tasks`, + updateUrl: `${url}/UpdateTask`, + onBeforeSend(_method, ajaxOptions) { + ajaxOptions.xhrFields = { withCredentials: true }; + }, +}); +const employeesStore: CustomStore = createStore({ + key: 'ID', + loadUrl: `${url}/Employees`, + onBeforeSend(_method, ajaxOptions) { + ajaxOptions.xhrFields = { withCredentials: true }; + }, +}); + +function draggedItemsRender(data: DragTemplateData): JSX.Element { + const draggedItems = data.itemData.map((item: Task) => { + const cellValues = (Object.keys(item) as (keyof Task)[]).map((key: keyof Task) => {item[key]}); + return ({cellValues}); + }); + return ( + {draggedItems} +
); +} +function dragChange(e: DataGridTypes.RowDraggingChangeEvent): void { + e.cancel = !canDrop(e); +} +function canDrop(e: DataGridTypes.RowDraggingChangeEvent): boolean { + const visibleRows = e.component.getVisibleRows(); + return !visibleRows.some((r) => r.isSelected && r.rowIndex === e.toIndex); +} + +function DataGridRemoteData(props: GridDemoComponentProps): JSX.Element { + const [updateInProgress, setUpdateInProgress] = useState(false); + + const canDrag = useCallback((e: DataGridTypes.RowDraggingStartEvent): boolean => { + if (updateInProgress) return false; + const visibleRows = e.component.getVisibleRows(); + return visibleRows.some((r) => r.isSelected && r.rowIndex === e.fromIndex); + }, [updateInProgress]); + + const dragStart = useCallback((e: DataGridTypes.RowDraggingStartEvent): void => { + const selectedData: Task[] = e.component.getSelectedRowsData(); + e.itemData = getVisibleRowValues(selectedData, e.component); + e.cancel = !canDrag(e); + }, [canDrag]); + + // eslint-disable-next-line space-before-function-paren + const updateOrderIndex = useCallback(async (e: DataGridTypes.RowDraggingReorderEvent): Promise => { + const visibleRows = e.component.getVisibleRows(); + const newOrderIndex = visibleRows[e.toIndex].data.OrderIndex; + const store = e.component.getDataSource().store(); + try { + setUpdateInProgress(true); + e.component.beginCustomLoading('Loading...'); + const promises = []; + for (const itemData of e.itemData) { + promises.push(store.update(itemData[keyExpr], { OrderIndex: newOrderIndex })); + } + await Promise.all(promises); + await e.component.refresh(); + } catch (error: unknown) { + notify(error, 'error', 1000); + } finally { + e.component.endCustomLoading(); + setUpdateInProgress(false); + } + }, []); + + const reorder = useCallback((e: DataGridTypes.RowDraggingReorderEvent): void => { + e.promise = updateOrderIndex(e); + if (props.shouldClearSelection) { e.component.clearSelection(); } + }, [props.shouldClearSelection, updateOrderIndex]); + + return ( + + void} + onDragChange={dragChange as () => void} + onDragStart={dragStart as () => void} + /> + + + + + + + + + + + + + ); +} + +export default DataGridRemoteData; diff --git a/React/src/data.ts b/React/src/data.ts new file mode 100644 index 0000000..121ed9a --- /dev/null +++ b/React/src/data.ts @@ -0,0 +1,103 @@ +export interface Task { + ID: number; + AssignedEmployee: number; + OrderIndex: number; + Owner: number; + Priority: number; + Status: number; + Subject: string; +} + +export interface Customer { + ID: number; + CompanyName: string; + Address: string; + City: string; + State: string; + Website: string; +} +export const customers: Customer[] = [{ + ID: 1, + CompanyName: 'Super Mart of the West', + Address: '702 SW 8th Street', + City: 'Bentonville', + State: 'Arkansas', + Website: 'http://www.nowebsitesupermart.com', +}, { + ID: 2, + CompanyName: 'Electronics Depot', + Address: '2455 Paces Ferry Road NW', + City: 'Atlanta', + State: 'Georgia', + Website: 'http://www.nowebsitedepot.com', +}, { + ID: 3, + CompanyName: 'K&S Music', + Address: '1000 Nicllet Mall', + City: 'Minneapolis', + State: 'Minnesota', + Website: 'http://www.nowebsitemusic.com', +}, { + ID: 4, + CompanyName: 'Tom\'s Club', + Address: '999 Lake Drive', + City: 'Issaquah', + State: 'Washington', + Website: 'http://www.nowebsitetomsclub.com', +}, { + ID: 5, + CompanyName: 'E-Mart', + Address: '3333 Beverly Rd', + City: 'Hoffman Estates', + State: 'Illinois', + Website: 'http://www.nowebsiteemart.com', +}, { + ID: 6, + CompanyName: 'Walters', + Address: '200 Wilmot Rd', + City: 'Deerfield', + State: 'Illinois', + Website: 'http://www.nowebsitewalters.com', +}, { + ID: 7, + CompanyName: 'StereoShack', + Address: '400 Commerce S', + City: 'Fort Worth', + State: 'Texas', + Website: 'http://www.nowebsiteshack.com', +}, { + ID: 8, + CompanyName: 'Circuit Town', + Address: '2200 Kensington Court', + City: 'Oak Brook', + State: 'Illinois', + Website: 'http://www.nowebsitecircuittown.com', +}, { + ID: 9, + CompanyName: 'Premier Buy', + Address: '7601 Penn Avenue South', + City: 'Richfield', + State: 'Minnesota', + Website: 'http://www.nowebsitepremierbuy.com', +}, { + ID: 10, + CompanyName: 'ElectrixMax', + Address: '263 Shuman Blvd', + City: 'Naperville', + State: 'Illinois', + Website: 'http://www.nowebsiteelectrixmax.com', +}, { + ID: 11, + CompanyName: 'Video Emporium', + Address: '1201 Elm Street', + City: 'Dallas', + State: 'Texas', + Website: 'http://www.nowebsitevideoemporium.com', +}, { + ID: 12, + CompanyName: 'Screen Shop', + Address: '1000 Lowes Blvd', + City: 'Mooresville', + State: 'North Carolina', + Website: 'http://www.nowebsitescreenshop.com', +}]; diff --git a/React/src/orig_App.css b/React/src/orig_App.css deleted file mode 100644 index e71447a..0000000 --- a/React/src/orig_App.css +++ /dev/null @@ -1,26 +0,0 @@ -.main { - margin: 50px 50px; - width: 90vh; -} -.tab-item-content { - margin: auto; -} -.demo-header { - display: flex; - justify-content: space-between; -} -#toggle-container { - padding-top: 20px; -} -#clearAfterDropSwitch { - vertical-align: text-bottom; -} -#toggle-container span { - padding-right: 10px; -} -.drag-container { - padding: 10px; -} -.drag-container td { - padding: 0px 10px 0px 10px; -} \ No newline at end of file diff --git a/React/src/orig_App.tsx b/React/src/orig_App.tsx deleted file mode 100644 index 125b82d..0000000 --- a/React/src/orig_App.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { useCallback, useState } from 'react'; -import './App.css'; -import 'devextreme/dist/css/dx.material.blue.light.compact.css'; -import TabPanel, { Item } from 'devextreme-react/tab-panel'; -import Switch from 'devextreme-react/switch'; -import { ValueChangedEvent } from 'devextreme/ui/switch'; -import DataGridLocalData from './DataGridLocalData'; -import DataGridRemoteData from './DataGridRemoteData'; - - -function App() { - const [shouldClearSelection, setShouldClearSelection] = useState(false); - - const switchValueChanged = useCallback((e: ValueChangedEvent) => { - setShouldClearSelection(e.value); - }, []); - - const dataGridLocalRender = useCallback(() => { - return
- -
- }, [shouldClearSelection]) - - const dataGridRemoteRender = useCallback(() => { - return
- -
- }, [shouldClearSelection]) - - return ( -
-
-

DataGrid - Select multiple items and drag'n'drop

-
- Clear selection after drop - -
-
- - - - -
- ); -} -export class GridDemoComponentProps { - shouldClearSelection: boolean = false; -} -export default App; diff --git a/React/src/orig_DataGridLocalData.tsx b/React/src/orig_DataGridLocalData.tsx deleted file mode 100644 index 8952b2a..0000000 --- a/React/src/orig_DataGridLocalData.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { useCallback } from 'react'; -import DataGrid, { Column, RowDragging, Sorting, Selection } from 'devextreme-react/data-grid'; -import { GridDemoComponentProps } from './App'; -import { Customer, customers } from './data'; -import { DragTemplateData } from 'devextreme/ui/draggable'; -import dxDataGrid, { RowDraggingStartEvent, RowDraggingChangeEvent, RowDraggingReorderEvent, Column as DxColumn } from 'devextreme/ui/data_grid'; - -const keyExpr: keyof Customer = "ID"; - -function draggedItemsRender(data: DragTemplateData) { - const draggedItems = data.itemData.map((item: Customer) => { - const cellValues = (Object.keys(item) as (keyof Customer)[]).map((key: keyof Customer) => - {item[key]} - ); - return ({cellValues}) - }); - return ( - {draggedItems} -
); -} - -function dragStart(e: RowDraggingStartEvent) { - const selectedData: Customer[] = e.component.getSelectedRowsData(); - e.itemData = getVisibleRowValues(selectedData, e.component); - e.cancel = !canDrag(e); -} -function dragChange(e: RowDraggingChangeEvent) { - e.cancel = !canDrop(e); -} -function canDrag(e: RowDraggingStartEvent) { - const visibleRows = e.component.getVisibleRows(); - return visibleRows.some(r => r.isSelected && r.rowIndex === e.fromIndex); -} -function canDrop(e: RowDraggingChangeEvent) { - const visibleRows = e.component.getVisibleRows(); - return !visibleRows.some(r => r.isSelected && r.rowIndex === e.toIndex); -} -function calculateToIndex(dataArray: Customer[], e: RowDraggingReorderEvent) { - const visibleRows = e.component.getVisibleRows(); - const toIndex = dataArray.findIndex((item) => item[keyExpr] === visibleRows[e.toIndex].data[keyExpr]); - return e.fromIndex >= e.toIndex ? toIndex : toIndex + 1; -} -function getVisibleRowValues(rowsData: Customer[], grid: dxDataGrid) { - const visbileColumns: DxColumn[] = grid.getVisibleColumns(); - const selectedData = rowsData.map(rowData => { - const visibleValues: any = {}; - visbileColumns.forEach((column: DxColumn) => { - if (column.dataField) - visibleValues[column.dataField] = getVisibleCellValue(column, rowData); - }); - return visibleValues; - }); - return selectedData; -} -function getVisibleCellValue(column: DxColumn, rowData: Customer) { - if (column.dataField) { - const propKey = column.dataField as (keyof Customer); - const cellValue = rowData[propKey]; - return column.lookup && column.lookup.calculateCellValue ? column.lookup.calculateCellValue(cellValue) : cellValue; - } -} - -function DataGridLocalData(props: GridDemoComponentProps) { - const reorder = useCallback((e: RowDraggingReorderEvent) => { - const fullDataToInsert: Customer[] = []; - e.itemData.forEach((rowData: any) => { - const indexToRemove = customers.findIndex((item: Customer) => item[keyExpr] === rowData[keyExpr]); - fullDataToInsert.push(customers[indexToRemove]); - customers.splice(indexToRemove, 1); - }); - const toIndex = calculateToIndex(customers, e); - customers.splice(toIndex, 0, ...fullDataToInsert); - e.component.refresh(); - if (props.shouldClearSelection) - e.component.clearSelection(); - }, [props.shouldClearSelection]); - - return ( - - - - - - - - - - - ) -} - -export default DataGridLocalData; \ No newline at end of file diff --git a/React/src/orig_DataGridRemoteData.tsx b/React/src/orig_DataGridRemoteData.tsx deleted file mode 100644 index 97fa737..0000000 --- a/React/src/orig_DataGridRemoteData.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { useCallback, useState } from 'react'; -import DataGrid, { Column, RowDragging, Sorting, Selection, Lookup, Scrolling } from 'devextreme-react/data-grid'; -import { GridDemoComponentProps } from './App'; -import { Task } from './data'; -import { DragTemplateData } from 'devextreme/ui/draggable'; -import dxDataGrid, { RowDraggingStartEvent, RowDraggingChangeEvent, RowDraggingReorderEvent, Column as DxColumn } from 'devextreme/ui/data_grid'; -import { createStore } from 'devextreme-aspnet-data-nojquery'; -import CustomStore from 'devextreme/data/custom_store'; - -const keyExpr: keyof Task = "ID"; -const url = 'https://js.devexpress.com/Demos/Mvc/api/RowReordering'; - -const tasksStore: CustomStore = createStore({ - key: 'ID', - loadUrl: `${url}/Tasks`, - updateUrl: `${url}/UpdateTask`, - onBeforeSend(method, ajaxOptions) { - ajaxOptions.xhrFields = { withCredentials: true }; - }, -}); -const employeesStore: CustomStore = createStore({ - key: 'ID', - loadUrl: `${url}/Employees`, - onBeforeSend(method, ajaxOptions) { - ajaxOptions.xhrFields = { withCredentials: true }; - }, -}); - -function draggedItemsRender(data: DragTemplateData) { - const draggedItems = data.itemData.map((item: Task) => { - const cellValues = (Object.keys(item) as (keyof Task)[]).map((key: keyof Task) => - {item[key]} - ); - return ({cellValues}) - }); - return ( - {draggedItems} -
); -} -function dragChange(e: RowDraggingChangeEvent) { - e.cancel = !canDrop(e); -} -function canDrop(e: RowDraggingChangeEvent) { - const visibleRows = e.component.getVisibleRows(); - return !visibleRows.some(r => r.isSelected && r.rowIndex === e.toIndex); -} -function getVisibleRowValues(rowsData: Task[], grid: dxDataGrid) { - const visbileColumns = grid.getVisibleColumns(); - const selectedData = rowsData.map((rowData: Task) => { - const visibleValues: any = {}; - visbileColumns.forEach((column: DxColumn) => { - if (column.dataField) - visibleValues[column.dataField] = getVisibleCellValue(column, rowData); - }); - return visibleValues; - }); - return selectedData; -} -function getVisibleCellValue(column: DxColumn, rowData: Task) { - if (column.dataField) { - const propKey = column.dataField as (keyof Task); - const cellValue = rowData[propKey]; - return column.lookup && column.lookup.calculateCellValue ? column.lookup.calculateCellValue(cellValue) : cellValue; - } -} - -function DataGridRemoteData(props: GridDemoComponentProps) { - const [updateInProgress, setUpdateInProgress] = useState(false); - - const canDrag = useCallback((e: RowDraggingStartEvent) => { - if (updateInProgress) return false; - const visibleRows = e.component.getVisibleRows(); - return visibleRows.some(r => r.isSelected && r.rowIndex === e.fromIndex); - }, [updateInProgress]); - - const dragStart = useCallback((e: RowDraggingStartEvent) => { - const selectedData: Task[] = e.component.getSelectedRowsData(); - e.itemData = getVisibleRowValues(selectedData, e.component); - e.cancel = !canDrag(e); - }, [canDrag]); - - const updateOrderIndex = useCallback(async (e: RowDraggingReorderEvent) => { - const visibleRows = e.component.getVisibleRows(); - const newOrderIndex = visibleRows[e.toIndex].data.OrderIndex; - const store = e.component.getDataSource().store(); - setUpdateInProgress(true); - e.component.beginCustomLoading("Loading..."); - for (let i = 0; i < e.itemData.length; i++) { - await store.update(e.itemData[i][keyExpr], { OrderIndex: newOrderIndex }); - } - e.component.refresh().then(() => { - e.component.endCustomLoading(); - setUpdateInProgress(false); - }); - }, []); - - const reorder = useCallback((e: RowDraggingReorderEvent) => { - e.promise = updateOrderIndex(e); - if (props.shouldClearSelection) - e.component.clearSelection(); - }, [props.shouldClearSelection, updateOrderIndex]); - - return ( - - - - - - - - - - - - - - - ) -} - -export default DataGridRemoteData; \ No newline at end of file diff --git a/React/src/orig_data.ts b/React/src/orig_data.ts deleted file mode 100644 index 8dbd5d5..0000000 --- a/React/src/orig_data.ts +++ /dev/null @@ -1,103 +0,0 @@ -export class Task { - ID: number = 0; - AssignedEmployee: number = 0; - OrderIndex: number = 0; - Owner: number = 0; - Priority: number = 0; - Status: number = 0; - Subject: string = ""; - } - -export class Customer { - ID: number = 0; - CompanyName: string = ""; - Address: string = ""; - City: string = ""; - State: string = ""; - Website: string = ""; - } -export const customers: Customer[] = [{ - ID: 1, - CompanyName: 'Super Mart of the West', - Address: '702 SW 8th Street', - City: 'Bentonville', - State: 'Arkansas', - Website: 'http://www.nowebsitesupermart.com', - }, { - ID: 2, - CompanyName: 'Electronics Depot', - Address: '2455 Paces Ferry Road NW', - City: 'Atlanta', - State: 'Georgia', - Website: 'http://www.nowebsitedepot.com', - }, { - ID: 3, - CompanyName: 'K&S Music', - Address: '1000 Nicllet Mall', - City: 'Minneapolis', - State: 'Minnesota', - Website: 'http://www.nowebsitemusic.com', - }, { - ID: 4, - CompanyName: "Tom's Club", - Address: '999 Lake Drive', - City: 'Issaquah', - State: 'Washington', - Website: 'http://www.nowebsitetomsclub.com', - }, { - ID: 5, - CompanyName: 'E-Mart', - Address: '3333 Beverly Rd', - City: 'Hoffman Estates', - State: 'Illinois', - Website: 'http://www.nowebsiteemart.com', - }, { - ID: 6, - CompanyName: 'Walters', - Address: '200 Wilmot Rd', - City: 'Deerfield', - State: 'Illinois', - Website: 'http://www.nowebsitewalters.com', - }, { - ID: 7, - CompanyName: 'StereoShack', - Address: '400 Commerce S', - City: 'Fort Worth', - State: 'Texas', - Website: 'http://www.nowebsiteshack.com', - }, { - ID: 8, - CompanyName: 'Circuit Town', - Address: '2200 Kensington Court', - City: 'Oak Brook', - State: 'Illinois', - Website: 'http://www.nowebsitecircuittown.com', - }, { - ID: 9, - CompanyName: 'Premier Buy', - Address: '7601 Penn Avenue South', - City: 'Richfield', - State: 'Minnesota', - Website: 'http://www.nowebsitepremierbuy.com', - }, { - ID: 10, - CompanyName: 'ElectrixMax', - Address: '263 Shuman Blvd', - City: 'Naperville', - State: 'Illinois', - Website: 'http://www.nowebsiteelectrixmax.com', - }, { - ID: 11, - CompanyName: 'Video Emporium', - Address: '1201 Elm Street', - City: 'Dallas', - State: 'Texas', - Website: 'http://www.nowebsitevideoemporium.com', - }, { - ID: 12, - CompanyName: 'Screen Shop', - Address: '1000 Lowes Blvd', - City: 'Mooresville', - State: 'North Carolina', - Website: 'http://www.nowebsitescreenshop.com', - }]; \ No newline at end of file diff --git a/React/src/orig_index.css b/React/src/orig_index.css deleted file mode 100644 index ebce15b..0000000 --- a/React/src/orig_index.css +++ /dev/null @@ -1,12 +0,0 @@ -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/React/src/orig_index.tsx b/React/src/orig_index.tsx deleted file mode 100644 index 032464f..0000000 --- a/React/src/orig_index.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; - -const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement -); -root.render( - - - -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/React/src/utils.ts b/React/src/utils.ts new file mode 100644 index 0000000..098ab95 --- /dev/null +++ b/React/src/utils.ts @@ -0,0 +1,32 @@ +import type { DataGridTypes } from 'devextreme-react/data-grid'; +import type dxDataGrid from 'devextreme/ui/data_grid'; + +export type CellValue = number | string | undefined; + +export function getVisibleCellValue(column: DataGridTypes.Column, rowData: T): CellValue { + if (column.dataField) { + const propKey = column.dataField as keyof T; + const cellValue = rowData[propKey]; + return column?.lookup?.calculateCellValue + ? column.lookup.calculateCellValue(cellValue) as CellValue + : cellValue as CellValue; + } + return undefined; +} + +export function getVisibleRowValues( + rowsData: T[], + grid: dxDataGrid, +): Record[] { + const visibleColumns: DataGridTypes.Column[] = grid.getVisibleColumns(); + const selectedData = rowsData.map((rowData) => { + const visibleValues: Record = {}; + visibleColumns.forEach((column: DataGridTypes.Column) => { + if (column.dataField) { + visibleValues[column.dataField] = getVisibleCellValue(column, rowData); + } + }); + return visibleValues; + }); + return selectedData; +} diff --git a/Vue/orig_index.html b/Vue/orig_index.html deleted file mode 100644 index 1f7c6cf..0000000 --- a/Vue/orig_index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Vite App - - -
- - - diff --git a/Vue/package-lock.json b/Vue/package-lock.json index b7315bd..b3b5581 100644 --- a/Vue/package-lock.json +++ b/Vue/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "devextreme": "25.1.3", + "devextreme-aspnet-data-nojquery": "^5.1.0", "devextreme-vue": "25.1.3", "vue": "^3.2.45", "vue-router": "^4.1.6" @@ -2948,6 +2949,15 @@ "devextreme-bundler-init": "bin/bundler-init.js" } }, + "node_modules/devextreme-aspnet-data-nojquery": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/devextreme-aspnet-data-nojquery/-/devextreme-aspnet-data-nojquery-5.1.0.tgz", + "integrity": "sha512-YJ7HxOLJTzz6bmpp1uOjXdhUL71THR6IidVONjJRF1R1loTyNVesa6s86gU+mUeNN044UnJKQhtCADc0+TmARQ==", + "license": "MIT", + "peerDependencies": { + "devextreme": ">=18.1.0" + } + }, "node_modules/devextreme-quill": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/devextreme-quill/-/devextreme-quill-1.7.3.tgz", diff --git a/Vue/package.json b/Vue/package.json index 948760e..bafd41f 100644 --- a/Vue/package.json +++ b/Vue/package.json @@ -13,6 +13,7 @@ }, "dependencies": { "devextreme": "25.1.3", + "devextreme-aspnet-data-nojquery": "^5.1.0", "devextreme-vue": "25.1.3", "vue": "^3.2.45", "vue-router": "^4.1.6" diff --git a/Vue/src/assets/main.css b/Vue/src/assets/main.css index e3267f4..cf6347c 100644 --- a/Vue/src/assets/main.css +++ b/Vue/src/assets/main.css @@ -3,6 +3,6 @@ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #2c3e50; - margin: 50px 50px; - width: 90vh; + margin: 50px; + width: 90vw; } \ No newline at end of file diff --git a/Vue/src/assets/orig_main.css b/Vue/src/assets/orig_main.css deleted file mode 100644 index e3267f4..0000000 --- a/Vue/src/assets/orig_main.css +++ /dev/null @@ -1,8 +0,0 @@ -.main { - font-family: Avenir, Helvetica, Arial, sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - color: #2c3e50; - margin: 50px 50px; - width: 90vh; -} \ No newline at end of file diff --git a/Vue/src/components/DataGridLocalData.vue b/Vue/src/components/DataGridLocalData.vue new file mode 100644 index 0000000..58424db --- /dev/null +++ b/Vue/src/components/DataGridLocalData.vue @@ -0,0 +1,102 @@ + + diff --git a/Vue/src/components/DataGridRemoteData.vue b/Vue/src/components/DataGridRemoteData.vue new file mode 100644 index 0000000..44b2cc8 --- /dev/null +++ b/Vue/src/components/DataGridRemoteData.vue @@ -0,0 +1,150 @@ + + diff --git a/Vue/src/components/HomeContent.vue b/Vue/src/components/HomeContent.vue index 76c6071..b8a7e5c 100644 --- a/Vue/src/components/HomeContent.vue +++ b/Vue/src/components/HomeContent.vue @@ -1,28 +1,56 @@ + diff --git a/Vue/src/components/orig_DataGridLocalData.vue b/Vue/src/components/orig_DataGridLocalData.vue deleted file mode 100644 index ed239f2..0000000 --- a/Vue/src/components/orig_DataGridLocalData.vue +++ /dev/null @@ -1,119 +0,0 @@ - - diff --git a/Vue/src/components/orig_DataGridRemoteData.vue b/Vue/src/components/orig_DataGridRemoteData.vue deleted file mode 100644 index 0d4012f..0000000 --- a/Vue/src/components/orig_DataGridRemoteData.vue +++ /dev/null @@ -1,153 +0,0 @@ - - diff --git a/Vue/src/components/orig_HomeContent.vue b/Vue/src/components/orig_HomeContent.vue deleted file mode 100644 index a10a418..0000000 --- a/Vue/src/components/orig_HomeContent.vue +++ /dev/null @@ -1,53 +0,0 @@ - - - diff --git a/jQuery/src/orig_DataArray.js b/Vue/src/data.ts similarity index 82% rename from jQuery/src/orig_DataArray.js rename to Vue/src/data.ts index 64eeec9..b9704d9 100644 --- a/jQuery/src/orig_DataArray.js +++ b/Vue/src/data.ts @@ -1,86 +1,116 @@ -const customers = [{ +export interface Task { + ID: number; + AssignedEmployee: number; + OrderIndex: number; + Owner: number; + Priority: number; + Status: number; + Subject: string; +} + +export interface Customer { + ID: number; + CompanyName: string; + Address: string; + City: string; + State: string; + Website: string; +} +export const customers: Customer[] = [ + { ID: 1, CompanyName: 'Super Mart of the West', Address: '702 SW 8th Street', City: 'Bentonville', State: 'Arkansas', Website: 'http://www.nowebsitesupermart.com', - }, { + }, + { ID: 2, CompanyName: 'Electronics Depot', Address: '2455 Paces Ferry Road NW', City: 'Atlanta', State: 'Georgia', Website: 'http://www.nowebsitedepot.com', - }, { + }, + { ID: 3, CompanyName: 'K&S Music', Address: '1000 Nicllet Mall', City: 'Minneapolis', State: 'Minnesota', Website: 'http://www.nowebsitemusic.com', - }, { + }, + { ID: 4, CompanyName: "Tom's Club", Address: '999 Lake Drive', City: 'Issaquah', State: 'Washington', Website: 'http://www.nowebsitetomsclub.com', - }, { + }, + { ID: 5, CompanyName: 'E-Mart', Address: '3333 Beverly Rd', City: 'Hoffman Estates', State: 'Illinois', Website: 'http://www.nowebsiteemart.com', - }, { + }, + { ID: 6, CompanyName: 'Walters', Address: '200 Wilmot Rd', City: 'Deerfield', State: 'Illinois', Website: 'http://www.nowebsitewalters.com', - }, { + }, + { ID: 7, CompanyName: 'StereoShack', Address: '400 Commerce S', City: 'Fort Worth', State: 'Texas', Website: 'http://www.nowebsiteshack.com', - }, { + }, + { ID: 8, CompanyName: 'Circuit Town', Address: '2200 Kensington Court', City: 'Oak Brook', State: 'Illinois', Website: 'http://www.nowebsitecircuittown.com', - }, { + }, + { ID: 9, CompanyName: 'Premier Buy', Address: '7601 Penn Avenue South', City: 'Richfield', State: 'Minnesota', Website: 'http://www.nowebsitepremierbuy.com', - }, { + }, + { ID: 10, CompanyName: 'ElectrixMax', Address: '263 Shuman Blvd', City: 'Naperville', State: 'Illinois', Website: 'http://www.nowebsiteelectrixmax.com', - }, { + }, + { ID: 11, CompanyName: 'Video Emporium', Address: '1201 Elm Street', City: 'Dallas', State: 'Texas', Website: 'http://www.nowebsitevideoemporium.com', - }, { + }, + { ID: 12, CompanyName: 'Screen Shop', Address: '1000 Lowes Blvd', City: 'Mooresville', State: 'North Carolina', Website: 'http://www.nowebsitescreenshop.com', - }]; - \ No newline at end of file + }, +]; diff --git a/Vue/src/orig_App.vue b/Vue/src/orig_App.vue deleted file mode 100644 index 74d6df9..0000000 --- a/Vue/src/orig_App.vue +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/Vue/src/orig_data.ts b/Vue/src/orig_data.ts deleted file mode 100644 index 7e277a8..0000000 --- a/Vue/src/orig_data.ts +++ /dev/null @@ -1,116 +0,0 @@ -export interface Task { - ID: number; - AssignedEmployee: number; - OrderIndex: number; - Owner: number; - Priority: number; - Status: number; - Subject: string; -} - -export interface Customer { - ID: number; - CompanyName: string; - Address: string; - City: string; - State: string; - Website: string; -} -export const customers: Customer[] = [ - { - ID: 1, - CompanyName: "Super Mart of the West", - Address: "702 SW 8th Street", - City: "Bentonville", - State: "Arkansas", - Website: "http://www.nowebsitesupermart.com", - }, - { - ID: 2, - CompanyName: "Electronics Depot", - Address: "2455 Paces Ferry Road NW", - City: "Atlanta", - State: "Georgia", - Website: "http://www.nowebsitedepot.com", - }, - { - ID: 3, - CompanyName: "K&S Music", - Address: "1000 Nicllet Mall", - City: "Minneapolis", - State: "Minnesota", - Website: "http://www.nowebsitemusic.com", - }, - { - ID: 4, - CompanyName: "Tom's Club", - Address: "999 Lake Drive", - City: "Issaquah", - State: "Washington", - Website: "http://www.nowebsitetomsclub.com", - }, - { - ID: 5, - CompanyName: "E-Mart", - Address: "3333 Beverly Rd", - City: "Hoffman Estates", - State: "Illinois", - Website: "http://www.nowebsiteemart.com", - }, - { - ID: 6, - CompanyName: "Walters", - Address: "200 Wilmot Rd", - City: "Deerfield", - State: "Illinois", - Website: "http://www.nowebsitewalters.com", - }, - { - ID: 7, - CompanyName: "StereoShack", - Address: "400 Commerce S", - City: "Fort Worth", - State: "Texas", - Website: "http://www.nowebsiteshack.com", - }, - { - ID: 8, - CompanyName: "Circuit Town", - Address: "2200 Kensington Court", - City: "Oak Brook", - State: "Illinois", - Website: "http://www.nowebsitecircuittown.com", - }, - { - ID: 9, - CompanyName: "Premier Buy", - Address: "7601 Penn Avenue South", - City: "Richfield", - State: "Minnesota", - Website: "http://www.nowebsitepremierbuy.com", - }, - { - ID: 10, - CompanyName: "ElectrixMax", - Address: "263 Shuman Blvd", - City: "Naperville", - State: "Illinois", - Website: "http://www.nowebsiteelectrixmax.com", - }, - { - ID: 11, - CompanyName: "Video Emporium", - Address: "1201 Elm Street", - City: "Dallas", - State: "Texas", - Website: "http://www.nowebsitevideoemporium.com", - }, - { - ID: 12, - CompanyName: "Screen Shop", - Address: "1000 Lowes Blvd", - City: "Mooresville", - State: "North Carolina", - Website: "http://www.nowebsitescreenshop.com", - }, -]; diff --git a/Vue/src/orig_main.ts b/Vue/src/orig_main.ts deleted file mode 100644 index a198b06..0000000 --- a/Vue/src/orig_main.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createApp } from "vue"; -import App from "./App.vue"; -import router from "./router"; - -import "./assets/main.css"; - -const app = createApp(App); - -app.use(router); - -app.mount("#app"); diff --git a/Vue/src/router/orig_index.ts b/Vue/src/router/orig_index.ts index c843786..eaba7c3 100644 --- a/Vue/src/router/orig_index.ts +++ b/Vue/src/router/orig_index.ts @@ -1,12 +1,12 @@ -import { createRouter, createWebHistory } from "vue-router"; -import HomeView from "../views/HomeView.vue"; +import { createRouter, createWebHistory } from 'vue-router'; +import HomeView from '../views/HomeView.vue'; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { - path: "/", - name: "home", + path: '/', + name: 'home', component: HomeView, }, ], diff --git a/Vue/src/utils.ts b/Vue/src/utils.ts new file mode 100644 index 0000000..4891d87 --- /dev/null +++ b/Vue/src/utils.ts @@ -0,0 +1,32 @@ +import type { DxDataGridTypes } from 'devextreme-vue/data-grid'; +import type dxDataGrid from 'devextreme/ui/data_grid'; + +export type CellValue = number | string | undefined; + +export function getVisibleCellValue(column: DxDataGridTypes.Column, rowData: T): CellValue { + if (column.dataField) { + const propKey = column.dataField as keyof T; + const cellValue = rowData[propKey]; + return column?.lookup?.calculateCellValue + ? column.lookup.calculateCellValue(cellValue) as CellValue + : (cellValue as unknown) as CellValue; + } + return undefined; +} + +export function getVisibleRowValues( + rowsData: T[], + grid: dxDataGrid, +): Record[] { + const visibleColumns: DxDataGridTypes.Column[] = grid.getVisibleColumns(); + const selectedData = rowsData.map((rowData) => { + const visibleValues: Record = {}; + visibleColumns.forEach((column: DxDataGridTypes.Column) => { + if (column.dataField) { + visibleValues[column.dataField] = getVisibleCellValue(column, rowData); + } + }); + return visibleValues; + }); + return selectedData; +} diff --git a/Vue/src/views/HomeView.vue b/Vue/src/views/HomeView.vue index ab8926f..49429e6 100644 --- a/Vue/src/views/HomeView.vue +++ b/Vue/src/views/HomeView.vue @@ -1,4 +1,5 @@ diff --git a/Vue/src/views/orig_HomeView.vue b/Vue/src/views/orig_HomeView.vue deleted file mode 100644 index 3ca6ca8..0000000 --- a/Vue/src/views/orig_HomeView.vue +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/jQuery/src/DataArray.js b/jQuery/src/DataArray.js new file mode 100644 index 0000000..4fd1ab8 --- /dev/null +++ b/jQuery/src/DataArray.js @@ -0,0 +1,85 @@ +const customers = [{ + ID: 1, + CompanyName: 'Super Mart of the West', + Address: '702 SW 8th Street', + City: 'Bentonville', + State: 'Arkansas', + Website: 'http://www.nowebsitesupermart.com', +}, { + ID: 2, + CompanyName: 'Electronics Depot', + Address: '2455 Paces Ferry Road NW', + City: 'Atlanta', + State: 'Georgia', + Website: 'http://www.nowebsitedepot.com', +}, { + ID: 3, + CompanyName: 'K&S Music', + Address: '1000 Nicllet Mall', + City: 'Minneapolis', + State: 'Minnesota', + Website: 'http://www.nowebsitemusic.com', +}, { + ID: 4, + CompanyName: "Tom's Club", + Address: '999 Lake Drive', + City: 'Issaquah', + State: 'Washington', + Website: 'http://www.nowebsitetomsclub.com', +}, { + ID: 5, + CompanyName: 'E-Mart', + Address: '3333 Beverly Rd', + City: 'Hoffman Estates', + State: 'Illinois', + Website: 'http://www.nowebsiteemart.com', +}, { + ID: 6, + CompanyName: 'Walters', + Address: '200 Wilmot Rd', + City: 'Deerfield', + State: 'Illinois', + Website: 'http://www.nowebsitewalters.com', +}, { + ID: 7, + CompanyName: 'StereoShack', + Address: '400 Commerce S', + City: 'Fort Worth', + State: 'Texas', + Website: 'http://www.nowebsiteshack.com', +}, { + ID: 8, + CompanyName: 'Circuit Town', + Address: '2200 Kensington Court', + City: 'Oak Brook', + State: 'Illinois', + Website: 'http://www.nowebsitecircuittown.com', +}, { + ID: 9, + CompanyName: 'Premier Buy', + Address: '725 B Riverside', + City: 'Reno', + State: 'Nevada', + Website: 'http://www.nowebsitepremierbuy.com', +}, { + ID: 10, + CompanyName: 'ElectrixMax', + Address: '263 Shuman Blvd', + City: 'Naperville', + State: 'Illinois', + Website: 'http://www.nowebsiteelectrixmax.com', +}, { + ID: 11, + CompanyName: 'Video Emporium', + Address: '1201 Elm Street', + City: 'Dallas', + State: 'Texas', + Website: 'http://www.nowebsitevideoemporium.com', +}, { + ID: 12, + CompanyName: 'Screen Shop', + Address: '1000 Lowes Blvd', + City: 'Mooresville', + State: 'North Carolina', + Website: 'http://www.nowebsitescreenshop.com', +}]; diff --git a/jQuery/src/GridLocalData.js b/jQuery/src/GridLocalData.js new file mode 100644 index 0000000..6cee90f --- /dev/null +++ b/jQuery/src/GridLocalData.js @@ -0,0 +1,91 @@ +$(() => { + const KEY_EXPR = 'ID'; + + $('#grid-local-data').dxDataGrid({ + keyExpr: KEY_EXPR, + dataSource: customers, + rowDragging: { + allowReordering: true, + onDragStart(e) { + const selectedData = e.component.getSelectedRowsData(); + e.itemData = getVisibleRowValues(selectedData, e.component); + e.cancel = !canDrag(e); + }, + dragTemplate(dragData) { + const itemsContainer = $('').addClass('drag-container'); + dragData.itemData.forEach(((rowData) => { + const itemContainer = $(''); + Object.keys(rowData).forEach((field) => { + itemContainer.append($('
').text(rowData[field])); + }); + itemsContainer.append(itemContainer); + })); + return $('
').append(itemsContainer); + }, + onDragChange(e) { + e.cancel = !canDrop(e); + }, + onReorder(e) { + const fullDataToInsert = []; + e.itemData.forEach((rowData) => { + const indexToRemove = customers.findIndex((item) => item[KEY_EXPR] === rowData[KEY_EXPR]); + fullDataToInsert.push(customers[indexToRemove]); + customers.splice(indexToRemove, 1); + }); + const toIndex = calculateToIndex(customers, e); + customers.splice(toIndex, 0, ...fullDataToInsert); + e.component.refresh(); + if (shouldClearSelection()) { + e.component.clearSelection(); + } + }, + }, + selection: { mode: 'multiple' }, + sorting: { mode: 'none' }, + columns: [{ + dataField: 'ID', + width: 55, + }, 'CompanyName', 'Address', 'City', 'State'], + }); + + function canDrag(e) { + const visibleRows = e.component.getVisibleRows(); + return visibleRows.some((r) => r.isSelected && r.rowIndex === e.fromIndex); + } + + function canDrop(e) { + const visibleRows = e.component.getVisibleRows(); + return !visibleRows.some((r) => r.isSelected && r.rowIndex === e.toIndex); + } + + function calculateToIndex(dataArray, e) { + const visibleRows = e.component.getVisibleRows(); + const toIndex = dataArray.findIndex( + (item) => item[KEY_EXPR] === visibleRows[e.toIndex].data[KEY_EXPR], + ); + return e.fromIndex >= e.toIndex ? toIndex : toIndex + 1; + } + + function getVisibleRowValues(rowsData, grid) { + const visibleColumns = grid.getVisibleColumns(); + const selectedData = rowsData.map((rowData) => { + const visibleValues = {}; + visibleColumns.forEach((column) => { + if (column.dataField) { + visibleValues[column.dataField] = getVisibleCellValue(column, rowData); + } + }); + return visibleValues; + }); + return selectedData; + } + + function getVisibleCellValue(column, rowData) { + const cellValue = rowData[column.dataField]; + return column.lookup ? column.lookup.calculateCellValue(cellValue) : cellValue; + } + + function shouldClearSelection() { + return $('#clear-after-drop-switch').dxSwitch('option', 'value'); + } +}); diff --git a/jQuery/src/GridRemoteData.js b/jQuery/src/GridRemoteData.js new file mode 100644 index 0000000..4c61c63 --- /dev/null +++ b/jQuery/src/GridRemoteData.js @@ -0,0 +1,133 @@ +$(() => { + const KEY_EXPR = 'ID'; + const url = 'https://js.devexpress.com/Demos/NetCore/api/DataGridRowReordering'; + const tasksStore = DevExpress.data.AspNet.createStore({ + key: KEY_EXPR, + loadUrl: `${url}/Tasks`, + updateUrl: `${url}/UpdateTask`, + onBeforeSend(method, ajaxOptions) { + ajaxOptions.xhrFields = { withCredentials: true }; + }, + }); + const employeesStore = DevExpress.data.AspNet.createStore({ + key: 'ID', + loadUrl: `${url}/Employees`, + onBeforeSend(method, ajaxOptions) { + ajaxOptions.xhrFields = { withCredentials: true }; + }, + }); + let updateInProgress = false; + + $('#grid-remote-data').dxDataGrid({ + height: 480, + dataSource: tasksStore, + remoteOperations: true, + scrolling: { mode: 'virtual' }, + sorting: { mode: 'none' }, + rowDragging: { + allowReordering: true, + onDragStart(e) { + const selectedData = e.component.getSelectedRowsData() + .sort((a, b) => (a.OrderIndex > b.OrderIndex ? 1 : -1)); + e.itemData = getVisibleRowValues(selectedData, e.component); + e.cancel = !canDrag(e); + }, + dragTemplate(dragData) { + const itemsContainer = $('').addClass('drag-container'); + dragData.itemData.forEach(((rowData) => { + const itemContainer = $(''); + Object.keys(rowData).forEach((field) => { + itemContainer.append($('
').text(rowData[field])); + }); + itemsContainer.append(itemContainer); + })); + return $('
').append(itemsContainer); + }, + onDragChange(e) { + e.cancel = !canDrop(e); + }, + onReorder(e) { + e.promise = updateOrderIndex(e); + if (shouldClearSelection()) { + e.component.clearSelection(); + } + }, + }, + selection: { mode: 'multiple' }, + columns: [{ + dataField: 'ID', + width: 55, + }, { + dataField: 'Owner', + lookup: { + dataSource: employeesStore, + valueExpr: 'ID', + displayExpr: 'FullName', + }, + width: 150, + }, { + dataField: 'AssignedEmployee', + caption: 'Assignee', + lookup: { + dataSource: employeesStore, + valueExpr: 'ID', + displayExpr: 'FullName', + }, + width: 150, + }, 'Subject'], + }); + + async function updateOrderIndex(e) { + const visibleRows = e.component.getVisibleRows(); + const newOrderIndex = visibleRows[e.toIndex].data.OrderIndex; + const store = e.component.getDataSource().store(); + try { + updateInProgress = true; + e.component.beginCustomLoading('Loading...'); + const updatePromises = e.itemData.map( + (itemData) => store.update(itemData[KEY_EXPR], { OrderIndex: newOrderIndex }), + ); + await Promise.all(updatePromises); + await e.component.refresh(); + } catch (error) { + DevExpress.ui.notify('An error occurred while updating the data.', 'error', 1000); + } finally { + e.component.endCustomLoading(); + updateInProgress = false; + } + } + + function canDrag(e) { + if (updateInProgress) return false; + const visibleRows = e.component.getVisibleRows(); + return visibleRows.some((r) => r.isSelected && r.rowIndex === e.fromIndex); + } + + function canDrop(e) { + const visibleRows = e.component.getVisibleRows(); + return !visibleRows.some((r) => r.isSelected && r.rowIndex === e.toIndex); + } + + function getVisibleRowValues(rowsData, grid) { + const visibleColumns = grid.getVisibleColumns(); + const selectedData = rowsData.map((rowData) => { + const visibleValues = {}; + visibleColumns.forEach((column) => { + if (column.dataField) { + visibleValues[column.dataField] = getVisibleCellValue(column, rowData); + } + }); + return visibleValues; + }); + return selectedData; + } + + function getVisibleCellValue(column, rowData) { + const cellValue = rowData[column.dataField]; + return column.lookup ? column.lookup.calculateCellValue(cellValue) : cellValue; + } + + function shouldClearSelection() { + return $('#clear-after-drop-switch').dxSwitch('option', 'value'); + } +}); diff --git a/jQuery/src/index.css b/jQuery/src/index.css index 278b9f8..a4eb252 100644 --- a/jQuery/src/index.css +++ b/jQuery/src/index.css @@ -2,3 +2,32 @@ margin: 50px; width: 90vw; } + +.tab-item-content { + margin: auto; +} + +.demo-header { + display: flex; + justify-content: space-between; +} + +#toggle-container { + padding-top: 20px; +} + +#clear-after-drop-switch { + vertical-align: text-bottom; +} + +#toggle-container span { + padding-right: 10px; +} + +.drag-container { + padding: 10px; +} + +.drag-container td { + padding: 0 10px; +} diff --git a/jQuery/src/index.html b/jQuery/src/index.html index 423bb30..b3c35fd 100644 --- a/jQuery/src/index.html +++ b/jQuery/src/index.html @@ -6,16 +6,27 @@ - + + + + +
-
+
+

DataGrid - Select multiple items and drag'n'drop

+
+ Clear selection after drop +
+
+
+
diff --git a/jQuery/src/index.js b/jQuery/src/index.js index 6d9c698..5f5b570 100644 --- a/jQuery/src/index.js +++ b/jQuery/src/index.js @@ -1,10 +1,22 @@ $(() => { - let count = 0; - $('#btn').dxButton({ - text: `Click count: ${count}`, - onClick(e) { - count += 1; - e.component.option('text', `Click count: ${count}`); - }, + function createTabItemTemplate(contentID) { + return $('
').attr('id', contentID).addClass('tab-item-content'); + } + + $('#tab-panel').dxTabPanel({ + deferRendering: false, + items: [{ + title: 'Local Data', + template() { + return createTabItemTemplate('grid-local-data'); + }, + }, { + title: 'Remote Data', + template() { + return createTabItemTemplate('grid-remote-data'); + }, + }], }); + + $('#clear-after-drop-switch').dxSwitch({}); }); diff --git a/jQuery/src/orig_GridLocalData.js b/jQuery/src/orig_GridLocalData.js deleted file mode 100644 index 813428b..0000000 --- a/jQuery/src/orig_GridLocalData.js +++ /dev/null @@ -1,80 +0,0 @@ -$(function () { - const KEY_EXPR = "ID"; - - $("#gridLocalData").dxDataGrid({ - keyExpr: KEY_EXPR, - dataSource: customers, - rowDragging: { - allowReordering: true, - onDragStart: function (e) { - const selectedData = e.component.getSelectedRowsData(); - e.itemData = getVisibleRowValues(selectedData, e.component); - e.cancel = !canDrag(e); - }, - dragTemplate: function(dragData) { - const itemsContainer = $("").addClass("drag-container"); - dragData.itemData.forEach((rowData => { - const itemContainer = $(""); - for (field in rowData) { - itemContainer.append($("
").text(rowData[field])); - } - itemsContainer.append(itemContainer); - })); - return $("
").append(itemsContainer); - }, - onDragChange(e) { - e.cancel = !canDrop(e); - }, - onReorder(e) { - const fullDataToInsert = []; - e.itemData.forEach(rowData => { - const indexToRemove = customers.findIndex(item => item[KEY_EXPR] === rowData[KEY_EXPR]); - fullDataToInsert.push(customers[indexToRemove]); - customers.splice(indexToRemove, 1); - }); - const toIndex = calculateToIndex(customers, e); - customers.splice(toIndex, 0, ...fullDataToInsert); - e.component.refresh(); - if (shouldClearSelection()) - e.component.clearSelection(); - }, - }, - selection: { mode: "multiple" }, - sorting: { mode: 'none' }, - columns: [{ - dataField: "ID", - width: 55 - }, "CompanyName", "Address", "City", "State" ], - }) - function canDrag(e) { - const visibleRows = e.component.getVisibleRows(); - return visibleRows.some(r => r.isSelected && r.rowIndex === e.fromIndex); - } - function canDrop(e) { - const visibleRows = e.component.getVisibleRows(); - return !visibleRows.some(r => r.isSelected && r.rowIndex === e.toIndex); - } - function calculateToIndex(dataArray, e) { - const visibleRows = e.component.getVisibleRows(); - const toIndex = dataArray.findIndex((item) => item[KEY_EXPR] === visibleRows[e.toIndex].data[KEY_EXPR]); - return e.fromIndex >= e.toIndex ? toIndex : toIndex + 1; - } - function getVisibleRowValues(rowsData, grid) { - const visbileColumns = grid.getVisibleColumns(); - const selectedData = rowsData.map(rowData => { - const visibleValues = {}; - visbileColumns.forEach(column => { - visibleValues[column.dataField] = getVisibleCellValue(column, rowData); - }); - return visibleValues; - }); - return selectedData; - } - function getVisibleCellValue(column, rowData) { - const cellValue = rowData[column.dataField]; - return column.lookup ? column.lookup.calculateCellValue(cellValue) : cellValue; - } - function shouldClearSelection() { - return $("#clearAfterDropSwitch").dxSwitch("option", "value"); - } -}); \ No newline at end of file diff --git a/jQuery/src/orig_GridRemoteData.js b/jQuery/src/orig_GridRemoteData.js deleted file mode 100644 index 98af76c..0000000 --- a/jQuery/src/orig_GridRemoteData.js +++ /dev/null @@ -1,119 +0,0 @@ -$(function () { - const KEY_EXPR = "ID"; - //const url = 'http://localhost:5555/api/DataGridRowReordering'; - const url = 'https://js.devexpress.com/Demos/Mvc/api/RowReordering'; - const tasksStore = DevExpress.data.AspNet.createStore({ - key: KEY_EXPR, - loadUrl: `${url}/Tasks`, - updateUrl: `${url}/UpdateTask`, - onBeforeSend(method, ajaxOptions) { - ajaxOptions.xhrFields = { withCredentials: true }; - }, - }); - const employeesStore = DevExpress.data.AspNet.createStore({ - key: 'ID', - loadUrl: `${url}/Employees`, - onBeforeSend(method, ajaxOptions) { - ajaxOptions.xhrFields = { withCredentials: true }; - }, - }); - let updateInProgress = false; - - $("#gridRemoteData").dxDataGrid({ - height: 480, - dataSource: tasksStore, - remoteOperations: true, - scrolling: { mode: 'virtual' }, - sorting: { mode: 'none' }, - rowDragging: { - allowReordering: true, - onDragStart: function (e) { - const selectedData = e.component.getSelectedRowsData().sort((a, b) => a.OrderIndex > b.OrderIndex ? 1 : -1); - e.itemData = getVisibleRowValues(selectedData, e.component); - e.cancel = !canDrag(e); - }, - dragTemplate: function(dragData) { - const itemsContainer = $("").addClass("drag-container"); - dragData.itemData.forEach((rowData => { - const itemContainer = $(""); - for (field in rowData) { - itemContainer.append($("
").text(rowData[field])); - } - itemsContainer.append(itemContainer); - })); - return $("
").append(itemsContainer); - }, - onDragChange(e) { - e.cancel = !canDrop(e); - }, - onReorder(e) { - e.promise = updateOrderIndex(e); - if (shouldClearSelection()) - e.component.clearSelection(); - }, - }, - selection: { mode: "multiple" }, - columns: [{ - dataField: 'ID', - width: 55, - }, { - dataField: 'Owner', - lookup: { - dataSource: employeesStore, - valueExpr: 'ID', - displayExpr: 'FullName', - }, - width: 150, - }, { - dataField: 'AssignedEmployee', - caption: 'Assignee', - lookup: { - dataSource: employeesStore, - valueExpr: 'ID', - displayExpr: 'FullName', - }, - width: 150, - }, 'Subject'], - }) - async function updateOrderIndex(e) { - const visibleRows = e.component.getVisibleRows(); - const newOrderIndex = visibleRows[e.toIndex].data.OrderIndex; - const store = e.component.getDataSource().store(); - updateInProgress = true; - e.component.beginCustomLoading("Loading..."); - for (let i = 0; i < e.itemData.length; i++) { - await store.update(e.itemData[i][KEY_EXPR], { OrderIndex: newOrderIndex }); - } - e.component.refresh().then(() => { - e.component.endCustomLoading(); - updateInProgress = false; - }); - } - function canDrag(e) { - if (updateInProgress) return false; - const visibleRows = e.component.getVisibleRows(); - return visibleRows.some(r => r.isSelected && r.rowIndex === e.fromIndex); - } - function canDrop(e) { - const visibleRows = e.component.getVisibleRows(); - return !visibleRows.some(r => r.isSelected && r.rowIndex === e.toIndex); - } - function getVisibleRowValues(rowsData, grid) { - const visbileColumns = grid.getVisibleColumns(); - const selectedData = rowsData.map(rowData => { - const visibleValues = {}; - visbileColumns.forEach(column => { - visibleValues[column.dataField] = getVisibleCellValue(column, rowData); - }); - return visibleValues; - }); - return selectedData; - } - function getVisibleCellValue(column, rowData) { - const cellValue = rowData[column.dataField]; - return column.lookup ? column.lookup.calculateCellValue(cellValue) : cellValue; - } - function shouldClearSelection() { - return $("#clearAfterDropSwitch").dxSwitch("option", "value"); - } -}); \ No newline at end of file diff --git a/jQuery/src/orig_index.css b/jQuery/src/orig_index.css deleted file mode 100644 index f3224a8..0000000 --- a/jQuery/src/orig_index.css +++ /dev/null @@ -1,26 +0,0 @@ -.demo-container { - margin: 50px 50px; - width: 90vh; -} -.tab-item-content { - margin: auto; -} -.demo-header { - display: flex; - justify-content: space-between; -} -#toggle-container { - padding-top: 20px; -} -#clearAfterDropSwitch { - vertical-align: text-bottom; -} -#toggle-container span { - padding-right: 10px; -} -.drag-container { - padding: 10px; -} -.drag-container td { - padding: 0px 10px 0px 10px; -} \ No newline at end of file diff --git a/jQuery/src/orig_index.html b/jQuery/src/orig_index.html deleted file mode 100644 index bd622cf..0000000 --- a/jQuery/src/orig_index.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - DevExtreme jQuery app - - - - - - - - - - - - - - - - - -
-
-

DataGrid - Select multiple items and drag'n'drop

-
- Clear selection after drop -
-
-
-
-
- - - \ No newline at end of file diff --git a/jQuery/src/orig_index.js b/jQuery/src/orig_index.js deleted file mode 100644 index 77e840a..0000000 --- a/jQuery/src/orig_index.js +++ /dev/null @@ -1,20 +0,0 @@ -$(function () { - function createTabItemTemplate(contentID) { - return $("
").attr("id", contentID).addClass("tab-item-content"); - } - $("#tabPanel").dxTabPanel({ - deferRendering: false, - items: [{ - title: "Local Data", - template: function() { - return createTabItemTemplate("gridLocalData"); - } - }, { - title: "Remote Data", - template: function() { - return createTabItemTemplate("gridRemoteData"); - } - }] - }) - $("#clearAfterDropSwitch").dxSwitch({}); -}); \ No newline at end of file