kamrify commited on
Commit
4da4a2a
·
1 Parent(s): 3d651f8

Add popover arrow positioning

Browse files
Files changed (1) hide show
  1. src/popover.ts +112 -7
src/popover.ts CHANGED
@@ -159,6 +159,7 @@ export function repositionPopover(element: Element) {
159
  return;
160
  }
161
 
 
162
  const requiredAlignment: Alignment = "start";
163
  const popoverPadding = STAGE_PADDING;
164
 
@@ -179,6 +180,7 @@ export function repositionPopover(element: Element) {
179
  const isRightOptimal = rightValue >= 0;
180
 
181
  const noneOptimal = !isTopOptimal && !isBottomOptimal && !isLeftOptimal && !isRightOptimal;
 
182
 
183
  if (noneOptimal) {
184
  const leftValue = window.innerWidth / 2 - popoverDimensions?.realWidth! / 2;
@@ -188,8 +190,6 @@ export function repositionPopover(element: Element) {
188
  popover.wrapper.style.right = `auto`;
189
  popover.wrapper.style.bottom = `${bottomValue}px`;
190
  popover.wrapper.style.top = `auto`;
191
-
192
- popover.arrow.classList.add("driver-popover-arrow-none");
193
  } else if (isLeftOptimal) {
194
  const leftToSet = Math.min(
195
  leftValue,
@@ -207,6 +207,8 @@ export function repositionPopover(element: Element) {
207
  popover.wrapper.style.top = `${topToSet}px`;
208
  popover.wrapper.style.bottom = `auto`;
209
  popover.wrapper.style.right = "auto";
 
 
210
  } else if (isRightOptimal) {
211
  const rightToSet = Math.min(
212
  rightValue,
@@ -223,6 +225,8 @@ export function repositionPopover(element: Element) {
223
  popover.wrapper.style.top = `${topToSet}px`;
224
  popover.wrapper.style.bottom = `auto`;
225
  popover.wrapper.style.left = "auto";
 
 
226
  } else if (isTopOptimal) {
227
  const topToSet = Math.min(
228
  topValue,
@@ -239,6 +243,8 @@ export function repositionPopover(element: Element) {
239
  popover.wrapper.style.left = `${leftToSet}px`;
240
  popover.wrapper.style.bottom = `auto`;
241
  popover.wrapper.style.right = "auto";
 
 
242
  } else if (isBottomOptimal) {
243
  const bottomToSet = Math.min(
244
  bottomValue,
@@ -256,15 +262,114 @@ export function repositionPopover(element: Element) {
256
  popover.wrapper.style.bottom = `${bottomToSet}px`;
257
  popover.wrapper.style.top = `auto`;
258
  popover.wrapper.style.right = "auto";
 
 
259
  }
260
 
261
- // We need to check which position we end up rendering the popover at
262
- // to be able to assign the proper arrow class
263
- renderPopoverArrow();
 
 
 
264
  }
265
 
266
- function renderPopoverArrow() {
267
- // @todo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  }
269
 
270
  function createPopover(): PopoverDOM {
 
159
  return;
160
  }
161
 
162
+ // Configure the popover positioning
163
  const requiredAlignment: Alignment = "start";
164
  const popoverPadding = STAGE_PADDING;
165
 
 
180
  const isRightOptimal = rightValue >= 0;
181
 
182
  const noneOptimal = !isTopOptimal && !isBottomOptimal && !isLeftOptimal && !isRightOptimal;
183
+ let popoverRenderedSide: Side;
184
 
185
  if (noneOptimal) {
186
  const leftValue = window.innerWidth / 2 - popoverDimensions?.realWidth! / 2;
 
190
  popover.wrapper.style.right = `auto`;
191
  popover.wrapper.style.bottom = `${bottomValue}px`;
192
  popover.wrapper.style.top = `auto`;
 
 
193
  } else if (isLeftOptimal) {
194
  const leftToSet = Math.min(
195
  leftValue,
 
207
  popover.wrapper.style.top = `${topToSet}px`;
208
  popover.wrapper.style.bottom = `auto`;
209
  popover.wrapper.style.right = "auto";
210
+
211
+ popoverRenderedSide = "left";
212
  } else if (isRightOptimal) {
213
  const rightToSet = Math.min(
214
  rightValue,
 
225
  popover.wrapper.style.top = `${topToSet}px`;
226
  popover.wrapper.style.bottom = `auto`;
227
  popover.wrapper.style.left = "auto";
228
+
229
+ popoverRenderedSide = "right";
230
  } else if (isTopOptimal) {
231
  const topToSet = Math.min(
232
  topValue,
 
243
  popover.wrapper.style.left = `${leftToSet}px`;
244
  popover.wrapper.style.bottom = `auto`;
245
  popover.wrapper.style.right = "auto";
246
+
247
+ popoverRenderedSide = "top";
248
  } else if (isBottomOptimal) {
249
  const bottomToSet = Math.min(
250
  bottomValue,
 
262
  popover.wrapper.style.bottom = `${bottomToSet}px`;
263
  popover.wrapper.style.top = `auto`;
264
  popover.wrapper.style.right = "auto";
265
+
266
+ popoverRenderedSide = "bottom";
267
  }
268
 
269
+ // Popover stays on the screen if the element scrolls out of the visible area.
270
+ // Render the arrow again to make sure it's in the correct position
271
+ // e.g. if element scrolled out of the screen to the top, the arrow should be rendered
272
+ // pointing to the top. If the element scrolled out of the screen to the bottom,
273
+ // the arrow should be rendered pointing to the bottom.
274
+ renderPopoverArrow(requiredAlignment, popoverRenderedSide, element);
275
  }
276
 
277
+ function renderPopoverArrow(alignment: Alignment, side: Side, element: Element) {
278
+ if (!popover) {
279
+ return;
280
+ }
281
+
282
+ const elementDimensions = element.getBoundingClientRect();
283
+ const popoverDimensions = getPopoverDimensions();
284
+ const popoverArrow = popover.arrow;
285
+
286
+ const popoverWidth = popoverDimensions?.width;
287
+ const windowWidth = window.innerWidth;
288
+ const elementWidth = elementDimensions.width;
289
+ const elementLeft = elementDimensions.left;
290
+
291
+ const popoverHeight = popoverDimensions?.height;
292
+ const windowHeight = window.innerHeight;
293
+ const elementTop = elementDimensions.top;
294
+ const elementHeight = elementDimensions.height;
295
+
296
+ // Remove all arrow classes
297
+ popoverArrow.className = "driver-popover-arrow";
298
+
299
+ let arrowSide = side;
300
+ let arrowAlignment = alignment;
301
+
302
+ if (side === "top") {
303
+ if (elementLeft + elementWidth <= 0) {
304
+ arrowSide = "right";
305
+ arrowAlignment = "end";
306
+ } else if (elementLeft + elementWidth - popoverWidth <= 0) {
307
+ arrowSide = "top";
308
+ arrowAlignment = "start";
309
+ }
310
+ if (elementLeft >= windowWidth) {
311
+ arrowSide = "left";
312
+ arrowAlignment = "end";
313
+ } else if (elementLeft + popoverWidth >= windowWidth) {
314
+ arrowSide = "top";
315
+ arrowAlignment = "end";
316
+ }
317
+ } else if (side === "bottom") {
318
+ if (elementLeft + elementWidth <= 0) {
319
+ arrowSide = "right";
320
+ arrowAlignment = "start";
321
+ } else if (elementLeft + elementWidth - popoverWidth <= 0) {
322
+ arrowSide = "bottom";
323
+ arrowAlignment = "start";
324
+ }
325
+ if (elementLeft >= windowWidth) {
326
+ arrowSide = "left";
327
+ arrowAlignment = "start";
328
+ } else if (elementLeft + popoverWidth >= windowWidth) {
329
+ arrowSide = "bottom";
330
+ arrowAlignment = "end";
331
+ }
332
+ } else if (side === "left") {
333
+ if (elementTop + elementHeight <= 0) {
334
+ arrowSide = "bottom";
335
+ arrowAlignment = "end";
336
+ } else if (elementTop + elementHeight - popoverHeight <= 0) {
337
+ arrowSide = "left";
338
+ arrowAlignment = "start";
339
+ }
340
+
341
+ if (elementTop >= windowHeight) {
342
+ arrowSide = "top";
343
+ arrowAlignment = "end";
344
+ } else if (elementTop + popoverHeight >= windowHeight) {
345
+ arrowSide = "left";
346
+ arrowAlignment = "end";
347
+ }
348
+ } else if (side === "right") {
349
+ if (elementTop + elementHeight <= 0) {
350
+ arrowSide = "bottom";
351
+ arrowAlignment = "start";
352
+ } else if (elementTop + elementHeight - popoverHeight <= 0) {
353
+ arrowSide = "right";
354
+ arrowAlignment = "start";
355
+ }
356
+
357
+ if (elementTop >= windowHeight) {
358
+ arrowSide = "top";
359
+ arrowAlignment = "start";
360
+ } else if (elementTop + popoverHeight >= windowHeight) {
361
+ arrowSide = "right";
362
+ arrowAlignment = "end";
363
+ }
364
+ } else {
365
+ }
366
+
367
+ if (!arrowSide) {
368
+ popoverArrow.classList.add("driver-popover-arrow-none");
369
+ } else {
370
+ popoverArrow.classList.add(`driver-popover-arrow-side-${arrowSide}`);
371
+ popoverArrow.classList.add(`driver-popover-arrow-align-${arrowAlignment}`);
372
+ }
373
  }
374
 
375
  function createPopover(): PopoverDOM {