{"id":341837,"date":"2022-11-28T15:00:10","date_gmt":"2022-11-28T15:00:10","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=341837"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=341837","title":{"rendered":"<span>React: \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0439 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/jc\/hw\/st\/jchwst6a3nwlxlscnmuw5tdhmry.png\" data-src=\"https:\/\/habrastorage.org\/webt\/jc\/hw\/st\/jchwst6a3nwlxlscnmuw5tdhmry.png\"\/>  <\/p>\n<p>  \u041f\u0440\u0438\u0432\u0435\u0442, \u0434\u0440\u0443\u0437\u044c\u044f!<\/p>\n<p>  <\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0435 \u044f \u0445\u043e\u0447\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441 \u0432\u0430\u043c\u0438 \u043e\u043f\u044b\u0442\u043e\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043e\u0434\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0439 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438.<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f \u0442\u043e\u0432\u0430\u0440\u043e\u0432. \u041d\u0430 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442\u0441\u044f \u0441\u043b\u0430\u0439\u0434\u0435\u0440 \u0441 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0430\u043c\u0438 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0441 \u0438\u0445 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438. \u0417\u0430\u0434\u0430\u0447\u0430 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u043b\u0430\u0439\u0434\u043e\u0432 \u0438 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0443 \u0442\u0430\u0431\u043b\u0438\u0446\u044b. \u0423\u0441\u043b\u043e\u0432\u0438\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0448\u0438\u0440\u0438\u043d\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0448\u0438\u0440\u0438\u043d\u0435 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430;<\/li>\n<li>\u0448\u0438\u0440\u0438\u043d\u0430 \u043a\u043e\u043b\u043e\u043d\u043a\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0448\u0438\u0440\u0438\u043d\u0435 \u0441\u043b\u0430\u0439\u0434\u0430;<\/li>\n<li>\u0441\u043b\u0430\u0439\u0434\u044b \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0435\u0440\u0435\u0442\u0430\u0441\u043a\u0438\u0432\u0430\u043d\u0438\u044f, \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438;<\/li>\n<li>\u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u043a\u0440\u0443\u0447\u0438\u0432\u0430\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043b\u0435\u0441\u0438\u043a\u0430 \u043c\u044b\u0448\u0438 (\u043d\u0430 \u0434\u0435\u0441\u043a\u0442\u043e\u043f\u0435) \u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044f (\u043d\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0435);<\/li>\n<li>\u043f\u0440\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441 \u043e\u0434\u043d\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u043c \u0432\u0442\u043e\u0440\u043e\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c: \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u0441\u043b\u0430\u0439\u0434\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u043f\u0440\u0438 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u2014 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u043b\u0430\u0439\u0434\u043e\u0432.<\/li>\n<\/ul>\n<p>  <\/p>\n<p><a href=\"https:\/\/github.com\/harryheman\/Blog-Posts\/tree\/master\/react-slider-table\">\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0441 \u043a\u043e\u0434\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a>.<\/p>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u044d\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u043f\u0440\u043e\u0448\u0443 \u043f\u043e\u0434 \u043a\u0430\u0442.<\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<h2 id=\"podgotovka-i-nastroyka-proekta\">\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h2>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438 \u044f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/yarnpkg.com\/\">Yarn<\/a>. \u041f\u0440\u043e\u0435\u043a\u0442 \u0431\u0443\u0434\u0435\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u043d\u0430 <a href=\"https:\/\/ru.reactjs.org\/\">React<\/a> \u0438 <a href=\"https:\/\/www.typescriptlang.org\/\">TypeScript<\/a>.<\/p>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0448\u0430\u0431\u043b\u043e\u043d \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/vitejs.dev\/\">Vite<\/a>:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\"># react-slider-table - \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 # react-ts - \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 \u0448\u0430\u0431\u043b\u043e\u043d yarn create vite react-slider-table --template react-ts<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u0443\u044e \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e, \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">cd react-slider-table yarn yarn dev<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <a href=\"https:\/\/swiperjs.com\/\">Swiper<\/a> (\u0434\u043b\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430 \u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c\u044b\u0435 <code>Swiper<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0430 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043c\u0435\u043d\u043d\u043e \u044d\u0442\u0443 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443). \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0435\u0435:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn add swiper yarn add -D @types\/swiper<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0441\u0442\u0438\u043b\u0438 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430 \u0432 \u0444\u0430\u0439\u043b\u0435 <code>main.tsx<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import \"swiper\/css\"; \/\/ \u0434\u043b\u044f \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u0438 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438 import \"swiper\/css\/navigation\"; import \"swiper\/css\/pagination\";<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u0442\u0438\u043b\u0438 \u0432 \u0444\u0430\u0439\u043b\u0435 <code>index.css<\/code> (\u0444\u0430\u0439\u043b <code>App.css<\/code> \u043c\u043e\u0436\u043d\u043e \u0443\u0434\u0430\u043b\u0438\u0442\u044c):<\/p>\n<p>  <\/p>\n<pre><code class=\"css\">@import url(\"https:\/\/fonts.googleapis.com\/css2?family=Montserrat&amp;display=swap\");  * {   font-family: \"Montserrat\", sans-serif; }  body {   margin: 0; }  .app {   margin: 0 auto;   padding: 1rem;   width: 768px; }  img {   max-width: 100%;   object-fit: cover; }  .table-wrapper {   overflow: scroll;   scrollbar-width: none; }  .table-wrapper::-webkit-scrollbar {   display: none; }  table {   border-collapse: collapse;   overflow: hidden; }  td {   border: 1px solid gray;   padding: 0.25rem;   text-align: center; }  .feature-name-row td {   font-weight: bold;   text-align: left; }  .feature-name {   position: relative; }<\/code><\/pre>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>, \u0447\u0442\u043e \u043c\u044b \u0444\u0438\u043a\u0441\u0438\u0440\u0443\u0435\u043c \u0448\u0438\u0440\u0438\u043d\u0443 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (<code>.app<\/code>), \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0445\u043e\u0442\u0438\u043c \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0438\u0442\u0441\u044f \u043d\u0430 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430 \u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b (\u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043e\u0442\u0437\u044b\u0432\u0447\u0438\u0432\u043e\u0433\u043e \u0434\u0438\u0437\u0430\u0439\u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0439).<\/p>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <code>types.ts<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">export type Feature = {   id: number;   value: string; };  export type Item = {   id: number;   title: string;   imageUrl: string;   price: number;   features: Feature[]; };  export type Items = Item[];<\/code><\/pre>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <code>data.ts<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { Items } from \".\/types\";  const items: Items = [   {     id: 1,     title: \"Title\",     imageUrl: `https:\/\/picsum.photos\/320?random=${Math.random()}`,     price: 100,     features: [       {         id: 1,         value: \"Feature\",       },       {         id: 2,         value: \"Feature2\",       },       {         id: 3,         value: \"Feature3\",       },       {         id: 4,         value: \"Feature4\",       },       {         id: 5,         value: \"Feature5\",       },       {         id: 6,         value: \"Feature6\",       },     ],   },   {     id: 2,     title: \"Title2\",     imageUrl: `https:\/\/picsum.photos\/320?random=${Math.random()}`,     price: 200,     features: [       {         id: 1,         value: \"Feature7\",       },       {         id: 2,         value: \"Feature8\",       },       {         id: 3,         value: \"Feature9\",       },       {         id: 4,         value: \"Feature10\",       },       {         id: 5,         value: \"Feature11\",       },       {         id: 6,         value: \"Feature12\",       },     ],   },   {     id: 3,     title: \"Title3\",     imageUrl: `https:\/\/picsum.photos\/320?random=${Math.random()}`,     price: 300,     features: [       {         id: 1,         value: \"Feature13\",       },       {         id: 2,         value: \"Feature14\",       },       {         id: 3,         value: \"Feature15\",       },       {         id: 4,         value: \"Feature16\",       },       {         id: 5,         value: \"Feature17\",       },       {         id: 6,         value: \"Feature18\",       },     ],   },   {     id: 4,     title: \"Title4\",     imageUrl: `https:\/\/picsum.photos\/320?random=${Math.random()}`,     price: 400,     features: [       {         id: 1,         value: \"Feature19\",       },       {         id: 2,         value: \"Feature20\",       },       {         id: 3,         value: \"Feature21\",       },       {         id: 4,         value: \"Feature22\",       },       {         id: 5,         value: \"Feature23\",       },       {         id: 6,         value: \"Feature24\",       },     ],   },   {     id: 5,     title: \"Title5\",     imageUrl: `https:\/\/picsum.photos\/320?random=${Math.random()}`,     price: 500,     features: [       {         id: 1,         value: \"Feature25\",       },       {         id: 2,         value: \"Feature26\",       },       {         id: 3,         value: \"Feature27\",       },       {         id: 4,         value: \"Feature28\",       },       {         id: 5,         value: \"Feature29\",       },       {         id: 6,         value: \"Feature30\",       },     ],   },   {     id: 6,     title: \"Title6\",     imageUrl: `https:\/\/picsum.photos\/320?random=${Math.random()}`,     price: 600,     features: [       {         id: 1,         value: \"Feature31\",       },       {         id: 2,         value: \"Feature32\",       },       {         id: 3,         value: \"Feature33\",       },       {         id: 4,         value: \"Feature34\",       },       {         id: 5,         value: \"Feature35\",       },       {         id: 6,         value: \"Feature36\",       },     ],   }, ];  export default items;<\/code><\/pre>\n<p>  <\/p>\n<p>\u0423 \u043d\u0430\u0441 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u043c\u0430\u0441\u0441\u0438\u0432, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 6 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0441 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043e \u0442\u043e\u0432\u0430\u0440\u0430\u0445. \u041a\u0430\u0436\u0434\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0442\u043e\u0432\u0430\u0440\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043c\u0430\u0441\u0441\u0438\u0432, \u0441\u043e\u0441\u0442\u043e\u044f\u0449\u0438\u0439 \u0438\u0437 6 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0441 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438 \u0442\u043e\u0432\u0430\u0440\u0430.<\/p>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e <code>components<\/code>.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430. \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <code>components\/Slider.tsx<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u043c\u043e\u0434\u0443\u043b\u0438 import { Navigation, Pagination } from \"swiper\"; \/\/ \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b import { Swiper, SwiperSlide } from \"swiper\/react\"; import { Items } from \"..\/types\";  type Props = {   items: Items; };  \/\/ \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u044b\u0445 \u0441\u043b\u0430\u0439\u0434\u043e\u0432 const SLIDES_PER_VIEW = 3;  function Slider({ items }: Props) {   return (     &lt;Swiper       \/\/ \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u0438 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u0438 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438       modules={[Navigation, Pagination]}       \/\/ \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438       navigation={SLIDES_PER_VIEW &lt; items.length}       \/\/ \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438       pagination={         SLIDES_PER_VIEW &lt; items.length           ? {               \/\/ \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043a\u043b\u0438\u043a\u0430\u0431\u0435\u043b\u044c\u043d\u044b\u043c\u0438               clickable: true,             }           : undefined       }       \/\/ \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u044b\u0445 \u0441\u043b\u0430\u0439\u0434\u043e\u0432       slidesPerView={SLIDES_PER_VIEW}     >       {items.map((item) => (         &lt;SwiperSlide key={item.id}>           &lt;img src={item.imageUrl} alt={item.title} \/>           &lt;div>             &lt;h2>{item.title}&lt;\/h2>             &lt;p>{item.price} \u20bd&lt;\/p>           &lt;\/div>         &lt;\/SwiperSlide>       ))}     &lt;\/Swiper>   ); }  export default Slider;<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0438 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043c \u0441\u043b\u0430\u0439\u0434\u0435\u0440 \u0432 \u0444\u0430\u0439\u043b\u0435 <code>App.tsx<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import Slider from \".\/components\/Slider\"; import data from \".\/data\";  function App() {   return (     &lt;div className=\"app\">       &lt;Slider items={data} \/>     &lt;\/div>   ); }  export default App;<\/code><\/pre>\n<p>  <\/p>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/lf\/bk\/55\/lfbk55os72pl1eywpe0u2m0rhoy.png\" data-src=\"https:\/\/habrastorage.org\/webt\/lf\/bk\/55\/lfbk55os72pl1eywpe0u2m0rhoy.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u044b. \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <code>components\/Table.tsx<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { Items } from \"..\/types\";  type Props = {   items: Items; };  \/\/ \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a const FEATURE_NAMES = [   \"Title\",   \"Title2\",   \"Title3\",   \"Title4\",   \"Title5\",   \"Title6\", ];  function Table({ items }: Props) {   return (     &lt;div className=\"table-wrapper\">       &lt;table>         &lt;tbody>           {items.map((item, i) => (             &lt;React.Fragment key={item.id}>               &lt;tr className=\"feature-name-row\">                 &lt;td colSpan={items.length}>                   &lt;span className=\"feature-name\">{FEATURE_NAMES[i]}&lt;\/span>                 &lt;\/td>               &lt;\/tr>               &lt;tr>                 {items.map((_, j) => {                   const key = \"\" + i + j;                   return &lt;td key={key}>{items[j].features[i].value}&lt;\/td>;                 })}               &lt;\/tr>             &lt;\/React.Fragment>           ))}         &lt;\/tbody>       &lt;\/table>     &lt;\/div>   ); }  export default Table;<\/code><\/pre>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em> \u043d\u0430 2 \u0432\u0435\u0449\u0438:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u043c\u044b \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0441 <code>overflow: hidden<\/code> \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0441 <code>overflow: scroll<\/code> (<code>.table-wrapper<\/code>);<\/li>\n<li>\u043a\u043e\u043b\u043e\u043d\u043a\u0430 \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0438 \u0440\u0430\u0441\u0442\u044f\u0433\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0432\u0441\u044e \u0448\u0438\u0440\u0438\u043d\u0443 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043f\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0443 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 (\u0430\u0442\u0440\u0438\u0431\u0443\u0442 <code>colspan<\/code>), \u0430 \u0441\u0430\u043c\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 <code>span<\/code>: \u043f\u0440\u0438 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0438 \u0434\u043e\u043b\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u0438\u0434\u0438\u043c\u044b\u043c.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0438 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0432 <code>App.tsx<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import Slider from \".\/components\/Slider\"; import Table from \".\/components\/Table\"; import data from \".\/data\";  function App() {   return (     &lt;div className=\"app\">       &lt;Slider items={data} \/>       &lt;Table items={data} \/>     &lt;\/div>   ); }  export default App;<\/code><\/pre>\n<p>  <\/p>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/k2\/-r\/iu\/k2-riu1jrwv7qpw0ys7icicswxs.png\" data-src=\"https:\/\/habrastorage.org\/webt\/k2\/-r\/iu\/k2-riu1jrwv7qpw0ys7icicswxs.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0430\u0442\u044c \u043a \u0438\u0445 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p>  <\/p>\n<h2 id=\"sinhronizaciya-shiriny-slayda-i-kolonki-tablicy\">\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u0448\u0438\u0440\u0438\u043d\u044b \u0441\u043b\u0430\u0439\u0434\u0430 \u0438 \u043a\u043e\u043b\u043e\u043d\u043a\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b<\/h2>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0448\u0438\u0440\u0438\u043d\u044b \u0441\u043b\u0430\u0439\u0434\u0430 \u0432 <code>App.tsx<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const [slideWidth, setSlideWidth] = useState(0);<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u0430\u043d\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0432 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0435, \u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u2014 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">&lt;Slider   items={data}   \/\/ !   setSlideWidth={setSlideWidth} \/> &lt;Table   items={data}   \/\/ !   slideWidth={slideWidth} \/><\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 <code>Swiper<\/code> \u0432 <code>Slider.tsx<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const swiperRef = useRef&lt;TSwiper>();<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0438\u043f <code>TSwiper<\/code> \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ types.ts import type Swiper from \"swiper\";  export type TSwiper = Swiper &amp; {   slides: {     swiperSlideSize: number;   }[]; };<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u0434\u043d\u0438\u043c \u0438\u0437 \u043f\u0440\u043e\u043f\u043e\u0432, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u043c\u044b\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u043c <code>Swiper<\/code>, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <code>onSwiper<\/code>. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430 \u043a\u043e\u043b\u043b\u0431\u044d\u043a\u0443 \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u043f\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 <code>Swiper<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">&lt;Swiper   onSwiper={(swiper) => {     console.log(swiper);      swiperRef.current = swiper as TSwiper;   }}   \/\/ ... ><\/code><\/pre>\n<p>  <\/p>\n<p>\u042d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 <code>Swiper<\/code> \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043c\u0430\u0441\u0441\u0443 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0445 \u0441\u0432\u043e\u0439\u0441\u0442\u0432:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/fq\/qd\/em\/fqqdemayy7zoaeqczuk71nhmlx8.png\" data-src=\"https:\/\/habrastorage.org\/webt\/fq\/qd\/em\/fqqdemayy7zoaeqczuk71nhmlx8.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u0418\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0449\u0435\u0435 \u043d\u0430\u0441 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0448\u0438\u0440\u0438\u043d\u044b \u0441\u043b\u0430\u0439\u0434\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044f \u0432 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0435 <code>slides[0].swiperSlideSize<\/code>:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/-a\/r3\/2v\/-ar32vdox8jde6ff_4wlh4l1av0.png\" data-src=\"https:\/\/habrastorage.org\/webt\/-a\/r3\/2v\/-ar32vdox8jde6ff_4wlh4l1av0.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u043e\u043f <code>onImageReady<\/code> \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 <code>Swiper<\/code> \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043a\u043e\u043b\u043b\u0431\u044d\u043a \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0432\u0441\u0435\u0445 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0445 \u0432 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0435, \u0447\u0442\u043e \u0432 \u0440\u044f\u0434\u0435 \u0441\u043b\u0443\u0447\u0430\u0435\u0432 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0430\u0436\u043d\u044b\u043c \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0448\u0438\u0440\u0438\u043d\u044b \u0441\u043b\u0430\u0439\u0434\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">&lt;Swiper   onSwiper={(swiper) => {     console.log(swiper);      swiperRef.current = swiper as TSwiper;   }}   onImagesReady={onImagesReady}   \/\/ ... ><\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>onImagesReady<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const onImagesReady = () => {   if (!swiperRef.current) return;    const slideWidth = swiperRef.current.slides[0].swiperSlideSize;   setSlideWidth(slideWidth); };<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u0440\u043e\u043f <code>slideWidth<\/code> \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0445 \u0441\u0442\u0438\u043b\u0435\u0439 (\u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u043d\u043e \u0438\u0437 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 <code>CSS-in-JS<\/code>, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <a href=\"https:\/\/github.com\/vercel\/styled-jsx\">styled-jsx<\/a> \u2014 \u0441\u043c. \u043a\u043e\u043d\u0435\u0446 \u0441\u0442\u0430\u0442\u044c\u0438):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">&lt;tr   \/\/ !   style={{     display: \"grid\",     \/\/ 6 \u043a\u043e\u043b\u043e\u043d\u043e\u043a \u0441 \u0448\u0438\u0440\u0438\u043d\u043e\u0439, \u0440\u0430\u0432\u043d\u043e\u0439 \u0448\u0438\u0440\u0438\u043d\u0435 \u0441\u043b\u0430\u0439\u0434\u0430     gridTemplateColumns: `repeat(${items.length}, ${slideWidth}px)`,   }} >   {items.map((_, j) => {     const key = \"\" + i + j;     return &lt;td key={key}>{items[j].features[i].value}&lt;\/td>;   })} &lt;\/tr><\/code><\/pre>\n<p>  <\/p>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/uu\/g9\/ny\/uug9nyhiuecjdghgse5osai3_sa.png\" data-src=\"https:\/\/habrastorage.org\/webt\/uu\/g9\/ny\/uug9nyhiuecjdghgse5osai3_sa.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<h2 id=\"sinhronizaciya-pereklyucheniya-slaydov-i-prokrutki-tablicy-obrabotka-pereklyucheniya-slaydov\">\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0441\u043b\u0430\u0439\u0434\u043e\u0432 \u0438 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b: \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0441\u043b\u0430\u0439\u0434\u043e\u0432<\/h2>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438 \u0432 <code>App.tsx<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const [scrollLeft, setScrollLeft] = useState(0);<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u0430\u043d\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, \u043a\u0430\u043a \u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0448\u0438\u0440\u0438\u043d\u044b \u0441\u043b\u0430\u0439\u0434\u0430, \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0432 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0435, \u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u2014 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">&lt;Slider   items={data}   setSlideWidth={setSlideWidth}   \/\/ !   setScrollLeft={setScrollLeft} \/> &lt;Table   items={data}   slideWidth={slideWidth}   \/\/ !   scrollLeft={scrollLeft} \/><\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0440\u043e\u043f <code>onSlideChange<\/code> \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 <code>Swiper<\/code> \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043a\u043e\u043b\u043b\u0431\u044d\u043a, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0441\u043b\u0430\u0439\u0434\u043e\u0432 (\u043b\u044e\u0431\u044b\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">&lt;Swiper   onSlideChange={onSlideChange}   \/\/ ... ><\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>onSlideChange<\/code>, \u0432\u0437\u0433\u043b\u044f\u043d\u0435\u043c \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u043c <code>div<\/code> \u0441 \u043a\u043b\u0430\u0441\u0441\u043e\u043c <code>swiper-wrapper<\/code> \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u0441\u043b\u0430\u0439\u0434\u043e\u0432:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/fs\/2l\/li\/fs2lliumnugjwhzwg8np06oydsm.png\" data-src=\"https:\/\/habrastorage.org\/webt\/fs\/2l\/li\/fs2lliumnugjwhzwg8np06oydsm.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u0412\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043a \u0434\u0430\u043d\u043d\u043e\u043c\u0443 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0443 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u0441\u0442\u0438\u043b\u044c <code>transform: translate3d(x, y, z)<\/code>, \u0433\u0434\u0435 <code>x<\/code> \u2014 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0449\u0435\u0435 \u043d\u0430\u0441 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438.<\/p>\n<p>  <\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f <code>onSlideChange<\/code> \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const onSlideChange = () => {   if (!swiperRef.current) return;    \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 `transform`   const { transform } = swiperRef.current.wrapperEl.style;   \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b `x`   const match = transform.match(\/-?\\d+(\\.\\d+)?px\/);   if (!match) return;    \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 (!) \u0447\u0438\u0441\u043b\u043e \u0438\u0437 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b `x`   \/\/ \u0441 \u0447\u0438\u0441\u043b\u0430\u043c\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0443\u0434\u043e\u0431\u043d\u0435\u0435, \u0447\u0435\u043c \u0441\u043e \u0441\u0442\u0440\u043e\u043a\u0430\u043c\u0438   const scrollLeft = Math.abs(Number(match[0].replace(\"px\", \"\")));   setScrollLeft(scrollLeft); };<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u0440\u043e\u043f <code>scrollLeft<\/code> \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0435\u0449\u0435\u0439.<\/p>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u0441\u044b\u043b\u043e\u043a \u043d\u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0434\u043b\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0441\u0430\u043c\u0443 \u0442\u0430\u0431\u043b\u0438\u0446\u0443, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u0441\u044b\u043b\u043e\u043a \u043d\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f\u043c\u0438 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const tableWrapperRef = useRef&lt;HTMLDivElement | null>(null); const tableRef = useRef&lt;HTMLTableElement | null>(null); const featureNameRefs = useRef&lt;HTMLSpanElement[]>([]);<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0441\u0441\u044b\u043b\u043a\u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">&lt;div   className=\"table-wrapper\"   \/\/ !   ref={tableWrapperRef} >   &lt;table     \/\/ !     ref={tableRef}   >     {\/* ... *\/}   &lt;\/table> &lt;\/div><\/code><\/pre>\n<p>  <\/p>\n<p>\u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f\u043c\u0438 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a \u043f\u043e\u0441\u043b\u0435 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">useEffect(() => {   if (!tableRef.current) return;    featureNameRefs.current = [     ...tableRef.current.querySelectorAll(\".feature-name\"),   ] as HTMLSpanElement[]; }, []);<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0443 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0441\u0434\u0432\u0438\u0433 \u043f\u043e \u043e\u0441\u0438 <code>x<\/code> \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0439 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f <code>scrollLeft<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">useEffect(() => {   if (!tableWrapperRef.current || !featureNameRefs.current.length) return;    tableWrapperRef.current.scrollLeft = scrollLeft;    featureNameRefs.current.forEach((el) => {     el.style.left = `${scrollLeft}px`;   }); }, [scrollLeft]);<\/code><\/pre>\n<p>  <\/p>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/82\/km\/bi\/82kmbindf4jh9wyytaaniy9x9dc.png\" data-src=\"https:\/\/habrastorage.org\/webt\/82\/km\/bi\/82kmbindf4jh9wyytaaniy9x9dc.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u0412\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u043b\u0430\u0439\u0434\u043e\u0432 \u043f\u0435\u0440\u0435\u0442\u0430\u0441\u043a\u0438\u0432\u0430\u043d\u0438\u0435\u043c, \u043d\u0430\u0436\u0430\u0442\u0438\u0435\u043c \u043a\u043d\u043e\u043f\u043e\u043a \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0441\u0434\u0432\u0438\u0433\u0443 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0439 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a \u043d\u0430 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u0437\u0438\u0446\u0438\u0438.<\/p>\n<p>  <\/p>\n<h2 id=\"sinhronizaciya-pereklyucheniya-slaydov-i-prokrutki-tablicy-obrabotka-prokrutki-tablicy\">\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0441\u043b\u0430\u0439\u0434\u043e\u0432 \u0438 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b: \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b<\/h2>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043e\u0442\u0441\u0442\u0443\u043f\u0430 \u043f\u043e \u043e\u0441\u0438 <code>x<\/code> \u0432 <code>App.tsx<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const [offsetX, setOffsetX] = useState(0);<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u0430\u043d\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435, \u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u2014 \u0432 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0435:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">&lt;Slider   items={data}   setSlideWidth={setSlideWidth}   setScrollLeft={setScrollLeft}   \/\/ !   offsetX={offsetX} \/> &lt;Table   items={data}   slideWidth={slideWidth}   scrollLeft={scrollLeft}   \/\/ !   setOffsetX={setOffsetX} \/><\/code><\/pre>\n<p>  <\/p>\n<p>\u041a\u0430\u043a \u043f\u0440\u0438 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043b\u0435\u0441\u0438\u043a\u0430 \u043c\u044b\u0448\u0438, \u0442\u0430\u043a \u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044f, \u043d\u0430 \u043e\u0431\u0435\u0440\u0442\u043a\u0435 \u0434\u043b\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u0435 <code>scroll<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">&lt;div   className=\"table-wrapper\"   \/\/ !   onScroll={debouncedOnScroll}   ref={tableWrapperRef} ><\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>onScroll<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const onScroll: React.UIEventHandler&lt;HTMLDivElement> = useCallback(() => {   if (!tableRef.current) return;   \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u043f\u043e\u0437\u0438\u0446\u0438\u044e \u043b\u0435\u0432\u043e\u0433\u043e \u043a\u0440\u0430\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043f\u043e \u043e\u0441\u0438 `x`   const { x } = tableRef.current.getBoundingClientRect();   \/\/ \u0434\u0435\u043b\u0430\u0435\u043c \u0447\u0438\u0441\u043b\u043e \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c   setOffsetX(Math.abs(x)); }, []);<\/code><\/pre>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>: \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438 \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0441 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u043e\u0439, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 <code>scrollLeft<\/code> \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u044e \u0441\u043e\u0431\u044b\u0442\u0438\u044f <code>scroll<\/code>, \u0447\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u043b\u0430\u0439\u0434\u043e\u0432 \u0438 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0443 \u0442\u0430\u0431\u043b\u0438\u0446\u044b:<\/p>\n<p>  <\/p>\n<ul>\n<li><code>offsetX<\/code> \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f \u0432 \u0441\u043b\u0430\u0439\u0434\u0435\u0440 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0441\u043b\u0430\u0439\u0434\u043e\u0432;<\/li>\n<li>\u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0435 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0441\u043b\u0430\u0439\u0434\u043e\u0432 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 <code>scrollLeft<\/code>;<\/li>\n<li><code>scrollLeft<\/code> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u2014 \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u0435 <code>scroll<\/code>, \u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f <code>offsetX<\/code>.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 <em>\u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>, \u0447\u0442\u043e \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e: \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0441\u0442\u0438\u043b\u044f <code>scroll-behavior: smooth<\/code> \u0438\u043b\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 <code>scrollTo({ left: scrollLeft, behavior: 'smooth' })<\/code> \u0441\u0434\u0435\u043b\u0430\u0435\u0442 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438 \u043d\u0435\u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c\u044b\u043c.<\/p>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <code>hooks\/useDebounce.ts<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { useCallback, useEffect, useRef } from \"react\";  const useDebounce = (fn: Function, delay: number) => {   const timeoutRef = useRef&lt;number>();    const clearTimer = useCallback(() => {     if (timeoutRef.current) {       clearTimeout(timeoutRef.current);       timeoutRef.current = undefined;     }   }, []);    useEffect(() => clearTimer, []);    const cb = useCallback(     (...args: any[]) => {       clearTimer();       timeoutRef.current = setTimeout(() => fn(...args), delay);     },     [fn, delay]   );    return cb; };  export default useDebounce;<\/code><\/pre>\n<p>  <\/p>\n<p>\u042d\u0442\u043e\u0442 \u0445\u0443\u043a \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f, \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u0432, \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u043f\u043e \u043f\u0440\u043e\u0448\u0435\u0441\u0442\u0432\u0438\u0438 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const ON_SCROLL_DELAY = 250;  const debouncedOnScroll = useDebounce(onScroll, ON_SCROLL_DELAY);<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0441\u0430\u043c\u043e\u0439 \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0430.<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u043f\u0430 <code>offsetX<\/code> \u0432 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0435 \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442 \u0437\u043d\u0430\u043d\u0438\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0435\u0433\u043e \u043a <code>offsetX<\/code> \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0438 \u0435\u0433\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u0435.<\/p>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u0441\u044b\u043b\u043e\u043a \u043d\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438 \u0438 \u0438\u0445 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u043f\u043e \u043e\u0441\u0438 <code>x<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const paginationBulletRefs = useRef&lt;HTMLSpanElement[]>([]); const paginationBulletXCoords = useRef&lt;number[]>([]);<\/code><\/pre>\n<p>  <\/p>\n<p>\u0421\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438 \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0435 <code>pagination.bullets<\/code> \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 <code>Swiper<\/code>. \u0414\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u0437\u0438\u0446\u0438\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043f\u043e \u043e\u0441\u0438 <code>x<\/code> \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0443\u043c\u043d\u043e\u0436\u0438\u0442\u044c \u0438\u043d\u0434\u0435\u043a\u0441 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u043d\u0430 \u0448\u0438\u0440\u0438\u043d\u0443 \u0441\u043b\u0430\u0439\u0434\u0430. \u0420\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>onImagesReady<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const bullets = swiperRef.current.pagination   .bullets as unknown as HTMLSpanElement[]; if (!bullets.length) return; paginationBulletRefs.current = bullets;  for (const i in bullets) {   paginationBulletXCoords.current.push(slideWidth * Number(i)); }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u044d\u0444\u0444\u0435\u043a\u0442 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0433\u043e \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 <code>offsetX<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">useEffect(() => {   \/\/ \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0434\u043b\u044f \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0437\u043d\u0438\u0446\u044b \u043c\u0435\u0436\u0434\u0443 \u043f\u043e\u0437\u0438\u0446\u0438\u0435\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0438 \u043e\u0442\u0441\u0442\u0443\u043f\u043e\u043c   let min = 0;   let i = 0;    for (const j in paginationBulletXCoords.current) {     \/\/ \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u0440\u0430\u0437\u043d\u0438\u0446\u0443     const dif = Math.abs(paginationBulletXCoords.current[j] - offsetX);      \/\/ \u0442\u0435\u043a\u0443\u0449\u0430\u044f \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u0440\u0430\u0432\u043d\u0430 `0`     if (dif === 0) {       min = 0;       i = 0;       break;     }      \/\/ \u0442\u0435\u043a\u0443\u0449\u0430\u044f \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u043d\u0435 \u0440\u0430\u0432\u043d\u0430 `0` \u0438 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u0440\u0430\u0432\u043d\u0430 `0` \u0438\u043b\u0438 \u0442\u0435\u043a\u0443\u0449\u0430\u044f \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u043c\u0435\u043d\u044c\u0448\u0435 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0437\u043d\u0438\u0446\u044b     if (dif !== 0 &amp;&amp; (min === 0 || dif &lt; min)) {       min = dif;       i = Number(j);     }   }    \/\/ \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u0435 \u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442   if (paginationBulletRefs.current[i]) {     paginationBulletRefs.current[i].click();   } }, [offsetX]);<\/code><\/pre>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>: \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u0435 \u043d\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0432\u044b\u0437\u043e\u0432\u0443 <code>onSlideChange<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 <code>scrollLeft<\/code>, \u0447\u0442\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0432\u044b\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u043d\u0438\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0439 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a.<\/p>\n<p>  <\/p>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/uc\/fw\/o2\/ucfwo2xeeei_gckxhclkrmmj-de.png\" data-src=\"https:\/\/habrastorage.org\/webt\/uc\/fw\/o2\/ucfwo2xeeei_gckxhclkrmmj-de.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u0412\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043b\u0435\u0441\u0438\u043a\u0430 \u043c\u044b\u0448\u0438 \u0438\u043b\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044f \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043a \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044e \u0441\u043b\u0430\u0439\u0434\u0430, \u0430 \u0437\u0430\u0442\u0435\u043c \u2014 \u043a \u0432\u044b\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u043d\u0438\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0439 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a.<\/p>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>: \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0438 \u0432\u044b\u0437\u043e\u0432\u0430 <code>onScroll<\/code> \u0441\u0434\u0435\u043b\u0430\u0435\u0442 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0443 \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u043d\u0430 \u043e\u0434\u0438\u043d \u0441\u043b\u0430\u0439\u0434 \u0437\u0430 \u0440\u0430\u0437 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0439, \u0442.\u0435. \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0430 \u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438 \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u043e\u0439.<\/p>\n<p>  <\/p>\n<p>\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0438\u043b\u0435\u0439 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u043c\u043e\u0436\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 <code>CSS-in-JS<\/code>, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e: <code>styled-jsx<\/code>. \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u044d\u0442\u0443 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn add styled-jsx yarn add -D @types\/styled-jsx<\/code><\/pre>\n<p>  <\/p>\n<p>\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b <code>vite.config.ts<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { defineConfig } from \"vite\"; import react from \"@vitejs\/plugin-react\";  export default defineConfig({   plugins: [     react({       babel: { plugins: [\"styled-jsx\/babel\"] },     }),   ], });<\/code><\/pre>\n<p>  <\/p>\n<p>\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b <code>vite-env.d.ts<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">\/\/\/ &lt;reference types=\"vite\/client\" \/> import \"react\";  declare module \"react\" {   interface StyleHTMLAttributes {     jsx?: boolean;     global?: boolean;   } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b <code>Table.tsx<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import React, { useCallback, useEffect, useRef } from \"react\"; import useDebounce from \"..\/hooks\/useDebounce\"; import { Items } from \"..\/types\";  type Props = {   items: Items;   slideWidth: number;   scrollLeft: number;   setOffsetX: React.Dispatch&lt;React.SetStateAction&lt;number>>; };  const FEATURE_NAMES = [   \"Title\",   \"Title2\",   \"Title3\",   \"Title4\",   \"Title5\",   \"Title6\", ];  const ON_SCROLL_DELAY = 250;  function Table({ items, slideWidth, scrollLeft, setOffsetX }: Props) {   const tableWrapperRef = useRef&lt;HTMLDivElement | null>(null);   const tableRef = useRef&lt;HTMLTableElement | null>(null);    useEffect(() => {     if (!tableWrapperRef.current) return;      tableWrapperRef.current.scrollLeft = scrollLeft;   }, [scrollLeft]);    const onScroll: React.UIEventHandler&lt;HTMLDivElement> = useCallback(() => {     if (!tableRef.current) return;      const { x } = tableRef.current.getBoundingClientRect();     setOffsetX(Math.abs(x));   }, []);    const debouncedOnScroll = useDebounce(onScroll, ON_SCROLL_DELAY);    return (     &lt;>       &lt;div         className=\"table-wrapper\"         onScroll={debouncedOnScroll}         ref={tableWrapperRef}       >         &lt;table ref={tableRef}>           &lt;tbody>             {items.map((item, i) => (               &lt;React.Fragment key={item.id}>                 &lt;tr className=\"feature-name-row\">                   &lt;td colSpan={items.length}>                     &lt;span className=\"feature-name\">{FEATURE_NAMES[i]}&lt;\/span>                   &lt;\/td>                 &lt;\/tr>                 {\/* ! *\/}                 &lt;tr className=\"feature-value-row\">                   {items.map((_, j) => {                     const key = \"\" + i + j;                     return &lt;td key={key}>{items[j].features[i].value}&lt;\/td>;                   })}                 &lt;\/tr>               &lt;\/React.Fragment>             ))}           &lt;\/tbody>         &lt;\/table>       &lt;\/div>       {\/* ! *\/}       &lt;style jsx>{`         .feature-name {           left: ${scrollLeft}px;         }         .feature-value-row {           display: grid;           grid-template-columns: repeat(${items.length}, ${slideWidth}px);         }       `}&lt;\/style>     &lt;\/>   ); }  export default Table;<\/code><\/pre>\n<p>  <\/p>\n<p>\u041c\u044b \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u043c \u043e\u0442\u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u0442\u044c \u043a\u043e\u0434 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430, \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0441\u043b\u0430\u0439\u0434\u043e\u0432 \u0432 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0443 \u0442\u0430\u0431\u043b\u0438\u0446\u044b. \u042d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 <code>Swiper<\/code> \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043c\u0435\u0442\u043e\u0434 <code>slideTo<\/code>, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e \u043f\u0440\u043e\u043a\u0440\u0443\u0447\u0438\u0432\u0430\u0442\u044c \u0441\u043b\u0430\u0439\u0434\u0435\u0440 \u043a \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u0441\u043b\u0430\u0439\u0434\u0443. \u0421\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u0432\u043c\u0435\u0441\u0442\u043e \u043f\u043e\u0437\u0438\u0446\u0438\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438 \u043f\u043e \u043e\u0441\u0438 <code>x<\/code> \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0437\u043d\u0430\u0442\u044c \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043b\u0430\u0439\u0434\u043e\u0432. \u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b <code>Slider.tsx<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { useEffect, useRef } from \"react\"; import { Navigation, Pagination } from \"swiper\"; import { Swiper, SwiperSlide } from \"swiper\/react\"; import { Items, TSwiper } from \"..\/types\";  type Props = {   items: Items;   setSlideWidth: React.Dispatch&lt;React.SetStateAction&lt;number>>;   setScrollLeft: React.Dispatch&lt;React.SetStateAction&lt;number>>;   offsetX: number; };  const SLIDES_PER_VIEW = 3;  function Slider({ items, setSlideWidth, setScrollLeft, offsetX }: Props) {   const swiperRef = useRef&lt;TSwiper>();   \/\/ !   const slideXPositions = useRef&lt;number[]>([]);    const onImagesReady = () => {     if (!swiperRef.current) return;      const slideWidth = swiperRef.current.slides[0].swiperSlideSize;      \/\/ !     for (const i in items) {       slideXPositions.current.push(slideWidth * Number(i));     }      setSlideWidth(slideWidth);   };    const onSlideChange = () => {     if (!swiperRef.current) return;      const { transform } = swiperRef.current.wrapperEl.style;     const match = transform.match(\/-?\\d+(\\.\\d+)?px\/);     if (!match) return;      const scrollLeft = Math.abs(Number(match[0].replace(\"px\", \"\")));     setScrollLeft(scrollLeft);   };    useEffect(() => {     if (!swiperRef.current) return;      let min = 0;     let i = 0;      for (const j in slideXPositions.current) {       const dif = Math.abs(slideXPositions.current[j] - offsetX);        if (dif === 0) {         min = 0;         i = 0;         break;       }        if (dif !== 0 &amp;&amp; (min === 0 || dif &lt; min)) {         min = dif;         i = Number(j);       }     }      \/\/ !     if (items[i]) {       swiperRef.current.slideTo(i);     }   }, [offsetX]);    return (     &lt;Swiper       onSwiper={(swiper) => {         console.log(swiper);          swiperRef.current = swiper as TSwiper;       }}       modules={[Navigation, Pagination]}       navigation={SLIDES_PER_VIEW &lt; items.length}       onImagesReady={onImagesReady}       onSlideChange={onSlideChange}       pagination={         SLIDES_PER_VIEW &lt; items.length           ? {               clickable: true,             }           : undefined       }       slidesPerView={SLIDES_PER_VIEW}     >       {items.map((item) => (         &lt;SwiperSlide key={item.id}>           &lt;img src={item.imageUrl} alt={item.title} \/>           &lt;div>             &lt;h2>{item.title}&lt;\/h2>             &lt;p>{item.price} \u20bd&lt;\/p>           &lt;\/div>         &lt;\/SwiperSlide>       ))}     &lt;\/Swiper>   ); }  export default Slider;<\/code><\/pre>\n<p>  <\/p>\n<p>\u0423\u0432\u0435\u0440\u0435\u043d, \u0447\u0442\u043e \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0442 \u0438 \u0434\u0440\u0443\u0433\u0438\u0435, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0434\u0430\u0436\u0435 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u043c\u0435\u0436\u0434\u0443 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u043e\u043c \u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u0435\u0439. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u0438\u0434\u0435\u0438 \u043d\u0430 \u044d\u0442\u043e\u0442 \u0441\u0447\u0435\u0442, \u0434\u0435\u043b\u0438\u0442\u0435\u0441\u044c \u0438\u043c\u0438 \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0432\u044b \u0443\u0437\u043d\u0430\u043b\u0438 \u0447\u0442\u043e-\u0442\u043e \u043d\u043e\u0432\u043e\u0435 \u0438 \u043d\u0435 \u0437\u0440\u044f \u043f\u043e\u0442\u0440\u0430\u0442\u0438\u043b\u0438 \u0432\u0440\u0435\u043c\u044f.<\/p>\n<p>  <\/p>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0438 happy coding!<\/p>\n<p>  <\/p>\n<hr\/>\n<p>  <\/p>\n<p><a href=\"https:\/\/timeweb.cloud\/?utm_source=habr&amp;utm_medium=banner&amp;utm_campaign=vds-promo-6-rub\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/p-\/u9\/l2\/p-u9l27ynelxi92bcmdxhu76ma8.png\" data-src=\"https:\/\/habrastorage.org\/webt\/p-\/u9\/l2\/p-u9l27ynelxi92bcmdxhu76ma8.png\"\/><\/a><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p> <!----> <!----><\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/company\/timeweb\/blog\/701972\/\"> https:\/\/habr.com\/ru\/company\/timeweb\/blog\/701972\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/jc\/hw\/st\/jchwst6a3nwlxlscnmuw5tdhmry.png\" data-src=\"https:\/\/habrastorage.org\/webt\/jc\/hw\/st\/jchwst6a3nwlxlscnmuw5tdhmry.png\"\/>  <\/p>\n<p>  \u041f\u0440\u0438\u0432\u0435\u0442, \u0434\u0440\u0443\u0437\u044c\u044f!<\/p>\n<p>  <\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0435 \u044f \u0445\u043e\u0447\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441 \u0432\u0430\u043c\u0438 \u043e\u043f\u044b\u0442\u043e\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043e\u0434\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0439 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438.<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f \u0442\u043e\u0432\u0430\u0440\u043e\u0432. \u041d\u0430 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442\u0441\u044f \u0441\u043b\u0430\u0439\u0434\u0435\u0440 \u0441 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0430\u043c\u0438 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0441 \u0438\u0445 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438. \u0417\u0430\u0434\u0430\u0447\u0430 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u043b\u0430\u0439\u0434\u043e\u0432 \u0438 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0443 \u0442\u0430\u0431\u043b\u0438\u0446\u044b. \u0423\u0441\u043b\u043e\u0432\u0438\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0448\u0438\u0440\u0438\u043d\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0448\u0438\u0440\u0438\u043d\u0435 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430;<\/li>\n<li>\u0448\u0438\u0440\u0438\u043d\u0430 \u043a\u043e\u043b\u043e\u043d\u043a\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0448\u0438\u0440\u0438\u043d\u0435 \u0441\u043b\u0430\u0439\u0434\u0430;<\/li>\n<li>\u0441\u043b\u0430\u0439\u0434\u044b \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0435\u0440\u0435\u0442\u0430\u0441\u043a\u0438\u0432\u0430\u043d\u0438\u044f, \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438;<\/li>\n<li>\u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u043a\u0440\u0443\u0447\u0438\u0432\u0430\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043b\u0435\u0441\u0438\u043a\u0430 \u043c\u044b\u0448\u0438 (\u043d\u0430 \u0434\u0435\u0441\u043a\u0442\u043e\u043f\u0435) \u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044f (\u043d\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0435);<\/li>\n<li>\u043f\u0440\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441 \u043e\u0434\u043d\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u043c \u0432\u0442\u043e\u0440\u043e\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c: \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u0441\u043b\u0430\u0439\u0434\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u043f\u0440\u0438 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u2014 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u043b\u0430\u0439\u0434\u043e\u0432.<\/li>\n<\/ul>\n<p>  <\/p>\n<p><a href=\"https:\/\/github.com\/harryheman\/Blog-Posts\/tree\/master\/react-slider-table\">\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0441 \u043a\u043e\u0434\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a>.<\/p>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u044d\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u043f\u0440\u043e\u0448\u0443 \u043f\u043e\u0434 \u043a\u0430\u0442.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-341837","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/341837","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=341837"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/341837\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=341837"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=341837"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=341837"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}