coupdoeil 1.0.0.pre.alpha.1 → 1.0.0.pre.alpha.3
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/app/assets/javascripts/coupdoeil.js +251 -249
- data/app/assets/javascripts/coupdoeil.min.js +1 -1
- data/app/assets/javascripts/coupdoeil.min.js.map +1 -1
- data/app/javascript/coupdoeil/elements/coupdoeil_element.js +1 -1
- data/app/javascript/coupdoeil/events/onclick.js +1 -2
- data/app/javascript/coupdoeil/events/onmouseover.js +8 -5
- data/app/javascript/coupdoeil/events.js +3 -1
- data/app/javascript/coupdoeil/hovercard/actions.js +0 -60
- data/app/javascript/coupdoeil/hovercard/cache.js +5 -1
- data/app/javascript/coupdoeil/hovercard/closing.js +52 -8
- data/app/javascript/coupdoeil/hovercard/controller.js +0 -2
- data/app/javascript/coupdoeil/hovercard/current.js +13 -0
- data/app/javascript/coupdoeil/hovercard/opening.js +90 -87
- data/app/javascript/coupdoeil/hovercard/optionsParser.js +3 -2
- data/app/javascript/coupdoeil/hovercard/positioning.js +5 -5
- data/app/javascript/coupdoeil/hovercard/state_check.js +2 -1
- data/app/models/coupdoeil/hovercard/options_set.rb +1 -3
- data/lib/coupdoeil/engine.rb +9 -1
- data/lib/coupdoeil/version.rb +1 -1
- data/lib/generators/coupdoeil/install/templates/layout.html.erb.tt +0 -1
- metadata +3 -2
@@ -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
|
-
|
5
|
+
cancelCloseRequest,
|
6
|
+
closeChildrenNow,
|
7
|
+
closeOnHoverChildrenLater,
|
5
8
|
closeTriggeredOnHoverLater, closeTriggeredOnHoverNow
|
6
|
-
} from "../hovercard/
|
7
|
-
import {
|
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
|
-
|
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 {
|
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 {
|
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,23 @@ 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
|
+
delete controller.coupdoeilElement.openingHovercard
|
20
|
+
}
|
21
|
+
|
13
22
|
export function cancelCloseRequest(controller) {
|
14
23
|
clearTimeout(controller.closingRequest)
|
15
24
|
controller.closingRequest = null
|
25
|
+
addToCurrents(controller.coupdoeilElement)
|
16
26
|
}
|
17
27
|
|
18
28
|
export function closeNow(controller, allowAnimation = true) {
|
19
|
-
if (controller.closing || controller.isClosed) return
|
29
|
+
if (controller.closing || (controller.isClosed && !controller.coupdoeilElement.openingHovercard)) return
|
20
30
|
|
21
31
|
controller.closing = true
|
22
32
|
|
@@ -28,7 +38,7 @@ export function closeNow(controller, allowAnimation = true) {
|
|
28
38
|
|
29
39
|
detachFromParent(controller)
|
30
40
|
|
31
|
-
if (allowAnimation && controller.card.dataset.animation) {
|
41
|
+
if (allowAnimation && controller.card && controller.card.dataset.animation) {
|
32
42
|
closeWithAnimation(controller)
|
33
43
|
} else {
|
34
44
|
closeWithoutAnimation(controller)
|
@@ -42,8 +52,10 @@ async function closeWithAnimation(controller) {
|
|
42
52
|
}
|
43
53
|
|
44
54
|
function closeWithoutAnimation(controller) {
|
45
|
-
controller.card
|
46
|
-
|
55
|
+
if (controller.card) {
|
56
|
+
controller.card.remove()
|
57
|
+
controller.card = null
|
58
|
+
}
|
47
59
|
delete controller.closing
|
48
60
|
delete controller.coupdoeilElement.dataset.hovercardOpen
|
49
61
|
}
|
@@ -79,3 +91,35 @@ export function closeOnHoverChildrenLater(controller) {
|
|
79
91
|
}
|
80
92
|
})
|
81
93
|
}
|
94
|
+
|
95
|
+
export function closeAllNow() {
|
96
|
+
for (const coupdoeilElement of CURRENT_HOVERCARDS_BY_ID.values()) {
|
97
|
+
closeNow(coupdoeilElement.hovercardController)
|
98
|
+
removeFromCurrents(coupdoeilElement)
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
export function clearAll() {
|
103
|
+
for (const coupdoeilElement of CURRENT_HOVERCARDS_BY_ID.values()) {
|
104
|
+
clearHovercard(coupdoeilElement.hovercardController)
|
105
|
+
removeFromCurrents(coupdoeilElement)
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
export function closeTriggeredOnHoverNow() {
|
110
|
+
for (const coupdoeilElement of CURRENT_HOVERCARDS_BY_ID.values()) {
|
111
|
+
if (triggeredOnHover(coupdoeilElement.hovercardController)) {
|
112
|
+
closeNow(coupdoeilElement.hovercardController)
|
113
|
+
removeFromCurrents(coupdoeilElement)
|
114
|
+
}
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
export function closeTriggeredOnHoverLater() {
|
119
|
+
for (const coupdoeilElement of CURRENT_HOVERCARDS_BY_ID.values()) {
|
120
|
+
if (triggeredOnHover(coupdoeilElement.hovercardController)) {
|
121
|
+
closeLater(coupdoeilElement.hovercardController)
|
122
|
+
removeFromCurrents(coupdoeilElement)
|
123
|
+
}
|
124
|
+
}
|
125
|
+
}
|
@@ -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,72 @@
|
|
1
|
-
import {defaultConfig, HOVERCARD_CLASS_NAME} from "./config"
|
2
|
-
import {getParams, getType, preloadedContentElement, triggeredOnClick} from "./attributes"
|
3
|
-
import {getHovercardContentHTML, setHovercardContentHTML} from "./cache"
|
4
|
-
import {
|
5
|
-
import {
|
6
|
-
import {
|
7
|
-
import {
|
8
|
-
import {
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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 }) {
|
19
51
|
if (controller.isOpen) {
|
20
|
-
cancelCloseRequest(controller)
|
21
|
-
return addToCurrents(controller.coupdoeilElement)
|
52
|
+
return cancelCloseRequest(controller)
|
22
53
|
}
|
23
54
|
if (parent) {
|
24
55
|
controller.parent = parent
|
25
56
|
parent.children.add(controller)
|
26
57
|
}
|
27
58
|
|
28
|
-
const
|
59
|
+
const delays = getDelayOptionsForController(controller)
|
29
60
|
const options = extractOptionsFromElement(controller.coupdoeilElement)
|
30
|
-
const { cache } = options
|
31
61
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
62
|
+
const openingDelay = new Promise(resolve => setTimeout(resolve, delays.opening))
|
63
|
+
const fetchDelay = loadHovercardContentHTML(controller, options, delays)
|
64
|
+
await Promise.all([fetchDelay, openingDelay])
|
65
|
+
|
54
66
|
const parentIsClosedOrClosing = controller.parent && (controller.parent.isClosed || controller.parent.closingRequest)
|
55
|
-
|
56
|
-
if
|
57
|
-
|
67
|
+
|
68
|
+
// but if opening has been canceled (nullified), the wait still happens, so we need to check again
|
69
|
+
if (controller.coupdoeilElement.openingHovercard && !parentIsClosedOrClosing) {
|
58
70
|
await display(controller, options)
|
59
71
|
}
|
60
72
|
}
|
@@ -71,69 +83,60 @@ async function display(controller, options) {
|
|
71
83
|
controller.card.dataset.animation = options.animation
|
72
84
|
}
|
73
85
|
|
74
|
-
|
75
|
-
controller.card
|
76
|
-
controller.card.classList.remove('hidden')
|
86
|
+
executeNextFrameIfStillOpening(controller, async () => {
|
87
|
+
await positionHovercard(controller.coupdoeilElement, controller.card, options)
|
77
88
|
|
78
|
-
|
79
|
-
|
89
|
+
// see buildHovercardElement() about next 2 lines
|
90
|
+
controller.card.classList.add('hidden')
|
91
|
+
controller.card.style.removeProperty('visibility')
|
80
92
|
|
81
|
-
|
82
|
-
|
93
|
+
executeNextFrameIfStillOpening(controller, async () => {
|
94
|
+
// // adding again the card to make sure it is in the map, could be better
|
95
|
+
addToCurrents(controller.coupdoeilElement)
|
96
|
+
delete controller.coupdoeilElement.openingHovercard
|
97
|
+
controller.coupdoeilElement.dataset.hovercardOpen = true
|
83
98
|
|
84
|
-
|
85
|
-
// adding again the card to make sure it is in the map, could be better
|
86
|
-
addToCurrents(controller.coupdoeilElement)
|
87
|
-
delete controller.coupdoeilElement.openingHovercard
|
88
|
-
controller.coupdoeilElement.dataset.hovercardOpen = true
|
89
|
-
|
90
|
-
await enter(controller.card, 'hovercard')
|
91
|
-
})
|
99
|
+
await enter(controller.card, 'hovercard')
|
92
100
|
})
|
93
101
|
})
|
94
102
|
}
|
95
103
|
|
104
|
+
function executeNextFrameIfStillOpening(controller, callback) {
|
105
|
+
requestAnimationFrame(() => {
|
106
|
+
if (controller.coupdoeilElement.openingHovercard) {
|
107
|
+
callback.call()
|
108
|
+
} else {
|
109
|
+
clearHovercard(controller)
|
110
|
+
}
|
111
|
+
})
|
112
|
+
}
|
113
|
+
|
96
114
|
function getDelayOptionsForController(controller) {
|
97
115
|
if (triggeredOnClick(controller)) {
|
98
|
-
return {
|
116
|
+
return { fetch: 0, opening: 0 }
|
99
117
|
}
|
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
|
106
|
-
}
|
107
|
-
}
|
108
118
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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 })
|
119
|
+
let fetchDelay
|
120
|
+
if (defaultConfig.openingDelay === 0) {
|
121
|
+
fetchDelay = 0
|
122
|
+
} else {
|
123
|
+
fetchDelay = defaultConfig.openingDelay / 2
|
120
124
|
}
|
121
|
-
return fetch
|
122
|
-
.then((response) => {
|
123
|
-
if (response.status >= 400) {
|
124
|
-
throw 'error while fetching hovercard content'
|
125
|
-
}
|
126
|
-
return response.text()
|
127
|
-
})
|
125
|
+
return { fetch: fetchDelay, opening: defaultConfig.openingDelay }
|
128
126
|
}
|
129
127
|
|
130
128
|
function buildHovercardElement(controller, options) {
|
131
129
|
const el = document.createElement('div')
|
132
130
|
el.setAttribute('role', 'dialog')
|
133
|
-
el.classList.add(HOVERCARD_CLASS_NAME
|
131
|
+
el.classList.add(HOVERCARD_CLASS_NAME)
|
134
132
|
el.style.cssText = 'position: absolute; left: 0; top: 0;'
|
135
133
|
el.innerHTML = getHovercardContentHTML(controller)
|
136
134
|
el.controller = controller
|
137
135
|
el.dataset.placement = options.placement
|
136
|
+
|
137
|
+
// Initial style is not .hidden (no display: none;) and visibility: 'hidden'; so the card is inserted
|
138
|
+
// in DOM the without being visible.
|
139
|
+
// This allows the browser to compute its actual size so the positioning is computed correctly.
|
140
|
+
el.style.visibility = 'hidden'
|
138
141
|
return el
|
139
142
|
}
|
@@ -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 = ["
|
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
|
-
|
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) {
|
@@ -6,10 +6,10 @@ import {
|
|
6
6
|
arrow
|
7
7
|
} from "@floating-ui/dom"
|
8
8
|
|
9
|
-
export async function positionHovercard(
|
9
|
+
export async function positionHovercard(target, card, options) {
|
10
10
|
let { placement: placements, offset: offsetValue } = options
|
11
11
|
const placement = placements[0]
|
12
|
-
const arrowElement =
|
12
|
+
const arrowElement = card.querySelector('[data-hovercard-arrow]')
|
13
13
|
const middleware = [AutoPositioningWithFallbacks(placements)]
|
14
14
|
|
15
15
|
if (arrowElement) {
|
@@ -21,7 +21,7 @@ export async function positionHovercard(controller, options) {
|
|
21
21
|
middleware.push(offset(offsetValue))
|
22
22
|
|
23
23
|
const computedPosition = await computePosition(
|
24
|
-
|
24
|
+
target, card, { placement, middleware }
|
25
25
|
)
|
26
26
|
const { x, y, placement: actualPlacement} = computedPosition
|
27
27
|
|
@@ -29,8 +29,8 @@ export async function positionHovercard(controller, options) {
|
|
29
29
|
positionArrow(arrowElement, computedPosition)
|
30
30
|
}
|
31
31
|
|
32
|
-
|
33
|
-
Object.assign(
|
32
|
+
card.dataset.placement = actualPlacement
|
33
|
+
Object.assign(card.style, { left: `${x}px`, top: `${y}px` })
|
34
34
|
}
|
35
35
|
|
36
36
|
const AutoPositioningWithFallbacks = (placements) => {
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import {HOVERCARD_CLOSE_BTN_SELECTOR} from "./config";
|
2
|
-
|
2
|
+
|
3
|
+
import {currentHovercardsById} from "./current";
|
3
4
|
|
4
5
|
export function isElementCloseHovercardButton(element) {
|
5
6
|
return element.closest(HOVERCARD_CLOSE_BTN_SELECTOR) ||
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# frozen_string_literal: true
|
4
|
-
|
5
3
|
module Coupdoeil
|
6
4
|
class Hovercard
|
7
5
|
class OptionsSet
|
@@ -19,7 +17,7 @@ module Coupdoeil
|
|
19
17
|
attr_reader :options
|
20
18
|
|
21
19
|
def dup = OptionsSet.new(options.deep_dup)
|
22
|
-
def preload? = options[:loading] ==
|
20
|
+
def preload? = options[:loading].to_s == "preload"
|
23
21
|
def custom_animation? = options[:animation].to_s == "custom"
|
24
22
|
def to_h = options
|
25
23
|
|
data/lib/coupdoeil/engine.rb
CHANGED
@@ -1,9 +1,17 @@
|
|
1
1
|
module Coupdoeil
|
2
2
|
class Engine < ::Rails::Engine
|
3
3
|
isolate_namespace Coupdoeil
|
4
|
-
|
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
|
#
|
data/lib/coupdoeil/version.rb
CHANGED
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.
|
4
|
+
version: 1.0.0.pre.alpha.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- PageHey
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-05-
|
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
|