diff --git a/examples/package-lock.json b/examples/package-lock.json index dd2e71d..c49c34d 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -15,13 +15,18 @@ "@types/node": "16.11.11", "@types/react": "17.0.37", "@types/react-dom": "17.0.11", - "canvasbar-react": "1.0.0-beta.2", + "canvasbar-react": "1.0.0", "lorem-ipsum": "2.0.4", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-router": "6.2.1", + "react-router-dom": "6.2.1", "react-scripts": "4.0.3", "typescript": "4.5.2", "web-vitals": "1.1.2" + }, + "devDependencies": { + "@types/react-router": "5.1.18" } }, "node_modules/@babel/code-frame": { @@ -3499,6 +3504,12 @@ "@types/node": "*" } }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "dev": true + }, "node_modules/@types/html-minifier-terser": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz", @@ -3739,6 +3750,16 @@ "@types/react": "*" } }, + "node_modules/@types/react-router": { + "version": "5.1.18", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.18.tgz", + "integrity": "sha512-YYknwy0D0iOwKQgz9v8nOzt2J6l4gouBmDnWqUUznltOTaon+r8US8ky8HvN0tXvc38U9m6z/t2RsVsnd1zM0g==", + "dev": true, + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", @@ -5772,9 +5793,9 @@ } }, "node_modules/canvasbar-react": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/canvasbar-react/-/canvasbar-react-1.0.0-beta.2.tgz", - "integrity": "sha512-ZFVgi+bOafhu5lH6FRBUtQEZ88UYSpWiw8snIDHk+p7hyimGNZqeXt+DiGjJE8yOguMIfpfBfArKCvxW7XrhpQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/canvasbar-react/-/canvasbar-react-1.0.0.tgz", + "integrity": "sha512-wmuWeeg+pvU7MJ3Ybaia6kj8kQaXJrF1iBQSeyrtAHv4dSE5kYJk9q7Xxw57g6DG6I/3+MaS3CjgWRcV6ATfOQ==", "peerDependencies": { "@types/react": ">=16.8.0", "@types/react-dom": ">=16.8.0", @@ -9889,6 +9910,14 @@ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, + "node_modules/history": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz", + "integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==", + "dependencies": { + "@babel/runtime": "^7.7.6" + } + }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -16376,6 +16405,30 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz", + "integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==", + "dependencies": { + "history": "^5.2.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz", + "integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==", + "dependencies": { + "history": "^5.2.0", + "react-router": "6.2.1" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-scripts": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-4.0.3.tgz", @@ -24076,6 +24129,12 @@ "@types/node": "*" } }, + "@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "dev": true + }, "@types/html-minifier-terser": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz", @@ -24278,6 +24337,16 @@ "@types/react": "*" } }, + "@types/react-router": { + "version": "5.1.18", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.18.tgz", + "integrity": "sha512-YYknwy0D0iOwKQgz9v8nOzt2J6l4gouBmDnWqUUznltOTaon+r8US8ky8HvN0tXvc38U9m6z/t2RsVsnd1zM0g==", + "dev": true, + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, "@types/resolve": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", @@ -25899,9 +25968,9 @@ "integrity": "sha512-t28SKa7g6kiIQi6NHeOcKrOrGMzCRrXvlasPwWC26TH2QNdglgzQIRUuJ0cR3NeQPH+5jpuveeeSFDLm2zbkEw==" }, "canvasbar-react": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/canvasbar-react/-/canvasbar-react-1.0.0-beta.2.tgz", - "integrity": "sha512-ZFVgi+bOafhu5lH6FRBUtQEZ88UYSpWiw8snIDHk+p7hyimGNZqeXt+DiGjJE8yOguMIfpfBfArKCvxW7XrhpQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/canvasbar-react/-/canvasbar-react-1.0.0.tgz", + "integrity": "sha512-wmuWeeg+pvU7MJ3Ybaia6kj8kQaXJrF1iBQSeyrtAHv4dSE5kYJk9q7Xxw57g6DG6I/3+MaS3CjgWRcV6ATfOQ==", "requires": {} }, "capture-exit": { @@ -29052,6 +29121,14 @@ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, + "history": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz", + "integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==", + "requires": { + "@babel/runtime": "^7.7.6" + } + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -34040,6 +34117,23 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", "integrity": "sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==" }, + "react-router": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz", + "integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==", + "requires": { + "history": "^5.2.0" + } + }, + "react-router-dom": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz", + "integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==", + "requires": { + "history": "^5.2.0", + "react-router": "6.2.1" + } + }, "react-scripts": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-4.0.3.tgz", diff --git a/examples/package.json b/examples/package.json index 30cfe4b..49873b6 100644 --- a/examples/package.json +++ b/examples/package.json @@ -10,10 +10,12 @@ "@types/node": "16.11.11", "@types/react": "17.0.37", "@types/react-dom": "17.0.11", - "canvasbar-react": "1.0.0-beta.2", + "canvasbar-react": "1.0.0", "lorem-ipsum": "2.0.4", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-router": "6.2.1", + "react-router-dom": "6.2.1", "react-scripts": "4.0.3", "typescript": "4.5.2", "web-vitals": "1.1.2" @@ -41,5 +43,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "@types/react-router": "5.1.18" } } diff --git a/examples/src/App.css b/examples/src/App.css index d0f949a..6013094 100644 --- a/examples/src/App.css +++ b/examples/src/App.css @@ -1,4 +1,4 @@ -.app { +.grid-page { display: grid; grid-template-rows: 1fr 1fr; grid-template-columns: 1fr 1fr; @@ -6,6 +6,12 @@ padding: 10px; } +.overflow-page { + min-width: 150vw; + min-height: 300vh; + padding: 10px; +} + .my-scrollable-container { border: 1px solid gray; width: 400px; @@ -18,3 +24,38 @@ width: 600px; height: 600px; } + +.my-scrollable-table, +.my-scrollable-table-sticky { + border: 1px solid gray; + width: 800px; + height: 500px; + margin: auto; + overflow: auto; +} + +.my-scrollable-table th, +.my-scrollable-table td, +.my-scrollable-table-sticky th, +.my-scrollable-table-sticky td { + white-space: nowrap; +} + +.my-scrollable-table-sticky table { + position: relative; +} + +.my-scrollable-table-sticky thead { + position: sticky; + top: 0; + left: 0; + background: white; + z-index: 1; +} + +.my-scrollable-table-sticky tbody td:first-child { + position: sticky; + left: 0; + background: white; + font-weight: bold; +} diff --git a/examples/src/App.tsx b/examples/src/App.tsx index 274af21..8ecc910 100644 --- a/examples/src/App.tsx +++ b/examples/src/App.tsx @@ -1,60 +1,36 @@ -import { loremIpsum } from 'lorem-ipsum'; -import { CanvasBar, useBodyCanvasBar, CanvasBarConfigContext } from 'canvasbar-react'; +import { + Route, + Routes, +} from 'react-router-dom'; +import { useBodyCanvasBar } from 'canvasbar-react'; -import './App.css'; +import { Nav } from './Nav'; +import { Basic } from './examples/Basic'; +import { Heavy } from './examples/Heavy'; +import { Customization } from './examples/Customization'; +import { BodyScrollbars } from './examples/BodyScrollbars'; +import { Table } from './examples/Table'; +import { Sticky } from './examples/Sticky'; -const TEXT = loremIpsum({ count: 100 }); -const TEXT_SMALL = loremIpsum({ count: 7 }); -const IMAGES_IDS = Array.from(Array(10).keys()); +import './App.css'; -function App() { +export default function App() { useBodyCanvasBar(); return ( -
- -
-

Container with both scrollbars

-

{TEXT}

-
-
- -

Container with 10 large images (3000x3000 resized to 600x600) and both scrollbars

- {IMAGES_IDS.map(id => ( - test - ))} -
- -

Container with vertical scrollbar only

-

{TEXT}

-
- -
-

Container with horizontal scrollbar only

-

{TEXT_SMALL}

-
-
- -
-

Container with customized scrollbars via config props

-

{TEXT}

-
-
- - -
-

Container with customized scrollbars via context

- It's better to use it for customization all (or multiple) scrollbars on the page. -

{TEXT}

-
-
-
- -

Container without scrollbar

-

{TEXT_SMALL}

-
+
+
); } - -export default App; diff --git a/examples/src/Nav/Nav.css b/examples/src/Nav/Nav.css new file mode 100644 index 0000000..55df350 --- /dev/null +++ b/examples/src/Nav/Nav.css @@ -0,0 +1,20 @@ +.nav-menu { + display: flex; + flex-wrap: wrap; + list-style: none; + padding: 0; + margin: 0; +} + +.nav-link, +.nav-link-active { + display: block; + margin: 12px; + white-space: nowrap; + text-decoration: none; +} + +.nav-link-active { + font-weight: bold; + text-decoration: underline; +} diff --git a/examples/src/Nav/Nav.tsx b/examples/src/Nav/Nav.tsx new file mode 100644 index 0000000..d74b29f --- /dev/null +++ b/examples/src/Nav/Nav.tsx @@ -0,0 +1,34 @@ +import { NavLink } from 'react-router-dom'; + +import './Nav.css'; + +function getClassName({ isActive }: { isActive: boolean }) { + return isActive ? 'nav-link-active' : 'nav-link'; +} + +export function Nav() { + return ( + + ); +} diff --git a/examples/src/Nav/index.ts b/examples/src/Nav/index.ts new file mode 100644 index 0000000..93467d7 --- /dev/null +++ b/examples/src/Nav/index.ts @@ -0,0 +1 @@ +export * from './Nav'; diff --git a/examples/src/examples/Basic.tsx b/examples/src/examples/Basic.tsx new file mode 100644 index 0000000..e521f0f --- /dev/null +++ b/examples/src/examples/Basic.tsx @@ -0,0 +1,32 @@ +import { loremIpsum } from 'lorem-ipsum'; +import { CanvasBar } from 'canvasbar-react'; + +const TEXT = loremIpsum({ count: 100 }); +const TEXT_SMALL = loremIpsum({ count: 7 }); + +export function Basic() { + return ( +
+ +
+

Container with both scrollbars

+

{TEXT}

+
+
+ +

Container with vertical scrollbar only

+

{TEXT}

+
+ +
+

Container with horizontal scrollbar only

+

{TEXT_SMALL}

+
+
+ +

Container without scrollbar

+

{TEXT_SMALL}

+
+
+ ); +} diff --git a/examples/src/examples/BodyScrollbars.tsx b/examples/src/examples/BodyScrollbars.tsx new file mode 100644 index 0000000..4bac78b --- /dev/null +++ b/examples/src/examples/BodyScrollbars.tsx @@ -0,0 +1,13 @@ +import { loremIpsum } from 'lorem-ipsum'; + +const TEXT = loremIpsum({ count: 1000 }); + +export function BodyScrollbars() { + return ( +
+

Large container with a lot of text

+ Body scrollbar customization is done with useBodyCanvasBar hook in the root component (see App.tsx). This page just displays a result when content is too large, so body scrollbars are shown. +

{TEXT}

+
+ ); +} diff --git a/examples/src/examples/Customization.tsx b/examples/src/examples/Customization.tsx new file mode 100644 index 0000000..8ccfa3e --- /dev/null +++ b/examples/src/examples/Customization.tsx @@ -0,0 +1,26 @@ +import { loremIpsum } from 'lorem-ipsum'; +import { CanvasBar, CanvasBarConfigContext } from 'canvasbar-react'; + +const TEXT = loremIpsum({ count: 100 }); + +export function Customization() { + return ( +
+ +
+

Container with customized scrollbars via config props

+

{TEXT}

+
+
+ + +
+

Container with customized scrollbars via context

+ It's better to use it for customization all (or multiple) scrollbars on the page. +

{TEXT}

+
+
+
+
+ ); +} diff --git a/examples/src/examples/Heavy.tsx b/examples/src/examples/Heavy.tsx new file mode 100644 index 0000000..6aeb14b --- /dev/null +++ b/examples/src/examples/Heavy.tsx @@ -0,0 +1,14 @@ +import { CanvasBar } from 'canvasbar-react'; + +const IMAGES_IDS = Array.from(Array(10).keys()); + +export function Heavy() { + return ( + +

Container with 10 large images (3000x3000 resized to 600x600) and both scrollbars

+ {IMAGES_IDS.map(id => ( + test + ))} +
+ ); +} diff --git a/examples/src/examples/Sticky.tsx b/examples/src/examples/Sticky.tsx new file mode 100644 index 0000000..d7c4176 --- /dev/null +++ b/examples/src/examples/Sticky.tsx @@ -0,0 +1,36 @@ +import { loremIpsum } from 'lorem-ipsum'; +import { CanvasBar } from 'canvasbar-react'; + +const WORDS = loremIpsum({ count: 1000 }).split(' '); +const COLUMNS_COUNT = 20; +const COLUMNS_IDS = Array.from(Array(COLUMNS_COUNT).keys()); +const ROWS_COUNT = Math.floor(WORDS.length / COLUMNS_COUNT); +const ROWS_IDS = Array.from(Array(ROWS_COUNT).keys()); + +export function Sticky() { + return ( + <> +

Large table with sticky first column and first row (with both scrollbars)

+ + + + + {COLUMNS_IDS.map(id => )} + + + + {ROWS_IDS.map(rowId => ( + + {COLUMNS_IDS.map(colId => ( + + ))} + + ))} + +
Column #{id + 1}
+ {colId ? WORDS[rowId * COLUMNS_COUNT + colId] : `Row #${rowId + 1}`} +
+
+ + ); +} diff --git a/examples/src/examples/Table.tsx b/examples/src/examples/Table.tsx new file mode 100644 index 0000000..a1145d9 --- /dev/null +++ b/examples/src/examples/Table.tsx @@ -0,0 +1,32 @@ +import { loremIpsum } from 'lorem-ipsum'; +import { CanvasBar } from 'canvasbar-react'; + +const WORDS = loremIpsum({ count: 1000 }).split(' '); +const COLUMNS_COUNT = 20; +const COLUMNS_IDS = Array.from(Array(COLUMNS_COUNT).keys()); +const ROWS_COUNT = Math.floor(WORDS.length / COLUMNS_COUNT); +const ROWS_IDS = Array.from(Array(ROWS_COUNT).keys()); + +export function Table() { + return ( + <> +

Large table with both scrollbars

+ + + + + {COLUMNS_IDS.map(id => )} + + + + {ROWS_IDS.map(rowId => ( + + {COLUMNS_IDS.map(colId => )} + + ))} + +
Column #{id + 1}
{WORDS[rowId * COLUMNS_COUNT + colId]}
+
+ + ); +} diff --git a/examples/src/index.css b/examples/src/index.css index ec2585e..0f731d5 100644 --- a/examples/src/index.css +++ b/examples/src/index.css @@ -11,3 +11,11 @@ code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } + +table { + border-spacing: 0; +} + +a:visited { + color: inherit; +} diff --git a/examples/src/index.tsx b/examples/src/index.tsx index ef2edf8..c95f213 100644 --- a/examples/src/index.tsx +++ b/examples/src/index.tsx @@ -1,12 +1,16 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { BrowserRouter } from 'react-router-dom'; + import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; ReactDOM.render( - + + + , document.getElementById('root') );