Optimize new table (#68)
Some checks failed
Main / Publish (push) Has been cancelled

This commit is contained in:
Rinat Arsaev 2024-09-20 13:15:57 +05:00 committed by GitHub
parent ecbe0eae42
commit 84ed598f27
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 458 additions and 295 deletions

344
package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -1,4 +1,4 @@
<mat-form-field [appearance]="appearance" [ngClass]="{ 'v-field__small': size === 'small' }">
<mat-form-field [appearance]="appearance" [ngClass]="{ 'v-input-field__small': size === 'small' }">
<mat-label>{{ label }}</mat-label>
<mtx-select

View File

@ -56,7 +56,7 @@
<mat-icon>file_save</mat-icon>
</button>
</div>
@if (!standaloneFilter() && false) {
@if (!standaloneFilter()) {
<div>
<v-input-field
[formControl]="filterControl"

View File

@ -45,8 +45,8 @@ export class TableInfoBarComponent implements OnInit {
preloadSize = input(0, { transform: numberAttribute });
count = input<number | undefined | null>(undefined);
filteredCount = input<number | undefined>(0);
selectedCount = input<number | undefined>(0);
filteredCount = input<number | undefined | null>(0);
selectedCount = input<number | undefined | null>(0);
filter = input<string>('');
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',
);

View File

@ -2,9 +2,9 @@
<div class="wrapper">
<v-table-info-bar
[count]="dataSourceData().length"
[count]="(dataSourceData$ | async)?.length"
[filter]="(filter$ | async) ?? ''"
[filteredCount]="0"
[filteredCount]="count$ | async"
[hasMore]="hasMore()"
[isPreload]="isPreload()"
[preloadSize]="maxSize()"
@ -20,9 +20,9 @@
<ng-content select="v-table-actions"></ng-content>
</v-table-info-bar>
<mat-card #scrollViewport class="card">
<v-table-progress-bar [progress]="progress()"></v-table-progress-bar>
<!-- <v-table-progress-bar [progress]="progress()"></v-table-progress-bar>-->
<v-no-records [noRecords]="!(count$ | async)" [progress]="progress()"></v-no-records>
<table [dataSource]="dataSource" mat-table>
<table #matTable [dataSource]="dataSource" mat-table matSort>
@if (rowSelectable()) {
<v-select-column
[data]="data()"
@ -47,15 +47,14 @@
[stickyEnd]="stickyEnd"
>
<th *matHeaderCellDef [ngClass]="columnClasses" mat-header-cell>
<v-value [value]="col?.header | async" inline></v-value>
<v-value [value]="col?.header | async" emptySymbol="" inline></v-value>
</th>
<ng-template let-element let-rowIndex="index" matCellDef>
@let cell = columnsData?.[rowIndex]?.[colIndex];
@let nextCell = columnsData?.[rowIndex + 1]?.[colIndex];
@let cell = columnsData?.get?.(element)?.[colIndex];
<td
[ngClass]="columnClasses"
[ngStyle]="col?.params?.style"
[style.border-bottom]="nextCell?.isChild && !col.child ? 'none' : ''"
[style.border-bottom]="cell?.isNextChild && !col.child ? 'none' : ''"
mat-cell
>
<v-value
@ -66,7 +65,7 @@
</td>
</ng-template>
<td *matFooterCellDef [ngClass]="columnClasses" mat-footer-cell>
<v-value [progress]="true" inline></v-value>
<v-content-loading></v-content-loading>
</td>
</ng-container>
}
@ -74,8 +73,8 @@
<tr *matHeaderRowDef="displayedColumns$ | async; sticky: true" mat-header-row></tr>
<tr *matRowDef="let row; columns: displayedColumns$ | async" mat-row></tr>
<tr
*matFooterRowDef="(hasShowMore$ | async) ? (displayedColumns$ | async) : []"
[ngClass]="{ row__hidden: !(hasShowMore$ | async) }"
*matFooterRowDef="(hasAutoShowMore$ | async) ? (displayedColumns$ | async) : []"
[ngClass]="{ row__hidden: !(hasAutoShowMore$ | async) }"
[vInfinityScrollProgress]="progress()"
mat-row
vInfinityScroll

View File

@ -12,6 +12,7 @@
width: 100%;
height: 100%;
overflow: auto;
transform: translateZ(0);
::ng-deep .mdc-data-table__row:last-child .mat-mdc-cell {
border-bottom-color: var(

View File

@ -11,16 +11,17 @@ import {
signal,
output,
Injector,
viewChild,
ElementRef,
runInInjectionContext,
OnInit,
ViewChild,
} from '@angular/core';
import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatIconButton } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatIcon } from '@angular/material/icon';
import { MatTableModule } from '@angular/material/table';
import { MatSort, MatSortModule } from '@angular/material/sort';
import { MatTableModule, MatTable } from '@angular/material/table';
import { MatTooltip } from '@angular/material/tooltip';
import {
combineLatest,
@ -31,13 +32,19 @@ import {
of,
BehaviorSubject,
debounceTime,
scan,
share,
first,
} from 'rxjs';
import { shareReplay, map, distinctUntilChanged, startWith } from 'rxjs/operators';
import { shareReplay, map, distinctUntilChanged, startWith, delay, filter } from 'rxjs/operators';
import { downloadFile, createCsv } from '../../../../utils';
import { ContentLoadingComponent } from '../../../content-loading';
import { ProgressModule } from '../../../progress';
import { Value, ValueComponent, ValueListComponent } from '../../../value';
import { sortDataByDefault } from '../../consts';
import { Column2, UpdateOptions, NormColumn } from '../../types';
import { cachedHeadMap } from '../../utils/cached-head-map';
import { TableDataSource } from '../../utils/table-data-source';
import { tableToCsvObject } from '../../utils/table-to-csv-object';
import { InfinityScrollDirective } from '../infinity-scroll.directive';
@ -84,6 +91,8 @@ export const TABLE_WRAPPER_STYLE = `
InfinityScrollDirective,
ValueListComponent,
SelectColumnComponent,
ProgressModule,
MatSortModule,
],
host: { style: TABLE_WRAPPER_STYLE },
})
@ -101,6 +110,7 @@ export class Table2Component<T extends object, C extends object> implements OnIn
filterChange = output<string>();
externalFilter = input(false, { transform: booleanAttribute });
filter$ = new BehaviorSubject<string>('');
filteredData$ = new BehaviorSubject<T[] | TreeInlineData<T, C>>([]);
// Select
rowSelectable = input(false, { transform: booleanAttribute });
@ -108,22 +118,33 @@ export class Table2Component<T extends object, C extends object> implements OnIn
rowSelectedChange = output<T[]>();
selected = signal<T[]>([]);
// Sort
@ViewChild(MatSort) sortComponent!: MatSort;
update = output<UpdateOptions>();
more = output<UpdateOptions>();
isTreeData = computed(() => !!this.treeData());
treeInlineData = computed<TreeInlineData<T, C>>(() =>
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<TreeInlineData<T, C>> = 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<T | TreeInlineDataItem<T, C>>();
dataSourceData$: Observable<T[] | TreeInlineData<T, C>> = combineLatest([
this.treeInlineData$,
toObservable(this.data),
toObservable(this.isTreeData),
]).pipe(
map(([treeData, data, isTreeData]) => (isTreeData ? treeData : data) ?? []),
shareReplay({ refCount: true, bufferSize: 1 }),
);
normColumns = computed<NormColumn<T>[]>(() => this.columns().map((c) => new NormColumn(c)));
displayedNormColumns$ = toObservable(this.normColumns).pipe(
switchMap((cols) =>
@ -133,58 +154,104 @@ export class Table2Component<T extends object, C extends object> implements OnIn
),
shareReplay({ refCount: true, bufferSize: 1 }),
);
columnsData$$: Observable<{ value: Observable<Value>; isChild?: boolean }[][]> = combineLatest([
columnsData$$: Observable<
Map<
T | TreeInlineDataItem<T, C>,
{ value: Observable<Value>; 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>({ 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, C> | T,
{ value: Observable<Value>; isChild?: boolean; isNextChild?: boolean }[]
>(
isTree
? (data as TreeInlineData<T, C>).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>({ value: '' })
).pipe(
shareReplay({
refCount: true,
bufferSize: 1,
}),
),
isChild: !d.value,
isNextChild: !(data as TreeInlineData<T, C>)[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<T, C>;
cols: NormColumn<T>[];
res: Map<
T | TreeInlineDataItem<T, C>,
{ value: Observable<Value>; 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<T[] | TreeInlineData<T, C>>(() =>
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<T extends object, C extends object> implements OnIn
);
columnDefs = COLUMN_DEFS;
scrollViewport = viewChild('scrollViewport', { read: ElementRef });
@ViewChild('scrollViewport', { read: ElementRef }) scrollViewport!: ElementRef;
@ViewChild('matTable', { static: false }) table!: MatTable<T>;
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<T extends object, C extends object> 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, C> | T[]) {
this.dataSource.sortData = filtered ? () => filtered : sortDataByDefault;
this.dataSource.sort = this.sortComponent;
}
}

View File

@ -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';

View File

@ -0,0 +1,21 @@
import { Observable, scan } from 'rxjs';
import { map } from 'rxjs/operators';
export function cachedHeadMap<T extends object, R>(fn: (el: T, idx: number) => R) {
return (src$: Observable<T[] | undefined>) => {
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),
);
};
}

View File

@ -0,0 +1,18 @@
import { BehaviorSubject } from 'rxjs';
export function createInputSubject<T extends Record<P, V>, P extends string, V>(
target: T,
propertyKey: P,
_initValue: V,
): BehaviorSubject<V> {
const sub$ = new BehaviorSubject<unknown>(target[propertyKey]);
Object.defineProperty(target, propertyKey, {
get: function () {
return sub$.value;
},
set: function (v) {
sub$.next(v);
},
});
return sub$ as never;
}

View File

@ -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);

View File

@ -1,8 +1,8 @@
@if (progress()) {
@if (progress || !(resValue$ | async)) {
<v-content-loading></v-content-loading>
} @else {
@if (resValue$ | async; as v) {
@if (isLoaded()) {
@if (isLoaded$ | async) {
@if (v.template) {
<ng-container *ngTemplateOutlet="v.template; context: {}"></ng-container>
} @else {
@ -40,8 +40,8 @@
>
{{ renderedValue }}
</span>
} @else if (emptySymbol()) {
<span style="color: #ccc">{{ emptySymbol() }}</span>
} @else if (emptySymbol) {
<span style="color: #ccc">{{ emptySymbol }}</span>
}
}
}
@ -58,7 +58,5 @@
<mat-icon>sync</mat-icon>
</button>
}
} @else {
<v-content-loading></v-content-loading>
}
}

View File

@ -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<Value | null>();
lazyValue = input<Observable<Value> | null>();
resultingValue = input<string>();
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<boolean>();
@Input() lazyValue: Observable<Value> | 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<boolean>();
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);
}
});
}
}

View File

@ -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;
}

View File

@ -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(