@integreat-app/react-sticky-headroom 1.2.3 → 2.1.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.
@@ -1,28 +1,24 @@
1
- // @flow
2
-
3
- import type { Node } from 'react'
4
- import * as React from 'react'
5
- import type { StyledComponent } from 'styled-components'
1
+ import React from 'react'
6
2
  import styled, { css, keyframes } from 'styled-components'
7
3
 
8
- const UPWARDS = 'up'
9
- const DOWNWARDS = 'down'
4
+ const DIRECTION_UP = 'up'
5
+ const DIRECTION_DOWN = 'down'
10
6
 
11
- const UNPINNED = 'unpinned'
12
- const PINNED = 'pinned'
13
- const STATIC = 'static'
7
+ const MODE_UNPINNED = 'unpinned'
8
+ const MODE_PINNED = 'pinned'
9
+ const MODE_STATIC = 'static'
14
10
 
15
- const NO_TRANSITION = 'none'
16
- const NORMAL_TRANSITION = 'normal'
17
- const PINNED_TO_STATIC = 'pinned-to-static'
11
+ const TRANSITION_NONE = 'none'
12
+ const TRANSITION_NORMAL = 'normal'
13
+ const TRANSITION_PINNED_TO_STATIC = 'pinned-to-static'
18
14
 
19
- type ModeType = 'pinned' | 'unpinned' | 'static'
20
- type DirectionType = 'up' | 'down'
21
- type TransitionType = 'none' | 'normal' | 'pinned-to-static'
15
+ type ModeType = typeof MODE_PINNED | typeof MODE_UNPINNED | typeof MODE_STATIC
16
+ type DirectionType = typeof DIRECTION_UP | typeof DIRECTION_DOWN
17
+ type TransitionType = typeof TRANSITION_NONE | typeof TRANSITION_NORMAL | typeof TRANSITION_PINNED_TO_STATIC
22
18
 
