coupdoeil 1.0.0.pre.alpha.1 → 1.0.0.pre.alpha.2

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,8 +1,8 @@
1
1
  import {HovercardController} from '../hovercard/controller'
2
2
  import {openHovercard} from '../hovercard/opening'
3
- import {addToCurrents} from "../hovercard/actions";
4
3
  import {HOVERCARD_SELECTOR} from "../hovercard/config";
5
4
  import {closeNow} from "../hovercard/closing";
5
+ import {addToCurrents} from "../hovercard/current";
6
6
 
7
7
  function generateUniqueId() {
8
8
  const array = new Uint32Array(1)
@@ -1,8 +1,7 @@
1
1
  import {HOVERCARD_SELECTOR} from "../hovercard/config";
2
2
  import {isElementCloseHovercardButton} from "../hovercard/state_check";
3
- import {closeAllNow} from "../hovercard/actions";
4
3
  import {noTriggeredOnClick} from "../hovercard/attributes";
5
- import {closeChildrenNow, closeNow} from "../hovercard/closing";
4
+ import {closeAllNow, closeChildrenNow, closeNow} from "../hovercard/closing";
6
5
 
7
6
  export const coupdoeilOnClickEvent = ({ target: clickedElement }) => {
8
7
  const coupdoeilElement = clickedElement.closest('coup-doeil')
@@ -1,11 +1,13 @@
1
1
  import {HOVERCARD_SELECTOR} from "../hovercard/config";
2
2
  import {isAnyHovercardOpened} from "../hovercard/state_check";
3
+ import {notTriggeredOnHover} from "../hovercard/attributes";
3
4
  import {
4
- addToCurrents as addToCurrentHovercards,
5
+ cancelCloseRequest,
6
+ closeChildrenNow,
7
+ closeOnHoverChildrenLater,
5
8
  closeTriggeredOnHoverLater, closeTriggeredOnHoverNow
6
- } from "../hovercard/actions";
7
- import {notTriggeredOnHover} from "../hovercard/attributes";
8
- import {cancelCloseRequest, closeChildrenNow, closeOnHoverChildrenLater} from "../hovercard/closing";
9
+ } from "../hovercard/closing";
10
+ import {addToCurrents as addToCurrentHovercards} from "../hovercard/current";
9
11
 
10
12
  export const onMouseOver = ({ target: hoveredElement }) => {
11
13
  const coupdoeilElement = hoveredElement.closest('coup-doeil')
@@ -32,12 +34,13 @@ function handleMouseOverCoupdoeilWithinHovercard(coupdoeilElement, hovercardElem
32
34
  return;
33
35
 
34
36
  if (childHovercard.isOpen) {
35
- // when mouse goes back from child hovercard to its coupdoeil element within parent hovercard
37
+ // when the mouse goes back from child hovercard to its coupdoeil element within parent hovercard
36
38
  // it means that this child hovercard was already open
37
39
  closeChildrenNow(childHovercard)
38
40
  } else {
39
41
  // ensures to close other children hovercards before opening the one that current one
40
42
  closeChildrenNow(parentHovercard)
43
+ // should also close any open hovercard outside of parent
41
44
  coupdoeilElement.openHovercard()
42
45
  }
43
46
  }
@@ -1,6 +1,8 @@
1
1
  import {coupdoeilOnClickEvent} from "./events/onclick";
2
2
  import {onMouseOver} from "./events/onmouseover";
3
- import {clearHovercardContentCache, clearAll} from "./hovercard/actions";
3
+
4
+ import {clearHovercardContentCache} from "./hovercard/cache";
5
+ import {clearAll} from "./hovercard/closing";
4
6
 
5
7
  document.addEventListener("DOMContentLoaded", () => {
6
8
  clearHovercardContentCache()
@@ -1,60 +0,0 @@
1
- import {hovercardContentHTMLMap} from "./cache";
2
- import {triggeredOnHover} from "./attributes";
3
- import {clear as clearHovercard, closeLater, closeNow, cancelCloseRequest} from "./closing";
4
- import {cancelOpening} from "./opening";
5
-
6
- const CURRENT_HOVERCARDS_BY_ID = new Map()
7
- window.hovercads = CURRENT_HOVERCARDS_BY_ID
8
-
9
- export function clearHovercardContentCache() {
10
- hovercardContentHTMLMap.clear()
11
- }
12
-
13
- export function currentHovercardsById() {
14
- return CURRENT_HOVERCARDS_BY_ID
15
- }
16
-
17
- export function addToCurrents(coupdoeilElement) {
18
- CURRENT_HOVERCARDS_BY_ID.set(coupdoeilElement.uniqueId, coupdoeilElement)
19
- }
20
-
21
- export function closeAllNow() {
22
- for (const coupdoeilElement of CURRENT_HOVERCARDS_BY_ID.values()) {
23
- closeNow(coupdoeilElement.hovercardController)
24
- removeFromCurrents(coupdoeilElement)
25
- }
26
- }
27
-
28
- export function clearAll() {
29
- for (const coupdoeilElement of CURRENT_HOVERCARDS_BY_ID.values()) {
30
- clearHovercard(coupdoeilElement.hovercardController)
31
- removeFromCurrents(coupdoeilElement)
32
- }
33
- }
34
-
35
- export function closeTriggeredOnHoverNow() {
36
- for (const coupdoeilElement of CURRENT_HOVERCARDS_BY_ID.values()) {
37
- if (triggeredOnHover(coupdoeilElement.hovercardController)) {
38
- closeNow(coupdoeilElement.hovercardController)
39
- removeFromCurrents(coupdoeilElement)
40
- }
41
- }
42
- }
43
-
44
- export function closeTriggeredOnHoverLater() {
45
- for (const coupdoeilElement of CURRENT_HOVERCARDS_BY_ID.values()) {
46
- if (triggeredOnHover(coupdoeilElement.hovercardController)) {
47
- closeLater(coupdoeilElement.hovercardController)
48
- removeFromCurrents(coupdoeilElement)
49
- }
50
- }
51
- }
52
-
53
- export function removeFromCurrents(coupdoeilElement) {
54
- CURRENT_HOVERCARDS_BY_ID.delete(coupdoeilElement.uniqueId)
55
- }
56
-
57
- export function cancelOpenCloseActions(controller) {
58
- cancelOpening(controller)
59
- cancelCloseRequest(controller)
60
- }
@@ -1,4 +1,4 @@
1
- import {getType, getParams, preloadedContentElement} from './attributes'
1
+ import {getParams, getType, preloadedContentElement} from './attributes'
2
2
 
3
3
  export const hovercardContentHTMLMap = new Map()
4
4
 
@@ -16,3 +16,7 @@ export function getHovercardContentHTML(controller) {
16
16
  export function setHovercardContentHTML(controller, value) {
17
17
  hovercardContentHTMLMap.set(cacheMapKey(controller), value)
18
18
  }
19
+
20
+ export function clearHovercardContentCache() {
21
+ hovercardContentHTMLMap.clear()
22
+ }
@@ -1,7 +1,7 @@
1
- import {triggeredOnHover} from "./attributes";
2
- import {defaultConfig} from "./config";
3
- import {leave} from "el-transition";
4
- import {cancelOpenCloseActions} from "./actions";
1
+ import {triggeredOnHover} from "./attributes"
2
+ import {defaultConfig} from "./config"
3
+ import {leave} from "el-transition"
4
+ import {addToCurrents, CURRENT_HOVERCARDS_BY_ID, removeFromCurrents} from "./current"
5
5
 
6
6
  function detachFromParent(controller) {
7
7
  if (controller.parent) {
@@ -10,13 +10,26 @@ function detachFromParent(controller) {
10
10
  }
11
11
  }
12
12
 
13
+ export function cancelOpenCloseActions(controller) {
14
+ cancelOpening(controller)
15
+ cancelCloseRequest(controller)
16
+ }
17
+
18
+ function cancelOpening(controller) {
19
+ console.log('deleting openingHovercard: ', controller.coupdoeilElement.uniqueId)
20
+ delete controller.coupdoeilElement.openingHovercard
21
+ }
22
+
13
23
  export function cancelCloseRequest(controller) {
14
24
  clearTimeout(controller.closingRequest)
15
25
  controller.closingRequest = null
26
+ addToCurrents(controller.coupdoeilElement)
16
27
  }
17
28
 
18
29
  export function closeNow(controller, allowAnimation = true) {
19
- if (controller.closing || controller.isClosed) return;
30
+ console.log('closing: ', controller.coupdoeilElement.uniqueId)
31
+ console.log({ closing: controller.closing, isClosed: controller.isClosed && !controller.coupdoeilElement.openingHovercard })
32
+ if (controller.closing || (controller.isClosed && !controller.coupdoeilElement.openingHovercard)) return
20
33
 
21
34
  controller.closing = true
22
35
 
@@ -28,7 +41,7 @@ export function closeNow(controller, allowAnimation = true) {
28
41
 
29
42
  detachFromParent(controller)
30
43
 
31
- if (allowAnimation && controller.card.dataset.animation) {
44
+ if (allowAnimation && controller.card && controller.card.dataset.animation) {
32
45
  closeWithAnimation(controller)
33
46
  } else {
34
47
  closeWithoutAnimation(controller)
@@ -42,8 +55,10 @@ async function closeWithAnimation(controller) {
42
55
  }
43
56
 
44
57
  function closeWithoutAnimation(controller) {
45
- controller.card.remove()
46
- controller.card = null
58
+ if (controller.card) {
59
+ controller.card.remove()
60
+ controller.card = null
61
+ }
47
62
  delete controller.closing
48
63
  delete controller.coupdoeilElement.dataset.hovercardOpen
49
64
  }
@@ -79,3 +94,35 @@ export function closeOnHoverChildrenLater(controller) {
79
94
  }
80
95
  })
81
96
  }
97
+
98
+ export function closeAllNow() {
99
+ for (const coupdoeilElement of CURRENT_HOVERCARDS_BY_ID.values()) {
100
+ closeNow(coupdoeilElement.hovercardController)
101
+ removeFromCurrents(coupdoeilElement)
102
+ }
103
+ }
104
+
105
+ export function clearAll() {
106
+ for (const coupdoeilElement of CURRENT_HOVERCARDS_BY_ID.values()) {
107
+ clearHovercard(coupdoeilElement.hovercardController)
108
+ removeFromCurrents(coupdoeilElement)
109
+ }
110
+ }
111
+
112
+ export function closeTriggeredOnHoverNow() {
113
+ for (const coupdoeilElement of CURRENT_HOVERCARDS_BY_ID.values()) {
114
+ if (triggeredOnHover(coupdoeilElement.hovercardController)) {
115
+ closeNow(coupdoeilElement.hovercardController)
116
+ removeFromCurrents(coupdoeilElement)
117
+ }
118
+ }
119
+ }
120
+
121
+ export function closeTriggeredOnHoverLater() {
122
+ for (const coupdoeilElement of CURRENT_HOVERCARDS_BY_ID.values()) {
123
+ if (triggeredOnHover(coupdoeilElement.hovercardController)) {
124
+ closeLater(coupdoeilElement.hovercardController)
125
+ removeFromCurrents(coupdoeilElement)
126
+ }
127
+ }
128
+ }
@@ -8,8 +8,6 @@ export class HovercardController {
8
8
  this.parent = null // can go on hovercardElement
9
9
 
10
10
  this.closingRequest = null // can go on coupdoeil element
11
- this.openingDelay = null // can go on coupdoeil element
12
- this.fetchDelay = null // can go on coupdoeil element
13
11
  }
14
12
 
15
13
  get isOpen() {
@@ -0,0 +1,13 @@
1
+ export const CURRENT_HOVERCARDS_BY_ID = new Map()
2
+
3
+ export function currentHovercardsById() {
4
+ return CURRENT_HOVERCARDS_BY_ID
5
+ }
6
+
7
+ export function addToCurrents(coupdoeilElement) {
8
+ CURRENT_HOVERCARDS_BY_ID.set(coupdoeilElement.uniqueId, coupdoeilElement)
9
+ }
10
+
11
+ export function removeFromCurrents(coupdoeilElement) {
12
+ CURRENT_HOVERCARDS_BY_ID.delete(coupdoeilElement.uniqueId)
13
+ }
@@ -1,60 +1,73 @@
1
- import {defaultConfig, HOVERCARD_CLASS_NAME} from "./config";
2
- import {getParams, getType, preloadedContentElement, triggeredOnClick} from "./attributes";
3
- import {getHovercardContentHTML, setHovercardContentHTML} from "./cache";
4
- import {addToCurrents} from "./actions";
5
- import {extractOptionsFromElement} from "./optionsParser";
6
- import {cancelCloseRequest} from "./closing";
7
- import {positionHovercard} from "./positioning";
8
- import {enter} from "el-transition";
9
-
10
- export function cancelOpening(controller) {
11
- clearTimeout(controller.openingDelay)
12
- controller.openingDelay = null
13
- clearTimeout(controller.fetchDelay)
14
- controller.fetchDelay = null
15
- delete controller.coupdoeilElement.openingHovercard
1
+ import {defaultConfig, HOVERCARD_CLASS_NAME} from "./config"
2
+ import {getParams, getType, preloadedContentElement, triggeredOnClick} from "./attributes"
3
+ import {getHovercardContentHTML, setHovercardContentHTML} from "./cache"
4
+ import {extractOptionsFromElement} from "./optionsParser"
5
+ import {positionHovercard} from "./positioning"
6
+ import {enter} from "el-transition"
7
+ import {addToCurrents} from "./current"
8
+ import {cancelCloseRequest, clear as clearHovercard} from "./closing"
9
+
10
+ function fetchHovercardContent(controller) {
11
+ const type = getType(controller)
12
+ const params = getParams(controller)
13
+ const authenticityToken = document.querySelector('meta[name=csrf-token]').content
14
+ let url = `/coupdoeil/hovercard`
15
+ const opts = {
16
+ method: 'POST',
17
+ headers: {
18
+ 'Content-Type': 'application/json'
19
+ },
20
+ body: JSON.stringify({ params, action_name: type, authenticity_token: authenticityToken })
21
+ }
22
+ return fetch(url, opts)
23
+ .then((response) => {
24
+ if (response.status >= 400) {
25
+ throw 'error while fetching hovercard content'
26
+ }
27
+ return response.text()
28
+ })
29
+ }
30
+
31
+ async function loadHovercardContentHTML(controller, options, delayOptions) {
32
+ return new Promise((resolve) => {
33
+ setTimeout(async () => {
34
+ if (!controller.coupdoeilElement.openingHovercard) return // opening has been canceled
35
+
36
+ if (options.cache === false || (options.cache && !getHovercardContentHTML(controller))) {
37
+ let html
38
+ if (options.loading === "preload") {
39
+ html = preloadedContentElement(controller).innerHTML
40
+ } else {
41
+ html = await fetchHovercardContent(controller)
42
+ }
43
+ setHovercardContentHTML(controller, html)
44
+ }
45
+ resolve()
46
+ }, delayOptions.fetch)
47
+ })
16
48
  }
17
49
 
18
50
  export async function openHovercard(controller, { parent }) {
51
+ console.log('opening: ', controller.coupdoeilElement.uniqueId)
19
52
  if (controller.isOpen) {
20
- cancelCloseRequest(controller)
21
- return addToCurrents(controller.coupdoeilElement)
53
+ return cancelCloseRequest(controller)
22
54
  }
23
55
  if (parent) {
24
56
  controller.parent = parent
25
57
  parent.children.add(controller)
26
58
  }
27
59
 
28
- const delayOptions = getDelayOptionsForController(controller)
60
+ const delays = getDelayOptionsForController(controller)
29
61
  const options = extractOptionsFromElement(controller.coupdoeilElement)
30
- const { cache } = options
31
62
 
32
- controller.openingDelay = new Promise((resolve) => {
33
- if (getHovercardContentHTML(controller) && cache) {
34
- setTimeout(resolve, delayOptions.reOpening)
35
- } else {
36
- setTimeout(resolve, delayOptions.actualOpening)
37
- }
38
- })
39
- if (!getHovercardContentHTML(controller) || !cache) {
40
- // prevent fetching if the user hovers in and out quickly
41
- controller.fetchDelay = new Promise((resolve) => {
42
- setTimeout(resolve, delayOptions.fetch)
43
- })
44
- await controller.fetchDelay
45
- if (!controller.fetchDelay) {
46
- return
47
- }
48
- controller.fetchDelay = null
49
- const html = preloadedContentElement(controller)?.innerHTML || await fetchHovercardContent(controller)
50
- setHovercardContentHTML(controller, html)
51
- }
52
- // still await the delay even if content is already fetched
53
- await controller.openingDelay
63
+ const openingDelay = new Promise(resolve => setTimeout(resolve, delays.opening))
64
+ const fetchDelay = loadHovercardContentHTML(controller, options, delays)
65
+ await Promise.all([fetchDelay, openingDelay])
66
+
54
67
  const parentIsClosedOrClosing = controller.parent && (controller.parent.isClosed || controller.parent.closingRequest)
55
- // but if opening has been cancelled (nullified), the wait still happens, so we need to check again
56
- if (controller.openingDelay && !parentIsClosedOrClosing) {
57
- controller.openingDelay = null
68
+
69
+ // but if opening has been canceled (nullified), the wait still happens, so we need to check again
70
+ if (controller.coupdoeilElement.openingHovercard && !parentIsClosedOrClosing) {
58
71
  await display(controller, options)
59
72
  }
60
73
  }
@@ -72,17 +85,29 @@ async function display(controller, options) {
72
85
  }
73
86
 
74
87
  requestAnimationFrame(async () => {
88
+ if (!controller.coupdoeilElement.openingHovercard) {
89
+ return clearHovercard(controller)
90
+ }
91
+
75
92
  controller.card.style.opacity = '0'
76
93
  controller.card.classList.remove('hidden')
77
94
 
78
95
  requestAnimationFrame(async () => {
79
- await positionHovercard(controller, options)
96
+ if (!controller.coupdoeilElement.openingHovercard) {
97
+ return clearHovercard(controller)
98
+ }
99
+
100
+ await positionHovercard(controller.coupdoeilElement, controller.card, options)
80
101
 
81
102
  controller.card.classList.add('hidden')
82
103
  controller.card.style.removeProperty('opacity')
83
104
 
84
105
  requestAnimationFrame(async () => {
85
- // adding again the card to make sure it is in the map, could be better
106
+ if (!controller.coupdoeilElement.openingHovercard) {
107
+ return clearHovercard(controller)
108
+ }
109
+
110
+ // // adding again the card to make sure it is in the map, could be better
86
111
  addToCurrents(controller.coupdoeilElement)
87
112
  delete controller.coupdoeilElement.openingHovercard
88
113
  controller.coupdoeilElement.dataset.hovercardOpen = true
@@ -95,36 +120,16 @@ async function display(controller, options) {
95
120
 
96
121
  function getDelayOptionsForController(controller) {
97
122
  if (triggeredOnClick(controller)) {
98
- return { reOpening: 0, actualOpening: 0, fetch: 0 }
99
- }
100
- return {
101
- fetch: defaultConfig.fetchDelay,
102
- // the time (ms) to wait if we already have fetched the content
103
- reOpening: defaultConfig.fetchDelay + defaultConfig.openingDelay,
104
- // the time (ms) to wait if we already have waited to fetch the content
105
- actualOpening: defaultConfig.openingDelay - defaultConfig.fetchDelay
123
+ return { fetch: 0, opening: 0 }
106
124
  }
107
- }
108
125
 
109
- function fetchHovercardContent(controller) {
110
- const type = getType(controller)
111
- const params = getParams(controller)
112
- const authenticityToken = document.querySelector('meta[name=csrf-token]').content
113
- let url = `/coupdoeil/hovercard`
114
- const opts = {
115
- method: 'POST',
116
- headers: {
117
- 'Content-Type': 'application/json'
118
- },
119
- body: JSON.stringify({ params, action_name: type, authenticity_token: authenticityToken })
126
+ let fetchDelay
127
+ if (defaultConfig.openingDelay === 0) {
128
+ fetchDelay = 0
129
+ } else {
130
+ fetchDelay = defaultConfig.openingDelay / 2
120
131
  }
121
- return fetch(url, opts)
122
- .then((response) => {
123
- if (response.status >= 400) {
124
- throw 'error while fetching hovercard content'
125
- }
126
- return response.text()
127
- })
132
+ return { fetch: fetchDelay, opening: defaultConfig.openingDelay }
128
133
  }
129
134
 
130
135
  function buildHovercardElement(controller, options) {
@@ -25,7 +25,7 @@ const PLACEMENTS = [
25
25
  'bottom', 'bottom-start', 'bottom-end',
26
26
  'left', 'left-start', 'left-end'
27
27
  ]
28
- const LOADINGS = ["asyn", "preload", "lazy"]
28
+ const LOADINGS = ["async", "preload", "lazy"]
29
29
 
30
30
  function parseCSSSize(value) {
31
31
  if (typeof value === 'number') {
@@ -78,7 +78,8 @@ function getCache(optionsInt) {
78
78
  }
79
79
 
80
80
  function getLoading(optionsInt) {
81
- return LOADINGS[optionsInt & 3 >> 1]
81
+ // Shift right 1 time to remove trigger bit, mask with 3 (0b11).
82
+ return LOADINGS[(optionsInt >> 1) & 3]
82
83
  }
83
84
 
84
85
  function getTrigger(optionsInt) {
@@ -5,11 +5,12 @@ import {
5
5
  offset,
6
6
  arrow
7
7
  } from "@floating-ui/dom"
8
+ import {clear as clearHovercard} from "./closing";
8
9
 
9
- export async function positionHovercard(controller, options) {
10
+ export async function positionHovercard(target, card, options) {
10
11
  let { placement: placements, offset: offsetValue } = options
11
12
  const placement = placements[0]
12
- const arrowElement = controller.card.querySelector('[data-hovercard-arrow]')
13
+ const arrowElement = card.querySelector('[data-hovercard-arrow]')
13
14
  const middleware = [AutoPositioningWithFallbacks(placements)]
14
15
 
15
16
  if (arrowElement) {
@@ -21,7 +22,7 @@ export async function positionHovercard(controller, options) {
21
22
  middleware.push(offset(offsetValue))
22
23
 
23
24
  const computedPosition = await computePosition(
24
- controller.coupdoeilElement, controller.card, { placement, middleware }
25
+ target, card, { placement, middleware }
25
26
  )
26
27
  const { x, y, placement: actualPlacement} = computedPosition
27
28
 
@@ -29,8 +30,8 @@ export async function positionHovercard(controller, options) {
29
30
  positionArrow(arrowElement, computedPosition)
30
31
  }
31
32
 
32
- controller.card.dataset.placement = actualPlacement
33
- Object.assign(controller.card.style, { left: `${x}px`, top: `${y}px` })
33
+ card.dataset.placement = actualPlacement
34
+ Object.assign(card.style, { left: `${x}px`, top: `${y}px` })
34
35
  }
35
36
 
36
37
  const AutoPositioningWithFallbacks = (placements) => {
@@ -1,5 +1,6 @@
1
1
  import {HOVERCARD_CLOSE_BTN_SELECTOR} from "./config";
2
- import {currentHovercardsById} from "./actions";
2
+
3
+ import {currentHovercardsById} from "./current";
3
4
 
4
5
  export function isElementCloseHovercardButton(element) {
5
6
  return element.closest(HOVERCARD_CLOSE_BTN_SELECTOR) ||
@@ -1,9 +1,17 @@
1
1
  module Coupdoeil
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace Coupdoeil
4
- # config.eager_load_namespaces << Coupdoeil
4
+ config.eager_load_namespaces << Coupdoeil
5
5
  config.coupdoeil = ActiveSupport::OrderedOptions.new
6
6
 
7
+ config.autoload_once_paths = %W(
8
+ #{root}/app/controllers
9
+ #{root}/app/controllers/concerns
10
+ #{root}/app/helpers
11
+ #{root}/app/models
12
+ #{root}/app/models/concerns
13
+ )
14
+
7
15
  # If you don't want to precompile Coupdoeil's assets (eg. because you're using webpack),
8
16
  # you can do this in an intiailzer:
9
17
  #
@@ -1,3 +1,3 @@
1
1
  module Coupdoeil
2
- VERSION = "1.0.0-alpha.1"
2
+ VERSION = "1.0.0-alpha.2"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coupdoeil
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.alpha.1
4
+ version: 1.0.0.pre.alpha.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - PageHey
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-05-10 00:00:00.000000000 Z
10
+ date: 2025-05-11 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails
@@ -69,6 +69,7 @@ files:
69
69
  - app/javascript/coupdoeil/hovercard/closing.js
70
70
  - app/javascript/coupdoeil/hovercard/config.js
71
71
  - app/javascript/coupdoeil/hovercard/controller.js
72
+ - app/javascript/coupdoeil/hovercard/current.js
72
73
  - app/javascript/coupdoeil/hovercard/opening.js
73
74
  - app/javascript/coupdoeil/hovercard/optionsParser.js
74
75
  - app/javascript/coupdoeil/hovercard/positioning.js