center-center 0.0.2 → 0.0.4

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/index.mjs CHANGED
@@ -1,7 +1,3 @@
1
- import debug from 'debug'
2
-
3
- const log = debug('center-center')
4
-
5
1
  export function getScrollingElement (document) {
6
2
  if (Reflect.has(document, 'scrollingElement')) {
7
3
  const scrollingElement = Reflect.get(document, 'scrollingElement')
@@ -16,239 +12,97 @@ export function getScrollingElement (document) {
16
12
 
17
13
  export function getViewportRect () {
18
14
  const {
19
- innerWidth: viewportW,
20
- innerHeight: viewportH
15
+ innerWidth: width,
16
+ innerHeight: height
21
17
  } = window
22
18
 
23
19
  const {
24
- scrollLeft: viewportL,
25
- scrollTop: viewportT
20
+ scrollLeft: left,
21
+ scrollTop: top
26
22
  } = getScrollingElement(document)
27
23
 
28
- const viewportR = (viewportL + viewportW)
29
- const viewportB = (viewportT + viewportH)
30
-
31
- return {
32
- top: viewportT,
33
- right: viewportR,
34
- bottom: viewportB,
35
- left: viewportL,
36
- width: viewportW,
37
- height: viewportH
38
- }
39
- }
40
-
41
- export function getContainerRect (container) {
42
- const {
43
- offsetLeft: containerL,
44
- offsetTop: containerT
45
- } = container
46
-
47
- /**
48
- * x/top is relative to the viewport
49
- */
50
- const {
51
- x: containerX,
52
- y: containerY,
53
- width: containerW,
54
- height: containerH
55
- } = container.getBoundingClientRect()
56
-
57
- const containerR = (containerL + containerW)
58
- const containerB = (containerT + containerH)
59
-
60
24
  return {
61
- x: containerX,
62
- y: containerY,
63
- top: containerT,
64
- right: containerR,
65
- bottom: containerB,
66
- left: containerL,
67
- width: containerW,
68
- height: containerH
25
+ width,
26
+ height,
27
+ left,
28
+ top,
29
+ right: (left + width),
30
+ bottom: (top + height)
69
31
  }
70
32
  }
71
33
 
72
- export function getTargetRect (target) {
34
+ export function getElementRect (element) {
73
35
  const {
74
- offsetWidth: targetW,
75
- offsetHeight: targetH
76
- } = target
36
+ offsetWidth: width,
37
+ offsetHeight: height,
38
+ offsetLeft: left,
39
+ offsetTop: top
40
+ } = element
77
41
 
78
42
  return {
79
- width: targetW,
80
- height: targetH
43
+ width,
44
+ height,
45
+ left,
46
+ top,
47
+ right: (left + width),
48
+ bottom: (top + height)
81
49
  }
82
50
  }
83
51
 
84
52
  export function getRects (container, target) {
85
53
  return {
86
54
  viewport: getViewportRect(),
87
- container: getContainerRect(container),
88
- target: getTargetRect(target)
55
+ container: getElementRect(container),
56
+ target: getElementRect(target)
89
57
  }
90
58
  }
91
59
 
92
60
  export function calculateLeft ({
93
61
  viewport: {
94
62
  left: viewportL,
95
- right: viewportR,
96
- width: viewportW
63
+ right: viewportR
97
64
  },
98
65
  container: {
66
+ width: containerW,
99
67
  left: containerL,
100
- right: containerR,
101
- width: containerW
68
+ right: containerR
102
69
  },
103
70
  target: {
104
71
  width: targetW
105
72
  }
106
73
  }) {
107
- if (viewportL === containerL) {
108
- if (viewportW === containerW) {
109
- const availableW = Math.max(targetW, containerW)
110
-
111
- return ((availableW / 2) - (targetW / 2))
112
- }
113
-
114
- if (viewportW > containerW) {
115
- const availableW = Math.max(targetW, (containerL + containerW))
116
-
117
- return ((availableW / 2) - (targetW / 2))
118
- }
119
- }
120
-
121
- if (viewportL > containerL) {
122
- if (containerR > viewportL) {
123
- if (viewportR > containerR) {
124
- log('calculate viewportL to containerR')
125
-
126
- const availableW = Math.max(targetW, (containerL + containerW) - viewportL)
127
-
128
- return (containerW - availableW) + ((availableW / 2) - (targetW / 2))
129
- }
130
-
131
- if (containerR > viewportR) {
132
- log('calculate viewportL to viewportR')
133
-
134
- const availableW = Math.max(targetW, viewportW)
135
-
136
- return (viewportL - containerL) + ((availableW / 2) - (targetW / 2))
137
- }
138
- }
139
-
140
- if (viewportL > containerR) {
141
- log('pin to right')
142
-
143
- const availableW = Math.max(targetW, containerW)
144
-
145
- /**
146
- * Pin to right
147
- */
148
- return (availableW - targetW)
149
- }
150
- }
151
-
152
- if (containerL > viewportL) {
153
- if (containerL > viewportR) {
154
- log('pin to left')
155
-
156
- /**
157
- * Pin to left
158
- *
159
- * Scroll is above container (container is offscreen)
160
- */
161
- return 0
162
- }
163
-
164
- if (viewportR > containerL) {
165
- log('calculate containerL to viewportR')
166
-
167
- const availableW = Math.max(targetW, (viewportR - containerL))
168
-
169
- return ((availableW / 2) - (targetW / 2))
170
- }
171
- }
74
+ const l = Math.max(viewportL, containerL)
75
+ const r = Math.min(viewportR, containerR)
76
+ const w = (r - l)
77
+ const x = (l - containerL)
78
+ const boundary = (containerW - targetW)
79
+
80
+ return (
81
+ Math.max(0, Math.min(boundary, x + ((w / 2) - (targetW / 2))))
82
+ )
172
83
  }
173
84
 
174
85
  export function calculateTop ({
175
86
  viewport: {
176
87
  top: viewportT,
177
- bottom: viewportB,
178
- height: viewportH
88
+ bottom: viewportB
179
89
  },
180
90
  container: {
91
+ height: containerH,
181
92
  top: containerT,
182
- bottom: containerB,
183
- height: containerH
93
+ bottom: containerB
184
94
  },
185
95
  target: {
186
96
  height: targetH
187
97
  }
188
98
  }) {
189
- if (viewportT === containerT) {
190
- if (viewportH === containerH) {
191
- const availableH = Math.max(targetH, containerH)
192
-
193
- return ((availableH / 2) - (targetH / 2))
194
- }
195
-
196
- if (viewportB > containerB) {
197
- const availableH = Math.max(targetH, (containerT + containerB))
198
-
199
- return ((availableH / 2) - (targetH / 2))
200
- }
201
- }
202
-
203
- if (viewportT > containerT) {
204
- if (containerB > viewportT) {
205
- if (viewportB > containerB) {
206
- log('calculate viewportT to containerB')
207
-
208
- const availableH = Math.max(targetH, (containerT + containerH) - viewportT)
209
-
210
- return (containerH - availableH) + ((availableH / 2) - (targetH / 2))
211
- }
212
-
213
- if (containerB > viewportB) {
214
- log('calculate viewportT to viewportB') // , viewportT > containerT, containerB > viewportB)
215
-
216
- const availableH = Math.max(targetH, viewportH)
217
-
218
- return (viewportT - containerT) + ((availableH / 2) - (targetH / 2))
219
- }
220
- }
221
-
222
- if (viewportT > containerB) {
223
- log('pin to bottom')
224
-
225
- const availableH = Math.max(targetH, containerH)
226
-
227
- /**
228
- * Pin to bottom
229
- */
230
- return (availableH - targetH)
231
- }
232
- }
233
-
234
- if (containerT > viewportT) {
235
- if (containerT > viewportB) {
236
- log('pin to top')
237
-
238
- /**
239
- * Pin to top
240
- *
241
- * Scroll is above container (container is offscreen)
242
- */
243
- return 0
244
- }
245
-
246
- if (viewportB > containerT) {
247
- log('calculate containerT to viewportB')
248
-
249
- const availableH = Math.max(targetH, (viewportB - containerT))
250
-
251
- return ((availableH / 2) - (targetH / 2))
252
- }
253
- }
99
+ const t = Math.max(viewportT, containerT)
100
+ const b = Math.min(viewportB, containerB)
101
+ const h = (b - t)
102
+ const y = (t - containerT)
103
+ const boundary = (containerH - targetH)
104
+
105
+ return (
106
+ Math.max(0, Math.min(boundary, y + ((h / 2) - (targetH / 2))))
107
+ )
254
108
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "center-center",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Position an element at the center center of a container",
5
5
  "main": "./index.mjs",
6
6
  "type": "module",
@@ -11,7 +11,8 @@
11
11
  },
12
12
  "scripts": {
13
13
  "lint": "cross-env NODE_ENV=production eslint . --ext .mjs,.cjs",
14
- "lint:fix": "npm run lint -- --fix"
14
+ "lint:fix": "npm run lint -- --fix",
15
+ "test": "cross-env NODE_ENV=test mocha test --recursive"
15
16
  },
16
17
  "dependencies": {
17
18
  "debug": "^4.3.4"
@@ -21,9 +22,14 @@
21
22
  "@babel/core": "^7.22.17",
22
23
  "@babel/eslint-parser": "^7.22.15",
23
24
  "@babel/preset-env": "^7.22.15",
25
+ "chai": "^5.0.0",
24
26
  "core-js": "^3.32.2",
25
27
  "cross-env": "^7.0.3",
26
28
  "eslint": "^8.49.0",
27
- "eslint-config-standard": "^17.1.0"
29
+ "eslint-config-standard": "^17.1.0",
30
+ "mocha": "^10.2.0"
31
+ },
32
+ "imports": {
33
+ "#center-center": "./index.mjs"
28
34
  }
29
35
  }
@@ -0,0 +1,316 @@
1
+ import {
2
+ expect
3
+ } from 'chai'
4
+
5
+ import {
6
+ getScrollingElement,
7
+ getViewportRect,
8
+ getElementRect,
9
+ getRects,
10
+ calculateLeft,
11
+ calculateTop
12
+ } from '#center-center'
13
+
14
+ describe('`center-center`', () => {
15
+ it('exports `getScrollingElement`', () => expect(getScrollingElement).to.be.a('function'))
16
+
17
+ it('exports `getViewportRect`', () => expect(getViewportRect).to.be.a('function'))
18
+
19
+ it('exports `getElementRect`', () => expect(getElementRect).to.be.a('function'))
20
+
21
+ it('exports `getRects`', () => expect(getRects).to.be.a('function'))
22
+
23
+ it('exports `calculateLeft`', () => expect(calculateLeft).to.be.a('function'))
24
+
25
+ it('exports `calculateTop`', () => expect(calculateTop).to.be.a('function'))
26
+
27
+ describe('`getScrollingElement()`', () => {
28
+ describe('`scrollingElement` is defined', () => {
29
+ describe('`scrollingElement` is null', () => {
30
+ it('returns an object', () => {
31
+ return (
32
+ expect(getScrollingElement({ scrollingElement: null }))
33
+ .to.eql({
34
+ scrollLeft: 0,
35
+ scrollTop: 0
36
+ })
37
+ )
38
+ })
39
+ })
40
+
41
+ describe('`scrollingElement` is an object', () => {
42
+ it('returns an object', () => {
43
+ return (
44
+ expect(getScrollingElement({ scrollingElement: { scrollLeft: 'MOCK SCROLL LEFT', scrollTop: 'MOCK SCROLL TOP' } }))
45
+ .to.eql({
46
+ scrollLeft: 'MOCK SCROLL LEFT',
47
+ scrollTop: 'MOCK SCROLL TOP'
48
+ })
49
+ )
50
+ })
51
+ })
52
+ })
53
+
54
+ describe('`scrollingElement` is not defined', () => {
55
+ it('returns an object', () => {
56
+ return (
57
+ expect(getScrollingElement({}))
58
+ .to.eql({
59
+ scrollLeft: 0,
60
+ scrollTop: 0
61
+ })
62
+ )
63
+ })
64
+ })
65
+ })
66
+
67
+ describe('`getViewportRect()`', () => {
68
+ beforeEach(() => {
69
+ global.window = {
70
+ innerWidth: 'MOCK INNER WIDTH',
71
+ innerHeight: 'MOCK INNER HEIGHT'
72
+ }
73
+
74
+ global.document = {
75
+ scrollingElement: {
76
+ scrollLeft: 'MOCK SCROLL LEFT',
77
+ scrollTop: 'MOCK SCROLL TOP'
78
+ }
79
+ }
80
+ })
81
+
82
+ afterEach(() => {
83
+ delete global.window
84
+ delete global.document
85
+ })
86
+
87
+ it('returns an object', () => expect(getViewportRect()).to.eql({
88
+ top: 'MOCK SCROLL TOP',
89
+ right: 'MOCK SCROLL LEFTMOCK INNER WIDTH',
90
+ bottom: 'MOCK SCROLL TOPMOCK INNER HEIGHT',
91
+ left: 'MOCK SCROLL LEFT',
92
+ width: 'MOCK INNER WIDTH',
93
+ height: 'MOCK INNER HEIGHT'
94
+ }))
95
+ })
96
+
97
+ describe('`getElementRect()`', () => {
98
+ const mockElement = {
99
+ offsetLeft: 'MOCK OFFSET LEFT',
100
+ offsetTop: 'MOCK OFFSET TOP',
101
+ offsetWidth: 'MOCK OFFSET WIDTH',
102
+ offsetHeight: 'MOCK OFFSET HEIGHT'
103
+ }
104
+
105
+ it('returns an object', () => {
106
+ return (
107
+ expect(getElementRect(mockElement))
108
+ .to.eql({
109
+ left: 'MOCK OFFSET LEFT',
110
+ top: 'MOCK OFFSET TOP',
111
+ width: 'MOCK OFFSET WIDTH',
112
+ height: 'MOCK OFFSET HEIGHT',
113
+ right: 'MOCK OFFSET LEFTMOCK OFFSET WIDTH',
114
+ bottom: 'MOCK OFFSET TOPMOCK OFFSET HEIGHT'
115
+ })
116
+ )
117
+ })
118
+ })
119
+
120
+ describe('`getRects()`', () => {
121
+ const mockContainer = {
122
+ offsetLeft: 'MOCK OFFSET LEFT',
123
+ offsetTop: 'MOCK OFFSET TOP',
124
+ offsetWidth: 'MOCK OFFSET WIDTH',
125
+ offsetHeight: 'MOCK OFFSET HEIGHT'
126
+ }
127
+
128
+ const mockTarget = {
129
+ offsetLeft: 'MOCK OFFSET LEFT',
130
+ offsetTop: 'MOCK OFFSET TOP',
131
+ offsetWidth: 'MOCK OFFSET WIDTH',
132
+ offsetHeight: 'MOCK OFFSET HEIGHT'
133
+ }
134
+
135
+ beforeEach(() => {
136
+ global.window = {
137
+ innerWidth: 'MOCK INNER WIDTH',
138
+ innerHeight: 'MOCK INNER HEIGHT'
139
+ }
140
+
141
+ global.document = {
142
+ scrollingElement: {
143
+ scrollLeft: 'MOCK SCROLL LEFT',
144
+ scrollTop: 'MOCK SCROLL TOP'
145
+ }
146
+ }
147
+ })
148
+
149
+ afterEach(() => {
150
+ delete global.window
151
+ delete global.document
152
+ })
153
+
154
+ it('returns an object', () => {
155
+ return (
156
+ expect(getRects(mockContainer, mockTarget))
157
+ .to.eql({
158
+ viewport: {
159
+ top: 'MOCK SCROLL TOP',
160
+ right: 'MOCK SCROLL LEFTMOCK INNER WIDTH',
161
+ bottom: 'MOCK SCROLL TOPMOCK INNER HEIGHT',
162
+ left: 'MOCK SCROLL LEFT',
163
+ width: 'MOCK INNER WIDTH',
164
+ height: 'MOCK INNER HEIGHT'
165
+ },
166
+ container: {
167
+ left: 'MOCK OFFSET LEFT',
168
+ top: 'MOCK OFFSET TOP',
169
+ width: 'MOCK OFFSET WIDTH',
170
+ height: 'MOCK OFFSET HEIGHT',
171
+ right: 'MOCK OFFSET LEFTMOCK OFFSET WIDTH',
172
+ bottom: 'MOCK OFFSET TOPMOCK OFFSET HEIGHT'
173
+ },
174
+ target: {
175
+ left: 'MOCK OFFSET LEFT',
176
+ top: 'MOCK OFFSET TOP',
177
+ width: 'MOCK OFFSET WIDTH',
178
+ height: 'MOCK OFFSET HEIGHT',
179
+ right: 'MOCK OFFSET LEFTMOCK OFFSET WIDTH',
180
+ bottom: 'MOCK OFFSET TOPMOCK OFFSET HEIGHT'
181
+ }
182
+ })
183
+ )
184
+ })
185
+ })
186
+
187
+ describe('Container is shorter than the Window', () => {
188
+ const rects = {
189
+ viewport: {
190
+ top: 0,
191
+ right: 1440,
192
+ bottom: 900,
193
+ left: 0,
194
+ width: 1440,
195
+ height: 900
196
+ },
197
+ container: {
198
+ x: 0,
199
+ y: 90,
200
+ top: 90,
201
+ right: 1440,
202
+ bottom: 810,
203
+ left: 0,
204
+ width: 1440,
205
+ height: 720
206
+ },
207
+ target: {
208
+ width: 50,
209
+ height: 50
210
+ }
211
+ }
212
+
213
+ describe('`calculateLeft()`', () => {
214
+ /**
215
+ * Window is 1440 x 900
216
+ * Header is 1440 x 90
217
+ * Container is 1440 x 720
218
+ * Footer is 1440 x 90
219
+ * Target is 50 x 50
220
+ */
221
+ it('returns an object', () => {
222
+ /**
223
+ * (1440 / 2) - (50 / 2)
224
+ */
225
+ return (
226
+ expect(calculateLeft(rects))
227
+ .to.equal(695)
228
+ )
229
+ })
230
+ })
231
+
232
+ describe('`calculateTop()`', () => {
233
+ /**
234
+ * Window is 1440 x 900
235
+ * Header is 1440 x 90
236
+ * Container is 1440 x 720
237
+ * Footer is 1440 x 90
238
+ * Target is 50 x 50
239
+ */
240
+ it('returns an object', () => {
241
+ /**
242
+ * (720 / 2) - (50 / 2)
243
+ */
244
+ return (
245
+ expect(calculateTop(rects))
246
+ .to.equal(335)
247
+ )
248
+ })
249
+ })
250
+ })
251
+
252
+ describe('Container is taller than the Window', () => {
253
+ const rects = {
254
+ viewport: {
255
+ top: 0,
256
+ right: 1440,
257
+ bottom: 900,
258
+ left: 0,
259
+ width: 1440,
260
+ height: 900
261
+ },
262
+ container: {
263
+ x: 0,
264
+ y: 90,
265
+ top: 90,
266
+ right: 1440,
267
+ bottom: 1080,
268
+ left: 0,
269
+ width: 1440,
270
+ height: 990
271
+ },
272
+ target: {
273
+ width: 50,
274
+ height: 50
275
+ }
276
+ }
277
+
278
+ describe('`calculateLeft()`', () => {
279
+ /**
280
+ * Window is 1440 x 900
281
+ * Header is 1440 x 90
282
+ * Container is 1440 x 990
283
+ * Footer is 1440 x 90
284
+ * Target is 50 x 50
285
+ */
286
+ it('returns an object', () => {
287
+ /**
288
+ * (1440 / 2) - (50 / 2)
289
+ */
290
+ return (
291
+ expect(calculateLeft(rects))
292
+ .to.equal(695)
293
+ )
294
+ })
295
+ })
296
+
297
+ describe('`calculateTop()`', () => {
298
+ /**
299
+ * Window is 1440 x 900
300
+ * Header is 1440 x 90
301
+ * Container is 1440 x 990
302
+ * Footer is 1440 x 90
303
+ * Target is 50 x 50
304
+ */
305
+ it('returns an object', () => {
306
+ /**
307
+ * (810 / 2) - (50 / 2)
308
+ */
309
+ return (
310
+ expect(calculateTop(rects))
311
+ .to.equal(380)
312
+ )
313
+ })
314
+ })
315
+ })
316
+ })