23
- type PropsType = $ReadOnly<{|
19
+ type PropsType = {
24
20
  /** The child node to be displayed as a header */
25
- children: Node,
21
+ children: React.ReactNode,
26
22
  /** The maximum amount of px the header should move up when scrolling */
27
23
  scrollHeight: number,
28
24
  /** The minimum scrollTop position where the transform should start */
@@ -30,45 +26,47 @@ type PropsType = $ReadOnly<{|
30
26
  /** Used for calculating the stickyTop position of an ancestor */
31
27
  height?: number,
32
28
  /** Fired, when Headroom changes its state. Passes stickyTop of the ancestor. */
33
- onStickyTopChanged?: (number) => void,
29
+ onStickyTopChanged?: (stickyTop: number) => void,
34
30
  /** True, if sticky position should be disabled (e.g. for edge 16 support) */
35
31
  positionStickyDisabled?: boolean,
36
32
  /** The parent element firing the scroll event. Defaults to document.documentElement */
37
- parent: ?HTMLElement,
33
+ parent?: HTMLElement | null,
38
34
  /** The z-index used by the wrapper. Defaults to 1. */
39
35
  zIndex?: number,
40
36
  /** A classname for applying custom styles to the wrapper. Use at your own risk. */
41
37
  className?: string
42
- |}>
38
+ }
43
39
 
44
- type StateType = $ReadOnly<{|
40
+ type StateType = {
45
41
  mode: ModeType,
46
42
  transition: TransitionType,
47
- animateUpFrom: ?number
48
- |}>
43
+ animateUpFrom: number | null
44
+ }
49
45
 
50
- const HeaderWrapper: StyledComponent<{|
51
- positionStickyDisabled: boolean,
52
- translateY: number,
53
- transition: TransitionType,
54
- animateUpFrom: ?number,
55
- zIndex: number
56
- |}, *, *, *> = styled.div`
57
- position: ${props => props.positionStickyDisabled ? 'static' : 'sticky'};
58
- top: ${props => props.top}px;
59
- z-index: ${props => props.zIndex};
60
- transform: translateY(${props => props.translateY}px);
46
+ const HeaderWrapper = styled.div<{
47
+ $positionStickyDisabled: boolean,
48
+ $translateY: number,
49
+ $transition: TransitionType,
50
+ $animateUpFrom: number | null,
51
+ $zIndex?: number,
52
+ $top: number,
53
+ $static: boolean
54
+ }>`
55
+ position: ${props => props.$positionStickyDisabled ? 'static' : 'sticky'};
56
+ top: ${props => props.$top}px;
57
+ z-index: ${props => props.$zIndex};
58
+ transform: translateY(${props => props.$translateY}px);
61
59
  animation-duration: 0.2s;
62
60
  animation-timing-function: ease-out;
63
- ${props => props.transition === NORMAL_TRANSITION && !props.static
61
+ ${props => props.$transition === TRANSITION_NORMAL && !props.$static
64
62
  ? 'transition: transform 0.2s ease-out;'
65
63
  : ''}
66
- ${props => props.transition === PINNED_TO_STATIC
64
+ ${props => props.$transition === TRANSITION_PINNED_TO_STATIC && props.$animateUpFrom !== null
67
65
  ? css`
68
- animation-name: ${keyframesMoveUpFrom(props.animateUpFrom)};
66
+ animation-name: ${keyframesMoveUpFrom(props.$animateUpFrom)};
69
67
  `
70
68
  : ''}
71
- ${props => props.static ? 'transition: none;' : ''}
69
+ ${props => props.$static ? 'transition: none;' : ''}
72
70
  `
73
71
 
74
72
  const keyframesMoveUpFrom = (from: number) => keyframes`
@@ -79,18 +77,18 @@ const keyframesMoveUpFrom = (from: number) => keyframes`
79
77
  to {
80
78
  transform: translateY(0)
81
79
  }
82
- `
80
+ `
83
81
 
84
82
  class Headroom extends React.PureComponent<PropsType, StateType> {
85
- static defaultProps: {| pinStart: number, zIndex: number, parent: ?HTMLElement |} = {
83
+ static defaultProps: { pinStart: number, zIndex: number, parent: HTMLElement | null } = {
86
84
  pinStart: 0,
87
85
  zIndex: 1,
88
86
  parent: window.document.documentElement
89
87
  }
90
88
 
91
89
  state: StateType = {
92
- mode: STATIC,
93
- transition: NO_TRANSITION,
90
+ mode: MODE_STATIC,
91
+ transition: TRANSITION_NONE,
94
92
  animateUpFrom: null
95
93
  }
96
94
 
@@ -106,36 +104,30 @@ class Headroom extends React.PureComponent<PropsType, StateType> {
106
104
  return parent.scrollTop
107
105
  }
108
106
  if (parent !== document.documentElement) {
109
- console.warn('Could not find parent for StickyHeadroom. Defaulting to window, documentElement or body.')
107
+ console.warn('Could not determine scrollTop from parent for StickyHeadroom. Defaulting to window.pageYOffset.')
110
108
  }
111
- if (window.pageYOffset !== undefined) {
112
- return window.pageYOffset
113
- } else if (window.scrollTop !== undefined) {
114
- return window.scrollTop
115
- } else if (document.documentElement) {
116
- return document.documentElement.scrollTop
117
- } else if (document.body) {
118
- return document.body.scrollTop
119
- } else {
120
- throw new Error('Could not determine scrollTop!')
109
+ if (window.pageYOffset === undefined) {
110
+ console.error('window.pageYOffset is undefined. Defaulting to 0.')
111
+ return 0
121
112
  }
113
+ return window.pageYOffset
122
114
  }
123
115
 
124
116
  componentDidMount () {
125
117
  this.addScrollListener(this.props.parent)
126
118
  }
127
119
 
128
- addScrollListener (parent: ?HTMLElement) {
120
+ addScrollListener (parent?: HTMLElement | null) {
129
121
  if (parent === window.document.documentElement) {
130
122
  window.addEventListener('scroll', this.handleEvent)
131
123
  } else if (parent) {
132
124
  parent.addEventListener('scroll', this.handleEvent)
133
125
  } else {
134
- console.debug('Property parent of Headroom is null. Assuming, parent will be set soon...')
126
+ console.debug("'parent' prop of Headroom is null. Assuming, it will be set soon...")
135
127
  }
136
128
  }
137
129
 
138
- removeScrollListener (parent: ?HTMLElement) {
130
+ removeScrollListener (parent?: HTMLElement | null) {
139
131
  if (parent === window.document.documentElement) {
140
132
  window.removeEventListener('scroll', this.handleEvent)
141
133
  } else if (parent) {
@@ -164,8 +156,10 @@ class Headroom extends React.PureComponent<PropsType, StateType> {
164
156
  * @returns {boolean} if we should set the header static
165
157
  */
166
158
  shouldSetStatic (scrollTop: number, direction: DirectionType): boolean {
167
- if (this.state.mode === STATIC || (this.state.mode === PINNED && direction ===
168
- DOWNWARDS)) {
159
+ if (
160
+ this.state.mode === MODE_STATIC ||
161
+ (this.state.mode === MODE_PINNED && direction === DIRECTION_DOWN)
162
+ ) {
169
163
  return this.props.pinStart + this.props.scrollHeight >= scrollTop
170
164
  } else {
171
165
  return this.props.pinStart >= scrollTop
@@ -180,9 +174,9 @@ class Headroom extends React.PureComponent<PropsType, StateType> {
180
174
  */
181
175
  determineMode (scrollTop: number, direction: DirectionType): ModeType {
182
176
  if (this.shouldSetStatic(scrollTop, direction)) {
183
- return STATIC
177
+ return MODE_STATIC
184
178
  } else {
185
- return direction === UPWARDS ? PINNED : UNPINNED
179
+ return direction === DIRECTION_UP ? MODE_PINNED : MODE_UNPINNED
186
180
  }
187
181
  }
188
182
 
@@ -191,20 +185,21 @@ class Headroom extends React.PureComponent<PropsType, StateType> {
191
185
  */
192
186
  determineTransition (mode: ModeType, direction: DirectionType): TransitionType {
193
187
  // Handle special case: If we're pinned and going to static, we need a special transition using css animation
194
- if (this.state.mode === PINNED && mode === STATIC) {
195
- return PINNED_TO_STATIC
188
+ if (this.state.mode === MODE_PINNED && mode === MODE_STATIC) {
189
+ return TRANSITION_PINNED_TO_STATIC
196
190
  }
197
191
  // If mode is static, then no transition, because we're already in the right spot
198
192
  // (and want to change transform and top properties seamlessly)
199
- if (mode === STATIC) {
200
- return this.state.transition === NO_TRANSITION
201
- ? NO_TRANSITION
202
- : PINNED_TO_STATIC
193
+ if (mode === MODE_STATIC) {
194
+ return this.state.transition === TRANSITION_NONE
195
+ ? TRANSITION_NONE
196
+ : TRANSITION_PINNED_TO_STATIC
203
197
  }
204
198
  // mode is not static, transition when moving upwards or when we've lastly did the transition
205
- return direction === UPWARDS || this.state.transition === NORMAL_TRANSITION
206
- ? NORMAL_TRANSITION
207
- : NO_TRANSITION
199
+ return direction === DIRECTION_UP ||
200
+ this.state.transition === TRANSITION_NORMAL
201
+ ? TRANSITION_NORMAL
202
+ : TRANSITION_NONE
208
203
  }
209
204
 
210
205
  /**
@@ -212,30 +207,24 @@ class Headroom extends React.PureComponent<PropsType, StateType> {
212
207
  */
213
208
  update: () => void = () => {
214
209
  const currentScrollTop = this.getScrollTop()
215
- const newState = {}
210
+ const newState: Partial<StateType> = {}
216
211
  if (currentScrollTop === this.lastKnownScrollTop) {
217
212
  return
218
213
  }
219
- const direction = this.lastKnownScrollTop < currentScrollTop
220
- ? DOWNWARDS
221
- : UPWARDS
214
+ const direction =
215
+ this.lastKnownScrollTop < currentScrollTop ? DIRECTION_DOWN : DIRECTION_UP
222
216
  newState.mode = this.determineMode(currentScrollTop, direction)
223
217
  newState.transition = this.determineTransition(newState.mode, direction)
224
218
 
225
- const {
226
- onStickyTopChanged,
227
- height,
228
- scrollHeight,
229
- pinStart
230
- } = this.props
231
- if (this.state.mode === PINNED && newState.mode === STATIC) {
219
+ const { onStickyTopChanged, height, scrollHeight, pinStart } = this.props
220
+ if (this.state.mode === MODE_PINNED && newState.mode === MODE_STATIC) {
232
221
  // animation in the special case from pinned to static
233
222
  newState.animateUpFrom = currentScrollTop - pinStart
234
223
  }
235
224
  if (onStickyTopChanged && newState.mode !== this.state.mode && height) {
236
225
  onStickyTopChanged(Headroom.calcStickyTop(newState.mode, height, scrollHeight))
237
226
  }
238
- this.setState(newState)
227
+ this.setState(newState as StateType)
239
228
  this.lastKnownScrollTop = currentScrollTop
240
229
  }
241
230
 
@@ -248,10 +237,10 @@ class Headroom extends React.PureComponent<PropsType, StateType> {
248
237
  height: number,
249
238
  scrollHeight: number
250
239
  ): number {
251
- return mode === PINNED ? height : height - scrollHeight
240
+ return mode === MODE_PINNED ? height : height - scrollHeight
252
241
  }
253
242
 
254
- render (): * {
243
+ render (): React.ReactElement {
255
244
  const {
256
245
  children,
257
246
  scrollHeight,
@@ -264,17 +253,17 @@ class Headroom extends React.PureComponent<PropsType, StateType> {
264
253
  transition,
265
254
  animateUpFrom
266
255
  } = this.state
267
- const transform = mode === UNPINNED ? -scrollHeight : 0
268
- const ownStickyTop = mode === STATIC ? -scrollHeight : 0
256
+ const transform = mode === MODE_UNPINNED ? -scrollHeight : 0
257
+ const ownStickyTop = mode === MODE_STATIC ? -scrollHeight : 0
269
258
  return <HeaderWrapper
270
259
  className={className}
271
- translateY={transform}
272
- top={ownStickyTop}
273
- transition={transition}
274
- positionStickyDisabled={positionStickyDisabled}
275
- static={mode === STATIC}
276
- animateUpFrom={animateUpFrom}
277
- zIndex={zIndex}>
260
+ $translateY={transform}
261
+ $top={ownStickyTop}
262
+ $transition={transition}
263
+ $positionStickyDisabled={!!positionStickyDisabled}
264
+ $static={mode === MODE_STATIC}
265
+ $animateUpFrom={animateUpFrom}
266
+ $zIndex={zIndex}>
278
267
  {children}
279
268
  </HeaderWrapper>
280
269
  }
package/package.json CHANGED
@@ -1,26 +1,31 @@
1
1
  {
2
2
  "name": "@integreat-app/react-sticky-headroom",
3
- "version": "1.2.3",
3
+ "version": "2.1.0",
4
4
  "engines": {
5
- "node": ">=6.9",
6
- "npm": ">=3.8"
5
+ "node": ">=18",
6
+ "npm": ">=10"
7
7
  },
8
- "browserslist": [
9
- "ie >= 11",
10
- "edge >= 16",
11
- "chrome >= 41",
12
- "firefox >= 40",
13
- "safari >= 6.2"
14
- ],
15
8
  "license": "MIT",
16
9
  "description": "ReactStickyHeadroom is a React Component for hiding the header when scrolling.",
17
10
  "author": "Michael Markl <marklmichael98@gmail.com>",
18
- "repository": "https://github.com/integreat/react-sticky-headroom",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/integreat/react-sticky-headroom.git"
14
+ },
19
15
  "files": [
20
16
  "index.js",
21
- "index.js.flow",
22
- "index.d.ts"
17
+ "index.js.map",
18
+ "index.cjs",
19
+ "index.cjs.map",
20
+ "index.d.ts",
21
+ "index.d.ts.map",
22
+ "index.tsx"
23
23
  ],
24
+ "exports": {
25
+ "import": "./index.js",
26
+ "require": "./index.cjs",
27
+ "types": "./index.d.ts"
28
+ },
24
29
  "publishConfig": {
25
30
  "access": "public"
26
31
  },
@@ -31,82 +36,74 @@
31
36
  "hide",
32
37
  "header",
33
38
  "styled-components",
34
- "typescript",
35
- "flow"
39
+ "typescript"
36
40
  ],
37
- "module": "index.js",
41
+ "module": "./index.js",
38
42
  "scripts": {
39
- "build": "node tools/build.js",
40
- "build:demo": "node tools/build.demo.js",
43
+ "build": "ts-node tools/build.ts",
44
+ "build:demo": "webpack --config tools/demo.webpack.config.ts",
41
45
  "test": "jest --config jest.config.json",
42
46
  "test:coverage": "jest --config jest.config.json --coverage",
43
47
  "test:watch": "jest --config jest.config.json --watchAll",
44
48
  "test:update": "jest --config jest.config.json -u",
45
49
  "lint": "npm run eslint && npm run stylelint",
46
- "lint:fix": "npm run eslint --fix && npm run stylelint",
50
+ "lint:fix": "eslint . --fix && npm run stylelint",
47
51
  "eslint": "eslint .",
48
- "stylelint": "stylelint './src/**/*.js'",
49
- "flow:check-now": "flow check",
50
- "flow:install-types": "flow-typed install --skip",
51
- "prepublishOnly": "npm run build && npm run build:demo && npm run test && npm run lint && npm run flow:check-now"
52
+ "stylelint": "stylelint './src/**/*.{ts,tsx}'",
53
+ "ts:check": "tsc",
54
+ "prepublishOnly": "npm run build && npm run build:demo && npm run test && npm run lint && npm run ts:check"
52
55
  },
56
+ "//": "browserslist only affects the build of the demo app, not the library itself.",
57
+ "browserslist": [
58
+ "ie >= 11",
59
+ "edge >= 16",
60
+ "chrome >= 41",
61
+ "firefox >= 40",
62
+ "safari >= 6.2"
63
+ ],
53
64
  "peerDependencies": {
54
65
  "react": "16.x.x || 17.x.x || 18.x.x",
55
- "styled-components": "4.x.x || 5.x.x"
66
+ "styled-components": "4.x.x || 5.x.x || 6.x.x"
56
67
  },
57
68
  "devDependencies": {
58
- "@babel/cli": "^7.17.6",
59
- "@babel/core": "^7.17.8",
60
- "@babel/eslint-parser": "^7.17.0",
61
- "@babel/plugin-proposal-class-properties": "^7.16.7",
62
- "@babel/plugin-proposal-do-expressions": "^7.16.7",
63
- "@babel/plugin-proposal-export-default-from": "^7.16.7",
64
- "@babel/plugin-proposal-export-namespace-from": "^7.16.7",
65
- "@babel/plugin-proposal-json-strings": "^7.16.7",
66
- "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7",
67
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
68
- "@babel/plugin-proposal-numeric-separator": "^7.16.7",
69
- "@babel/plugin-proposal-optional-chaining": "^7.16.7",
70
- "@babel/plugin-proposal-pipeline-operator": "^7.17.6",
71
- "@babel/plugin-proposal-throw-expressions": "^7.16.7",
72
- "@babel/plugin-syntax-dynamic-import": "^7.8.3",
73
- "@babel/plugin-syntax-import-meta": "^7.10.4",
74
- "@babel/plugin-transform-react-jsx": "^7.17.3",
75
- "@babel/plugin-transform-runtime": "^7.17.0",
76
- "@babel/preset-env": "^7.16.11",
77
- "@babel/preset-flow": "^7.16.7",
78
- "@babel/preset-react": "^7.16.7",
79
- "@babel/runtime": "^7.17.8",
80
69
  "@stylelint/postcss-css-in-js": "^0.38.0",
81
- "babel-jest": "^27.5.1",
82
- "babel-loader": "^8.2.4",
83
- "babel-plugin-styled-components": "^2.0.6",
84
- "browserslist": "^4.20.2",
70
+ "@swc/core": "^1.3.100",
71
+ "@swc/jest": "^0.2.29",
72
+ "@swc/plugin-styled-components": "^1.5.105",
73
+ "@types/enzyme": "^3.10.15",
74
+ "@types/enzyme-adapter-react-16": "^1.0.8",
75
+ "@types/jest": "^29.5.6",
76
+ "@types/node": "^20.8.7",
77
+ "@types/react": "16",
78
+ "@types/react-dom": "^18.2.13",
79
+ "@types/styled-components": "^5.1.34",
80
+ "@typescript-eslint/eslint-plugin": "6.8.0",
81
+ "@typescript-eslint/parser": "^6.8.0",
82
+ "browserslist": "^4.22.2",
85
83
  "enzyme": "^3.11.0",
86
84
  "enzyme-adapter-react-16": "^1.15.6",
87
85
  "enzyme-to-json": "^3.6.2",
88
- "eslint": "^8.25.0",
89
- "eslint-config-standard": "^17.0.0",
90
- "eslint-plugin-flowtype": "^8.0.3",
91
- "eslint-plugin-import": "^2.25.4",
92
- "eslint-plugin-jest": "^26.1.3",
86
+ "eslint": "^8.51.0",
87
+ "eslint-config-standard": "^17.1.0",
88
+ "eslint-plugin-import": "^2.28.1",
89
+ "eslint-plugin-jest": "^27.4.2",
93
90
  "eslint-plugin-node": "^11.1.0",
94
- "eslint-plugin-promise": "^6.0.0",
95
- "eslint-plugin-react": "^7.29.4",
96
- "flow-bin": "~0.174.1",
97
- "flow-copy-source": "^2.0.9",
98
- "flow-typed": "^3.8.0",
91
+ "eslint-plugin-promise": "^6.1.1",
92
+ "eslint-plugin-react": "^7.33.2",
99
93
  "jest": "^27.5.1",
100
94
  "jest-junit": "^13.0.0",
101
95
  "jest-styled-components": "^7.0.8",
102
96
  "raf": "^3.4.1",
103
97
  "react": "^16.14.0",
104
98
  "react-dom": "^16.14.0",
105
- "rimraf": "^3.0.2",
106
- "styled-components": "^5.3.3",
107
- "stylelint": "^14.13.0",
108
- "stylelint-config-recommended": "^7.0.0",
99
+ "styled-components": "^6.1.2",
100
+ "stylelint": "^15.6.0",
101
+ "stylelint-config-recommended": "^12.0.0",
109
102
  "stylelint-config-styled-components": "^0.1.1",
110
- "webpack": "^5.70.0"
103
+ "swc-loader": "^0.2.3",
104
+ "ts-node": "^10.9.1",
105
+ "typescript": "^5.2.2",
106
+ "webpack": "^5.70.0",
107
+ "webpack-cli": "^5.1.4"
111
108
  }
112
109
  }