diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f5185e5..90a5da6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: matrix: os: [ubuntu-latest] # os: [ubuntu-latest, macos-latest, windows-latest] - node-version: [18.x] + node-version: [22.x] steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e40b5f6..3e39c53 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node-version: [18.x] + node-version: [22.x] steps: - uses: actions/checkout@v2 diff --git a/package.json b/package.json index 5b545c4..5a47c84 100644 --- a/package.json +++ b/package.json @@ -40,21 +40,20 @@ "access": "public" }, "devDependencies": { - "@playwright/test": "^1.49.1", - "typedoc": "^0.27.0", - "@prospective.co/procss": "^0.1.16", - "@types/react": "^17.0.0", - "esbuild": "^0.25.0", - "glob": "^10", + "@playwright/test": "^1.62.0", + "@prospective.co/procss": "^0.1.18", + "@types/react": "^17.0.93", + "esbuild": "^0.25.12", + "glob": "^10.5.0", "http-server": "^0.12.3", - "npm-run-all": "^4.1.3", - "prettier": "^3.4.2", - "react": "15", - "react-dom": "15", + "npm-run-all": "^4.1.5", + "prettier": "^3.9.6", + "react": "^15.7.0", + "react-dom": "^15.7.0", "superstore-arrow": "^1.0.0", - "typescript": "^5" + "typedoc": "^0.27.9", + "typescript": "^5.9.3" }, - "dependencies": {}, "pnpm": { "onlyBuiltDependencies": [ "esbuild", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a7fb448..b9e1d01 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,43 +9,43 @@ importers: .: devDependencies: '@playwright/test': - specifier: ^1.49.1 - version: 1.57.0 + specifier: ^1.62.0 + version: 1.62.0 '@prospective.co/procss': - specifier: ^0.1.16 - version: 0.1.17 + specifier: ^0.1.18 + version: 0.1.18 '@types/react': - specifier: ^17.0.0 - version: 17.0.90 + specifier: ^17.0.93 + version: 17.0.93 esbuild: - specifier: ^0.25.0 + specifier: ^0.25.12 version: 0.25.12 glob: - specifier: ^10 + specifier: ^10.5.0 version: 10.5.0 http-server: specifier: ^0.12.3 version: 0.12.3 npm-run-all: - specifier: ^4.1.3 + specifier: ^4.1.5 version: 4.1.5 prettier: - specifier: ^3.4.2 - version: 3.7.4 + specifier: ^3.9.6 + version: 3.9.6 react: - specifier: '15' + specifier: ^15.7.0 version: 15.7.0 react-dom: - specifier: '15' + specifier: ^15.7.0 version: 15.7.0(react@15.7.0) superstore-arrow: specifier: ^1.0.0 version: 1.0.0 typedoc: - specifier: ^0.27.0 + specifier: ^0.27.9 version: 0.27.9(typescript@5.9.3) typescript: - specifier: ^5 + specifier: ^5.9.3 version: 5.9.3 packages: @@ -217,13 +217,13 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@playwright/test@1.57.0': - resolution: {integrity: sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==} - engines: {node: '>=18'} + '@playwright/test@1.62.0': + resolution: {integrity: sha512-9zOJ6ZQRAena31MpOH9VSzIz8Ou3YJ/wtY/eQm5T2uhfhG7/U3COrMS8xOtUrZrp9OgdmzEnIYODye3nY1VqzA==} + engines: {node: '>=20'} hasBin: true - '@prospective.co/procss@0.1.17': - resolution: {integrity: sha512-jZCbEji9gTgJIJ41a0VQQV8GARUNKp9ils8/6347fbNkGBtA3cRwhQfRAxnqc0dhyo6pTSjMSflqop5ToIiSwQ==} + '@prospective.co/procss@0.1.18': + resolution: {integrity: sha512-odl+Vd/Lm2XdkB223/eghsGS4A5x5q8D9f/vCei2Wb/ljyLCGFWvP7w4Dtglid9xpvDCpJZtogeZASLcitMxug==} '@shikijs/engine-oniguruma@1.29.2': resolution: {integrity: sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==} @@ -234,14 +234,14 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - '@types/hast@3.0.4': - resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} - '@types/react@17.0.90': - resolution: {integrity: sha512-P9beVR/x06U9rCJzSxtENnOr4BrbJ6VrsrDTc+73TtHv9XHhryXKbjGRB+6oooB2r0G/pQkD/S4dHo/7jUfwFw==} + '@types/react@17.0.93': + resolution: {integrity: sha512-KM4Ty/ZTLZupiYxZVAlP+InNJS3De6uBMdq0ePa6/04+eG9Y7ftnWfst1xTLQ5rwAhgHwQ4momt/O4KepdGBTw==} '@types/scheduler@0.16.8': resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} @@ -301,18 +301,18 @@ packages: resolution: {integrity: sha512-CtGuTyWf3ig+sgRyC7uP6DM3N+5ur/p8L+FPfsd+BbIfIs74TFfCajZTHnCw6K5dqM0bZEbRIqRy1fAdiUJhTA==} engines: {node: '>= 0.6'} - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@1.1.16: + resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==} - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@2.1.2: + resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==} call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} engines: {node: '>= 0.4'} call-bound@1.0.4: @@ -422,8 +422,12 @@ packages: error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - es-abstract@1.24.1: - resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} + es-abstract-get@1.0.0: + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + engines: {node: '>= 0.4'} + + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -434,16 +438,16 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - es-to-primitive@1.3.0: - resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + es-to-primitive@1.3.4: + resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} engines: {node: '>= 0.4'} esbuild@0.25.12: @@ -461,8 +465,8 @@ packages: fbjs@0.8.18: resolution: {integrity: sha512-EQaWFK+fEPSoibjNy8IxUtaFOMXcWsY0JaVrQoZR9zC8N2Ygf9iDITPWjUTVIax95b6I742JFLqASHfsag/vKA==} - follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -486,8 +490,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.1.8: - resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + function.prototype.name@1.2.0: + resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} engines: {node: '>= 0.4'} functions-have-names@1.2.3: @@ -511,6 +515,7 @@ packages: glob@10.5.0: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true globalthis@1.0.4: @@ -547,8 +552,8 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} he@1.2.0: @@ -598,8 +603,8 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} engines: {node: '>= 0.4'} is-data-view@1.0.2: @@ -610,6 +615,10 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} + is-document.all@1.0.0: + resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} + engines: {node: '>= 0.4'} + is-finalizationregistry@1.1.1: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} @@ -692,8 +701,8 @@ packages: json-parse-better-errors@1.0.2: resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} - linkify-it@5.0.0: - resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + linkify-it@5.0.2: + resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} load-json-file@4.0.0: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} @@ -709,16 +718,16 @@ packages: lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} - markdown-it@14.1.0: - resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + markdown-it@14.3.0: + resolution: {integrity: sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==} hasBin: true math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - mdurl@2.0.0: - resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + mdurl@2.1.0: + resolution: {integrity: sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==} memorystream@0.3.1: resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} @@ -729,18 +738,18 @@ packages: engines: {node: '>=4'} hasBin: true - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} ms@2.1.3: @@ -780,8 +789,8 @@ packages: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true - own-keys@1.0.1: - resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + own-keys@1.0.2: + resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==} engines: {node: '>= 0.4'} package-json-from-dist@1.0.1: @@ -819,14 +828,14 @@ packages: resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} engines: {node: '>=4'} - playwright-core@1.57.0: - resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==} - engines: {node: '>=18'} + playwright-core@1.62.0: + resolution: {integrity: sha512-nsNRyq0r2zsG8AcRHWknc9QRA5XCueC7gWMrs+Gx2tlZn9hcl8zudfh00lhJPY1DE7NmZ6bDsT9g2yey8mXljA==} + engines: {node: '>=20'} hasBin: true - playwright@1.57.0: - resolution: {integrity: sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==} - engines: {node: '>=18'} + playwright@1.62.0: + resolution: {integrity: sha512-Z14dG305dgaLu6foB1TXQagFiW8JfSUIUaUuPaKQ6NtBPKF1P/qXcqfh6c6K/icPqdy37JmjbiBXf6JNg6Sylw==} + engines: {node: '>=20'} hasBin: true portfinder@1.0.38: @@ -837,8 +846,8 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} - prettier@3.7.4: - resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} + prettier@3.9.6: + resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} engines: {node: '>=14'} hasBin: true @@ -852,8 +861,8 @@ packages: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} - qs@6.14.1: - resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} + qs@6.15.3: + resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} engines: {node: '>=0.6'} react-dom@15.7.0: @@ -883,13 +892,13 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} hasBin: true - safe-array-concat@1.1.3: - resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} safe-push-apply@1.0.0: @@ -941,12 +950,12 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.3: - resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + shell-quote@1.10.0: + resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} engines: {node: '>= 0.4'} - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} side-channel-map@1.0.1: @@ -957,8 +966,8 @@ packages: resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} engines: {node: '>= 0.4'} - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} engines: {node: '>= 0.4'} signal-exit@4.1.0: @@ -974,8 +983,8 @@ packages: spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - spdx-license-ids@3.0.22: - resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + spdx-license-ids@3.0.23: + resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==} stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} @@ -993,12 +1002,12 @@ packages: resolution: {integrity: sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==} engines: {node: '>= 0.4'} - string.prototype.trim@1.2.10: - resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + string.prototype.trim@1.2.11: + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} engines: {node: '>= 0.4'} - string.prototype.trimend@1.0.9: - resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + string.prototype.trimend@1.0.10: + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} engines: {node: '>= 0.4'} string.prototype.trimstart@1.0.8: @@ -1009,8 +1018,8 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} strip-bom@3.0.0: @@ -1040,8 +1049,8 @@ packages: resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} engines: {node: '>= 0.4'} - typed-array-length@1.0.7: - resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + typed-array-length@1.0.8: + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} engines: {node: '>= 0.4'} typedoc@0.27.9: @@ -1092,8 +1101,8 @@ packages: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} - which-typed-array@1.1.19: - resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + which-typed-array@1.1.22: + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} engines: {node: '>= 0.4'} which@1.3.1: @@ -1113,8 +1122,8 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} - yaml@2.8.2: - resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} hasBin: true @@ -1208,7 +1217,7 @@ snapshots: dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.2 + strip-ansi: 7.2.0 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 @@ -1216,11 +1225,11 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/test@1.57.0': + '@playwright/test@1.62.0': dependencies: - playwright: 1.57.0 + playwright: 1.62.0 - '@prospective.co/procss@0.1.17': {} + '@prospective.co/procss@0.1.18': {} '@shikijs/engine-oniguruma@1.29.2': dependencies: @@ -1230,17 +1239,17 @@ snapshots: '@shikijs/types@1.29.2': dependencies: '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@shikijs/vscode-textmate@10.0.2': {} - '@types/hast@3.0.4': + '@types/hast@3.0.5': dependencies: '@types/unist': 3.0.3 '@types/prop-types@15.7.15': {} - '@types/react@17.0.90': + '@types/react@17.0.93': dependencies: '@types/prop-types': 15.7.15 '@types/scheduler': 0.16.8 @@ -1274,9 +1283,9 @@ snapshots: arraybuffer.prototype.slice@1.0.4: dependencies: array-buffer-byte-length: 1.0.2 - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 @@ -1295,12 +1304,12 @@ snapshots: basic-auth@1.1.0: {} - brace-expansion@1.1.12: + brace-expansion@1.1.16: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.2: + brace-expansion@2.1.2: dependencies: balanced-match: 1.0.2 @@ -1309,7 +1318,7 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.8: + call-bind@1.0.9: dependencies: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 @@ -1431,22 +1440,29 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-abstract@1.24.1: + es-abstract-get@1.0.0: + dependencies: + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + is-callable: 1.2.7 + object-inspect: 1.13.4 + + es-abstract@1.24.2: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 available-typed-arrays: 1.0.7 - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 data-view-buffer: 1.0.2 data-view-byte-length: 1.0.2 data-view-byte-offset: 1.0.1 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.0 - function.prototype.name: 1.1.8 + es-to-primitive: 1.3.4 + function.prototype.name: 1.2.0 get-intrinsic: 1.3.0 get-proto: 1.0.1 get-symbol-description: 1.1.0 @@ -1455,7 +1471,7 @@ snapshots: has-property-descriptors: 1.0.2 has-proto: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.2 + hasown: 2.0.4 internal-slot: 1.1.0 is-array-buffer: 3.0.5 is-callable: 1.2.7 @@ -1471,28 +1487,28 @@ snapshots: object-inspect: 1.13.4 object-keys: 1.1.1 object.assign: 4.1.7 - own-keys: 1.0.1 + own-keys: 1.0.2 regexp.prototype.flags: 1.5.4 - safe-array-concat: 1.1.3 + safe-array-concat: 1.1.4 safe-push-apply: 1.0.0 safe-regex-test: 1.1.0 set-proto: 1.0.0 stop-iteration-iterator: 1.1.0 - string.prototype.trim: 1.2.10 - string.prototype.trimend: 1.0.9 + string.prototype.trim: 1.2.11 + string.prototype.trimend: 1.0.10 string.prototype.trimstart: 1.0.8 typed-array-buffer: 1.0.3 typed-array-byte-length: 1.0.3 typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.7 + typed-array-length: 1.0.8 unbox-primitive: 1.1.0 - which-typed-array: 1.1.19 + which-typed-array: 1.1.22 es-define-property@1.0.1: {} es-errors@1.3.0: {} - es-object-atoms@1.1.1: + es-object-atoms@1.1.2: dependencies: es-errors: 1.3.0 @@ -1501,10 +1517,13 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 - es-to-primitive@1.3.0: + es-to-primitive@1.3.4: dependencies: + es-abstract-get: 1.0.0 + es-define-property: 1.0.1 + es-errors: 1.3.0 is-callable: 1.2.7 is-date-object: 1.1.0 is-symbol: 1.1.1 @@ -1552,7 +1571,7 @@ snapshots: setimmediate: 1.0.5 ua-parser-js: 0.7.41 - follow-redirects@1.15.11: {} + follow-redirects@1.16.0: {} for-each@0.3.5: dependencies: @@ -1568,14 +1587,17 @@ snapshots: function-bind@1.1.2: {} - function.prototype.name@1.1.8: + function.prototype.name@1.2.0: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 - define-properties: 1.2.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 functions-have-names: 1.2.3 - hasown: 2.0.2 + has-property-descriptors: 1.0.2 + hasown: 2.0.4 is-callable: 1.2.7 + is-document.all: 1.0.0 functions-have-names@1.2.3: {} @@ -1586,18 +1608,18 @@ snapshots: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 function-bind: 1.1.2 get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.2 + hasown: 2.0.4 math-intrinsics: 1.1.0 get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-symbol-description@1.1.0: dependencies: @@ -1609,8 +1631,8 @@ snapshots: dependencies: foreground-child: 3.3.1 jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 + minimatch: 9.0.9 + minipass: 7.1.3 package-json-from-dist: 1.0.1 path-scurry: 1.11.1 @@ -1641,7 +1663,7 @@ snapshots: dependencies: has-symbols: 1.1.0 - hasown@2.0.2: + hasown@2.0.4: dependencies: function-bind: 1.1.2 @@ -1652,7 +1674,7 @@ snapshots: http-proxy@1.18.1: dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.15.11 + follow-redirects: 1.16.0 requires-port: 1.0.0 transitivePeerDependencies: - debug @@ -1680,12 +1702,12 @@ snapshots: internal-slot@1.1.0: dependencies: es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.1.0 + hasown: 2.0.4 + side-channel: 1.1.1 is-array-buffer@3.0.5: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 get-intrinsic: 1.3.0 @@ -1710,9 +1732,9 @@ snapshots: is-callable@1.2.7: {} - is-core-module@2.16.1: + is-core-module@2.16.2: dependencies: - hasown: 2.0.2 + hasown: 2.0.4 is-data-view@1.0.2: dependencies: @@ -1725,6 +1747,10 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-document.all@1.0.0: + dependencies: + call-bound: 1.0.4 + is-finalizationregistry@1.1.1: dependencies: call-bound: 1.0.4 @@ -1753,7 +1779,7 @@ snapshots: call-bound: 1.0.4 gopd: 1.2.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 is-set@2.0.3: {} @@ -1776,7 +1802,7 @@ snapshots: is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.19 + which-typed-array: 1.1.22 is-weakmap@2.0.2: {} @@ -1808,7 +1834,7 @@ snapshots: json-parse-better-errors@1.0.2: {} - linkify-it@5.0.0: + linkify-it@5.0.2: dependencies: uc.micro: 2.1.0 @@ -1827,34 +1853,34 @@ snapshots: lunr@2.3.9: {} - markdown-it@14.1.0: + markdown-it@14.3.0: dependencies: argparse: 2.0.1 entities: 4.5.0 - linkify-it: 5.0.0 - mdurl: 2.0.0 + linkify-it: 5.0.2 + mdurl: 2.1.0 punycode.js: 2.3.1 uc.micro: 2.1.0 math-intrinsics@1.1.0: {} - mdurl@2.0.0: {} + mdurl@2.1.0: {} memorystream@0.3.1: {} mime@1.6.0: {} - minimatch@3.1.2: + minimatch@3.1.5: dependencies: - brace-expansion: 1.1.12 + brace-expansion: 1.1.16 - minimatch@9.0.5: + minimatch@9.0.9: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 2.1.2 minimist@1.2.8: {} - minipass@7.1.2: {} + minipass@7.1.3: {} ms@2.1.3: {} @@ -1868,7 +1894,7 @@ snapshots: normalize-package-data@2.5.0: dependencies: hosted-git-info: 2.8.9 - resolve: 1.22.11 + resolve: 1.22.12 semver: 5.7.2 validate-npm-package-license: 3.0.4 @@ -1878,10 +1904,10 @@ snapshots: chalk: 2.4.2 cross-spawn: 6.0.6 memorystream: 0.3.1 - minimatch: 3.1.2 + minimatch: 3.1.5 pidtree: 0.3.1 read-pkg: 3.0.0 - shell-quote: 1.8.3 + shell-quote: 1.10.0 string.prototype.padend: 3.1.6 object-assign@4.1.1: {} @@ -1892,17 +1918,18 @@ snapshots: object.assign@4.1.7: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 has-symbols: 1.1.0 object-keys: 1.1.1 opener@1.5.2: {} - own-keys@1.0.1: + own-keys@1.0.2: dependencies: + call-bound: 1.0.4 get-intrinsic: 1.3.0 object-keys: 1.1.1 safe-push-apply: 1.0.0 @@ -1923,7 +1950,7 @@ snapshots: path-scurry@1.11.1: dependencies: lru-cache: 10.4.3 - minipass: 7.1.2 + minipass: 7.1.3 path-type@3.0.0: dependencies: @@ -1933,11 +1960,11 @@ snapshots: pify@3.0.0: {} - playwright-core@1.57.0: {} + playwright-core@1.62.0: {} - playwright@1.57.0: + playwright@1.62.0: dependencies: - playwright-core: 1.57.0 + playwright-core: 1.62.0 optionalDependencies: fsevents: 2.3.2 @@ -1950,7 +1977,7 @@ snapshots: possible-typed-array-names@1.1.0: {} - prettier@3.7.4: {} + prettier@3.9.6: {} promise@7.3.1: dependencies: @@ -1964,9 +1991,10 @@ snapshots: punycode.js@2.3.1: {} - qs@6.14.1: + qs@6.15.3: dependencies: - side-channel: 1.1.0 + es-define-property: 1.0.1 + side-channel: 1.1.1 react-dom@15.7.0(react@15.7.0): dependencies: @@ -1994,18 +2022,18 @@ snapshots: reflect.getprototypeof@1.0.10: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-intrinsic: 1.3.0 get-proto: 1.0.1 which-builtin-type: 1.2.1 regexp.prototype.flags@1.5.4: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 es-errors: 1.3.0 get-proto: 1.0.1 @@ -2014,15 +2042,16 @@ snapshots: requires-port@1.0.0: {} - resolve@1.22.11: + resolve@1.22.12: dependencies: - is-core-module: 2.16.1 + es-errors: 1.3.0 + is-core-module: 2.16.2 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - safe-array-concat@1.1.3: + safe-array-concat@1.1.4: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 get-intrinsic: 1.3.0 has-symbols: 1.1.0 @@ -2065,7 +2094,7 @@ snapshots: dependencies: dunder-proto: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 setimmediate@1.0.5: {} @@ -2081,9 +2110,9 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.3: {} + shell-quote@1.10.0: {} - side-channel-list@1.0.0: + side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 @@ -2103,11 +2132,11 @@ snapshots: object-inspect: 1.13.4 side-channel-map: 1.0.1 - side-channel@1.1.0: + side-channel@1.1.1: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 - side-channel-list: 1.0.0 + side-channel-list: 1.0.1 side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 @@ -2116,16 +2145,16 @@ snapshots: spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.22 + spdx-license-ids: 3.0.23 spdx-exceptions@2.5.0: {} spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.22 + spdx-license-ids: 3.0.23 - spdx-license-ids@3.0.22: {} + spdx-license-ids@3.0.23: {} stop-iteration-iterator@1.1.0: dependencies: @@ -2142,43 +2171,44 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.2 + strip-ansi: 7.2.0 string.prototype.padend@3.1.6: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-abstract: 1.24.1 - es-object-atoms: 1.1.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 - string.prototype.trim@1.2.10: + string.prototype.trim@1.2.11: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.24.1 - es-object-atoms: 1.1.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 has-property-descriptors: 1.0.2 + safe-regex-test: 1.1.0 - string.prototype.trimend@1.0.9: + string.prototype.trimend@1.0.10: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 string.prototype.trimstart@1.0.8: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.2: + strip-ansi@7.2.0: dependencies: ansi-regex: 6.2.2 @@ -2200,7 +2230,7 @@ snapshots: typed-array-byte-length@1.0.3: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 @@ -2209,16 +2239,16 @@ snapshots: typed-array-byte-offset@1.0.4: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.8 + call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 reflect.getprototypeof: 1.0.10 - typed-array-length@1.0.7: + typed-array-length@1.0.8: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 is-typed-array: 1.1.15 @@ -2229,10 +2259,10 @@ snapshots: dependencies: '@gerrit0/mini-shiki': 1.27.2 lunr: 2.3.9 - markdown-it: 14.1.0 - minimatch: 9.0.5 + markdown-it: 14.3.0 + minimatch: 9.0.9 typescript: 5.9.3 - yaml: 2.8.2 + yaml: 2.9.0 typescript@5.9.3: {} @@ -2249,7 +2279,7 @@ snapshots: union@0.5.0: dependencies: - qs: 6.14.1 + qs: 6.15.3 url-join@2.0.5: {} @@ -2271,7 +2301,7 @@ snapshots: which-builtin-type@1.2.1: dependencies: call-bound: 1.0.4 - function.prototype.name: 1.1.8 + function.prototype.name: 1.2.0 has-tostringtag: 1.0.2 is-async-function: 2.1.1 is-date-object: 1.1.0 @@ -2282,7 +2312,7 @@ snapshots: isarray: 2.0.5 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.19 + which-typed-array: 1.1.22 which-collection@1.0.2: dependencies: @@ -2291,10 +2321,10 @@ snapshots: is-weakmap: 2.0.2 is-weakset: 2.0.4 - which-typed-array@1.1.19: + which-typed-array@1.1.22: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 for-each: 0.3.5 get-proto: 1.0.1 @@ -2319,6 +2349,6 @@ snapshots: dependencies: ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.1.2 + strip-ansi: 7.2.0 - yaml@2.8.2: {} + yaml@2.9.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..6810e3a --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + esbuild: true \ No newline at end of file diff --git a/src/ts/events.ts b/src/ts/events.ts index 05d7561..68583b8 100644 --- a/src/ts/events.ts +++ b/src/ts/events.ts @@ -210,7 +210,7 @@ export class RegularViewEventModel extends RegularVirtualTableViewModel { { start_row: 0, end_row: 0, - ...this._calculate_column_range(0), + ...this._calculate_column_range(0, this.scrollLeft), }, this._view_cache.row_headers_length, ); @@ -372,7 +372,7 @@ export class RegularViewEventModel extends RegularVirtualTableViewModel { { start_row: 0, end_row: 0, - ...this._calculate_column_range(0), + ...this._calculate_column_range(0, this.scrollLeft), }, this._view_cache.row_headers_length, ); diff --git a/src/ts/regular-table.ts b/src/ts/regular-table.ts index 4dfc35d..459ef97 100644 --- a/src/ts/regular-table.ts +++ b/src/ts/regular-table.ts @@ -22,6 +22,8 @@ import { METADATA_MAP } from "./view_model"; export type VirtualMode = "both" | "horizontal" | "vertical" | "none"; +export type { PredrawCommit, PredrawOptions } from "./types"; + const VIRTUAL_MODES: VirtualMode[] = ["both", "horizontal", "vertical", "none"]; export type ResetAutoSizeOptions = { diff --git a/src/ts/scroll_panel.ts b/src/ts/scroll_panel.ts index b844e6c..7bd655f 100644 --- a/src/ts/scroll_panel.ts +++ b/src/ts/scroll_panel.ts @@ -15,9 +15,12 @@ import type { RegularTableViewModel } from "./table"; import { RegularTableElement } from "./regular-table"; import { ColumnSizes, + DataResponse, StyleCallback, ContainerSize, DrawOptions, + PredrawCommit, + PredrawOptions, ViewCache, Viewport, ViewportValidation, @@ -38,6 +41,8 @@ const TRANSFORM_Y = "--regular-table--transform-y"; const HTML_TEMPLATE = `
`; +const PREDRAW_COLUMN_PAD = 1; + /** * Handles the virtual scroll pane, as well as the double buffering * of the underlying . This DOM structure looks a little like @@ -128,6 +133,369 @@ export class RegularVirtualTableViewModel extends HTMLElement { } while (this._scroll_pending); } + /** + * Prepare a draw without applying it to the DOM. `predraw()` performs + * the asynchronous portion of `draw()` - `DataListener` invocation and + * viewport calculation - against the provided viewport dimensions, and + * returns a closure which applies the prepared render to the DOM + * synchronously, so the visible mutation can be scheduled within a + * larger async workflow (e.g. batched with other component updates in a + * single animation frame). + * + * # Examples + * + * ```javascript + * const commit = await table.predraw(table.clientWidth, table.clientHeight); + * requestAnimationFrame(() => commit()); + * ``` + * + * @param {number} width - The width of the scrollable viewport region to + * fill. This is the box `draw()` measures as its internal scroll clip's + * `clientWidth`: the element's `clientWidth` less any classic scrollbar + * gutter (they are identical under overlay scrollbars). + * @param {number} height - The height of the scrollable viewport region + * to fill (as `width`, the element's `clientHeight` less any classic + * scrollbar gutter). + * @param {PredrawOptions} [options] + * @returns A closure which synchronously applies this render to the DOM. + */ + async predraw( + width: number, + height: number, + options: PredrawOptions = {}, + ): Promise { + const { + invalid_viewport = true, + preserve_width = false, + cache = false, + scroll_left = this.scrollLeft, + scroll_top = this.scrollTop, + container_height = height, + } = options; + + const noop = (): PredrawCommit => + Object.assign(() => true, { inline: true }); + + const inline = async (): Promise => { + await this.draw({ invalid_viewport, preserve_width, cache }); + return noop(); + }; + + if (!this._view_cache) { + console.warn("data listener not configured"); + return noop(); + } + + const { num_columns, num_rows } = await this._update_dim_state(cache); + if (!this._column_sizes.row_height) { + return await inline(); + } + + const safe_num_rows = num_rows || 0; + const safe_num_columns = num_columns || 0; + const container_size = this._resolve_container_size( + () => width, + () => height, + () => container_height, + ); + + this._container_size = container_size; + const panel_height = Math.round( + this._virtual_panel_height(safe_num_rows), + ); + + const clamped_scroll_top = Number.isFinite( + container_size.containerHeight, + ) + ? Math.min( + scroll_top, + Math.max(0, panel_height - container_size.containerHeight), + ) + : scroll_top; + + let viewport!: Viewport; + let view_response: DataResponse | undefined; + let should_render = false; + let invalid_schema_or_column = false; + for (let attempt = 0; attempt < 2; attempt++) { + const planned_row_headers_length = + this._view_cache.row_headers_length; + + const replanned = this._calculate_viewport( + safe_num_rows, + safe_num_columns, + clamped_scroll_top, + scroll_left, + ); + + const end_col = this._plan_column_extent( + replanned, + safe_num_columns, + ); + + if (end_col === undefined) { + return await inline(); + } + + replanned.end_col = Math.min( + safe_num_columns, + end_col + PREDRAW_COLUMN_PAD, + ); + + if (attempt === 0) { + const { invalid_row, invalid_column } = + this._validate_viewport(replanned); + + invalid_schema_or_column = !!( + this._invalid_schema || invalid_column + ); + + should_render = + invalid_schema_or_column || invalid_row || invalid_viewport; + } else if ( + Math.floor(replanned.start_col) === + Math.floor(viewport.start_col) && + Math.ceil(replanned.end_col) <= Math.ceil(viewport.end_col) + ) { + // The already-fetched slab is a superset; keep its extent. + replanned.end_col = viewport.end_col; + viewport = replanned; + break; + } + + viewport = replanned; + if (!should_render) { + break; + } + + view_response = await this._view_cache.view( + Math.floor(viewport.start_col), + Math.floor(viewport.start_row), + Math.ceil(viewport.end_col), + Math.ceil(viewport.end_row), + ); + + const actual_row_headers_length = + view_response.num_row_headers ?? + view_response.row_headers?.[0]?.length ?? + 0; + + if (actual_row_headers_length === planned_row_headers_length) { + break; + } + + this._view_cache.row_headers_length = actual_row_headers_length; + } + + const view_cache = this._view_cache; + return Object.assign( + () => { + this._container_size = container_size; + this._commit_viewport(viewport); + this._update_virtual_panels( + invalid_viewport, + safe_num_rows, + safe_num_columns, + preserve_width, + ); + + if (!should_render || view_response === undefined) { + this._update_sub_cell_offset(viewport); + return true; + } + + let first_iteration = true; + const style_callback = () => { + if (first_iteration) { + this._update_sub_cell_offset(viewport); + first_iteration = false; + } + + for (const callback of this._style_callbacks) { + // Style listeners may be async, but a synchronous commit + // cannot await them; any DOM effects they defer land + // after this commit and are not measured by it. + callback({ + detail: this as unknown as RegularTableElement, + }); + } + }; + + const complete = this.table_model.draw_sync( + container_size, + view_cache, + this._selected_id, + preserve_width, + viewport, + safe_num_columns, + view_response, + style_callback, + ); + + this._post_render( + viewport, + safe_num_rows, + safe_num_columns, + preserve_width, + invalid_schema_or_column, + ); + + if (view_cache === this._view_cache) { + this._invalid_schema = false; + } + + if (!complete) { + this.draw({ invalid_viewport: true }); + } + + return complete; + }, + { inline: false }, + ); + } + + /** + * Fetch the data set dimensions and apply them to view state - the + * shared head of both `draw()` and `predraw()`. + */ + protected async _update_dim_state( + cache: boolean | undefined, + ): Promise { + if (!cache) { + this.table_model._resetDimState(); + } + + const dims = await this.table_model._getDimState(this._view_cache); + this._column_sizes.row_height = + dims.row_height || this._column_sizes.row_height; + + if (dims.num_row_headers !== undefined) { + this._view_cache.row_headers_length = dims.num_row_headers; + } + + if (dims.num_column_headers !== undefined) { + this._view_cache.column_headers_length = dims.num_column_headers; + } + + return dims; + } + + /** + * Mask the viewport dimensions to `Infinity` on axes this table does not + * virtualize. Dimensions are supplied as thunks so that a masked axis's + * value is never evaluated - `draw()` passes `clientWidth` etc. reads, + * which must not force layout for axes that don't need them. + */ + protected _resolve_container_size( + width: () => number, + height: () => number, + containerHeight: () => number, + ): ContainerSize { + const is_non_vertical = + this._virtual_mode === "none" || + this._virtual_mode === "horizontal"; + + const is_non_horizontal = + this._virtual_mode === "none" || this._virtual_mode === "vertical"; + + return { + width: is_non_horizontal ? Infinity : width(), + height: is_non_vertical ? Infinity : height(), + containerHeight: is_non_vertical ? Infinity : containerHeight(), + }; + } + + /** + * The virtual panel writes preceding a render. + */ + protected _update_virtual_panels( + invalid: boolean, + nrows: number, + num_columns: number, + preserve_width: boolean, + ): void { + this._update_virtual_panel_height(nrows); + if (!preserve_width) { + this._update_virtual_panel_width(invalid, num_columns); + } + } + + /** + * The DOM writes following a render - the shared tail of both `draw()` + * and `predraw()`'s commit. + */ + protected _post_render( + viewport: Viewport, + nrows: number, + num_columns: number, + preserve_width: boolean, + invalid: boolean, + ): void { + this._update_sub_cell_offset(viewport); + + const old_height = this._column_sizes.row_height; + this.table_model.header.reset_header_cache(); + if (old_height !== this._column_sizes.row_height) { + this._update_virtual_panel_height(nrows); + } + + if (!preserve_width) { + this._update_virtual_panel_width(invalid, num_columns); + } + } + + /** + * Attempt to pre-calculate the exact column extent of the viewport from + * cached column widths, without rendering. Returns the exclusive + * `end_col`, or `undefined` if any column in the candidate extent has + * never been measured, in which case the extent cannot be known without + * rendering and `predraw()` must fall back to drawing inline. + */ + private _plan_column_extent( + viewport: Viewport, + num_columns: number, + ): number | undefined { + if ( + this._virtual_mode === "none" || + this._virtual_mode === "vertical" + ) { + return viewport.end_col; + } + + const { indices, override } = this._column_sizes; + const effective_width = (size_key: number) => + override[size_key] ?? indices[size_key]; + + const row_headers_length = this._view_cache.row_headers_length; + let width = 0; + for (let i = 0; i < row_headers_length; i++) { + const w = effective_width(i); + if (w === undefined) { + return undefined; + } + + width += w; + } + + const start_col = Math.floor(viewport.start_col); + const sub_cell_offset = indices[row_headers_length + start_col] ?? 0; + let end_col = start_col; + while ( + end_col < num_columns && + width - sub_cell_offset <= this._container_size.width + ) { + const w = effective_width(row_headers_length + end_col); + if (w === undefined) { + return undefined; + } + + width += w; + end_col++; + } + + return end_col; + } + /** * Probe the shadow DOM to measure the default row height from CSS. */ @@ -215,10 +583,17 @@ export class RegularVirtualTableViewModel extends HTMLElement { protected _calculate_viewport( nrows: number, num_columns: number, + scroll_top: number, + scroll_left: number, ): Viewport { - const { start_row, end_row } = this._calculate_row_range(nrows); - const { start_col, end_col } = - this._calculate_column_range(num_columns); + const { start_row, end_row } = this._calculate_row_range( + nrows, + scroll_top, + ); + const { start_col, end_col } = this._calculate_column_range( + num_columns, + scroll_left, + ); return { start_col, end_col, start_row, end_row }; } @@ -247,13 +622,27 @@ export class RegularVirtualTableViewModel extends HTMLElement { this._start_row !== start_row || this._end_row !== end_row || this._end_col !== end_col; - this._start_col = start_col; - this._end_col = end_col; - this._start_row = start_row; - this._end_row = end_row; return { invalid_column, invalid_row }; } + /** + * Records the viewport most recently applied to the DOM, against which + * `_validate_viewport` compares. Split from `_validate_viewport` so that + * `predraw()` can validate speculatively without recording until its + * commit closure actually runs. + */ + protected _commit_viewport({ + start_col, + end_col, + start_row, + end_row, + }: Viewport): void { + this._start_col = Math.floor(start_col); + this._end_col = Math.ceil(end_col); + this._start_row = Math.floor(start_row); + this._end_row = Math.ceil(end_row); + } + /** * Updates the `virtual_panel` width based on view state. * @@ -297,15 +686,21 @@ export class RegularVirtualTableViewModel extends HTMLElement { * @param {*} nrows */ protected _update_virtual_panel_height(nrows: number): void { + const virtual_panel_px_size = this._virtual_panel_height(nrows); + this._virtual_panel.style.height = `${virtual_panel_px_size.toPrecision(10)}px`; + } + + /** + * The px height `_update_virtual_panel_height` sets (or will set) on the + * virtual panel. Computing this directly rather than reading + * `_virtual_panel.offsetHeight` back avoids a forced layout in the draw + * path, and lets `predraw()` calculate row ranges without any DOM reads. + */ + protected _virtual_panel_height(nrows: number): number { const { row_height = 0 } = this._column_sizes; const header_height = this._view_cache.column_headers_length * row_height; - let virtual_panel_px_size; - virtual_panel_px_size = Math.min( - BROWSER_MAX_HEIGHT, - nrows * row_height + header_height, - ); - this._virtual_panel.style.height = `${virtual_panel_px_size.toPrecision(10)}px`; + return Math.min(BROWSER_MAX_HEIGHT, nrows * row_height + header_height); } /** @@ -340,7 +735,10 @@ export class RegularVirtualTableViewModel extends HTMLElement { * * @returns */ - protected _calculate_column_range(num_columns: number): { + protected _calculate_column_range( + num_columns: number, + scroll_left: number, + ): { start_col: number; end_col: number; } { @@ -350,7 +748,7 @@ export class RegularVirtualTableViewModel extends HTMLElement { ) { return { start_col: 0, end_col: Infinity }; } else { - const start_col = this._calc_start_column(); + const start_col = this._calc_start_column(scroll_left); const vis_cols = this.table_model.num_columns() || Math.min( @@ -398,20 +796,21 @@ export class RegularVirtualTableViewModel extends HTMLElement { * @param {*} nrows * @returns */ - private _calculate_row_range(nrows: number): { + private _calculate_row_range( + nrows: number, + scroll_top: number, + ): { start_row: number; end_row: number; } { const { height, containerHeight } = this._container_size; const row_height = this._column_sizes.row_height || 0; const header_levels = this._view_cache.column_headers_length; - const total_scroll_height = Math.max( - 1, - this._virtual_panel.offsetHeight - containerHeight, - ); + const panel_height = Math.round(this._virtual_panel_height(nrows)); + const total_scroll_height = Math.max(1, panel_height - containerHeight); const percent_scroll = - Math.max(Math.ceil(this.scrollTop), 0) / total_scroll_height; + Math.max(Math.ceil(scroll_top), 0) / total_scroll_height; const clip_panel_row_height = height / row_height - header_levels; const relative_nrows = nrows || 0; @@ -427,15 +826,15 @@ export class RegularVirtualTableViewModel extends HTMLElement { return { start_row, end_row }; } - private _calc_start_column(): number { + private _calc_start_column(scroll_left: number): number { const scroll_index_offset = this._view_cache.row_headers_length; let start_col = 0; let offset_width = 0; let diff = 0; - while (offset_width < this.scrollLeft) { + while (offset_width < scroll_left) { const new_val = this._column_sizes.indices[start_col + scroll_index_offset]; - diff = this.scrollLeft - offset_width; + diff = scroll_left - offset_width; start_col += 1; offset_width += new_val !== undefined ? new_val : 60; } @@ -531,54 +930,41 @@ async function internal_draw( options: DrawOptions, ): Promise { const __debug_start_time__ = DEBUG && performance.now(); - if (!options.cache) { - this.table_model._resetDimState(); - } - const { invalid_viewport = true, preserve_width = false } = options; - const { - num_columns, - num_rows, - num_row_headers, - num_column_headers, - row_height, - } = await this.table_model._getDimState(this._view_cache); - - this._column_sizes.row_height = row_height || this._column_sizes.row_height; + const { num_columns, num_rows } = await this._update_dim_state( + options.cache, + ); + if (!this._column_sizes.row_height) { this._probe_row_height(); } - if (num_row_headers !== undefined) { - this._view_cache.row_headers_length = num_row_headers; - } - - if (num_column_headers !== undefined) { - this._view_cache.column_headers_length = num_column_headers; - } - - // Cache virtual mode checks and default values - const is_non_vertical = - this._virtual_mode === "none" || this._virtual_mode === "horizontal"; - - const is_non_horizontal = - this._virtual_mode === "none" || this._virtual_mode === "vertical"; - const safe_num_rows = num_rows || 0; const safe_num_columns = num_columns || 0; - this._container_size = { - width: is_non_horizontal ? Infinity : this._table_clip.clientWidth, - height: is_non_vertical ? Infinity : this._table_clip.clientHeight, - containerHeight: is_non_vertical ? Infinity : this.clientHeight, - }; - - this._update_virtual_panel_height(safe_num_rows); - if (!preserve_width) { - this._update_virtual_panel_width(invalid_viewport, safe_num_columns); - } + this._container_size = this._resolve_container_size( + () => this._table_clip.clientWidth, + () => this._table_clip.clientHeight, + () => this.clientHeight, + ); + + this._update_virtual_panels( + invalid_viewport, + safe_num_rows, + safe_num_columns, + preserve_width, + ); + + // Scroll positions are read after the virtual panel writes above, as the + // browser may clamp them when the panel shrinks. + const viewport = this._calculate_viewport( + safe_num_rows, + safe_num_columns, + this.scrollTop, + this.scrollLeft, + ); - const viewport = this._calculate_viewport(safe_num_rows, safe_num_columns); const { invalid_row, invalid_column } = this._validate_viewport(viewport); + this._commit_viewport(viewport); const invalid_schema_or_column = this._invalid_schema || invalid_column; if (invalid_schema_or_column || invalid_row || invalid_viewport) { let first_iteration = true; @@ -604,22 +990,13 @@ async function internal_draw( }, ); - // Re-apply with post-measurement widths, in case the first - // application used an estimate for a never-measured leading column. - this._update_sub_cell_offset(viewport); - - const old_height = this._column_sizes.row_height; - this.table_model.header.reset_header_cache(); - if (old_height !== this._column_sizes.row_height) { - this._update_virtual_panel_height(safe_num_rows); - } - - if (!preserve_width) { - this._update_virtual_panel_width( - invalid_schema_or_column, - safe_num_columns, - ); - } + this._post_render( + viewport, + safe_num_rows, + safe_num_columns, + preserve_width, + invalid_schema_or_column, + ); this._invalid_schema = false; } else { diff --git a/src/ts/table.ts b/src/ts/table.ts index 385d9c7..39efce0 100644 --- a/src/ts/table.ts +++ b/src/ts/table.ts @@ -620,11 +620,8 @@ export class RegularTableViewModel extends RegularTableViewModelBase { preserve_width: boolean, viewport: Viewport, num_columns: number, - style_callback: (last_cells: CellTuple) => Promise, + style_callback: () => Promise, ): Promise { - const { width: container_width, height: container_height } = - container_size; - // Fetch and prepare initial data const view_response = (this._lastDataResponse = await view_cache.view( Math.floor(viewport.start_col), @@ -633,6 +630,103 @@ export class RegularTableViewModel extends RegularTableViewModelBase { Math.ceil(viewport.end_row), )); + const render_pass = this._render_pass( + container_size, + view_cache, + selected_id, + preserve_width, + viewport, + num_columns, + view_response, + ); + + try { + let step = render_pass.next(); + while (!step.done) { + await style_callback(); + step = render_pass.next( + step.value === "style" + ? undefined + : await step.value.fetch(), + ); + } + } finally { + render_pass.return(false); + } + } + + /** + * Synchronous variant of `draw()`, used by the commit closure returned + * from `predraw()`. The data slab must be fully pre-fetched: `fetch` + * effects yielded by the render pass are not executed (so the pass + * finishes on its data-exhausted path when the slab runs dry), and + * `style_callback` is invoked without being awaited. + * + * @returns `false` if `view_response` was exhausted before the viewport + * was filled (e.g. the data set changed shape between plan and commit, or + * a style listener shrank columns below their measured widths), in which + * case the caller should schedule a corrective `draw()`; `true` otherwise. + */ + draw_sync( + container_size: { width: number; height: number }, + view_cache: ViewCache, + selected_id: number | undefined, + preserve_width: boolean, + viewport: Viewport, + num_columns: number, + view_response: DataResponse, + style_callback: () => void, + ): boolean { + this._lastDataResponse = view_response; + const render_pass = this._render_pass( + container_size, + view_cache, + selected_id, + preserve_width, + viewport, + num_columns, + view_response, + ); + + let step = render_pass.next(); + while (!step.done) { + if (step.value === "style") { + style_callback(); + } + + step = render_pass.next(); + } + + return step.value; + } + + /** + * The single render pass shared by `draw()` (which awaits each yielded + * effect) and `draw_sync()` (which runs it to completion synchronously + * against a pre-fetched slab). Yields `"style"` where style listeners + * must be invoked, and `{ fetch }` thunks where additional column data + * is required; a driver which cannot fetch resumes with `undefined` and + * the pass finishes on its data-exhausted path. + * + * @returns `true` if the viewport was filled or the data set exhausted, + * `false` if the pass ran out of data before the viewport was filled. + */ + private *_render_pass( + container_size: { width: number; height: number }, + view_cache: ViewCache, + selected_id: number | undefined, + preserve_width: boolean, + viewport: Viewport, + num_columns: number, + view_response: DataResponse, + ): Generator< + "style" | { fetch: () => Promise }, + boolean, + FetchResult | undefined + > { + const { width: container_width, height: container_height } = + container_size; + let { column_header_merge_depth, merge_headers = "both" } = view_response; @@ -710,25 +804,27 @@ export class RegularTableViewModel extends RegularTableViewModelBase { view_cache.row_headers_length, ); - await style_callback(last_cells[this._row_headers_length]); - const fetch_result = await this._fetchMissingColumns( - viewport, - view, - view_response, - dcidx, - view_state, - container_width, - num_columns, - _virtual_x, - x0, - ); - - if (fetch_result.column_header_merge_depth !== undefined) { + const fetch_result = yield { + fetch: () => + this._fetchMissingColumns( + viewport, + view, + view_response, + dcidx, + view_state, + container_width, + num_columns, + _virtual_x, + x0, + ), + }; + + if (fetch_result?.column_header_merge_depth !== undefined) { column_header_merge_depth = fetch_result.column_header_merge_depth; } - if (fetch_result.merge_headers !== undefined) { + if (fetch_result?.merge_headers !== undefined) { merge_headers = fetch_result.merge_headers; } @@ -740,16 +836,16 @@ export class RegularTableViewModel extends RegularTableViewModelBase { view_cache.row_headers_length, ); - await style_callback( - last_cells[this._row_headers_length], - ); - + yield "style"; this.autosize_cells( last_cells, this._column_sizes.row_height, ); - return; + return this._isViewportFilled( + view_state, + container_width, + ); } } @@ -817,14 +913,14 @@ export class RegularTableViewModel extends RegularTableViewModelBase { view_cache.row_headers_length, ); - await style_callback(last_cells[this._row_headers_length]); + yield "style"; // `preserve_width` draws skip measurement, so the // estimate-based filled check above is final; a follow-up // draw (e.g. on resize mouseup) re-measures. if (preserve_width) { this._carriageReturn(); - return; + return true; } // Recalculate after style listeners @@ -845,7 +941,7 @@ export class RegularTableViewModel extends RegularTableViewModelBase { if (this._isViewportFilled(view_state, container_width)) { this._carriageReturn(); - return; + return true; } } } @@ -857,8 +953,9 @@ export class RegularTableViewModel extends RegularTableViewModelBase { view_cache.row_headers_length, ); - await style_callback(last_cells[this._row_headers_length]); + yield "style"; this.autosize_cells(last_cells, this._column_sizes.row_height); + return true; } finally { this._cleanupAfterDraw(cont_body, _virtual_x); } diff --git a/src/ts/types.ts b/src/ts/types.ts index 5ac62eb..4c405e7 100644 --- a/src/ts/types.ts +++ b/src/ts/types.ts @@ -338,6 +338,48 @@ export interface DrawOptions { cache?: boolean; } +/** + * Options for the `predraw()` method. + * + * @property {boolean} [invalid_viewport=true] Assume the viewport contents + * have changed, forcing a full render at commit (as `DrawOptions`). + * @property {boolean} [preserve_width=false] Skip column auto-sizing (as + * `DrawOptions`). + * @property {boolean} [cache=false] Reuse the previous `DataResponse` for + * dimensions rather than re-requesting (as `DrawOptions`). + * @property {number} [scroll_left] Horizontal scroll position to render at, + * defaulting to the element's `scrollLeft` when `predraw()` is called. + * @property {number} [scroll_top] Vertical scroll position to render at, + * defaulting to the element's `scrollTop` when `predraw()` is called. + * @property {number} [container_height] The `clientHeight` of the + * `` element itself, when it differs from the `height` + * argument (the height of the visible scrollable region). + */ +export interface PredrawOptions { + invalid_viewport?: boolean; + preserve_width?: boolean; + cache?: boolean; + scroll_left?: number; + scroll_top?: number; + container_height?: number; +} + +/** + * The commit closure returned by `predraw()`. Invoking it applies the + * prepared render to the DOM synchronously. It returns `false` if the + * pre-fetched data did not fill the viewport and a corrective async + * `draw()` was scheduled, `true` otherwise; callers which don't care may + * treat it as `() => void`. + */ +export type PredrawCommit = (() => boolean) & { + /** + * `true` when the column widths of the target viewport were not yet + * known, so `predraw()` fell back to drawing inline (equivalent to + * `draw()`) and this closure is a no-op. + */ + inline: boolean; +}; + /** * View state containing viewport and rendering information */ diff --git a/tests/predraw.spec.js b/tests/predraw.spec.js new file mode 100644 index 0000000..de998a9 --- /dev/null +++ b/tests/predraw.spec.js @@ -0,0 +1,253 @@ +// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░ +// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░ +// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░ +// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +// ┃ * Copyright (c) 2020, the Regular Table Authors. This file is part * ┃ +// ┃ * of the Regular Table library, distributed under the terms of the * ┃ +// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃ +// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +import { test, expect } from "@playwright/test"; + +test.describe("predraw()", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/tests/api.html"); + await page.waitForSelector("regular-table table tbody tr td"); + }); + + test.describe("inline fallback", () => { + test("falls back to drawing inline on a never-drawn table", async ({ + page, + }) => { + const result = await page.evaluate(async () => { + const table = document.createElement("regular-table"); + table.style.width = "400px"; + table.style.height = "300px"; + document.body.appendChild(table); + table.setDataListener(window.dataListener); + const commit = await table.predraw(400, 300); + const num_cells_before_commit = + table.querySelectorAll("td").length; + const committed = commit(); + return { + inline: commit.inline, + committed, + num_cells_before_commit, + }; + }); + + expect(result.inline).toBe(true); + expect(result.committed).toBe(true); + expect(result.num_cells_before_commit).toBeGreaterThan(0); + }); + + test("falls back to drawing inline after resetAutoSize()", async ({ + page, + }) => { + const result = await page.evaluate(async () => { + const table = document.querySelector("regular-table"); + await table.draw(); + table.resetAutoSize(); + const commit = await table.predraw( + table.clientWidth, + table.clientHeight, + ); + return { inline: commit.inline }; + }); + + expect(result.inline).toBe(true); + }); + }); + + test.describe("synchronous commit", () => { + test("returns a deferred commit closure on a warm table", async ({ + page, + }) => { + const result = await page.evaluate(async () => { + const table = document.querySelector("regular-table"); + await table.draw(); + const commit = await table.predraw( + table.clientWidth, + table.clientHeight, + ); + return { inline: commit.inline, committed: commit() }; + }); + + expect(result.inline).toBe(false); + expect(result.committed).toBe(true); + }); + + test("does not mutate the DOM until commit is invoked", async ({ + page, + }) => { + const result = await page.evaluate(async () => { + const table = document.querySelector("regular-table"); + await table.draw(); + const before = table.querySelector("td").textContent; + + // Bind new marker data over the same shape, `preserve_state` + // to keep this a pure data update. + table.setDataListener( + async (x0, y0, x1, y1) => { + const response = await window.dataListener( + x0, + y0, + x1, + y1, + ); + response.data = response.data.map((col) => + col.map((val) => `X${val}`), + ); + return response; + }, + { preserve_state: true }, + ); + + const commit = await table.predraw( + table.clientWidth, + table.clientHeight, + ); + + const during = table.querySelector("td").textContent; + commit(); + const after = table.querySelector("td").textContent; + return { inline: commit.inline, before, during, after }; + }); + + expect(result.inline).toBe(false); + expect(result.during).toBe(result.before); + expect(result.after).toBe(`X${result.before}`); + }); + + test("renders the same DOM as an equivalent draw()", async ({ + page, + }) => { + const snapshot = (table) => ({ + cells: Array.from(table.querySelectorAll("td")).map( + (td) => td.textContent, + ), + headers: Array.from(table.querySelectorAll("th")).map( + (th) => th.textContent, + ), + }); + + const via_draw = await page.evaluate(async (snapshot_src) => { + const snapshot = eval(snapshot_src); + const table = document.querySelector("regular-table"); + const overrides = {}; + for (let i = 0; i < 40; i++) { + overrides[i] = 80; + } + + table.restoreColumnSizes(overrides); + await table.draw(); + await table.draw(); + return snapshot(table); + }, snapshot.toString()); + + const via_predraw = await page.evaluate(async (snapshot_src) => { + const snapshot = eval(snapshot_src); + const table = document.querySelector("regular-table"); + const clip = table.shadowRoot.children[1]; + const commit = await table.predraw( + clip.clientWidth, + clip.clientHeight, + { container_height: table.clientHeight }, + ); + commit(); + return { inline: commit.inline, ...snapshot(table) }; + }, snapshot.toString()); + + expect(via_predraw.inline).toBe(false); + expect(via_predraw.cells).toEqual(via_draw.cells); + expect(via_predraw.headers).toEqual(via_draw.headers); + }); + + test("invokes style listeners during commit", async ({ page }) => { + const result = await page.evaluate(async () => { + const table = document.querySelector("regular-table"); + await table.draw(); + let calls = 0; + table.addStyleListener(() => { + calls++; + }); + + const commit = await table.predraw( + table.clientWidth, + table.clientHeight, + ); + + const calls_before_commit = calls; + commit(); + return { + inline: commit.inline, + calls_before_commit, + calls_after_commit: calls, + }; + }); + + expect(result.inline).toBe(false); + expect(result.calls_before_commit).toBe(0); + expect(result.calls_after_commit).toBeGreaterThan(0); + }); + + test("respects the scroll position snapshot options", async ({ + page, + }) => { + const result = await page.evaluate(async () => { + const table = document.querySelector("regular-table"); + await table.draw(); + const commit = await table.predraw( + table.clientWidth, + table.clientHeight, + { scroll_top: table.scrollHeight }, + ); + + commit(); + const meta = table.getMeta(table.querySelector("td")); + return { inline: commit.inline, y0: meta.y0 }; + }); + + expect(result.inline).toBe(false); + expect(result.y0).toBeGreaterThan(0); + }); + }); + + test.describe("data listener call pattern", () => { + test("performs all DataListener calls before resolving", async ({ + page, + }) => { + const result = await page.evaluate(async () => { + const table = document.querySelector("regular-table"); + await table.draw(); + let calls = 0; + table.setDataListener( + async (...args) => { + calls++; + return await window.dataListener(...args); + }, + { preserve_state: true }, + ); + + const commit = await table.predraw( + table.clientWidth, + table.clientHeight, + ); + + const calls_before_commit = calls; + commit(); + return { + inline: commit.inline, + calls_before_commit, + calls_after_commit: calls, + }; + }); + + expect(result.inline).toBe(false); + expect(result.calls_before_commit).toBeGreaterThan(0); + expect(result.calls_after_commit).toBe(result.calls_before_commit); + }); + }); +});