@infonomic/uikit 5.2.1 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/astro.d.ts +45 -0
- package/dist/astro.js +45 -0
- package/dist/components/accordion/accordion.module.css +60 -0
- package/dist/components/avatar/avatar.module.css +54 -0
- package/dist/components/badge/badge.module.css +61 -0
- package/dist/components/button/button-group.module.css +19 -0
- package/dist/components/button/button.astro +86 -0
- package/dist/components/button/button.module.css +431 -0
- package/dist/components/button/control-buttons.module.css +132 -0
- package/dist/components/button/copy-button.module.css +56 -0
- package/dist/components/button/icon-button.astro +47 -0
- package/dist/components/card/card-content.astro +14 -0
- package/dist/components/card/card-description.astro +14 -0
- package/dist/components/card/card-footer.astro +14 -0
- package/dist/components/card/card-header.astro +14 -0
- package/dist/components/card/card-title.astro +14 -0
- package/dist/components/card/card.astro +41 -0
- package/dist/components/card/card.module.css +77 -0
- package/dist/components/container/container.astro +13 -0
- package/dist/components/container/container.module.css +36 -0
- package/dist/components/dropdown/dropdown.module.css +120 -0
- package/dist/components/forms/calendar.module.css +269 -0
- package/dist/components/forms/checkbox.module.css +211 -0
- package/dist/components/forms/error-text.astro +14 -0
- package/dist/components/forms/error-text.module.css +9 -0
- package/dist/components/forms/help-text.astro +13 -0
- package/dist/components/forms/help-text.module.css +9 -0
- package/dist/components/forms/input-adornment.astro +26 -0
- package/dist/components/forms/input-adornment.module.css +21 -0
- package/dist/components/forms/input.astro +99 -0
- package/dist/components/forms/input.module.css +278 -0
- package/dist/components/forms/label.astro +24 -0
- package/dist/components/forms/label.module.css +15 -0
- package/dist/components/forms/radio-group.module.css +163 -0
- package/dist/components/forms/select.module.css +68 -0
- package/dist/components/forms/text-area.module.css +10 -0
- package/dist/components/hamburger/hamburger.astro +30 -0
- package/dist/components/notifications/alert.module.css +110 -0
- package/dist/components/notifications/toast.module.css +237 -0
- package/dist/components/overlay/overlay.module.css +41 -0
- package/dist/components/pager/pagination.module.css +149 -0
- package/dist/components/scroll-area/scroll-area.module.css +64 -0
- package/dist/components/scroll-to-top/scroll-to-top.astro +75 -0
- package/dist/components/scroll-to-top/scroll-to-top.d.ts +12 -7
- package/dist/components/scroll-to-top/scroll-to-top.d.ts.map +1 -1
- package/dist/components/scroll-to-top/scroll-to-top.js +24 -14
- package/dist/components/scroll-to-top/scroll-to-top.module.css +86 -0
- package/dist/components/section/section.astro +13 -0
- package/dist/components/section/section.module.css +9 -0
- package/dist/components/shimmer/shimmer.module.css +53 -0
- package/dist/components/table/table.module.css +100 -0
- package/dist/components/tabs/tabs.module.css +66 -0
- package/dist/components/tooltip/tooltip.module.css +45 -0
- package/dist/icons/close-icon.astro +38 -0
- package/dist/icons/icon-element.astro +27 -0
- package/dist/icons/icons.module.css +155 -0
- package/dist/icons/light-icon.astro +36 -0
- package/dist/icons/moon-icon.astro +38 -0
- package/dist/icons/search-icon.astro +40 -0
- package/dist/widgets/datepicker/datepicker.module.css +189 -0
- package/dist/widgets/drawer/drawer.module.css +112 -0
- package/dist/widgets/modal/modal.module.css +81 -0
- package/dist/widgets/timeline/timeline.module.css +87 -0
- package/package.json +5 -4
- package/src/astro.d.ts +2 -0
- package/src/astro.js +3 -0
- package/src/components/scroll-to-top/scroll-to-top.astro +75 -0
- package/src/components/scroll-to-top/scroll-to-top.tsx +52 -33
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
@layer infonomic-base, infonomic-utilities, infonomic-theme, infonomic-components;
|
|
2
|
+
|
|
3
|
+
@layer infonomic-components {
|
|
4
|
+
.drawer-wrapper {
|
|
5
|
+
position: fixed;
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
justify-content: end;
|
|
9
|
+
top: 0;
|
|
10
|
+
left: 0;
|
|
11
|
+
right: 0;
|
|
12
|
+
bottom: 0;
|
|
13
|
+
z-index: 30;
|
|
14
|
+
opacity: 1;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
.drawer-container {
|
|
19
|
+
position: absolute;
|
|
20
|
+
right: 0;
|
|
21
|
+
height: 100%;
|
|
22
|
+
background-color: var(--surface-panel);
|
|
23
|
+
box-shadow: var(--shadow-md);
|
|
24
|
+
z-index: inherit;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.drawer-container[aria-hidden="false"] {
|
|
28
|
+
animation: slideIn 150ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.drawer-depth-0.drawer-medium .drawer-container,
|
|
32
|
+
.drawer-depth-0.drawer-wide .drawer-container {
|
|
33
|
+
width: 90%;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.drawer-depth-1.drawer-medium .drawer-container,
|
|
37
|
+
.drawer-depth-1.drawer-wide .drawer-container {
|
|
38
|
+
width: 88%;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.drawer-depth-2.drawer- .drawer-container,
|
|
42
|
+
.drawer-depth-2.drawer-wide .drawer-container {
|
|
43
|
+
width: 86%;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@media (min-width: 48rem) {
|
|
47
|
+
.drawer-depth-0.drawer-medium .drawer-container {
|
|
48
|
+
width: 600px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.drawer-depth-1.drawer-medium .drawer-container {
|
|
52
|
+
width: 580px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.drawer-depth-2.drawer- .drawer-container {
|
|
56
|
+
width: 560px;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.drawer-narrow .drawer-container {
|
|
61
|
+
width: calc(100% - 32px);
|
|
62
|
+
max-width: 400px
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.drawer-container[aria-hidden="true"] {
|
|
66
|
+
transform: translateX(100%);
|
|
67
|
+
opacity: 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.drawer-top-actions {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
justify-content: flex-end;
|
|
74
|
+
padding-top: var(--spacing-12);
|
|
75
|
+
padding-left: var(--spacing-12);
|
|
76
|
+
padding-right: var(--spacing-12);
|
|
77
|
+
padding-bottom: 0;
|
|
78
|
+
max-width: 100%;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.drawer-header {
|
|
82
|
+
display: flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
justify-content: space-between;
|
|
85
|
+
gap: var(--gap-3);
|
|
86
|
+
padding-top: 0;
|
|
87
|
+
padding-left: var(--spacing-12);
|
|
88
|
+
padding-right: var(--spacing-12);
|
|
89
|
+
max-width: 100%;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.drawer-header h2,
|
|
93
|
+
.drawer-header h3 {
|
|
94
|
+
margin: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.drawer-content {
|
|
98
|
+
padding: var(--spacing-12);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@keyframes slideIn {
|
|
102
|
+
from {
|
|
103
|
+
transform: translateX(100%);
|
|
104
|
+
opacity: 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
to {
|
|
108
|
+
transform: translateX(0);
|
|
109
|
+
opacity: 1;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
@layer infonomic-base, infonomic-utilities, infonomic-theme, infonomic-components;
|
|
2
|
+
|
|
3
|
+
@layer infonomic-components {
|
|
4
|
+
.modal-wrapper {
|
|
5
|
+
position: fixed;
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
align-items: center;
|
|
10
|
+
top: 0;
|
|
11
|
+
left: 0;
|
|
12
|
+
right: 0;
|
|
13
|
+
bottom: 0;
|
|
14
|
+
z-index: 30;
|
|
15
|
+
opacity: 1;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.modal-container {
|
|
19
|
+
position: absolute;
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
box-shadow: var(--shadow-md);
|
|
23
|
+
border-radius: var(--border-radius-md);
|
|
24
|
+
z-index: 20;
|
|
25
|
+
min-height: 300px;
|
|
26
|
+
max-height: 88vh;
|
|
27
|
+
width: 100%;
|
|
28
|
+
bottom: 0;
|
|
29
|
+
background-color: var(--surface-panel);
|
|
30
|
+
border: 1px solid var(--border-color);
|
|
31
|
+
margin-left: var(--spacing-8);
|
|
32
|
+
margin-right: var(--spacing-8);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@media screen and (min-width: 40rem) {
|
|
36
|
+
.modal-container {
|
|
37
|
+
max-height: calc(100vh - 24px);
|
|
38
|
+
min-height: auto;
|
|
39
|
+
bottom: auto;
|
|
40
|
+
width: auto;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.modal-header {
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
justify-content: space-between;
|
|
48
|
+
gap: var(--gap-3);
|
|
49
|
+
padding-left: var(--spacing-16);
|
|
50
|
+
padding-right: var(--spacing-16);
|
|
51
|
+
padding-top: var(--spacing-20);
|
|
52
|
+
max-width: 100%;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.modal-header h2,
|
|
56
|
+
.modal-header h3 {
|
|
57
|
+
margin: 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.modal-content {
|
|
61
|
+
display: flex;
|
|
62
|
+
flex: 1;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
gap: var(--gap-3);
|
|
65
|
+
padding-left: var(--spacing-16);
|
|
66
|
+
padding-right: var(--spacing-16);
|
|
67
|
+
padding-bottom: var(--spacing-12);
|
|
68
|
+
max-width: 100%;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.modal-actions {
|
|
72
|
+
display: flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
justify-content: flex-end;
|
|
75
|
+
gap: var(--gap-3);
|
|
76
|
+
padding-top: var(--spacing-8);
|
|
77
|
+
padding-bottom: var(--spacing-20);
|
|
78
|
+
padding-left: var(--spacing-16);
|
|
79
|
+
padding-right: var(--spacing-16);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
@layer infonomic-base, infonomic-functional, infonomic-utilities, infonomic-theme, infonomic-typography, infonomic-components;
|
|
2
|
+
|
|
3
|
+
@layer infonomic-components {
|
|
4
|
+
.timeline {
|
|
5
|
+
margin-left: var(--spacing-8);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.timeline-root {
|
|
9
|
+
position: relative;
|
|
10
|
+
list-style: none;
|
|
11
|
+
margin-left: var(--spacing-8);
|
|
12
|
+
padding-left: var(--spacing-8);
|
|
13
|
+
border-left-width: var(--border-width-thin);
|
|
14
|
+
border-left-color: var(--border-color);
|
|
15
|
+
border-left-style: var(--border-style-solid);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.timeline-item {
|
|
19
|
+
margin: 0 0 var(--spacing-16) var(--spacing-20);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* 'absolute block flex items-center justify-center w-[36px] h-[36px] rounded-full -mt-[3px] sm:mt-0 -left-[18px] border-[6px] bg-secondary-600 dark:bg-secondary-900 border-gray-50 dark:border-canvas-600', */
|
|
23
|
+
.timeline-icon {
|
|
24
|
+
position: absolute;
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: center;
|
|
28
|
+
width: 36px;
|
|
29
|
+
height: 36px;
|
|
30
|
+
border-radius: 50%;
|
|
31
|
+
margin-top: -3px;
|
|
32
|
+
left: -18px;
|
|
33
|
+
border-width: 6px;
|
|
34
|
+
color: white;
|
|
35
|
+
background-color: var(--primary-600);
|
|
36
|
+
border-color: var(--gray-50);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.timeline-icon svg {
|
|
40
|
+
display: block;
|
|
41
|
+
width: 60%;
|
|
42
|
+
color: white;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* 'text-[1.5rem] sm:text-[1.57rem] leading-[1.2] !m-0 font-semibold' */
|
|
46
|
+
.timeline-heading {
|
|
47
|
+
font-size: 1.5rem;
|
|
48
|
+
line-height: 1.2;
|
|
49
|
+
margin: 0;
|
|
50
|
+
font-weight: var(--font-weight-semibold);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* 'block mt-2 mb-2 text-[0.9em] font-medium leading-none text-gray-900 dark:text-gray-100' */
|
|
54
|
+
.timeline-date {
|
|
55
|
+
display: block;
|
|
56
|
+
margin-top: var(--spacing-8);
|
|
57
|
+
margin-bottom: var(--spacing-8);
|
|
58
|
+
font-size: 0.9em;
|
|
59
|
+
font-weight: var(--font-weight-medium);
|
|
60
|
+
line-height: 0;
|
|
61
|
+
color: var(--gray-600);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* '[&_p]:m-0 [&_p]:mb-4 [&_p]:sm:m-0 [&_p]:sm:mb-4 text-lg font-normal text-gray-900 dark:text-gray-300' */
|
|
65
|
+
.timeline-content {
|
|
66
|
+
margin: 0;
|
|
67
|
+
margin-bottom: var(--spacing-16);
|
|
68
|
+
font-size: 1.125rem;
|
|
69
|
+
font-weight: var(--font-weight-normal);
|
|
70
|
+
|
|
71
|
+
& p {
|
|
72
|
+
margin: 0;
|
|
73
|
+
margin-bottom: var(--spacing-16);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* 🌙 Dark via `.dark` class. We rely on the
|
|
78
|
+
* consuming application to detect a user's preferred
|
|
79
|
+
* color scheme - either by header or cookie, and set
|
|
80
|
+
* a root html class accordingly
|
|
81
|
+
*/
|
|
82
|
+
:global(.dark) {
|
|
83
|
+
.timeline-icon:not(:where([class~="not-dark"], [class~="not-dark"] *)) {
|
|
84
|
+
border-color: var(--canvas-600);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@infonomic/uikit",
|
|
3
3
|
"private": false,
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "5.
|
|
5
|
+
"version": "5.4.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"description": "Infonomic UI kit is a collection of reusable UI components and utilities for React and Astro.",
|
|
8
8
|
"keywords": [
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
],
|
|
31
31
|
"exports": {
|
|
32
32
|
"./astro": {
|
|
33
|
-
"types": "./
|
|
34
|
-
"import": "./
|
|
35
|
-
"default": "./
|
|
33
|
+
"types": "./dist/astro.d.ts",
|
|
34
|
+
"import": "./dist/astro.js",
|
|
35
|
+
"default": "./dist/astro.js"
|
|
36
36
|
},
|
|
37
37
|
"./react": {
|
|
38
38
|
"types": "./dist/react.d.ts",
|
|
@@ -114,6 +114,7 @@
|
|
|
114
114
|
"build": "run-s build:*",
|
|
115
115
|
"build:clean": "rimraf dist build",
|
|
116
116
|
"build:code": "rslib build",
|
|
117
|
+
"build:astro": "node ./scripts/build-astro.js",
|
|
117
118
|
"build:css": "lightningcss --bundle ./src/styles/styles.css ./src/styles/typography.css --output-dir ./dist/styles",
|
|
118
119
|
"build:minify": "lightningcss --minify ./dist/styles/styles.css ./dist/styles/typography.css --output-dir ./dist/styles",
|
|
119
120
|
"build:vanilla-components-css": "node ./scripts/task-merge-vanilla-components-css.js",
|
package/src/astro.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type InputComponent from './components/forms/input.astro'
|
|
|
14
14
|
import type InputAdornmentComponent from './components/forms/input-adornment.astro'
|
|
15
15
|
import type LabelComponent from './components/forms/label.astro'
|
|
16
16
|
import type SectionComponent from './components/section/section.astro'
|
|
17
|
+
import type ScrollToTopComponent from './components/scroll-to-top/scroll-to-top.astro'
|
|
17
18
|
import type CloseIconComponent from './icons/close-icon.astro'
|
|
18
19
|
import type IconElementComponent from './icons/icon-element.astro'
|
|
19
20
|
import type LightIconComponent from './icons/light-icon.astro'
|
|
@@ -41,3 +42,4 @@ export declare const LightIcon: typeof LightIconComponent
|
|
|
41
42
|
export declare const MoonIcon: typeof MoonIconComponent
|
|
42
43
|
export declare const SearchIcon: typeof SearchIconComponent
|
|
43
44
|
export declare const Section: typeof SectionComponent
|
|
45
|
+
export declare const ScrollToTop: typeof ScrollToTopComponent
|
package/src/astro.js
CHANGED
|
@@ -14,12 +14,14 @@ import InputComponent from './components/forms/input.astro'
|
|
|
14
14
|
import InputAdornmentComponent from './components/forms/input-adornment.astro'
|
|
15
15
|
import LabelComponent from './components/forms/label.astro'
|
|
16
16
|
import SectionComponent from './components/section/section.astro'
|
|
17
|
+
import ScrollToTopComponent from './components/scroll-to-top/scroll-to-top.astro'
|
|
17
18
|
import CloseIconComponent from './icons/close-icon.astro'
|
|
18
19
|
import IconElementComponent from './icons/icon-element.astro'
|
|
19
20
|
import LightIconComponent from './icons/light-icon.astro'
|
|
20
21
|
import MoonIconComponent from './icons/moon-icon.astro'
|
|
21
22
|
import SearchIconComponent from './icons/search-icon.astro'
|
|
22
23
|
|
|
24
|
+
|
|
23
25
|
export const Button = ButtonComponent
|
|
24
26
|
export const Hamburger = HamburgerComponent
|
|
25
27
|
export const IconButton = IconButtonComponent
|
|
@@ -36,6 +38,7 @@ export const InputAdornment = InputAdornmentComponent
|
|
|
36
38
|
export const Input = InputComponent
|
|
37
39
|
export const Label = LabelComponent
|
|
38
40
|
export const Section = SectionComponent
|
|
41
|
+
export const ScrollToTop = ScrollToTopComponent
|
|
39
42
|
export const IconElement = IconElementComponent
|
|
40
43
|
export const LightIcon = LightIconComponent
|
|
41
44
|
export const MoonIcon = MoonIconComponent
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
import styles from './scroll-to-top.module.css'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
class?: string
|
|
6
|
+
showAt?: number
|
|
7
|
+
offset?: number
|
|
8
|
+
'aria-label'?: string
|
|
9
|
+
[key: string]: any
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
class: className,
|
|
14
|
+
showAt = 200,
|
|
15
|
+
offset = -65,
|
|
16
|
+
'aria-label': ariaLabel = 'Scroll to top',
|
|
17
|
+
...rest
|
|
18
|
+
} = Astro.props
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<button
|
|
22
|
+
type="button"
|
|
23
|
+
aria-label={ariaLabel}
|
|
24
|
+
class:list={['scroll-to-top', styles['scroll-to-top'], className]}
|
|
25
|
+
data-show-at={showAt}
|
|
26
|
+
data-offset={offset}
|
|
27
|
+
data-class-shown={styles['scroll-to-top-shown']}
|
|
28
|
+
{...rest}
|
|
29
|
+
>
|
|
30
|
+
<span aria-hidden="true">
|
|
31
|
+
<svg class="icon" style="fill: currentColor" focusable="false" viewBox="0 0 51 32">
|
|
32
|
+
<path d="M25.4,9.8L45.6,30l4.5-4.5L25.4,0.8L0.8,25.4L5.3,30L25.4,9.8z"></path>
|
|
33
|
+
</svg>
|
|
34
|
+
</span>
|
|
35
|
+
</button>
|
|
36
|
+
|
|
37
|
+
<script>
|
|
38
|
+
const buttons = document.querySelectorAll('button.scroll-to-top')
|
|
39
|
+
|
|
40
|
+
buttons.forEach((button) => {
|
|
41
|
+
if (!(button instanceof HTMLElement)) return
|
|
42
|
+
|
|
43
|
+
const showAt = Number(button.dataset.showAt) || 200
|
|
44
|
+
const offset = Number(button.dataset.offset) || -65
|
|
45
|
+
const classShown = button.dataset.classShown
|
|
46
|
+
|
|
47
|
+
if (classShown == null) return
|
|
48
|
+
|
|
49
|
+
// Click Handler
|
|
50
|
+
button.addEventListener('click', () => {
|
|
51
|
+
window.scrollTo({ top: offset, left: 0, behavior: 'smooth' })
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// Scroll Handler
|
|
55
|
+
let ticking = false
|
|
56
|
+
const handleOnScroll = () => {
|
|
57
|
+
if (!ticking) {
|
|
58
|
+
window.requestAnimationFrame(() => {
|
|
59
|
+
const shouldShow = window.scrollY > showAt
|
|
60
|
+
if (shouldShow) {
|
|
61
|
+
button.classList.add(classShown)
|
|
62
|
+
button.classList.add('scroll-to-top-shown')
|
|
63
|
+
} else {
|
|
64
|
+
button.classList.remove(classShown)
|
|
65
|
+
button.classList.remove('scroll-to-top-shown')
|
|
66
|
+
}
|
|
67
|
+
ticking = false
|
|
68
|
+
})
|
|
69
|
+
ticking = true
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
window.addEventListener('scroll', handleOnScroll, { passive: true })
|
|
74
|
+
})
|
|
75
|
+
</script>
|
|
@@ -1,62 +1,81 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import type React from 'react'
|
|
4
3
|
import cx from 'classnames'
|
|
5
|
-
import { useEffect, useState } from 'react'
|
|
4
|
+
import { type ComponentProps, useEffect, useState } from 'react'
|
|
6
5
|
|
|
7
|
-
import
|
|
6
|
+
import styles from './scroll-to-top.module.css'
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
export interface ScrollToTopProps extends ComponentProps<'button'> {
|
|
9
|
+
/**
|
|
10
|
+
* The scroll position (Y-axis) in pixels that triggers the button to appear.
|
|
11
|
+
* @default 200
|
|
12
|
+
*/
|
|
13
|
+
showAt?: number
|
|
14
|
+
/**
|
|
15
|
+
* The target scroll position (Y-axis) to scroll to when clicked.
|
|
16
|
+
* @default -65
|
|
17
|
+
*/
|
|
11
18
|
offset?: number
|
|
12
19
|
}
|
|
13
20
|
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
ref,
|
|
21
|
+
export function ScrollToTop({
|
|
22
|
+
className,
|
|
23
|
+
showAt = 200,
|
|
18
24
|
offset = -65,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
type = 'button',
|
|
26
|
+
'aria-label': ariaLabel = 'Scroll to top',
|
|
27
|
+
onClick,
|
|
28
|
+
...props
|
|
29
|
+
}: ScrollToTopProps) {
|
|
23
30
|
const [show, setShow] = useState(false)
|
|
24
31
|
|
|
25
|
-
const handleOnClick = (): void => {
|
|
32
|
+
const handleOnClick = (e: React.MouseEvent<HTMLButtonElement>): void => {
|
|
26
33
|
window.scrollTo({ top: offset, left: 0, behavior: 'smooth' })
|
|
34
|
+
onClick?.(e)
|
|
27
35
|
}
|
|
28
36
|
|
|
29
37
|
useEffect(() => {
|
|
38
|
+
let ticking = false
|
|
39
|
+
|
|
30
40
|
const handleOnScroll = (): void => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
if (!ticking) {
|
|
42
|
+
window.requestAnimationFrame(() => {
|
|
43
|
+
setShow(window.scrollY > showAt)
|
|
44
|
+
ticking = false
|
|
45
|
+
})
|
|
46
|
+
ticking = true
|
|
36
47
|
}
|
|
37
48
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
49
|
+
|
|
50
|
+
window.addEventListener('scroll', handleOnScroll, { passive: true })
|
|
41
51
|
return () => {
|
|
42
52
|
window.removeEventListener('scroll', handleOnScroll)
|
|
43
53
|
}
|
|
44
|
-
}, [])
|
|
54
|
+
}, [showAt])
|
|
45
55
|
|
|
46
56
|
return (
|
|
47
57
|
<button
|
|
48
|
-
|
|
49
|
-
{
|
|
58
|
+
type={type}
|
|
59
|
+
aria-label={ariaLabel}
|
|
50
60
|
onClick={handleOnClick}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
className={cx(
|
|
62
|
+
'scroll-to-top',
|
|
63
|
+
styles['scroll-to-top'],
|
|
64
|
+
{
|
|
65
|
+
'scroll-to-top-shown': show,
|
|
66
|
+
[styles['scroll-to-top-shown']]: show,
|
|
67
|
+
},
|
|
68
|
+
className
|
|
69
|
+
)}
|
|
70
|
+
{...props}
|
|
57
71
|
>
|
|
58
|
-
<span>
|
|
59
|
-
<svg
|
|
72
|
+
<span aria-hidden="true">
|
|
73
|
+
<svg
|
|
74
|
+
className="icon"
|
|
75
|
+
style={{ fill: 'currentColor' }}
|
|
76
|
+
focusable="false"
|
|
77
|
+
viewBox="0 0 51 32"
|
|
78
|
+
>
|
|
60
79
|
<path d="M25.4,9.8L45.6,30l4.5-4.5L25.4,0.8L0.8,25.4L5.3,30L25.4,9.8z" />
|
|
61
80
|
</svg>
|
|
62
81
|
</span>
|