From 84ed598f27634318b3f7aa82f3aa67d7ba2862c1 Mon Sep 17 00:00:00 2001 From: Rinat Arsaev <11846445+A77AY@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:15:57 +0500 Subject: [PATCH] Optimize new table (#68) --- package-lock.json | 344 ++++++++++-------- package.json | 22 +- .../select-field/select-field.component.html | 2 +- .../table-info-bar.component.html | 2 +- .../table-info-bar.component.ts | 10 +- .../components/table2/table2.component.html | 21 +- .../components/table2/table2.component.scss | 1 + .../components/table2/table2.component.ts | 224 ++++++++---- .../ng-core/src/lib/components/table/index.ts | 1 + .../components/table/utils/cached-head-map.ts | 21 ++ .../value/utils/create-input-subject.ts | 18 + .../utils/currency-amount-value-to-string.ts | 2 +- .../lib/components/value/value.component.html | 10 +- .../lib/components/value/value.component.ts | 71 ++-- .../ng-core/src/lib/services/url.service.ts | 2 +- .../src/lib/utils/currency/format-currency.ts | 2 +- 16 files changed, 458 insertions(+), 295 deletions(-) create mode 100644 projects/ng-core/src/lib/components/table/utils/cached-head-map.ts create mode 100644 projects/ng-core/src/lib/components/value/utils/create-input-subject.ts diff --git a/package-lock.json b/package-lock.json index bd28517..1d64e2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,14 +11,14 @@ "./projects/*" ], "dependencies": { - "@angular/animations": "^18.2.2", - "@angular/common": "^18.2.2", - "@angular/compiler": "^18.2.2", - "@angular/core": "^18.2.2", - "@angular/forms": "^18.2.2", - "@angular/platform-browser": "^18.2.2", - "@angular/platform-browser-dynamic": "^18.2.2", - "@angular/router": "^18.2.2", + "@angular/animations": "^18.2.4", + "@angular/common": "^18.2.4", + "@angular/compiler": "^18.2.4", + "@angular/core": "^18.2.4", + "@angular/forms": "^18.2.4", + "@angular/platform-browser": "^18.2.4", + "@angular/platform-browser-dynamic": "^18.2.4", + "@angular/router": "^18.2.4", "@ng-doc/app": "17.5.5", "@ng-doc/builder": "17.5.5", "@ng-doc/core": "17.5.5", @@ -28,9 +28,9 @@ "zone.js": "~0.14.10" }, "devDependencies": { - "@angular-devkit/build-angular": "^18.2.2", - "@angular/cli": "~18.2.2", - "@angular/compiler-cli": "^18.2.2", + "@angular-devkit/build-angular": "^18.2.4", + "@angular/cli": "~18.2.4", + "@angular/compiler-cli": "^18.2.4", "@types/jasmine": "~4.3.0", "cspell": "^8.3.2", "eslint": "^8.57.0", @@ -65,11 +65,11 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1802.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.2.tgz", - "integrity": "sha512-LPRl9jhcf0NgshaL6RoUy1uL/cAyNt7oxctoZ9EHUu8eh5E9W/jZGhVowjOLpirwqYhmEzKJJIeS49Ssqs3RQg==", + "version": "0.1802.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.4.tgz", + "integrity": "sha512-VH7AwGng1zuWPTJoH1IgHYeNhqZIgzlwDx39JPmArZAW/WZHDILWB7ipbTNw0R4U4VncrXJqDmMVex7NdHP6sg==", "dependencies": { - "@angular-devkit/core": "18.2.2", + "@angular-devkit/core": "18.2.4", "rxjs": "7.8.1" }, "engines": { @@ -79,15 +79,15 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.2.tgz", - "integrity": "sha512-7HEnTN2T1jnjuItXKcApOsoYGgfou4+POju3ZbwIQukDZ3B2COskvQkVTxqPNrQ0ZjT2mxZYoVlmGW9M+7N25g==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.4.tgz", + "integrity": "sha512-zLDstS95Yb7iBA1ZCYe3LsOLpIhr0ZC3sZ03PhVvAGbVRGSbQNnhQRZLKMk+LDhYJiG+eNFQGLfU3RfZrGds7A==", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.2", - "@angular-devkit/build-webpack": "0.1802.2", - "@angular-devkit/core": "18.2.2", - "@angular/build": "18.2.2", + "@angular-devkit/architect": "0.1802.4", + "@angular-devkit/build-webpack": "0.1802.4", + "@angular-devkit/core": "18.2.4", + "@angular/build": "18.2.4", "@babel/core": "7.25.2", "@babel/generator": "7.25.0", "@babel/helper-annotate-as-pure": "7.24.7", @@ -98,7 +98,7 @@ "@babel/preset-env": "7.25.3", "@babel/runtime": "7.25.0", "@discoveryjs/json-ext": "0.6.1", - "@ngtools/webpack": "18.2.2", + "@ngtools/webpack": "18.2.4", "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", "autoprefixer": "10.4.20", @@ -141,7 +141,7 @@ "vite": "5.4.0", "watchpack": "2.4.1", "webpack": "5.94.0", - "webpack-dev-middleware": "7.3.0", + "webpack-dev-middleware": "7.4.2", "webpack-dev-server": "5.0.4", "webpack-merge": "6.0.1", "webpack-subresource-integrity": "5.1.0" @@ -632,11 +632,11 @@ "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1802.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.2.tgz", - "integrity": "sha512-Pj+YmKh0nJOKl6QAsqYh3SqfuVJrFqjyp5WrG9BgfsMD9GCMD+5teMHNYJlp+vG/C8e7VdZp4rqOon8K9Xn4Mw==", + "version": "0.1802.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.4.tgz", + "integrity": "sha512-juaDoguYccObm2xnzRDRlOtiL7ZyZcSAyiyls6QuO8hoo/h6phdHALJkUhI9+SIhCRQ6eUQtolC7hN3J+FZKnA==", "dependencies": { - "@angular-devkit/architect": "0.1802.2", + "@angular-devkit/architect": "0.1802.4", "rxjs": "7.8.1" }, "engines": { @@ -650,9 +650,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.2.tgz", - "integrity": "sha512-Zz0tGptI/QQnUBDdp+1G5wGwQWMjpfe2oO+UohkrDVgFS71yVj4VDnOy51kMTxBvzw+36evTgthPpmzqPIfxBw==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.4.tgz", + "integrity": "sha512-svlgZ0vbLrfNJAQE5WePAutcYIyA7C0OfzKSTMsfV2X1I+1blYDaZIu/ocnHqofMHu6ZqdSaaU/p/rieqU8fcA==", "dependencies": { "ajv": "8.17.1", "ajv-formats": "3.0.1", @@ -703,11 +703,11 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.2.tgz", - "integrity": "sha512-PU6+3nX+gQ3gofR7BGwXuvNUNeeV2raURaZjlPfGpBqjyTBxukMV71QsTTWptAZT4WibCWkTFp6X1gvsOGbjMg==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.4.tgz", + "integrity": "sha512-s2WdUhyLlKj5kOjb6vrvJg9/31KvgyRJGjy7PnzS43tpwF9MLuM3AYhuJsXHPhx+i0nyWn/Jnd8ZLjMzXljSxg==", "dependencies": { - "@angular-devkit/core": "18.2.2", + "@angular-devkit/core": "18.2.4", "jsonc-parser": "3.3.1", "magic-string": "0.30.11", "ora": "5.4.1", @@ -753,9 +753,9 @@ } }, "node_modules/@angular/animations": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.2.tgz", - "integrity": "sha512-jh/dGrY77HGm54HdTiQsxmvoRfFeJgHeWAK2+nWCPoc4b7OHcWxy/04cYffs0/27ThmABmppP7ERAyZ0f60uow==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.4.tgz", + "integrity": "sha512-ajjXpLD+SyxbHnmhj2ZvYpXneviOjcBgU9L2UX4OVS0jVWxCNHLhJrcMqtqFHA6U5fPnhPNR9cmnt6tmqri0rA==", "dependencies": { "tslib": "^2.3.0" }, @@ -763,16 +763,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.2" + "@angular/core": "18.2.4" } }, "node_modules/@angular/build": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.2.tgz", - "integrity": "sha512-okaDdTMXnDhvnnnih6rPQnexL6htfEAPr19bB1Ci9d31gEjVuKZCjlcw2sPZ6BUyilwC9nZlCI5vbH1Ljf6mzA==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.4.tgz", + "integrity": "sha512-GVs7O7wxNMJCkqh6Vv2u9GEArWg9jyEt8Fofd6CJGzxKBYQ4hR5gjzL/lU6kNFiMcioS1wm1f6qtJtgilUO+9A==", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.2", + "@angular-devkit/architect": "0.1802.4", "@babel/core": "7.25.2", "@babel/helper-annotate-as-pure": "7.24.7", "@babel/helper-split-export-declaration": "7.24.7", @@ -1256,17 +1256,17 @@ } }, "node_modules/@angular/cli": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.2.tgz", - "integrity": "sha512-HVVaMxnbID0q+V3KE+JqzGbPHcBUFo1RKhBZ/jxY7USZNzgtyYbRc0IYqPWNdr99UT5QefTJrjVazJo1nqQZvQ==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.4.tgz", + "integrity": "sha512-n+Y2xlgcpTZ+MZmycf2b3ceVvANDJFkDEodobVtyG63WvGOhkZ3aGhT7sHguKpAQwJLicSf8zF2z+v1Yi0DvRw==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1802.2", - "@angular-devkit/core": "18.2.2", - "@angular-devkit/schematics": "18.2.2", + "@angular-devkit/architect": "0.1802.4", + "@angular-devkit/core": "18.2.4", + "@angular-devkit/schematics": "18.2.4", "@inquirer/prompts": "5.3.8", "@listr2/prompt-adapter-inquirer": "2.0.15", - "@schematics/angular": "18.2.2", + "@schematics/angular": "18.2.4", "@yarnpkg/lockfile": "1.1.0", "ini": "4.1.3", "jsonc-parser": "3.3.1", @@ -1310,9 +1310,9 @@ } }, "node_modules/@angular/common": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.2.tgz", - "integrity": "sha512-AQe4xnnNNch/sXRnV82C8FmhijxPATKfPGojC2qbAG2o6VkWKgt5Lbj0O8WxvSIOS5Syedv+O2kLY/JMGWHNtw==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.4.tgz", + "integrity": "sha512-flUaKhdr8KvtjH0cLC6Rrjirt8GsiFlrmZLZplr784O3Gkei2VBBNFoopgmlEzbVGPiIG5QlFZH9yvah6JPQZw==", "dependencies": { "tslib": "^2.3.0" }, @@ -1320,14 +1320,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.2", + "@angular/core": "18.2.4", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.2.tgz", - "integrity": "sha512-gmVNCXZiv/CIk2eKRLnH19N9VsPuE2s3Oxm0MNi003zk1cLy7D4YEm4fSrjKXtPY8MMpRXiu5f63W94hLwWEVw==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.4.tgz", + "integrity": "sha512-o3ngFr1Bjt7cKOu4DSZJGCUF9YPTxJee97wFon2eNFj6FFNTmnGwAvsnJjHBMmk90fmZLC2/HpPdhYz7WprMZQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -1335,7 +1335,7 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.2" + "@angular/core": "18.2.4" }, "peerDependenciesMeta": { "@angular/core": { @@ -1344,9 +1344,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.2.tgz", - "integrity": "sha512-fF7lDrTA12YGqVjF4LyMi4hm58cv9G6CWmzSlvun0nMYCwrbRNnakZsj19dOfiIqqu4MwHaF4w3PTmUSxkMuiw==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.4.tgz", + "integrity": "sha512-BIp5zr+npqSs/4KWPxwKdn7+sjo008ieNOQDlXyQms9BKlxx/gDnj7W2TsxhrkDTYCIHF73fJZ7u2U8Qy4JWfw==", "dependencies": { "@babel/core": "7.25.2", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -1366,14 +1366,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.2.2", + "@angular/compiler": "18.2.4", "typescript": ">=5.4 <5.6" } }, "node_modules/@angular/core": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.2.tgz", - "integrity": "sha512-Rx6XajL0Ydj9hXUSPDvL2Q/kMzWtbiE3VxZFJnkE+fLQiWvr0GncB+NTb/nQ6QlPQ0ly60DvuI3KLcGDuFtGVA==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.4.tgz", + "integrity": "sha512-ulYmYpI/ZVQ5BL38rBy4DS/9wgGWmVD9Uo6tcrLqCzt1G1G2nKwseZv009536pHfk6dj2HdPSkpcerhWh57DWw==", "dependencies": { "tslib": "^2.3.0" }, @@ -1386,9 +1386,9 @@ } }, "node_modules/@angular/forms": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.2.tgz", - "integrity": "sha512-K8cv0w6o7+ocQfUrdSA3XaKrYfa1+2TlmtyxPHjEd2mCu2R+Yqo5RqJ3P8keFewJ1+bSLhz6xnn6mumwl0RnUQ==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.4.tgz", + "integrity": "sha512-rlLhReauUz6jhLCEkGabLqqF6xLaTfvxafuj2ojzcqoKGSZcXDIM/UOSoWX756B8NtrpsuglpGBZjZlsrAZGsg==", "dependencies": { "tslib": "^2.3.0" }, @@ -1396,9 +1396,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.2", - "@angular/core": "18.2.2", - "@angular/platform-browser": "18.2.2", + "@angular/common": "18.2.4", + "@angular/core": "18.2.4", + "@angular/platform-browser": "18.2.4", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -1434,9 +1434,9 @@ } }, "node_modules/@angular/platform-browser": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.2.tgz", - "integrity": "sha512-Bfvl8elCFxyJ9vlwamr4X5sVMcp/tSwBal2coyl0WR+/PH2PAAtf+/WMYxIN90yZmPiJx6RZWUSJRlHOFiFp3A==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.4.tgz", + "integrity": "sha512-ddzq5MyPvFyTv0kUe8U9fbhE1nZtLYBCFKDqICrzHXiVRAdqLv6qtE5PtbURrdspSy1u/YEGV4LdkNJK3UgnZQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -1444,9 +1444,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/animations": "18.2.2", - "@angular/common": "18.2.2", - "@angular/core": "18.2.2" + "@angular/animations": "18.2.4", + "@angular/common": "18.2.4", + "@angular/core": "18.2.4" }, "peerDependenciesMeta": { "@angular/animations": { @@ -1455,9 +1455,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.2.tgz", - "integrity": "sha512-UM/+1nY4iIj1v4lxAmV3XRHPAh/4qfNKScCLq8tJGot64rPCbtCl0Rl8rFFGqxAFvTErVDaJycUgWNZSfVl/hw==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.4.tgz", + "integrity": "sha512-0nA04zJueGzdnl37TJ7guDCrzxYS4fjqgvYKiOpFktpMHPuNrBlAQo5YA7u20HGFG3i47PQh7hEWhQaqcXXpQw==", "dependencies": { "tslib": "^2.3.0" }, @@ -1465,16 +1465,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.2", - "@angular/compiler": "18.2.2", - "@angular/core": "18.2.2", - "@angular/platform-browser": "18.2.2" + "@angular/common": "18.2.4", + "@angular/compiler": "18.2.4", + "@angular/core": "18.2.4", + "@angular/platform-browser": "18.2.4" } }, "node_modules/@angular/router": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.2.tgz", - "integrity": "sha512-tBHwuNtZNjzYAoVdveTI1ke/ZnQjKhc7gqDk9HCH2JUpdQhGbTvCKwDM51ktJpPMPcZlA263lQyy7VIyvdtK0A==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.4.tgz", + "integrity": "sha512-kYNHD3K1Xou2PRMqbG2tVahtMobgDlhwHdMB7G5oFHg6K13gQ2TmopF1U5A2wYtIMdsC+AkVGIJEOxQN8fmgcA==", "dependencies": { "tslib": "^2.3.0" }, @@ -1482,9 +1482,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.2", - "@angular/core": "18.2.2", - "@angular/platform-browser": "18.2.2", + "@angular/common": "18.2.4", + "@angular/core": "18.2.4", + "@angular/platform-browser": "18.2.4", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -5281,9 +5281,9 @@ } }, "node_modules/@ng-select/ng-select": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-13.7.0.tgz", - "integrity": "sha512-GMNu3bLYxWAbgy9pXZ4RgnWp/cxRcrWRQdxLLyg8p9gMCLpim1p4TXR8laXJKK25MKG/LEaWgs+90yCVOoWgZA==", + "version": "13.7.1", + "resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-13.7.1.tgz", + "integrity": "sha512-v/GwSBpuHd31DyoYFQECh+rCwn7xmCBpwMQTcwWerKaDQSr1egpGPSnCq2SzvfHqiJ5e1ckx7ZNTuk+swBweag==", "dependencies": { "tslib": "^2.3.1" }, @@ -5325,9 +5325,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.2.tgz", - "integrity": "sha512-YhADmc+lVjLt3kze07A+yLry2yzcghdclu+7D3EDfa6fG2Pk33HK3MY2I0Z0BO+Ivoq7cV7yxm+naR+Od0Y5ng==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.4.tgz", + "integrity": "sha512-JVDRexu3q7lg2oqJG36RtX7cqTheoZRwg2HhMV8hYXUDL0fyOrv2galwTCgXrx7vAjlx45L2uR2kuWbgW0VVcQ==", "engines": { "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", @@ -6010,13 +6010,13 @@ } }, "node_modules/@schematics/angular": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.2.tgz", - "integrity": "sha512-0uPA1kQ38RnbNrzMlveX/QAqQIDu2INl5IYd3EUbJZRfYSp1VVyOSyuIBJ+1iUl5Y5VUa2uylaVZXhFdKWprXw==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.4.tgz", + "integrity": "sha512-GxrPv4eOPrjMKoAVhch9eprW8H/DFhBy5Zgp7CgGui9NprYkkubxw/yyo11WfR5CFZ/q5AfsjV76dPCkhLwLmA==", "dev": true, "dependencies": { - "@angular-devkit/core": "18.2.2", - "@angular-devkit/schematics": "18.2.2", + "@angular-devkit/core": "18.2.4", + "@angular-devkit/schematics": "18.2.4", "jsonc-parser": "3.3.1" }, "engines": { @@ -6412,9 +6412,9 @@ "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==" }, "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==" }, "node_modules/@types/range-parser": { "version": "1.2.7", @@ -7503,8 +7503,9 @@ } }, "node_modules/body-parser": { - "version": "1.20.2", - "license": "MIT", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -7514,7 +7515,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -10133,36 +10134,36 @@ "dev": true }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -10189,13 +10190,21 @@ "ms": "2.0.0" } }, + "node_modules/express/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/express/node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -12602,9 +12611,9 @@ } }, "node_modules/launch-editor": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.2.tgz", - "integrity": "sha512-eF5slEUZXmi6WvFzI3dYcv+hA24/iKnROf24HztcURJpSz9RBmBgz5cNCVOeguouf1llrwy6Yctl4C4HM+xI8g==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", + "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", "dependencies": { "picocolors": "^1.0.0", "shell-quote": "^1.8.1" @@ -13347,9 +13356,12 @@ } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -15185,9 +15197,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "node_modules/path-type": { "version": "4.0.0", @@ -15672,10 +15684,11 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "license": "BSD-3-Clause", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -15763,9 +15776,9 @@ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "dependencies": { "regenerate": "^1.4.2" }, @@ -16777,9 +16790,9 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -16907,19 +16920,27 @@ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/serve-static/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -16989,12 +17010,17 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "license": "MIT", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -17998,9 +18024,9 @@ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "engines": { "node": ">=4" } @@ -18018,9 +18044,9 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", "engines": { "node": ">=4" } @@ -18531,9 +18557,9 @@ } }, "node_modules/webpack-dev-middleware": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.3.0.tgz", - "integrity": "sha512-xD2qnNew+F6KwOGZR7kWdbIou/ud7cVqLEXeK1q0nHcNsX/u7ul/fSdlOTX4ntSL5FNFy7ZJJXbf0piF591JYw==", + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", + "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==", "dependencies": { "colorette": "^2.0.10", "memfs": "^4.6.0", diff --git a/package.json b/package.json index a6f4561..c551320 100644 --- a/package.json +++ b/package.json @@ -26,14 +26,14 @@ "url": "https://github.com/valitydev/ng-libs" }, "dependencies": { - "@angular/animations": "^18.2.2", - "@angular/common": "^18.2.2", - "@angular/compiler": "^18.2.2", - "@angular/core": "^18.2.2", - "@angular/forms": "^18.2.2", - "@angular/platform-browser": "^18.2.2", - "@angular/platform-browser-dynamic": "^18.2.2", - "@angular/router": "^18.2.2", + "@angular/animations": "^18.2.4", + "@angular/common": "^18.2.4", + "@angular/compiler": "^18.2.4", + "@angular/core": "^18.2.4", + "@angular/forms": "^18.2.4", + "@angular/platform-browser": "^18.2.4", + "@angular/platform-browser-dynamic": "^18.2.4", + "@angular/router": "^18.2.4", "@ng-doc/app": "17.5.5", "@ng-doc/builder": "17.5.5", "@ng-doc/core": "17.5.5", @@ -43,9 +43,9 @@ "zone.js": "~0.14.10" }, "devDependencies": { - "@angular-devkit/build-angular": "^18.2.2", - "@angular/cli": "~18.2.2", - "@angular/compiler-cli": "^18.2.2", + "@angular-devkit/build-angular": "^18.2.4", + "@angular/cli": "~18.2.4", + "@angular/compiler-cli": "^18.2.4", "@types/jasmine": "~4.3.0", "cspell": "^8.3.2", "eslint": "^8.57.0", diff --git a/projects/ng-core/src/lib/components/select-autocomplete/select-field/select-field.component.html b/projects/ng-core/src/lib/components/select-autocomplete/select-field/select-field.component.html index bdaed94..dd85c78 100644 --- a/projects/ng-core/src/lib/components/select-autocomplete/select-field/select-field.component.html +++ b/projects/ng-core/src/lib/components/select-autocomplete/select-field/select-field.component.html @@ -1,4 +1,4 @@ - + {{ label }} file_save - @if (!standaloneFilter() && false) { + @if (!standaloneFilter()) {
(undefined); - filteredCount = input(0); - selectedCount = input(0); + filteredCount = input(0); + selectedCount = input(0); filter = input(''); standaloneFilter = input(false, { transform: booleanAttribute }); @@ -59,10 +59,12 @@ export class TableInfoBarComponent implements OnInit { countText = computed(() => this.count() - ? (this.filteredCount() ? this.filteredCount() + '/' : '') + + ? (this.filter() && this.filteredCount() !== this.count() + ? this.filteredCount() + '/' + : '') + (this.hasMore() ? '>' : '') + this.count() - : this.progress() + : this.progress() || this.count() !== 0 ? '...' : '0', ); diff --git a/projects/ng-core/src/lib/components/table/components/table2/table2.component.html b/projects/ng-core/src/lib/components/table/components/table2/table2.component.html index 3885fec..3d1e3df 100644 --- a/projects/ng-core/src/lib/components/table/components/table2/table2.component.html +++ b/projects/ng-core/src/lib/components/table/components/table2/table2.component.html @@ -2,9 +2,9 @@
- + - +
@if (rowSelectable()) { - @let cell = columnsData?.[rowIndex]?.[colIndex]; - @let nextCell = columnsData?.[rowIndex + 1]?.[colIndex]; + @let cell = columnsData?.get?.(element)?.[colIndex]; } @@ -74,8 +73,8 @@ implements OnIn filterChange = output(); externalFilter = input(false, { transform: booleanAttribute }); filter$ = new BehaviorSubject(''); + filteredData$ = new BehaviorSubject>([]); // Select rowSelectable = input(false, { transform: booleanAttribute }); @@ -108,22 +118,33 @@ export class Table2Component implements OnIn rowSelectedChange = output(); selected = signal([]); + // Sort + @ViewChild(MatSort) sortComponent!: MatSort; + update = output(); more = output(); isTreeData = computed(() => !!this.treeData()); - treeInlineData = computed>(() => - this.isTreeData() - ? (this.treeData() ?? []).flatMap((d) => { - const children = d.children ?? []; - return [ - children.length ? { value: d.value, child: children[0] } : { value: d.value }, - ...children.slice(1).map((child) => ({ child })), - ]; - }) - : [], + treeInlineData$: Observable> = toObservable(this.treeData).pipe( + cachedHeadMap((d) => { + const children = d.children ?? []; + return [ + children.length ? { value: d.value, child: children[0] } : { value: d.value }, + ...children.slice(1).map((child) => ({ child })), + ]; + }), + map((v) => v.flat()), + shareReplay({ refCount: true, bufferSize: 1 }), ); dataSource = new TableDataSource>(); + dataSourceData$: Observable> = combineLatest([ + this.treeInlineData$, + toObservable(this.data), + toObservable(this.isTreeData), + ]).pipe( + map(([treeData, data, isTreeData]) => (isTreeData ? treeData : data) ?? []), + shareReplay({ refCount: true, bufferSize: 1 }), + ); normColumns = computed[]>(() => this.columns().map((c) => new NormColumn(c))); displayedNormColumns$ = toObservable(this.normColumns).pipe( switchMap((cols) => @@ -133,58 +154,104 @@ export class Table2Component implements OnIn ), shareReplay({ refCount: true, bufferSize: 1 }), ); - columnsData$$: Observable<{ value: Observable; isChild?: boolean }[][]> = combineLatest([ + columnsData$$: Observable< + Map< + T | TreeInlineDataItem, + { value: Observable; isChild?: boolean; isNextChild?: boolean }[] + > + > = combineLatest([ toObservable(this.isTreeData), - toObservable(this.treeInlineData), - toObservable(this.data), + this.dataSourceData$, this.displayedNormColumns$, ]).pipe( - map(([isTree, inlineData, data, cols]) => { - if (isTree) { - return inlineData.map((d, idx) => - cols.map((c) => ({ - value: (d.child && c.child - ? c.child(d.child, idx) - : d.value - ? c.cell(d.value, idx) - : of({ value: '' }) - ).pipe(shareReplay({ refCount: true, bufferSize: 1, windowTime: 30_000 })), - isChild: !d.value, - })), - ); - } - return (data || []).map((d, idx) => - cols.map((c) => ({ - value: c - .cell(d, idx) - .pipe(shareReplay({ refCount: true, bufferSize: 1, windowTime: 30_000 })), - })), - ); - }), + scan( + (acc, [isTree, data, cols]) => { + const isColsNotChanged = acc.cols === cols; + return { + res: new Map< + TreeInlineDataItem | T, + { value: Observable; isChild?: boolean; isNextChild?: boolean }[] + >( + isTree + ? (data as TreeInlineData).map((d, idx) => [ + d, + isColsNotChanged && + d === acc.data[idx] && + // This is not the last value, because we need to calculate isNextChild + idx !== acc.data.length - 1 + ? (acc.res.get(d) as never) + : cols.map((c) => ({ + value: (d.child && c.child + ? c.child(d.child, idx) + : d.value + ? c.cell(d.value, idx) + : of({ value: '' }) + ).pipe( + shareReplay({ + refCount: true, + bufferSize: 1, + }), + ), + isChild: !d.value, + isNextChild: !(data as TreeInlineData)[idx + 1] + ?.value, + })), + ]) + : ((data as T[]) || []).map((d, idx) => [ + d, + isColsNotChanged && d === acc.data[idx] + ? (acc.res.get(d) as never) + : cols.map((c) => ({ + value: c.cell(d, idx).pipe( + shareReplay({ + refCount: true, + bufferSize: 1, + }), + ), + })), + ]), + ), + data: data, + cols: cols, + }; + }, + { data: [], cols: [], res: new Map() } as { + data: T[] | TreeInlineData; + cols: NormColumn[]; + res: Map< + T | TreeInlineDataItem, + { value: Observable; isChild?: boolean; isNextChild?: boolean }[] + >; + }, + ), + map((v) => v.res), shareReplay({ refCount: true, bufferSize: 1 }), ); columnsData$ = this.columnsData$$.pipe( - switchMap((d) => combineLatest(d.map((v) => combineLatest(v.map((v) => v.value))))), + switchMap((d) => + combineLatest(Array.from(d.values()).map((v) => combineLatest(v.map((v) => v.value)))), + ), shareReplay({ refCount: true, bufferSize: 1 }), ); isPreload = signal(false); loadSize = computed(() => (this.isPreload() ? this.maxSize() : this.size())); - count$ = this.columnsData$$.pipe( - map((d) => d?.length), + count$ = combineLatest([this.filter$, this.filteredData$, this.dataSourceData$]).pipe( + map(([filter, filtered, source]) => (filter ? filtered?.length : source?.length)), shareReplay({ refCount: true, bufferSize: 1 }), ); - dataSourceData = computed>(() => - this.isTreeData() ? this.treeInlineData() : this.data(), - ); - hasShowMore$ = combineLatest([ + hasAutoShowMore$ = combineLatest([ toObservable(this.hasMore), - toObservable(this.dataSourceData), + this.dataSourceData$, this.dataSource.paginator.page.pipe( startWith(null), map(() => this.dataSource.paginator.pageSize), ), + this.filteredData$, ]).pipe( - map(([hasMore, data, pageSize]) => hasMore || pageSize < data.length), + map( + ([hasMore, data, pageSize, filteredData]) => + (hasMore || pageSize < data.length) && filteredData === data, + ), shareReplay({ refCount: true, bufferSize: 1 }), ); @@ -199,42 +266,60 @@ export class Table2Component implements OnIn ); columnDefs = COLUMN_DEFS; - scrollViewport = viewChild('scrollViewport', { read: ElementRef }); + @ViewChild('scrollViewport', { read: ElementRef }) scrollViewport!: ElementRef; + @ViewChild('matTable', { static: false }) table!: MatTable; constructor( private dr: DestroyRef, private injector: Injector, ) { - effect( - () => { - this.dataSource.data = this.dataSourceData(); - }, - { - // TODO: not a necessary line, but after adding viewChild signal requires - allowSignalWrites: true, - }, - ); effect( () => { this.selected.set(this.rowSelected()); }, { allowSignalWrites: true }, ); - effect(() => { - this.filter$.next(this.filter()); - }); } ngOnInit() { - this.filter$ + this.dataSourceData$.pipe(takeUntilDestroyed(this.dr)).subscribe((data) => { + this.dataSource.data = data; + }); + const filter$ = this.filter$.pipe( + map((filter) => filter?.trim?.() ?? ''), + distinctUntilChanged(), + debounceTime(500), + share(), + ); + toObservable(this.filter, { injector: this.injector }) + .pipe(takeUntilDestroyed(this.dr)) + .subscribe((filter) => { + this.filter$.next(filter); + }); + filter$.pipe(takeUntilDestroyed(this.dr)).subscribe((filter) => { + this.filterChange.emit(filter); + }); + combineLatest([filter$, this.dataSourceData$]) .pipe( - map((filter) => filter?.trim?.() ?? ''), - distinctUntilChanged(), - debounceTime(500), + map(([f, v]) => { + return f ? v.filter((i) => JSON.stringify(i).toLowerCase().includes(f)) : v; + }), takeUntilDestroyed(this.dr), ) - .subscribe((filter) => { - this.filterChange.emit(filter); + .subscribe((filtered) => { + this.filteredData$.next(filtered); + this.updateSortFilter(filtered); + }); + // TODO: 2, 3 column is torn away from the previous one, fixed by calling update + this.dataSourceData$ + .pipe( + filter((v) => !!v?.length), + first(), + delay(100), + takeUntilDestroyed(this.dr), + ) + .subscribe(() => { + this.table.updateStickyColumnStyles(); }); } @@ -286,8 +371,13 @@ export class Table2Component implements OnIn } private reload() { - this.scrollViewport()?.nativeElement?.scrollTo?.(0, 0); + this.scrollViewport?.nativeElement?.scrollTo?.(0, 0); this.update.emit({ size: this.loadSize() }); this.dataSource.paginator.reload(); } + + private updateSortFilter(filtered: TreeInlineData | T[]) { + this.dataSource.sortData = filtered ? () => filtered : sortDataByDefault; + this.dataSource.sort = this.sortComponent; + } } diff --git a/projects/ng-core/src/lib/components/table/index.ts b/projects/ng-core/src/lib/components/table/index.ts index cd3dced..d3734dc 100644 --- a/projects/ng-core/src/lib/components/table/index.ts +++ b/projects/ng-core/src/lib/components/table/index.ts @@ -3,6 +3,7 @@ export * from './table.module'; export * from './utils/create-columns-objects'; export * from './utils/correct-priorities'; export * from './utils/create-column'; +export * from './utils/cached-head-map'; export * from './components/table2'; export * from './components/table-actions.component'; export * from './components/table-inputs.component'; diff --git a/projects/ng-core/src/lib/components/table/utils/cached-head-map.ts b/projects/ng-core/src/lib/components/table/utils/cached-head-map.ts new file mode 100644 index 0000000..3dbb4a5 --- /dev/null +++ b/projects/ng-core/src/lib/components/table/utils/cached-head-map.ts @@ -0,0 +1,21 @@ +import { Observable, scan } from 'rxjs'; +import { map } from 'rxjs/operators'; + +export function cachedHeadMap(fn: (el: T, idx: number) => R) { + return (src$: Observable) => { + return src$.pipe( + scan( + (acc, v) => { + return { + prev: v ?? [], + res: (v ?? []).map((el, idx) => + acc.prev[idx] === el ? acc.res[idx] : fn(el, idx), + ), + }; + }, + { prev: [], res: [] } as { prev: T[]; res: R[] }, + ), + map(({ res }) => res), + ); + }; +} diff --git a/projects/ng-core/src/lib/components/value/utils/create-input-subject.ts b/projects/ng-core/src/lib/components/value/utils/create-input-subject.ts new file mode 100644 index 0000000..4b2c478 --- /dev/null +++ b/projects/ng-core/src/lib/components/value/utils/create-input-subject.ts @@ -0,0 +1,18 @@ +import { BehaviorSubject } from 'rxjs'; + +export function createInputSubject, P extends string, V>( + target: T, + propertyKey: P, + _initValue: V, +): BehaviorSubject { + const sub$ = new BehaviorSubject(target[propertyKey]); + Object.defineProperty(target, propertyKey, { + get: function () { + return sub$.value; + }, + set: function (v) { + sub$.next(v); + }, + }); + return sub$ as never; +} diff --git a/projects/ng-core/src/lib/components/value/utils/currency-amount-value-to-string.ts b/projects/ng-core/src/lib/components/value/utils/currency-amount-value-to-string.ts index 4c2e781..4cc7884 100644 --- a/projects/ng-core/src/lib/components/value/utils/currency-amount-value-to-string.ts +++ b/projects/ng-core/src/lib/components/value/utils/currency-amount-value-to-string.ts @@ -10,7 +10,7 @@ export type CurrencyAmountValue = TypedParamsValue< >; export function currencyAmountValueToString(value: CurrencyAmountValue) { - if (!value?.value) { + if (typeof value?.value !== 'number') { return ''; } const locale = inject(LOCALE_ID); diff --git a/projects/ng-core/src/lib/components/value/value.component.html b/projects/ng-core/src/lib/components/value/value.component.html index 33fe39e..51ac3e5 100644 --- a/projects/ng-core/src/lib/components/value/value.component.html +++ b/projects/ng-core/src/lib/components/value/value.component.html @@ -1,8 +1,8 @@ -@if (progress()) { +@if (progress || !(resValue$ | async)) { } @else { @if (resValue$ | async; as v) { - @if (isLoaded()) { + @if (isLoaded$ | async) { @if (v.template) { } @else { @@ -40,8 +40,8 @@ > {{ renderedValue }} - } @else if (emptySymbol()) { - {{ emptySymbol() }} + } @else if (emptySymbol) { + {{ emptySymbol }} } } } @@ -58,7 +58,5 @@ sync } - } @else { - } } diff --git a/projects/ng-core/src/lib/components/value/value.component.ts b/projects/ng-core/src/lib/components/value/value.component.ts index 343665d..ee1926b 100644 --- a/projects/ng-core/src/lib/components/value/value.component.ts +++ b/projects/ng-core/src/lib/components/value/value.component.ts @@ -1,27 +1,26 @@ import { CommonModule } from '@angular/common'; import { Component, - input, - computed, - signal, - output, booleanAttribute, runInInjectionContext, Injector, ChangeDetectionStrategy, + Input, + Output, + EventEmitter, } from '@angular/core'; -import { toObservable } from '@angular/core/rxjs-interop'; import { MatIconButton } from '@angular/material/button'; import { MatIcon } from '@angular/material/icon'; import { MatTooltip } from '@angular/material/tooltip'; -import { Observable, combineLatest, switchMap, of, isObservable } from 'rxjs'; -import { map, shareReplay } from 'rxjs/operators'; +import { combineLatest, switchMap, of, isObservable, BehaviorSubject, Observable } from 'rxjs'; +import { map, shareReplay, distinctUntilChanged } from 'rxjs/operators'; import { ContentLoadingComponent } from '../content-loading'; import { TagModule } from '../tag'; import { MenuValueComponent } from './components/menu-value.component'; import { Value } from './types/value'; +import { createInputSubject } from './utils/create-input-subject'; import { valueToString } from './utils/value-to-string'; @Component({ @@ -39,29 +38,39 @@ import { valueToString } from './utils/value-to-string'; templateUrl: './value.component.html', styleUrl: './value.component.scss', host: { - '[class.inline]': 'inline()', + '[class.inline]': 'inline', }, changeDetection: ChangeDetectionStrategy.OnPush, }) export class ValueComponent { - value = input(); - lazyValue = input | null>(); - resultingValue = input(); - progress = input(false, { transform: booleanAttribute }); - inline = input(false, { transform: booleanAttribute }); - emptySymbol = input(undefined, { - transform: (v) => (v === true || v === '' ? '―' : typeof v === 'string' ? v : ''), - }); + @Input() value: Value | null = null; + value$ = createInputSubject(this, 'value', this.value); - lazyVisibleChange = output(); + @Input() lazyValue: Observable | undefined = undefined; + lazyValue$ = createInputSubject(this, 'lazyValue', this.lazyValue); - lazyVisible = signal(false); - isLoaded = computed(() => !this.lazyValue() || this.lazyVisible()); - resValue$ = combineLatest([toObservable(this.value), toObservable(this.lazyValue)]).pipe( + @Input({ transform: booleanAttribute }) progress = false; + @Input({ transform: booleanAttribute }) inline = false; + @Input({ + transform: (v: unknown): string => (v === false ? '' : typeof v === 'string' ? v : '―'), + }) + emptySymbol = '―'; + + resultingValue$ = of(null); + + @Output() lazyVisibleChange = new EventEmitter(); + + lazyVisible = new BehaviorSubject(false); + isLoaded$ = combineLatest([of(null), this.lazyVisible]).pipe( + map(([lazyValue, lazyVisible]) => !lazyValue || lazyVisible), + distinctUntilChanged(), + shareReplay({ refCount: true, bufferSize: 1 }), + ); + resValue$ = combineLatest([this.value$, this.lazyValue$]).pipe( switchMap(([value, lazyValue]) => (isObservable(lazyValue) ? lazyValue : of(value))), shareReplay({ refCount: true, bufferSize: 1 }), ); - renderedValue$ = combineLatest([toObservable(this.resultingValue), this.resValue$]).pipe( + renderedValue$ = combineLatest([this.resultingValue$, this.resValue$]).pipe( map( ([resultingValue, resValue]) => resultingValue ?? @@ -73,19 +82,17 @@ export class ValueComponent { constructor(private injector: Injector) {} toggleLazyVisible() { - this.lazyVisible.set(true); - this.lazyVisibleChange.emit(this.lazyVisible()); + this.lazyVisible.next(true); + this.lazyVisibleChange.emit(true); } click(event: MouseEvent) { - if (this.value()?.click) { - runInInjectionContext(this.injector, () => { - this.value()?.click?.(event); - }); - } else if (typeof this.value()?.link === 'function') { - runInInjectionContext(this.injector, () => { - this.value()?.link?.(event); - }); - } + runInInjectionContext(this.injector, () => { + if (this.value?.click) { + this.value.click(event); + } else if (typeof this.value?.link === 'function') { + this.value.link(event); + } + }); } } diff --git a/projects/ng-core/src/lib/services/url.service.ts b/projects/ng-core/src/lib/services/url.service.ts index e742ac0..790cbbb 100644 --- a/projects/ng-core/src/lib/services/url.service.ts +++ b/projects/ng-core/src/lib/services/url.service.ts @@ -19,7 +19,7 @@ export class UrlService { ); get url() { - return this.router.url + return this.router.url && this.router.url !== '/' ? this.router.url.split('?', 1)[0].split('#', 1)[0] : window.location.pathname; } diff --git a/projects/ng-core/src/lib/utils/currency/format-currency.ts b/projects/ng-core/src/lib/utils/currency/format-currency.ts index 66c5438..2c2f49c 100644 --- a/projects/ng-core/src/lib/utils/currency/format-currency.ts +++ b/projects/ng-core/src/lib/utils/currency/format-currency.ts @@ -11,7 +11,7 @@ export function formatCurrency( exponent?: number, isMajor: boolean = false, ): string { - if (!exponent) { + if (typeof exponent !== 'number') { exponent = getCurrencyExponent(currencyCode) || 0; } return ngFormatCurrency(
- + - +