coupdoeil 1.0.0.pre.beta.1 → 1.0.0.pre.beta.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/README.md +21 -21
- data/app/assets/javascripts/coupdoeil.js +132 -132
- data/app/assets/javascripts/coupdoeil.min.js +1 -1
- data/app/assets/javascripts/coupdoeil.min.js.map +1 -1
- data/app/assets/stylesheets/coupdoeil/popover-animation.css +44 -0
- data/app/assets/stylesheets/coupdoeil/popover-arrow.css +39 -0
- data/app/assets/stylesheets/coupdoeil/popover.css +84 -0
- data/app/controllers/coupdoeil/{hovercards_controller.rb → popovers_controller.rb} +13 -13
- data/app/helpers/coupdoeil/application_helper.rb +4 -4
- data/app/javascript/coupdoeil/elements/coupdoeil_element.js +13 -13
- data/app/javascript/coupdoeil/events/onclick.js +36 -36
- data/app/javascript/coupdoeil/events/onmouseover.js +52 -52
- data/app/javascript/coupdoeil/events.js +4 -4
- data/app/javascript/coupdoeil/index.js +1 -1
- data/app/javascript/coupdoeil/{hovercard → popover}/attributes.js +3 -3
- data/app/javascript/coupdoeil/popover/cache.js +22 -0
- data/app/javascript/coupdoeil/{hovercard → popover}/closing.js +18 -18
- data/app/javascript/coupdoeil/{hovercard → popover}/config.js +6 -6
- data/app/javascript/coupdoeil/popover/controller.js +20 -0
- data/app/javascript/coupdoeil/popover/current.js +13 -0
- data/app/javascript/coupdoeil/{hovercard → popover}/opening.js +26 -26
- data/app/javascript/coupdoeil/{hovercard → popover}/optionsParser.js +5 -5
- data/app/javascript/coupdoeil/{hovercard → popover}/positioning.js +2 -2
- data/app/javascript/coupdoeil/popover/state_check.js +12 -0
- data/app/models/coupdoeil/params.rb +4 -4
- data/app/models/coupdoeil/{hovercard → popover}/option/animation.rb +2 -2
- data/app/models/coupdoeil/{hovercard → popover}/option/cache.rb +2 -2
- data/app/models/coupdoeil/{hovercard → popover}/option/loading.rb +2 -2
- data/app/models/coupdoeil/{hovercard → popover}/option/offset.rb +2 -2
- data/app/models/coupdoeil/{hovercard → popover}/option/opening_delay.rb +2 -2
- data/app/models/coupdoeil/{hovercard → popover}/option/placement.rb +2 -2
- data/app/models/coupdoeil/{hovercard → popover}/option/trigger.rb +2 -2
- data/app/models/coupdoeil/{hovercard → popover}/option.rb +1 -1
- data/app/models/coupdoeil/{hovercard → popover}/options_set.rb +1 -1
- data/app/models/coupdoeil/{hovercard → popover}/registry.rb +2 -2
- data/app/models/coupdoeil/{hovercard → popover}/setup.rb +2 -2
- data/app/models/coupdoeil/{hovercard → popover}/view_context_delegation.rb +3 -3
- data/app/models/coupdoeil/{hovercard.rb → popover.rb} +8 -8
- data/app/models/coupdoeil/tag.rb +15 -15
- data/app/style/{hovercard-animation.scss → popover-animation.scss} +7 -7
- data/app/style/popover-arrow.scss +40 -0
- data/app/style/popover.scss +2 -0
- data/config/routes.rb +1 -1
- data/lib/coupdoeil/engine.rb +8 -8
- data/lib/coupdoeil/version.rb +1 -1
- data/lib/generators/coupdoeil/install/install_generator.rb +8 -8
- data/lib/generators/coupdoeil/install/templates/layout.html.erb.tt +1 -1
- data/lib/generators/coupdoeil/popover/USAGE +15 -0
- data/lib/generators/coupdoeil/popover/popover_generator.rb +22 -0
- data/lib/generators/coupdoeil/{hovercard/templates/hovercard.rb.tt → popover/templates/popover.rb.tt} +1 -1
- metadata +39 -39
- data/app/assets/stylesheets/coupdoeil/hovercard-animation.css +0 -44
- data/app/assets/stylesheets/coupdoeil/hovercard-arrow.css +0 -39
- data/app/assets/stylesheets/coupdoeil/hovercard.css +0 -84
- data/app/javascript/coupdoeil/hovercard/cache.js +0 -22
- data/app/javascript/coupdoeil/hovercard/controller.js +0 -20
- data/app/javascript/coupdoeil/hovercard/current.js +0 -13
- data/app/javascript/coupdoeil/hovercard/state_check.js +0 -12
- data/app/style/hovercard-arrow.scss +0 -40
- data/app/style/hovercard.scss +0 -2
- data/lib/generators/coupdoeil/hovercard/USAGE +0 -15
- data/lib/generators/coupdoeil/hovercard/hovercard_generator.rb +0 -22
- /data/app/javascript/coupdoeil/{hovercard → popover}/actions.js +0 -0
- /data/app/javascript/coupdoeil/{hovercard.js → popover.js} +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
import {triggeredOnHover} from "./attributes"
|
2
2
|
import {CLOSING_DELAY_MS} from "./config"
|
3
3
|
import {leave} from "el-transition"
|
4
|
-
import {addToCurrents,
|
4
|
+
import {addToCurrents, CURRENT_POPOVERS_BY_ID, removeFromCurrents} from "./current"
|
5
5
|
|
6
6
|
function detachFromParent(controller) {
|
7
7
|
if (controller.parent) {
|
@@ -16,7 +16,7 @@ export function cancelOpenCloseActions(controller) {
|
|
16
16
|
}
|
17
17
|
|
18
18
|
function cancelOpening(controller) {
|
19
|
-
delete controller.coupdoeilElement.
|
19
|
+
delete controller.coupdoeilElement.openingPopover
|
20
20
|
}
|
21
21
|
|
22
22
|
export function cancelCloseRequest(controller) {
|
@@ -26,7 +26,7 @@ export function cancelCloseRequest(controller) {
|
|
26
26
|
}
|
27
27
|
|
28
28
|
export function closeNow(controller, allowAnimation = true) {
|
29
|
-
if (controller.closing || (controller.isClosed && !controller.coupdoeilElement.
|
29
|
+
if (controller.closing || (controller.isClosed && !controller.coupdoeilElement.openingPopover)) return
|
30
30
|
|
31
31
|
controller.closing = true
|
32
32
|
|
@@ -46,7 +46,7 @@ export function closeNow(controller, allowAnimation = true) {
|
|
46
46
|
}
|
47
47
|
|
48
48
|
async function closeWithAnimation(controller) {
|
49
|
-
await leave(controller.card, '
|
49
|
+
await leave(controller.card, 'popover')
|
50
50
|
|
51
51
|
closeWithoutAnimation(controller)
|
52
52
|
}
|
@@ -57,7 +57,7 @@ function closeWithoutAnimation(controller) {
|
|
57
57
|
controller.card = null
|
58
58
|
}
|
59
59
|
delete controller.closing
|
60
|
-
delete controller.coupdoeilElement.dataset.
|
60
|
+
delete controller.coupdoeilElement.dataset.popoverOpen
|
61
61
|
}
|
62
62
|
|
63
63
|
export function clear(controller) {
|
@@ -93,32 +93,32 @@ export function closeOnHoverChildrenLater(controller) {
|
|
93
93
|
}
|
94
94
|
|
95
95
|
export function closeAllNow() {
|
96
|
-
for (const coupdoeilElement of
|
97
|
-
closeNow(coupdoeilElement.
|
96
|
+
for (const coupdoeilElement of CURRENT_POPOVERS_BY_ID.values()) {
|
97
|
+
closeNow(coupdoeilElement.popoverController)
|
98
98
|
removeFromCurrents(coupdoeilElement)
|
99
99
|
}
|
100
100
|
}
|
101
101
|
|
102
102
|
export function clearAll() {
|
103
|
-
for (const coupdoeilElement of
|
104
|
-
clear(coupdoeilElement.
|
103
|
+
for (const coupdoeilElement of CURRENT_POPOVERS_BY_ID.values()) {
|
104
|
+
clear(coupdoeilElement.popoverController)
|
105
105
|
removeFromCurrents(coupdoeilElement)
|
106
106
|
}
|
107
107
|
}
|
108
108
|
|
109
109
|
export function closeTriggeredOnHoverNow() {
|
110
|
-
for (const coupdoeilElement of
|
111
|
-
if (triggeredOnHover(coupdoeilElement.
|
112
|
-
closeNow(coupdoeilElement.
|
110
|
+
for (const coupdoeilElement of CURRENT_POPOVERS_BY_ID.values()) {
|
111
|
+
if (triggeredOnHover(coupdoeilElement.popoverController)) {
|
112
|
+
closeNow(coupdoeilElement.popoverController)
|
113
113
|
removeFromCurrents(coupdoeilElement)
|
114
114
|
}
|
115
115
|
}
|
116
116
|
}
|
117
117
|
|
118
118
|
export function closeTriggeredOnHoverLater() {
|
119
|
-
for (const coupdoeilElement of
|
120
|
-
if (triggeredOnHover(coupdoeilElement.
|
121
|
-
closeLater(coupdoeilElement.
|
119
|
+
for (const coupdoeilElement of CURRENT_POPOVERS_BY_ID.values()) {
|
120
|
+
if (triggeredOnHover(coupdoeilElement.popoverController)) {
|
121
|
+
closeLater(coupdoeilElement.popoverController)
|
122
122
|
removeFromCurrents(coupdoeilElement)
|
123
123
|
}
|
124
124
|
}
|
@@ -131,9 +131,9 @@ export function closeTriggeredOnHoverNowUnlessAncestor(controller) {
|
|
131
131
|
}
|
132
132
|
const idToSkip = topMostParent.coupdoeilElement.uniqueId
|
133
133
|
|
134
|
-
for (const coupdoeilElement of
|
135
|
-
if (coupdoeilElement.uniqueId !== idToSkip && triggeredOnHover(coupdoeilElement.
|
136
|
-
closeNow(coupdoeilElement.
|
134
|
+
for (const coupdoeilElement of CURRENT_POPOVERS_BY_ID.values()) {
|
135
|
+
if (coupdoeilElement.uniqueId !== idToSkip && triggeredOnHover(coupdoeilElement.popoverController)) {
|
136
|
+
closeNow(coupdoeilElement.popoverController)
|
137
137
|
removeFromCurrents(coupdoeilElement)
|
138
138
|
}
|
139
139
|
}
|
@@ -1,15 +1,15 @@
|
|
1
|
-
export const
|
2
|
-
export const
|
3
|
-
export const
|
1
|
+
export const POPOVER_CLASS_NAME = 'coupdoeil--popover'
|
2
|
+
export const POPOVER_SELECTOR = `.${POPOVER_CLASS_NAME}`
|
3
|
+
export const POPOVER_CLOSE_BTN_SELECTOR = '[data-popover-close]'
|
4
4
|
|
5
|
-
// the time (ms) to wait before closing the
|
5
|
+
// the time (ms) to wait before closing the popover,
|
6
6
|
// to avoid flickering if the user hovers out and in quickly,
|
7
|
-
// or if the user moves the mouse from the target to the
|
7
|
+
// or if the user moves the mouse from the target to the popover
|
8
8
|
export const CLOSING_DELAY_MS = 75
|
9
9
|
|
10
10
|
// the time (ms) to wait before starting to fetch the content,
|
11
11
|
// to avoid fetching too soon if the user hovers in and out the target quickly
|
12
12
|
export const FETCH_DELAY_MS = 100
|
13
13
|
|
14
|
-
// the minimum time (ms) the user should wait before seeing the
|
14
|
+
// the minimum time (ms) the user should wait before seeing the popover
|
15
15
|
export const OPENING_DELAY_MS = 200
|
@@ -0,0 +1,20 @@
|
|
1
|
+
export class PopoverController {
|
2
|
+
constructor(coupdoeilElement) {
|
3
|
+
this.coupdoeilElement = coupdoeilElement
|
4
|
+
|
5
|
+
this.card = null
|
6
|
+
|
7
|
+
this.children = new Set()
|
8
|
+
this.parent = null
|
9
|
+
|
10
|
+
this.closingRequest = null
|
11
|
+
}
|
12
|
+
|
13
|
+
get isOpen() {
|
14
|
+
return !!this.coupdoeilElement.dataset.popoverOpen
|
15
|
+
}
|
16
|
+
|
17
|
+
get isClosed() {
|
18
|
+
return !this.isOpen
|
19
|
+
}
|
20
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
export const CURRENT_POPOVERS_BY_ID = new Map()
|
2
|
+
|
3
|
+
export function currentPopoversById() {
|
4
|
+
return CURRENT_POPOVERS_BY_ID
|
5
|
+
}
|
6
|
+
|
7
|
+
export function addToCurrents(coupdoeilElement) {
|
8
|
+
CURRENT_POPOVERS_BY_ID.set(coupdoeilElement.uniqueId, coupdoeilElement)
|
9
|
+
}
|
10
|
+
|
11
|
+
export function removeFromCurrents(coupdoeilElement) {
|
12
|
+
CURRENT_POPOVERS_BY_ID.delete(coupdoeilElement.uniqueId)
|
13
|
+
}
|
@@ -1,17 +1,17 @@
|
|
1
|
-
import {FETCH_DELAY_MS,
|
1
|
+
import {FETCH_DELAY_MS, POPOVER_CLASS_NAME, OPENING_DELAY_MS} from "./config"
|
2
2
|
import {getParams, getType, preloadedContentElement, triggeredOnClick} from "./attributes"
|
3
|
-
import {
|
3
|
+
import {getPopoverContentHTML, setPopoverContentHTML} from "./cache"
|
4
4
|
import {extractOptionsFromElement} from "./optionsParser"
|
5
|
-
import {
|
5
|
+
import {positionPopover} from "./positioning"
|
6
6
|
import {enter} from "el-transition"
|
7
7
|
import {addToCurrents} from "./current"
|
8
|
-
import {cancelCloseRequest, clear as
|
8
|
+
import {cancelCloseRequest, clear as clearPopover} from "./closing"
|
9
9
|
|
10
|
-
function
|
10
|
+
function fetchPopoverContent(controller) {
|
11
11
|
const type = getType(controller)
|
12
12
|
const params = getParams(controller)
|
13
13
|
const authenticityToken = document.querySelector('meta[name=csrf-token]').content
|
14
|
-
let url = `/coupdoeil/
|
14
|
+
let url = `/coupdoeil/popover`
|
15
15
|
const opts = {
|
16
16
|
method: 'POST',
|
17
17
|
headers: {
|
@@ -22,32 +22,32 @@ function fetchHovercardContent(controller) {
|
|
22
22
|
return fetch(url, opts)
|
23
23
|
.then((response) => {
|
24
24
|
if (response.status >= 400) {
|
25
|
-
throw 'error while fetching
|
25
|
+
throw 'error while fetching popover content'
|
26
26
|
}
|
27
27
|
return response.text()
|
28
28
|
})
|
29
29
|
}
|
30
30
|
|
31
|
-
async function
|
31
|
+
async function loadPopoverContentHTML(controller, options, delayOptions) {
|
32
32
|
return new Promise((resolve) => {
|
33
33
|
setTimeout(async () => {
|
34
|
-
if (!controller.coupdoeilElement.
|
34
|
+
if (!controller.coupdoeilElement.openingPopover) return // opening has been canceled
|
35
35
|
|
36
|
-
if (options.cache === false || (options.cache && !
|
36
|
+
if (options.cache === false || (options.cache && !getPopoverContentHTML(controller))) {
|
37
37
|
let html
|
38
38
|
if (options.loading === "preload") {
|
39
39
|
html = preloadedContentElement(controller).innerHTML
|
40
40
|
} else {
|
41
|
-
html = await
|
41
|
+
html = await fetchPopoverContent(controller)
|
42
42
|
}
|
43
|
-
|
43
|
+
setPopoverContentHTML(controller, html)
|
44
44
|
}
|
45
45
|
resolve()
|
46
46
|
}, delayOptions.fetch)
|
47
47
|
})
|
48
48
|
}
|
49
49
|
|
50
|
-
export async function
|
50
|
+
export async function openPopover(controller, { parent, beforeDisplay }) {
|
51
51
|
if (controller.isOpen) {
|
52
52
|
return cancelCloseRequest(controller)
|
53
53
|
}
|
@@ -60,13 +60,13 @@ export async function openHovercard(controller, { parent, beforeDisplay }) {
|
|
60
60
|
const delays = getDelayOptionsForController(controller, options)
|
61
61
|
|
62
62
|
const openingDelay = new Promise(resolve => setTimeout(resolve, delays.opening))
|
63
|
-
const fetchDelay =
|
63
|
+
const fetchDelay = loadPopoverContentHTML(controller, options, delays)
|
64
64
|
await Promise.all([fetchDelay, openingDelay])
|
65
65
|
|
66
66
|
const parentIsClosedOrClosing = controller.parent && (controller.parent.isClosed || controller.parent.closingRequest)
|
67
67
|
|
68
68
|
// but if opening has been canceled (nullified), the wait still happens, so we need to check again
|
69
|
-
if (controller.coupdoeilElement.
|
69
|
+
if (controller.coupdoeilElement.openingPopover && !parentIsClosedOrClosing) {
|
70
70
|
await display(controller, options, beforeDisplay)
|
71
71
|
}
|
72
72
|
}
|
@@ -76,7 +76,7 @@ async function display(controller, options, beforeDisplay) {
|
|
76
76
|
|
77
77
|
cancelCloseRequest(controller)
|
78
78
|
|
79
|
-
controller.card =
|
79
|
+
controller.card = buildPopoverElement(controller, options)
|
80
80
|
document.body.appendChild(controller.card)
|
81
81
|
|
82
82
|
if (options.animation) {
|
@@ -84,9 +84,9 @@ async function display(controller, options, beforeDisplay) {
|
|
84
84
|
}
|
85
85
|
|
86
86
|
executeNextFrameIfStillOpening(controller, async () => {
|
87
|
-
await
|
87
|
+
await positionPopover(controller.coupdoeilElement, controller.card, options)
|
88
88
|
|
89
|
-
// see
|
89
|
+
// see buildPopoverElement() about next 2 lines
|
90
90
|
controller.card.classList.add('hidden')
|
91
91
|
controller.card.style.removeProperty('visibility')
|
92
92
|
|
@@ -96,20 +96,20 @@ async function display(controller, options, beforeDisplay) {
|
|
96
96
|
}
|
97
97
|
// // adding again the card to make sure it is in the map, could be better
|
98
98
|
addToCurrents(controller.coupdoeilElement)
|
99
|
-
delete controller.coupdoeilElement.
|
100
|
-
controller.coupdoeilElement.dataset.
|
99
|
+
delete controller.coupdoeilElement.openingPopover
|
100
|
+
controller.coupdoeilElement.dataset.popoverOpen = true
|
101
101
|
|
102
|
-
await enter(controller.card, '
|
102
|
+
await enter(controller.card, 'popover')
|
103
103
|
})
|
104
104
|
})
|
105
105
|
}
|
106
106
|
|
107
107
|
function executeNextFrameIfStillOpening(controller, callback) {
|
108
108
|
requestAnimationFrame(() => {
|
109
|
-
if (controller.coupdoeilElement.
|
109
|
+
if (controller.coupdoeilElement.openingPopover) {
|
110
110
|
callback.call()
|
111
111
|
} else {
|
112
|
-
|
112
|
+
clearPopover(controller)
|
113
113
|
}
|
114
114
|
})
|
115
115
|
}
|
@@ -122,12 +122,12 @@ function getDelayOptionsForController(controller, options) {
|
|
122
122
|
return { fetch: FETCH_DELAY_MS, opening: OPENING_DELAY_MS }
|
123
123
|
}
|
124
124
|
|
125
|
-
function
|
125
|
+
function buildPopoverElement(controller, options) {
|
126
126
|
const el = document.createElement('div')
|
127
127
|
el.setAttribute('role', 'dialog')
|
128
|
-
el.classList.add(
|
128
|
+
el.classList.add(POPOVER_CLASS_NAME)
|
129
129
|
el.style.cssText = 'position: absolute; left: 0; top: 0;'
|
130
|
-
el.innerHTML =
|
130
|
+
el.innerHTML = getPopoverContentHTML(controller)
|
131
131
|
el.controller = controller
|
132
132
|
el.dataset.placement = options.placement
|
133
133
|
|
@@ -93,7 +93,7 @@ function getTrigger(optionsInt) {
|
|
93
93
|
return TRIGGERS[optionsInt & 1]
|
94
94
|
}
|
95
95
|
|
96
|
-
const
|
96
|
+
const popoverOptions = {
|
97
97
|
animation: undefined,
|
98
98
|
cache: undefined,
|
99
99
|
loading: undefined,
|
@@ -104,8 +104,8 @@ const HovercardOptions = {
|
|
104
104
|
}
|
105
105
|
|
106
106
|
export function extractOptionsFromElement(coupdoeilElement) {
|
107
|
-
const optionsInt = coupdoeilElement.
|
108
|
-
const options = Object.create(
|
107
|
+
const optionsInt = coupdoeilElement.popoverController.optionsInt ||= parseOtionsInt((coupdoeilElement))
|
108
|
+
const options = Object.create(popoverOptions)
|
109
109
|
|
110
110
|
for (const option of ORDERED_OPTIONS) {
|
111
111
|
options[option] = OPTIONS[option].getter(optionsInt)
|
@@ -115,12 +115,12 @@ export function extractOptionsFromElement(coupdoeilElement) {
|
|
115
115
|
}
|
116
116
|
|
117
117
|
function parseOtionsInt(coupdoeilElement) {
|
118
|
-
const optionsString = coupdoeilElement.getAttribute("
|
118
|
+
const optionsString = coupdoeilElement.getAttribute("popover-options")
|
119
119
|
return parseInt(optionsString, 36)
|
120
120
|
}
|
121
121
|
|
122
122
|
export function extractOptionFromElement(coupdoeilElement, optionName) {
|
123
|
-
const optionsInt = coupdoeilElement.
|
123
|
+
const optionsInt = coupdoeilElement.popoverController.optionsInt ||= parseOtionsInt((coupdoeilElement))
|
124
124
|
|
125
125
|
return OPTIONS[optionName].getter(optionsInt)
|
126
126
|
}
|
@@ -6,10 +6,10 @@ import {
|
|
6
6
|
arrow
|
7
7
|
} from "@floating-ui/dom"
|
8
8
|
|
9
|
-
export async function
|
9
|
+
export async function positionPopover(target, card, options) {
|
10
10
|
let { placement: placements, offset: offsetValue } = options
|
11
11
|
const placement = placements[0]
|
12
|
-
const arrowElement = card.querySelector('[data-
|
12
|
+
const arrowElement = card.querySelector('[data-popover-arrow]')
|
13
13
|
const middleware = [AutoPositioningWithFallbacks(placements)]
|
14
14
|
|
15
15
|
if (arrowElement) {
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import {POPOVER_CLOSE_BTN_SELECTOR} from "./config";
|
2
|
+
|
3
|
+
import {currentPopoversById} from "./current";
|
4
|
+
|
5
|
+
export function isElementClosePopoverButton(element) {
|
6
|
+
return element.closest(POPOVER_CLOSE_BTN_SELECTOR) ||
|
7
|
+
element.dataset.hasOwnProperty("popoverClose")
|
8
|
+
}
|
9
|
+
|
10
|
+
export function isAnyPopoverOpened() {
|
11
|
+
return currentPopoversById().size > 0
|
12
|
+
}
|
@@ -17,7 +17,7 @@ module Coupdoeil
|
|
17
17
|
def serialized_global_id?(hash) = hash.size == 1 && hash.include?(GLOBAL_ID_KEY)
|
18
18
|
|
19
19
|
def serialize(*params)
|
20
|
-
ActiveSupport::Notifications.instrument("
|
20
|
+
ActiveSupport::Notifications.instrument("serialize_popover_params.coupdoeil") do
|
21
21
|
params.map { |param| serialize_param(param) }
|
22
22
|
end
|
23
23
|
end
|
@@ -50,18 +50,18 @@ module Coupdoeil
|
|
50
50
|
def serialize_hash_key(key)
|
51
51
|
case key
|
52
52
|
when *RESERVED_KEYS
|
53
|
-
raise
|
53
|
+
raise SerializationError, "Can't serialize a Hash with reserved key #{key.inspect}"
|
54
54
|
when String, Symbol
|
55
55
|
key.to_s
|
56
56
|
else
|
57
|
-
raise
|
57
|
+
raise SerializationError, "Only string and symbol hash keys may be serialized as popover params, but #{key.inspect} is a #{key.class}"
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
61
|
def convert_to_global_id_hash(param)
|
62
62
|
{ GLOBAL_ID_KEY => param.to_global_id.to_s }
|
63
63
|
rescue URI::GID::MissingModelIdError
|
64
|
-
raise
|
64
|
+
raise SerializationError, "Unable to serialize #{param.class} " \
|
65
65
|
"without an id. (Maybe you forgot to call save?)"
|
66
66
|
end
|
67
67
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Coupdoeil
|
4
|
-
class
|
4
|
+
class Popover
|
5
5
|
class Option
|
6
|
-
class Animation < Coupdoeil::
|
6
|
+
class Animation < Coupdoeil::Popover::Option
|
7
7
|
self.bit_size = 3
|
8
8
|
|
9
9
|
VALUES = %w[slide-in fade-in slide-out custom].unshift(false).freeze
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Coupdoeil
|
4
|
-
class
|
4
|
+
class Popover
|
5
5
|
class Registry
|
6
6
|
attr_reader :registry, :semaphore
|
7
7
|
|
@@ -17,7 +17,7 @@ module Coupdoeil
|
|
17
17
|
def lookup_or_register(type)
|
18
18
|
safe_lookup(type) ||
|
19
19
|
begin
|
20
|
-
register(type, (type.classify + "
|
20
|
+
register(type, (type.classify + "Popover").constantize)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Coupdoeil
|
4
|
-
class
|
4
|
+
class Popover
|
5
5
|
class Setup
|
6
6
|
attr_reader :klass, :type, :params
|
7
7
|
|
@@ -14,7 +14,7 @@ module Coupdoeil
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def default_options = klass.default_options_for(type)
|
17
|
-
def identifier = "#{type}@#{klass.
|
17
|
+
def identifier = "#{type}@#{klass.popover_resource_name}"
|
18
18
|
def render_in(view_context) = klass.new(params, view_context).process(type)
|
19
19
|
|
20
20
|
def with_type(type)
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Coupdoeil
|
4
|
-
class
|
4
|
+
class Popover
|
5
5
|
module ViewContextDelegation
|
6
|
-
attr_accessor :__cp_view_context, :
|
6
|
+
attr_accessor :__cp_view_context, :popover
|
7
7
|
|
8
8
|
# For CSRF authenticity tokens in forms
|
9
9
|
def config = __cp_view_context.config
|
@@ -13,7 +13,7 @@ module Coupdoeil
|
|
13
13
|
|
14
14
|
def helpers = __cp_view_context
|
15
15
|
def controller = __cp_view_context.controller
|
16
|
-
def params =
|
16
|
+
def params = popover.params
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Coupdoeil
|
4
|
-
class
|
4
|
+
class Popover < AbstractController::Base
|
5
5
|
abstract!
|
6
6
|
|
7
7
|
include AbstractController::Rendering
|
@@ -17,9 +17,9 @@ module Coupdoeil
|
|
17
17
|
|
18
18
|
include Rails.application.routes.url_helpers
|
19
19
|
|
20
|
-
layout "
|
20
|
+
layout "popover"
|
21
21
|
|
22
|
-
# so coupdoeil helpers are always available within
|
22
|
+
# so coupdoeil helpers are always available within popovers
|
23
23
|
helper Coupdoeil::ApplicationHelper
|
24
24
|
|
25
25
|
@registry = Registry.new
|
@@ -45,12 +45,12 @@ module Coupdoeil
|
|
45
45
|
class << self
|
46
46
|
attr_reader :registry
|
47
47
|
|
48
|
-
def
|
48
|
+
def popover_resource_name = @popover_resource_name ||= name.delete_suffix("Popover").underscore
|
49
49
|
def with(...) = Setup.new(self).with_params(...)
|
50
50
|
|
51
51
|
def inherited(subclass)
|
52
52
|
super
|
53
|
-
Coupdoeil::
|
53
|
+
Coupdoeil::Popover.registry.register(subclass.popover_resource_name, subclass)
|
54
54
|
subclass.instance_variable_set(:@default_options_by_method, @default_options_by_method.dup)
|
55
55
|
end
|
56
56
|
|
@@ -93,7 +93,7 @@ module Coupdoeil
|
|
93
93
|
def view_context
|
94
94
|
super.tap do |context|
|
95
95
|
context.extend ViewContextDelegation
|
96
|
-
context.
|
96
|
+
context.popover = self
|
97
97
|
context.__cp_view_context = @__cp_view_context
|
98
98
|
end
|
99
99
|
end
|
@@ -106,8 +106,8 @@ Also note that render does not terminate execution of the action."
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def process(method_name, *)
|
109
|
-
benchmark("processed
|
110
|
-
ActiveSupport::Notifications.instrument("
|
109
|
+
benchmark("processed popover #{self.class.popover_resource_name}/#{method_name}", silence: true) do
|
110
|
+
ActiveSupport::Notifications.instrument("render_popover.coupdoeil") do
|
111
111
|
super
|
112
112
|
response_body || render(action_name)
|
113
113
|
end
|