{"_id":"@benjosivo/table-query","_rev":"9-046ab43e1ee0af8d674dd97dd3f4ae22","name":"@benjosivo/table-query","dist-tags":{"latest":"1.3.0"},"versions":{"1.0.0":{"name":"@benjosivo/table-query","version":"1.0.0","keywords":[],"author":{"name":"benjosivo"},"license":"ISC","_id":"@benjosivo/table-query@1.0.0","maintainers":[{"name":"benjosivo","email":"macklem75007@gmail.com"}],"homepage":"https://github.com/benjosivo/table-query#readme","bugs":{"url":"https://github.com/benjosivo/table-query/issues"},"dist":{"shasum":"6c97deb461aea4516bbd289f46cdcc6f0bda70d9","tarball":"https://registry.npmjs.org/@benjosivo/table-query/-/table-query-1.0.0.tgz","fileCount":26,"integrity":"sha512-g27IUxnU+Gyn43Vy3rRASaUsY5FBqmCh0xNEhrMT2PNbw2WRpeQV97a3MdOf9OZnt2njd6pIPMO5zJOp8CBr9g==","signatures":[{"sig":"MEUCIHRykJMdUen5086IUIQTUrH9Qd+uEPRAvKxrLYuUsDrtAiEA52JJXQ0xJyi4KhIOb5JREY6YStQETHWnc4lYmZR/jOQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":57769},"main":"index.js","type":"module","exports":{"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"c01b694f8f582a17a0cabea3e8faadf7179d5bb8","scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"benjosivo","email":"macklem75007@gmail.com"},"repository":{"url":"git+https://github.com/benjosivo/table-query.git","type":"git"},"_npmVersion":"12.0.2","description":"Table triable/filtrable/paginée : hook + composants React d'un côté (`/react`), logique SQL de pagination/tri/filtres côté serveur de l'autre (`/server`). Un projet peut n'utiliser qu'un des deux côtés.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"@benjosivo/mysql":"^1.3.2"},"publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0","@types/react":"^18.0.0","@types/express":"^4.17.0"},"peerDependencies":{"react":"^18.0.0","express":"^4.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"express":{"optional":true},"@benjosivo/mysql":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/table-query_1.0.0_1789033045001_0.1355008374680602","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@benjosivo/table-query","version":"1.0.1","keywords":[],"author":{"name":"benjosivo"},"license":"ISC","_id":"@benjosivo/table-query@1.0.1","maintainers":[{"name":"benjosivo","email":"macklem75007@gmail.com"}],"homepage":"https://github.com/benjosivo/table-query#readme","bugs":{"url":"https://github.com/benjosivo/table-query/issues"},"dist":{"shasum":"7fa87f4bc6e809e3349f6b96bc23445532eb790a","tarball":"https://registry.npmjs.org/@benjosivo/table-query/-/table-query-1.0.1.tgz","fileCount":26,"integrity":"sha512-FF+ZFfGaS8Jh38QXhyVRjAzTUTG7jqae6/T3wc1zdE2Yg+ooyhcnJM4Ofr96jZxdmUK4g8+8kRgKH16xONxlmA==","signatures":[{"sig":"MEUCIBGaGK1AWT/hQLqKqzWhdl1uOdui5WsIQPl7Dg1I3/hlAiEA6czSaU7xXXpwr+zuP7Kk852caO9NsuwPu8Xo4m2Fy4I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":62663},"main":"index.js","type":"module","exports":{"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"fe2dfb18618d917d02c1630a3486f91d22fbe7f9","scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"benjosivo","email":"macklem75007@gmail.com"},"repository":{"url":"git+https://github.com/benjosivo/table-query.git","type":"git"},"_npmVersion":"12.0.2","description":"Table triable/filtrable/paginée : hook + composants React d'un côté (`/react`), logique SQL de pagination/tri/filtres côté serveur de l'autre (`/server`). Un projet peut n'utiliser qu'un des deux côtés.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"express":"^5.2.1","@benjosivo/mysql":"^1.3.2"},"publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0","@types/react":"^18.0.0","@types/express":"^5.0.6"},"peerDependencies":{"react":"^18.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"express":{"optional":true},"@benjosivo/mysql":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/table-query_1.0.1_1789033586710_0.8458755506189932","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@benjosivo/table-query","version":"1.0.2","keywords":[],"author":{"name":"benjosivo"},"license":"ISC","_id":"@benjosivo/table-query@1.0.2","maintainers":[{"name":"benjosivo","email":"macklem75007@gmail.com"}],"homepage":"https://github.com/benjosivo/table-query#readme","bugs":{"url":"https://github.com/benjosivo/table-query/issues"},"dist":{"shasum":"8332185a9668cfd2f9a288a1d9021b9c1e8f2179","tarball":"https://registry.npmjs.org/@benjosivo/table-query/-/table-query-1.0.2.tgz","fileCount":26,"integrity":"sha512-OIhZIocMjY8Tk8lFZ4gMMhGZJdHs5X/DM0Pw5f/7vHa6AoK5CHaZuh+ULmfZ47BVvfCA8CSHeXvGTfLH42bFdg==","signatures":[{"sig":"MEUCIAlZjP4uVeiWkBpE5Dv+AEMrBiu+rKzaZ8yQMZ0xa7znAiEA5Uvua7l+ecuzbMoGy1sGX5dcIo8mhqbM0lgpV7mipB4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":62674},"main":"index.js","type":"module","exports":{"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"4f7115debf8823f14fa10137a10cf3f2d109858b","scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"benjosivo","email":"macklem75007@gmail.com"},"repository":{"url":"git+https://github.com/benjosivo/table-query.git","type":"git"},"_npmVersion":"12.0.2","description":"Table triable/filtrable/paginée : hook + composants React d'un côté (`/react`), logique SQL de pagination/tri/filtres côté serveur de l'autre (`/server`). Un projet peut n'utiliser qu'un des deux côtés.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"express":"^5.2.1","@benjosivo/mysql":"^1.3.2"},"publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0","@types/react":"^18.0.0","@types/express":"^5.0.6"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"express":{"optional":true},"@benjosivo/mysql":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/table-query_1.0.2_1789033944013_0.13129139680796986","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@benjosivo/table-query","version":"1.0.3","keywords":[],"author":{"name":"benjosivo"},"license":"ISC","_id":"@benjosivo/table-query@1.0.3","maintainers":[{"name":"benjosivo","email":"macklem75007@gmail.com"}],"homepage":"https://github.com/benjosivo/table-query#readme","bugs":{"url":"https://github.com/benjosivo/table-query/issues"},"dist":{"shasum":"175787deaf7defd64868c06abe0b151e0a86e751","tarball":"https://registry.npmjs.org/@benjosivo/table-query/-/table-query-1.0.3.tgz","fileCount":26,"integrity":"sha512-gNVwxmaMVj3LaZ36jibzSBeLET9xGiXIekz32V/ZKEHEoiuCneUpUhmlUXZ7y2qKpqiM+xYnbRioAeEhMPspTg==","signatures":[{"sig":"MEUCIDDklNeQtm7y2pNGUEFy/SEoXh7QbRsiusMImH79JAeMAiEA/xkAsB5rCI5V+smNx9KK6bNA4iruJrVcOr69KvPqt7k=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":62953},"main":"index.js","type":"module","exports":{"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"e866e8a44f2eb3888f457ab027821ce5b488fc0c","scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"benjosivo","email":"macklem75007@gmail.com"},"repository":{"url":"git+https://github.com/benjosivo/table-query.git","type":"git"},"_npmVersion":"12.0.2","description":"Table triable/filtrable/paginée : hook + composants React d'un côté (`/react`), logique SQL de pagination/tri/filtres côté serveur de l'autre (`/server`). Un projet peut n'utiliser qu'un des deux côtés.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"express":"^5.2.1","@benjosivo/mysql":"^1.3.2"},"publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0","@types/react":"^18.0.0","@types/express":"^5.0.6"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"express":{"optional":true},"@benjosivo/mysql":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/table-query_1.0.3_1789044076261_0.5032879260433061","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@benjosivo/table-query","version":"1.0.4","keywords":[],"author":{"name":"benjosivo"},"license":"ISC","_id":"@benjosivo/table-query@1.0.4","maintainers":[{"name":"benjosivo","email":"macklem75007@gmail.com"}],"homepage":"https://github.com/benjosivo/table-query#readme","bugs":{"url":"https://github.com/benjosivo/table-query/issues"},"dist":{"shasum":"9dc9176c0cce9648e25e73cb0072a33e23bd903d","tarball":"https://registry.npmjs.org/@benjosivo/table-query/-/table-query-1.0.4.tgz","fileCount":26,"integrity":"sha512-/UsdJXJpAYHGjdJRD4vcvf2YjPtLq2PfVE5WlmODLImUdYE6oIzKbcMzjTqGVxhNtnP+8CN7RcL/ZvErHNCBIw==","signatures":[{"sig":"MEUCIAtOO8v1cDH4W0kboybdBd55LYBR8F9MhaH0jj9KubqhAiEA9W/q17IahQMgDVBrBDo9sJtBDV8y0mygL2nGi/zjK+U=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIAEeJxAtVZHgvnHPQWOIum0ZIprukyezVdRPkbXIfuUEAiB/GX4sSkWK6C6na7PuiUEACGIGkq4POq3jNiRXEa2LPQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":77132},"main":"index.js","type":"module","exports":{"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"875dcba4b2c204cfd4714a50ce5d6abff220f49c","scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"benjosivo","email":"macklem75007@gmail.com"},"repository":{"url":"git+https://github.com/benjosivo/table-query.git","type":"git"},"_npmVersion":"12.0.2","description":"Table triable/filtrable/paginée : hook + composants React d'un côté (`/react`), logique SQL de pagination/tri/filtres côté serveur de l'autre (`/server`). Un projet peut n'utiliser qu'un des deux côtés.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"express":"^5.2.1","@benjosivo/mysql":"^1.3.2"},"publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0","@types/react":"^18.0.0","@types/express":"^5.0.6"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"express":{"optional":true},"@benjosivo/mysql":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/table-query_1.0.4_1789454956527_0.10980540915657055","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@benjosivo/table-query","version":"1.2.0","keywords":[],"author":{"name":"benjosivo"},"license":"ISC","_id":"@benjosivo/table-query@1.2.0","maintainers":[{"name":"benjosivo","email":"macklem75007@gmail.com"}],"homepage":"https://github.com/benjosivo/table-query#readme","bugs":{"url":"https://github.com/benjosivo/table-query/issues"},"dist":{"shasum":"10cf94193d97b5aea8d97edbcbdc602faf342ef6","tarball":"https://registry.npmjs.org/@benjosivo/table-query/-/table-query-1.2.0.tgz","fileCount":34,"integrity":"sha512-2oEYqZhruhi8zVJdPS7EIKhX4HmHrHZQ09GZ1IU/3ZMTSSkl4eTTdrQJZiuBQ+wFw/EJ7DW0uhHeFT8HDx/zSA==","signatures":[{"sig":"MEUCIEfeFvSRfDPBg2wScfEz7T/8BwEtC336Vz4QyEUgjGgFAiEAjj+yFjYX9BCXYOb1of4oMGMgdTWkDpyOwY/m4XFKO3A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIA2l3jbHScu+s/1JqELwMSXEDdEbxSEgkqK/AUUlCzL+AiEAiUjpWvRWzXAfFioaDi3QxoxQnO4Mg2XZRzAjVYIT9Uw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":141912},"main":"index.js","type":"module","exports":{"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"c00b4b4fc2fe580403c9148f43c9a78fa5b35932","scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"benjosivo","email":"macklem75007@gmail.com"},"repository":{"url":"git+https://github.com/benjosivo/table-query.git","type":"git"},"_npmVersion":"12.0.2","description":"Table triable/filtrable/paginée : hook + composants React d'un côté (`/react`), logique SQL de pagination/tri/filtres côté serveur de l'autre (`/server`). Un projet peut n'utiliser qu'un des deux côtés.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"express":"^5.2.1","@benjosivo/mysql":"^1.3.2"},"publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0","@types/react":"^18.0.0","@types/express":"^5.0.6"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"express":{"optional":true},"@benjosivo/mysql":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/table-query_1.2.0_1789465988438_0.007713094154701672","host":"s3://npm-registry-packages-npm-production"}},"1.2.1":{"name":"@benjosivo/table-query","version":"1.2.1","keywords":[],"author":{"name":"benjosivo"},"license":"ISC","_id":"@benjosivo/table-query@1.2.1","maintainers":[{"name":"benjosivo","email":"macklem75007@gmail.com"}],"homepage":"https://github.com/benjosivo/table-query#readme","bugs":{"url":"https://github.com/benjosivo/table-query/issues"},"dist":{"shasum":"bcfc68825559e5f72083b6891d650a25e1f509e0","tarball":"https://registry.npmjs.org/@benjosivo/table-query/-/table-query-1.2.1.tgz","fileCount":34,"integrity":"sha512-KFZUh2FhgQQOz/GpeKB8eD5TVBDdc5pW1at2efIoOSbLza45PCgFwuMpIGLV4v+OyiE4TP8oI6AR08v3KaM12A==","signatures":[{"sig":"MEUCIH6z+H7zWkkTePEc3fzuAgMnJlUvW4JGQqeZ1vEY5gQtAiEAiHF1XVTX1rKB43+q2LaXS5v8hvDWo7Z3SD8CX+a2gD0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQDZsFBGESScRVySmelTHaHUVUYydMaZZrw+JE+uux94iAIgWOTNzzmLQ4eNtHHnQJcU4SkwuURek3MrJHglzlw2UXs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":142375},"main":"index.js","type":"module","exports":{"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"686c1ef4838bed96026501bed22173f35b580532","scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"benjosivo","email":"macklem75007@gmail.com"},"repository":{"url":"git+https://github.com/benjosivo/table-query.git","type":"git"},"_npmVersion":"12.0.2","description":"Table triable/filtrable/paginée : hook + composants React d'un côté (`/react`), logique SQL de pagination/tri/filtres côté serveur de l'autre (`/server`). Un projet peut n'utiliser qu'un des deux côtés.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"express":"^5.2.1","@benjosivo/mysql":"^1.3.2"},"publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0","@types/react":"^18.0.0","@types/express":"^5.0.6"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"express":{"optional":true},"@benjosivo/mysql":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/table-query_1.2.1_1789626445585_0.22787067208104506","host":"s3://npm-registry-packages-npm-production"}},"1.2.2":{"name":"@benjosivo/table-query","version":"1.2.2","keywords":[],"author":{"name":"benjosivo"},"license":"ISC","_id":"@benjosivo/table-query@1.2.2","maintainers":[{"name":"benjosivo","email":"macklem75007@gmail.com"}],"homepage":"https://github.com/benjosivo/table-query#readme","bugs":{"url":"https://github.com/benjosivo/table-query/issues"},"dist":{"shasum":"325b5f7205eebfb15b8d597b363c3b1d81b0f765","tarball":"https://registry.npmjs.org/@benjosivo/table-query/-/table-query-1.2.2.tgz","fileCount":34,"integrity":"sha512-v1eK2g5RKvH2Wjl1mXhFXmXUwvFg+QuitC46roCOWbNhaVLNlsH6fYrSPf4qgFz7k2GZ8RIqm/GkqV79WZmXvQ==","signatures":[{"sig":"MEUCIQDDv7wBM3m1svOriuDO/4bAHMZJ962W9LvrjWtkWBHQvgIgLBGKdDvIuA33qhKmzYAvjL151cvt7pbsN+7TM/sal7E=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIAsVy6ag40Pma8JTw3DqvvTV4T3baCyPELKhu3D2SX+gAiEAhP6WI++pmOEMfVLiCNG5Tme7GoJq5ExvZV6O8x+W5b8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":141693},"main":"index.js","type":"module","exports":{"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"650144f4ebd8e503f6f0cb93a0a49f4721599dd4","scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"benjosivo","email":"macklem75007@gmail.com"},"repository":{"url":"git+https://github.com/benjosivo/table-query.git","type":"git"},"_npmVersion":"12.0.2","description":"Table triable/filtrable/paginée : hook + composants React d'un côté (`/react`), logique SQL de pagination/tri/filtres côté serveur de l'autre (`/server`). Un projet peut n'utiliser qu'un des deux côtés.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"express":"^5.2.1","@benjosivo/mysql":"^1.3.2"},"publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0","@types/react":"^18.0.0","@types/express":"^5.0.6"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"express":{"optional":true},"@benjosivo/mysql":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/table-query_1.2.2_1789720540722_0.2868074469946149","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"_id":"@benjosivo/table-query@1.3.0","bugs":{"url":"https://github.com/benjosivo/table-query/issues"},"dist":{"shasum":"155b3e90f0cf9500cc63a3a542ef5e7378cffd58","tarball":"https://registry.npmjs.org/@benjosivo/table-query/-/table-query-1.3.0.tgz","fileCount":34,"integrity":"sha512-HAnaR2F+6Lh+Hb+VOIrGlhVAvOLlAySg1WUGd8o1OBfsWMnNd+oB888ga9gLTt7hzfMWs67WN7I7ix6Mo4wjMg==","signatures":[{"sig":"MEUCIQCEOTh5dqnHJDJNQ3XlUy0b8JsuJu1FH/oPiOSFeUf1oQIgANiGB2Cfr9QEHwHg6XgXMd7jPnwVNE2oIOlveIJwBbk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIC2sswdln8RZxAkM3jBly7ir07YAtL5PbsLCwF/cQ2vAAiB/PZU72i+rYojWI5Y4E5UdsFevcPYlIbClciNH2VAGJw=="}],"unpackedSize":151214},"main":"index.js","name":"@benjosivo/table-query","type":"module","author":{"name":"benjosivo"},"exports":{"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"594f3d16b9f059646b7e229e1057d1785c9ed926","license":"ISC","scripts":{"build":"tsc","prepublishOnly":"npm run build"},"version":"1.3.0","_npmUser":{"name":"benjosivo","email":"macklem75007@gmail.com"},"homepage":"https://github.com/benjosivo/table-query#readme","keywords":[],"repository":{"url":"git+https://github.com/benjosivo/table-query.git","type":"git"},"_npmVersion":"12.0.2","description":"Table triable/filtrable/paginée : hook + composants React d'un côté (`/react`), logique SQL de pagination/tri/filtres côté serveur de l'autre (`/server`). Un projet peut n'utiliser qu'un des deux côtés.","directories":{},"maintainers":[{"name":"benjosivo","email":"macklem75007@gmail.com"}],"_nodeVersion":"24.15.0","dependencies":{"express":"^5.2.1","@benjosivo/mysql":"^1.3.2"},"publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0","@types/react":"^18.0.0","@types/express":"^5.0.6"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"express":{"optional":true},"@benjosivo/mysql":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/table-query_1.3.0_1790073179560_0.6770488851500398"}}},"time":{"created":"2026-09-10T09:37:24.772Z","modified":"2026-09-22T10:32:59.805Z","1.0.0":"2026-09-10T09:37:25.126Z","1.0.1":"2026-09-10T09:46:26.849Z","1.0.2":"2026-09-10T09:52:24.141Z","1.0.3":"2026-09-10T12:41:16.400Z","1.0.4":"2026-09-15T06:49:16.675Z","1.2.0":"2026-09-15T09:53:08.542Z","1.2.1":"2026-09-17T06:27:25.674Z","1.2.2":"2026-09-18T08:35:40.823Z","1.3.0":"2026-09-22T10:32:59.666Z"},"bugs":{"url":"https://github.com/benjosivo/table-query/issues"},"author":{"name":"benjosivo"},"license":"ISC","homepage":"https://github.com/benjosivo/table-query#readme","keywords":[],"repository":{"url":"git+https://github.com/benjosivo/table-query.git","type":"git"},"description":"Table triable/filtrable/paginée : hook + composants React d'un côté (`/react`), logique SQL de pagination/tri/filtres côté serveur de l'autre (`/server`). Un projet peut n'utiliser qu'un des deux côtés.","maintainers":[{"name":"benjosivo","email":"macklem75007@gmail.com"}],"readme":"# @benjosivo/table-query\r\n\r\nTableau de données triable, filtrable et paginé, en deux parties indépendantes :\r\n\r\n- **`@benjosivo/table-query/server`** — logique de requêtage côté serveur (Express + MySQL) : pagination, tri, filtres, calcul des valeurs disponibles pour chaque colonne, mise en cache optionnelle.\r\n- **`@benjosivo/table-query/react`** — composants et hook React pour afficher ce type de données : tableau, pagination, filtre rapide par colonne, panneau de filtres avancés (multiselect / plage numérique / plage de dates).\r\n\r\nLes deux parties fonctionnent ensemble mais peuvent aussi être utilisées séparément (par exemple le composant React seul, avec ton propre backend).\r\n\r\n## Installation\r\n\r\n```bash\r\nnpm install @benjosivo/table-query\r\n```\r\n\r\nSelon la partie utilisée, il faut aussi avoir installé dans le projet :\r\n\r\n| Partie utilisée | Dépendances nécessaires |\r\n|---|---|\r\n| `/server` | `express`, `@benjosivo/mysql` |\r\n| `/react` | `react` (v18+) |\r\n\r\nCe sont des `peerDependencies` : le package ne les installe pas lui-même, il utilise celles déjà présentes dans ton projet.\r\n\r\n---\r\n\r\n## Côté serveur\r\n\r\n### Créer le module\r\n\r\n```ts\r\nimport { createTableQueryModule } from '@benjosivo/table-query/server';\r\n\r\nconst { router, reqTableQuery } = createTableQueryModule({\r\n    // Formate une date/heure au format attendu par MySQL\r\n    convertToMySQLDateTime: (date) => /* ta fonction */,\r\n\r\n    // Enveloppe un handler Express (gestion d'erreurs, etc.)\r\n    wrapRouteHandler: (fn) => /* ta fonction */,\r\n\r\n    // Optionnel : uniquement nécessaire si tu veux pouvoir utiliser le cache (voir plus bas)\r\n    cache: {\r\n        getSQLCache: (key) => /* lit une entrée de cache */,\r\n        setSQLCache: (key, value) => /* écrit une entrée de cache */,\r\n        deleteSQLCache: (key) => /* supprime une entrée de cache */,\r\n    },\r\n});\r\n```\r\n\r\n`router` expose la route `GET /getFiltres`, utilisée en interne pour récupérer les filtres calculés en tâche de fond. Monte-le sur le chemin de ton choix :\r\n\r\n```ts\r\napp.use('/tableCreation/api', router);\r\n```\r\n\r\n### Répondre à une requête de table\r\n\r\n```ts\r\napp.post('/api/commandes', wrapRouteHandler(async (req, res) => {\r\n    const result = await reqTableQuery({\r\n        query: `SELECT c.*, COUNT(*) OVER() AS TotalCount FROM commandes c`,\r\n        req,\r\n        // Un type de filtre par colonne renvoyée par la requête (sauf la 1ère, l'identifiant)\r\n        paramFilter: ['MULTISELECT', 'HIDE', 'DATE', 'MULTISELECT'],\r\n    });\r\n\r\n    if (result.error) return res.status(result.status ?? 500).send(result.error);\r\n    res.json(result.data);\r\n}));\r\n```\r\n\r\n`reqTableQuery` lit `limit`, `offset`, `sorting`, `filtre` et `setFilter` dans `req.body` (ou `req.query`) — c'est exactement ce que le composant React `DataTable`/`useDataTable` envoie, donc les deux côtés s'emboîtent directement.\r\n\r\n### Types de filtre disponibles (`paramFilter`)\r\n\r\n| Type | Effet |\r\n|---|---|\r\n| `'MULTISELECT'` | Liste de valeurs distinctes de la colonne, sélection multiple |\r\n| `'UNGROUP_MULTISELECT'` | Comme `MULTISELECT`, mais éclate les valeurs séparées par `, ` dans une même cellule |\r\n| `'SLIDER'` | Plage numérique (min/max) |\r\n| `'DATE'` / `'DATETIME'` | Plage de dates |\r\n| `'JSON'` / `'FILE'` | Pas de filtre, juste un rendu spécifique côté React |\r\n| `'HIDE'` | Colonne sans filtre |\r\n| `null` | Colonne sans filtre particulier |\r\n\r\n### Règles de mise en forme (`formattingRules`)\r\n\r\n`reqTableQuery` peut transporter des règles de couleur jusqu'au client. Elles sont renvoyées **telles quelles** dans `payload.formattingRules` : le serveur ne les évalue jamais et elles ne touchent **jamais** au SQL.\r\n\r\n```js\r\nconst { data } = await reqTableQuery({\r\n    query: `SELECT ...`,\r\n    req,\r\n    paramFilter: [null, 'MULTISELECT', 'SLIDER'],\r\n    formattingRules: [\r\n        { column: 'statut', operator: '=', value: 'en retard', style: { backgroundColor: '#ffd7d7' } },\r\n        { column: 'montant', operator: '>', value: 10000, target: 'cell', style: { fontWeight: 'bold' } },\r\n    ],\r\n});\r\n```\r\n\r\nLes règles sont indexées **par nom de colonne** (contrairement à `paramFilter`, qui est positionnel). Une règle dont la `column` — ou celle d'une des `conditions` supplémentaires — ne correspond à aucune colonne de la requête est ignorée en entier, avec un `console.warn` — jamais une erreur.\r\n\r\n### Le cache (`useCache`)\r\n\r\nLe cache est activé **par appel**, pas globalement :\r\n\r\n```ts\r\nreqTableQuery({ query, req, paramFilter, useCache: true });  // utilise le cache\r\nreqTableQuery({ query, req, paramFilter, useCache: false }); // toujours une donnée fraîche\r\n```\r\n\r\n- Si `cache` a été fourni à `createTableQueryModule`, `useCache` vaut `true` par défaut.\r\n- Si `cache` n'a pas été fourni, `useCache` vaut `false` par défaut (aucun Redis requis).\r\n- Demander `useCache: true` sans avoir fourni `cache` lève une erreur explicite.\r\n- Avec `useCache: false`, les valeurs de filtre disponibles sont calculées et renvoyées directement dans la réponse. Avec `useCache: true`, elles sont calculées en tâche de fond et le composant React va les chercher via `/getFiltres` (polling automatique, rien à faire côté appelant).\r\n\r\n---\r\n\r\n## Côté React\r\n\r\n### Utilisation simple (tout inclus)\r\n\r\n```tsx\r\nimport { DataTable } from '@benjosivo/table-query/react';\r\n\r\nfunction CommandesTable() {\r\n    return (\r\n        <DataTable\r\n            fetchData={(params) =>\r\n                fetch('/api/commandes', {\r\n                    method: 'POST',\r\n                    headers: { 'Content-Type': 'application/json' },\r\n                    body: JSON.stringify(params),\r\n                }).then((res) => (res.ok ? res.json() : null))\r\n            }\r\n            onRowClick={(id, row) => console.log('ligne cliquée', id, row)}\r\n            advancedFilters\r\n        />\r\n    );\r\n}\r\n```\r\n\r\n#### Props de `<DataTable />`\r\n\r\n| Prop | Type | Description |\r\n|---|---|---|\r\n| `fetchData` | `(params) => Promise<FetchResult \\| null>` | **Obligatoire.** Appelée à chaque changement de page/tri/filtre. |\r\n| `onRowClick` | `(id, row) => void` | Appelée au clic sur une ligne |\r\n| `filterEnabled` | `boolean` (défaut `true`) | Affiche le bouton de filtre rapide par colonne |\r\n| `sortingEnabled` | `boolean` (défaut `true`) | Active le tri au clic sur l'en-tête |\r\n| `advancedFilters` | `boolean` (défaut `false`) | Affiche le panneau de filtres avancés (nécessite `setFilter`/`paramFilter` côté serveur) |\r\n| `height` | `string` (défaut `'76vh'`) | Hauteur max de la zone de défilement |\r\n| `rowsPerPageOptions` | `number[]` | Choix disponibles pour le nombre de lignes par page |\r\n| `defaultRowsPerPage` | `number` | Valeur par défaut |\r\n| `onImagePreview` | `(src) => void` | Appelée au clic sur une image (sinon ouverture dans un nouvel onglet) |\r\n| `selectable` | `boolean` (défaut `false`) | Affiche une colonne de checkbox (voir « Sélection de lignes ») |\r\n| `selectionColumnPosition` | `number \\| 'start' \\| 'end'` (défaut `'start'`) | Position de la colonne de checkbox parmi les colonnes visibles |\r\n| `selectedIds` | `any[]` | Sélection contrôlée par le parent (voir plus bas) |\r\n| `onSelectionChange` | `(ids, rows) => void` | Appelée à chaque changement de sélection, avec les ids et les lignes complètes |\r\n| `formattingRules` | `FormattingRule[]` | Règles de mise en forme conditionnelle (voir « Mise en forme conditionnelle ») |\r\n| `getRowFormatting` | `(row, i) => {style?, className?}` | Échappatoire : style de ligne calculé en JS, appliqué après toutes les règles |\r\n| `getCellFormatting` | `(col, value, row, i) => {style?, className?}` | Idem, par cellule |\r\n| `formattingEditor` | `boolean` (défaut `false`) | Affiche le bouton « Mise en forme » pour l'utilisateur final |\r\n| `formattingStorageKey` | `string` | Sauvegarde les règles de l'utilisateur dans `localStorage` sous cette clé |\r\n| `onFormattingRulesChange` | `(rules) => void` | Appelée à chaque modification des règles de l'utilisateur |\r\n| `initialUserFormattingRules` | `FormattingRule[]` | Réhydrate les règles utilisateur depuis ton backend (prioritaire sur `localStorage`) |\r\n| `formattingButtonLabel` | `string` (défaut `'Mise en forme'`) | Libellé du bouton de la barre d'outils |\r\n\r\n### Sélection de lignes (checkbox)\r\n\r\nAvec `selectable`, une colonne de checkbox est ajoutée. La checkbox de l'en-tête coche/décoche **toutes les lignes affichées** (la page courante) et passe en état indéterminé quand seule une partie l'est.\r\n\r\n```tsx\r\nconst [selection, setSelection] = useState<any[]>([]);\r\n\r\n<DataTable\r\n    fetchData={fetchCommandes}\r\n    selectable\r\n    selectionColumnPosition='start'   // 'start' | 'end' | index (ex. 2)\r\n    onSelectionChange={(ids, rows) => {\r\n        setSelection(ids);            // ids = valeur de la 1ère colonne de chaque ligne\r\n        console.log(rows);            // les lignes entières, comme dans onRowClick\r\n    }}\r\n/>\r\n```\r\n\r\n- **Identifiant d'une ligne** : la valeur de sa **première colonne** — la même règle que `onRowClick`. La colonne peut être masquée (`HIDE`), l'identifiant reste utilisable.\r\n- **Position** : `'start'` (défaut), `'end'`, ou un index 0-based **parmi les colonnes visibles** (`2` = 3ᵉ colonne ; les colonnes `HIDE` ne comptent pas). Un index hors limites est ramené au début ou à la fin.\r\n- **Entre les pages** : la sélection est cumulative — on peut cocher des lignes page 1, aller page 2, et `onSelectionChange` renvoie l'ensemble (ids **et** lignes complètes, même celles qui ne sont plus affichées).\r\n- **Changement de tri ou de filtre** : le jeu de données n'est plus le même, la sélection est donc vidée et `onSelectionChange([], [])` est appelée. Changer de page ou le nombre de lignes par page ne la vide pas.\r\n- Cliquer une checkbox ne déclenche pas `onRowClick`.\r\n\r\n#### Sélection contrôlée\r\n\r\nSi le parent passe `selectedIds`, c'est lui qui détient l'état : le tableau n'affiche que ce qu'on lui donne. Pratique pour cocher des lignes par programme ou vider la sélection après une action groupée.\r\n\r\n```tsx\r\nconst [selection, setSelection] = useState<any[]>([]);\r\n\r\nconst supprimer = async () => {\r\n    await fetch('/api/commandes/suppression', { method: 'POST', body: JSON.stringify({ ids: selection }) });\r\n    setSelection([]); // on vide la sélection nous-mêmes\r\n};\r\n\r\n<DataTable\r\n    fetchData={fetchCommandes}\r\n    selectable\r\n    selectedIds={selection}\r\n    onSelectionChange={(ids) => setSelection(ids)}\r\n/>\r\n```\r\n\r\n### Mise en forme conditionnelle (couleurs)\r\n\r\nColorer des lignes ou des cellules selon leurs valeurs, façon Excel. Les règles sont des objets **sérialisables** : elles peuvent être écrites en dur, stockées en base, ou renvoyées par l'API.\r\n\r\n```tsx\r\n<DataTable\r\n    fetchData={fetchCommandes}\r\n    formattingRules={[\r\n        // Toute la ligne en rouge pâle quand le statut vaut \"en retard\"\r\n        { column: 'statut', operator: '=', value: 'en retard', style: { backgroundColor: '#ffd7d7' } },\r\n        // Seulement la cellule \"montant\" en gras vert au-dessus de 10 000\r\n        { column: 'montant', operator: '>', value: 10000, target: 'cell', style: { color: '#0a7d32', fontWeight: 'bold' } },\r\n        // Deux colonnes précises, via une classe CSS de ton app\r\n        { column: 'livraison', operator: 'isNull', target: ['livraison', 'transporteur'], className: 'a-completer' },\r\n        // Plusieurs colonnes DANS la condition : en retard ET montant élevé, avec une info-bulle\r\n        {\r\n            column: 'statut',\r\n            operator: '=',\r\n            value: 'en retard',\r\n            conditions: [{ column: 'montant', operator: '>', value: 10000 }],\r\n            style: { backgroundColor: '#c0392b', color: 'white' },\r\n            title: 'En retard et montant élevé',\r\n        },\r\n    ]}\r\n/>\r\n```\r\n\r\nLa librairie ne livre **aucun CSS** : `style` (inline) fonctionne sans configuration, `className` suppose que ton app définit la classe.\r\n\r\n#### Écrire une règle (`FormattingRule`)\r\n\r\n| Champ | Type | Description |\r\n|---|---|---|\r\n| `column` | `string` | **Obligatoire.** Nom de la colonne testée par la condition principale (une clé des objets de `items`) |\r\n| `operator` | voir table ci-dessous | **Obligatoire.** |\r\n| `value` | `any` | L'opérande ; sa forme dépend de l'opérateur |\r\n| `conditions` | `FormattingCondition[]` | Conditions supplémentaires (`{ column, operator, value?, valueType? }`), combinées avec la condition principale via `conditionLogic` |\r\n| `conditionLogic` | `'AND' \\| 'OR'` | Comment `conditions` se combine à la condition principale. Défaut `'AND'` |\r\n| `target` | `'row' \\| 'cell' \\| string[]` | Ce qui est coloré. Défaut `'row'`. `'cell'` désigne toujours la colonne de la condition **principale**, quel que soit le nombre de `conditions` |\r\n| `style` | `CSSProperties` | Style inline appliqué au `<tr>` ou au `<td>` |\r\n| `className` | `string` | Classe CSS ajoutée au `<tr>` ou au `<td>` |\r\n| `title` | `string` | Attribut HTML `title` (info-bulle native) posé sur le `<tr>` ou le `<td>` quand la règle matche |\r\n| `valueType` | `'auto' \\| 'string' \\| 'number' \\| 'date' \\| 'boolean'` | Force le mode de comparaison. Défaut `'auto'` |\r\n| `stopIfTrue` | `boolean` | Arrête les règles suivantes sur ce que cette règle a coloré |\r\n| `enabled` | `boolean` | `false` conserve la règle sans l'appliquer. Défaut `true` |\r\n| `label` | `string` | Libellé affiché dans l'éditeur |\r\n| `id` | `string` | Généré automatiquement si absent |\r\n\r\n| Opérateur | Opérande |\r\n|---|---|\r\n| `=` `!=` `<` `<=` `>` `>=` | une valeur |\r\n| `between` | `[min, max]` ou `{min, max}` — **bornes incluses**, inversées tolérées |\r\n| `in` | un tableau, ou une chaîne `\"a, b, c\"` |\r\n| `contains` `notContains` `startsWith` `endsWith` | une valeur (toujours comparée en texte) |\r\n| `isNull` `isNotNull` | aucune |\r\n\r\n#### Cible d'une règle (`target`)\r\n\r\n- `'row'` (défaut) → le `<tr>` entier.\r\n- `'cell'` → seulement la cellule de la colonne testée.\r\n- `['col_a', 'col_b']` → ces colonnes-là.\r\n\r\nLe style de ligne est **aussi** posé sur chaque `<td>`. Sans cela, le moindre CSS de ton app sur `td` (zébrage, `tbody td { background: #fff }`) recouvrirait le fond du `<tr>` et la couleur semblerait ne pas marcher. Une règle `'cell'` est étalée **par-dessus**, elle l'emporte donc propriété par propriété.\r\n\r\nLes `className`, eux, ne descendent **pas** sur les `<td>` : une classe de ligne est un point d'accroche pour ton propre CSS, écris `tr.ma-classe td { ... }`.\r\n\r\n#### Conditions multiples (`conditions`, `conditionLogic`)\r\n\r\nUne règle peut tester **plusieurs colonnes à la fois** avant de colorer, indépendamment de ce qu'elle colore (`target`) :\r\n\r\n```tsx\r\n{\r\n    column: 'age', operator: '>', value: 18,\r\n    conditions: [{ column: 'statut', operator: '=', value: 'actif' }],\r\n    conditionLogic: 'AND', // défaut — 'OR' matche si au moins une condition est vraie\r\n    target: 'row',\r\n    style: { backgroundColor: '#eaffea' },\r\n}\r\n```\r\n\r\n`conditions` est une liste **plate** (pas de groupes imbriqués ET-de-OU) combinée à la condition principale (`column`/`operator`/`value`) par un seul `conditionLogic`. Une règle sans `conditions` se comporte exactement comme avant.\r\n\r\n#### Ordre, fusion et `stopIfTrue`\r\n\r\nLes règles sont évaluées **dans l'ordre**, et cet ordre **est** la priorité :\r\n\r\n1. `formattingRules` (props de ton app)\r\n2. les règles renvoyées par l'API\r\n3. les règles créées par l'utilisateur dans l'éditeur\r\n\r\nLes styles se **cumulent** ; sur une même propriété CSS, **la dernière règle gagne**. Deux règles peuvent donc apporter l'une le fond, l'autre le gras.\r\n\r\n`stopIfTrue` gèle exactement ce que la règle a coloré : posé sur une règle `'row'`, il bloque les règles `'row'` suivantes mais **pas** les règles `'cell'` ; posé sur une règle `'cell'`, il ne gèle que cette cellule.\r\n\r\n#### Comparaison des valeurs\r\n\r\nLes valeurs viennent de MySQL : un `DECIMAL` arrive en chaîne, une `DATE` en objet `Date`. En mode `'auto'`, la comparaison essaie dans cet ordre : **date** (si un `Date` est en jeu) → **nombre** (si les deux côtés sont numériques) → **date ISO** → **texte**.\r\n\r\n- Le texte est comparé **sans tenir compte de la casse**.\r\n- `'007'` et `'7'` sont **égaux** en mode auto (comparaison numérique). Pour une référence ou un code postal, mets `valueType: 'string'`.\r\n- `'2024'` est traité comme un **nombre**, pas comme une année.\r\n- Une valeur `'YYYY-MM-DD'` désigne le **jour entier** : `= '2024-01-05'` matche un `DATETIME` du 5 à 14h32, et `between` inclut toute la journée de fin.\r\n\r\n**Valeurs nulles** : `isNull` matche `null`, `undefined` et la chaîne vide ; `!=` et `notContains` matchent sur une valeur nulle ; **tous les autres opérateurs ne matchent jamais** sur `null`. Une règle visant une colonne **inexistante** ne matche rien du tout (y compris `isNull`).\r\n\r\n#### Message d'info-bulle (`title`)\r\n\r\nQuand une règle matche, son `title` (s'il est défini) est posé comme attribut HTML natif sur le `<tr>` (ou le `<td>` pour une cible `'cell'`/colonnes choisies) — l'utilisateur final voit *pourquoi* c'est coloré au survol, sans ouvrir l'éditeur. Plusieurs règles qui matchent le même élément **concatènent** leurs messages (séparés par un saut de ligne) plutôt que de s'écraser.\r\n\r\nPas besoin de répéter le message sur chaque cellule : l'attribut `title` **s'hérite nativement** en HTML, un `<td>` sans son propre `title` affiche celui de son `<tr>` au survol. Une cellule avec sa propre règle `'cell'`/`title` affiche le sien à la place, exactement comme son style l'emporte sur celui de la ligne.\r\n\r\n#### Échappatoire : `getRowFormatting` / `getCellFormatting`\r\n\r\nPour une logique qui croise plusieurs colonnes, hors de portée d'une règle déclarative :\r\n\r\n```tsx\r\nconst getRowFormatting = useCallback(\r\n    (row) => (row.livree > row.commandee ? { style: { backgroundColor: '#ffe6e6' } } : null),\r\n    [],\r\n);\r\n\r\n<DataTable fetchData={fetchCommandes} getRowFormatting={getRowFormatting} />\r\n```\r\n\r\nCes callbacks sont appliqués **en dernier** et ignorent `stopIfTrue` — ils l'emportent toujours. **Enveloppe-les dans `useCallback`**, sinon le calcul des couleurs est refait à chaque rendu.\r\n\r\n#### L'éditeur pour l'utilisateur final (`formattingEditor`)\r\n\r\nDésactivé par défaut. Avec `formattingEditor`, un bouton « Mise en forme » apparaît au-dessus du tableau et ouvre une modale où l'utilisateur ajoute, réordonne, désactive et supprime ses propres règles. Chaque règle peut recevoir des conditions supplémentaires (bouton « + Ajouter une condition », combinées en ET/OU) et un message d'info-bulle (« Message (info-bulle) »).\r\n\r\n```tsx\r\n<DataTable\r\n    fetchData={fetchCommandes}\r\n    formattingEditor\r\n    formattingStorageKey='commandes'            // persistance locale, gérée par la lib\r\n    onFormattingRulesChange={(rules) => save(rules)}  // ou ta propre persistance serveur\r\n    initialUserFormattingRules={reglesDeMonBackend}   // prioritaire sur localStorage\r\n/>\r\n```\r\n\r\n- `formattingStorageKey` écrit sous la clé réelle `tableQuery:formatting:<clé>`, au format `{\"v\":1,\"rules\":[...],\"disabled\":[...]}`. Tout accès au stockage est protégé (SSR, navigation privée, quota dépassé) et une version inconnue est ignorée.\r\n- L'utilisateur n'édite que **sa** couche. Les règles venues des props et de l'API s'affichent en lecture seule, avec une case pour les désactiver.\r\n- Sans `formattingEditor`, aucun nœud supplémentaire n'est ajouté au DOM.\r\n\r\n#### Limites connues\r\n\r\n- Sur une colonne `JSON`, la coloration du texte peut sembler sans effet : le rendu JSON pose ses propres `<span class=\"key|string|number\">`, et le CSS de ton app sur ces classes l'emporte sur la couleur héritée du `<td>`. Le fond, lui, fonctionne.\r\n- Sur une colonne `FILE`/`BLOB`, le contenu est un `<img>` ou un `<button>` : le fond s'affiche autour, mais la couleur du texte est écrasée par le style de tes boutons.\r\n- Une règle visant une colonne masquée (`HIDE`) est bien évaluée, mais une cible `'cell'` sur cette colonne n'a aucun effet visible.\r\n\r\n### Utilisation avec ta propre UI de filtre\r\n\r\nSi le système de filtre par défaut ne convient pas, utilise le hook et les composants séparément :\r\n\r\n```tsx\r\nimport { useDataTable, Table, Pagination } from '@benjosivo/table-query/react';\r\n\r\nfunction CommandesTable() {\r\n    const table = useDataTable({ fetchData: fetchCommandes });\r\n    const [recherche, setRecherche] = useState('');\r\n\r\n    const rechercher = () => {\r\n        table.replaceFilters({ reference: [`/*/${recherche}/*/`] });\r\n        // ou, filtre par filtre : table.setColumnFilter('statut', ['en_cours']);\r\n    };\r\n\r\n    return (\r\n        <>\r\n            <input value={recherche} onChange={(e) => setRecherche(e.target.value)} />\r\n            <button onClick={rechercher}>Rechercher</button>\r\n\r\n            <Table\r\n                items={table.items}\r\n                fieldsType={table.fieldsType}\r\n                sortColumn={table.sortColumn}\r\n                sortDirection={table.sortDirection}\r\n                onSort={table.toggleSort}\r\n            />\r\n\r\n            <Pagination\r\n                page={table.page}\r\n                totalPages={table.totalPages}\r\n                perPage={table.perPage}\r\n                count={table.count}\r\n                onChangePerPage={table.changePerPage}\r\n                onFirst={table.firstPage}\r\n                onPrevious={table.previousPage}\r\n                onNext={table.nextPage}\r\n                onLast={table.lastPage}\r\n            />\r\n        </>\r\n    );\r\n}\r\n```\r\n\r\n`useDataTable` gère la pagination, le tri et l'appel réseau ; il n'impose aucune UI de filtre — `table.setColumnFilter(colonne, valeur)` et `table.replaceFilters(objet)` permettent de piloter le filtrage depuis n'importe quelle interface.\r\n\r\nLe hook gère aussi la sélection, que tu utilises `<DataTable />` ou le `<Table />` nu :\r\n\r\n```tsx\r\nconst table = useDataTable({ fetchData, onSelectionChange: (ids, rows) => console.log(ids, rows) });\r\n\r\n<Table\r\n    items={table.items}\r\n    fieldsType={table.fieldsType}\r\n    selectable\r\n    selectionColumnPosition='end'\r\n    selectedIds={table.selectedIds}\r\n    onToggleRow={table.setRowSelected}\r\n    onToggleAllRows={table.setAllRowsSelected}\r\n/>\r\n```\r\n\r\n| Valeur renvoyée | Type | Description |\r\n|---|---|---|\r\n| `selectedIds` | `any[]` | Ids sélectionnés (1ère colonne de chaque ligne) |\r\n| `selectedRows` | `T[]` | Les lignes complètes correspondantes, pages précédentes comprises |\r\n| `isRowSelected` | `(row) => boolean` | |\r\n| `setRowSelected` | `(row, selected) => void` | |\r\n| `toggleRowSelection` | `(row) => void` | |\r\n| `setAllRowsSelected` | `(selected) => void` | Toutes les lignes affichées ; les autres pages ne sont pas touchées |\r\n| `clearSelection` | `() => void` | Vide la sélection |\r\n\r\n### Format attendu par `fetchData`\r\n\r\n```ts\r\ninterface FetchParams {\r\n    limit: number;\r\n    offset: number;\r\n    sorting?: string;\r\n    filtre?: Record<string, unknown>;\r\n    setFilter?: boolean;\r\n}\r\n\r\ninterface FetchResult<T> {\r\n    items: T[];\r\n    count: number;\r\n    fieldsType?: { fieldType: string }[]; // 'DATE' | 'DATETIME' | 'JSON' | 'FILE' | 'BLOB' | ...\r\n    filtre?: FilterConfig;       // renvoyé par le serveur si des filtres avancés sont demandés\r\n    cleRecupFiltre?: string;     // utilisé en interne pour le polling du cache\r\n}\r\n```\r\n\r\nC'est exactement la forme renvoyée par `reqTableQuery` côté serveur.\r\n\r\n---\r\n\r\n## Build\r\n\r\n```bash\r\nnpm run build   # compile src/react et src/server dans dist/\r\n```","readmeFilename":"README.md"}