spina 2.19.0 → 2.20.0
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.
Potentially problematic release.
This version of spina might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/assets/config/spina/manifest.js +2 -2
- data/app/assets/javascripts/spina/application.js +1 -1
- data/app/assets/javascripts/spina/controllers/confetti_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/data_binding_controller.js +234 -1
- data/app/assets/javascripts/spina/controllers/form_controller.js +1 -2
- data/app/assets/javascripts/spina/controllers/hotkeys_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/reveal_controller.js +424 -1
- data/app/assets/javascripts/spina/controllers/sortable_controller.js +1 -2
- data/app/assets/javascripts/spina/libraries/canvas-confetti.js +1 -1
- data/app/assets/javascripts/spina/libraries/hotkeys.js +1 -1
- data/app/assets/javascripts/spina/libraries/sortablejs.js +1 -1
- data/app/assets/javascripts/spina/libraries/trix.js +1 -1
- data/app/assets/stylesheets/spina/fonts-propshaft.css +114 -0
- data/app/helpers/spina/admin/pages_helper.rb +23 -4
- data/app/models/spina/embeds/youtube.rb +1 -1
- data/app/views/layouts/spina/admin/application.html.erb +9 -2
- data/config/locales/de.yml +2 -0
- data/db/migrate/18_change_default_spina_resources_slug.rb +9 -0
- data/lib/spina/engine.rb +0 -1
- data/lib/spina/railtie.rb +4 -1
- data/lib/spina/version.rb +1 -1
- metadata +11 -27
- data/app/assets/javascripts/spina/libraries/form-request-submit-polyfill.js +0 -1
- data/app/assets/javascripts/spina/libraries/form-request-submit-polyfill@2.0.0.js +0 -27
- data/app/assets/javascripts/spina/libraries/stimulus-data-bindings@1.3.2.js +0 -234
- data/app/assets/javascripts/spina/libraries/stimulus-reveal@1.4.2.js +0 -424
- /data/app/assets/stylesheets/spina/{fonts.css.erb → fonts-sprockets.css.erb} +0 -0
@@ -1,424 +0,0 @@
|
|
1
|
-
import { Controller } from "@hotwired/stimulus";
|
2
|
-
|
3
|
-
/**
|
4
|
-
* Stimulus controller to toggle element visibility
|
5
|
-
* @extends Controller
|
6
|
-
*/
|
7
|
-
export default class RevealController extends Controller {
|
8
|
-
static values = {
|
9
|
-
open: Boolean,
|
10
|
-
transitioning: Boolean,
|
11
|
-
targetSelector: String,
|
12
|
-
toggleKeys: String,
|
13
|
-
showKeys: String,
|
14
|
-
hideKeys: String,
|
15
|
-
away: Boolean,
|
16
|
-
debug: Boolean,
|
17
|
-
};
|
18
|
-
|
19
|
-
connect() {
|
20
|
-
this._initCloseKeypressListener();
|
21
|
-
this._initToggleKeypressListener();
|
22
|
-
this._initShowKeypressListener();
|
23
|
-
this._awayHandler = this._awayHandler.bind(this);
|
24
|
-
}
|
25
|
-
|
26
|
-
disconnect() {
|
27
|
-
this._teardown();
|
28
|
-
}
|
29
|
-
|
30
|
-
/**
|
31
|
-
* Shows elements connected to the controller.
|
32
|
-
* @param {Event} event - an event with a currentTarget DOMElement
|
33
|
-
*/
|
34
|
-
show(event) {
|
35
|
-
if (this.openValue || this.transitioningValue) return;
|
36
|
-
|
37
|
-
this._init(event, true);
|
38
|
-
}
|
39
|
-
|
40
|
-
/**
|
41
|
-
* Hides elements connected to the controller.
|
42
|
-
* @param {Event} event - an event with a currentTarget DOMElement
|
43
|
-
*/
|
44
|
-
hide(event) {
|
45
|
-
if (!this.openValue || this.transitioningValue) return;
|
46
|
-
|
47
|
-
this._init(event, false);
|
48
|
-
}
|
49
|
-
|
50
|
-
/**
|
51
|
-
* Toggles elements connected to the controller.
|
52
|
-
* @param {Event} event - an event with a currentTarget DOMElement
|
53
|
-
*/
|
54
|
-
toggle(event) {
|
55
|
-
if (this.transitioningValue) return;
|
56
|
-
|
57
|
-
this._init(event, !this.openValue);
|
58
|
-
}
|
59
|
-
|
60
|
-
/**
|
61
|
-
* Stops event propagation of elements connected to the controller.
|
62
|
-
* @param {Event} event - an event with a currentTarget DOMElement
|
63
|
-
*/
|
64
|
-
stop(event) {
|
65
|
-
event.stopPropagation();
|
66
|
-
}
|
67
|
-
|
68
|
-
// Private methods
|
69
|
-
|
70
|
-
/**
|
71
|
-
* @private
|
72
|
-
* @param {Event} event
|
73
|
-
* @param {Event} shouldOpen
|
74
|
-
*/
|
75
|
-
async _init(event, shouldOpen) {
|
76
|
-
if (event && event.currentTarget && event.currentTarget.dataset) {
|
77
|
-
if ("revealPreventDefault" in event.currentTarget.dataset) {
|
78
|
-
event.preventDefault();
|
79
|
-
}
|
80
|
-
if ("revealStopPropagation" in event.currentTarget.dataset) {
|
81
|
-
event.stopPropagation();
|
82
|
-
}
|
83
|
-
}
|
84
|
-
// start stuff
|
85
|
-
const startSelector = `${this.selector}[data-${shouldOpen ? "enter" : "leave"
|
86
|
-
}-start]`;
|
87
|
-
const startPromises = this._didInitWithPromise(startSelector, shouldOpen);
|
88
|
-
await Promise.all(startPromises);
|
89
|
-
|
90
|
-
const defaultSelector = `${this.selector}:not([data-${shouldOpen ? "enter" : "leave"
|
91
|
-
}-start]):not([data-${shouldOpen ? "enter" : "leave"}-end])`;
|
92
|
-
const defaultPromises = this._didInitWithPromise(
|
93
|
-
defaultSelector,
|
94
|
-
shouldOpen
|
95
|
-
);
|
96
|
-
await Promise.all(defaultPromises);
|
97
|
-
|
98
|
-
// end stuff
|
99
|
-
const endSelector = `${this.selector}[data-${shouldOpen ? "enter" : "leave"
|
100
|
-
}-end]`;
|
101
|
-
const endPromises = this._didInitWithPromise(endSelector, shouldOpen);
|
102
|
-
await Promise.all(endPromises);
|
103
|
-
}
|
104
|
-
|
105
|
-
/**
|
106
|
-
* @private
|
107
|
-
*/
|
108
|
-
_teardown() {
|
109
|
-
if (this.hasAwayValue) {
|
110
|
-
document.removeEventListener("click", this._awayHandler);
|
111
|
-
}
|
112
|
-
}
|
113
|
-
|
114
|
-
_didInitWithPromise(selector, shouldOpen) {
|
115
|
-
this._debug("selecting", selector, this._fetchElements(selector));
|
116
|
-
return this._fetchElements(selector).map((element) => {
|
117
|
-
return this._doInitTransition(element, shouldOpen);
|
118
|
-
});
|
119
|
-
}
|
120
|
-
|
121
|
-
/**
|
122
|
-
* @private
|
123
|
-
*/
|
124
|
-
_initCloseKeypressListener() {
|
125
|
-
if (this.hasHideKeysValue) {
|
126
|
-
document.addEventListener("keydown", (event) => {
|
127
|
-
if (!this.openValue) return;
|
128
|
-
if (!this.hideKeysValue.split(",").includes(event.key.toLowerCase())) {
|
129
|
-
return;
|
130
|
-
}
|
131
|
-
|
132
|
-
event.stopPropagation();
|
133
|
-
this.toggle(event);
|
134
|
-
});
|
135
|
-
}
|
136
|
-
}
|
137
|
-
|
138
|
-
/**
|
139
|
-
* @private
|
140
|
-
*/
|
141
|
-
_initToggleKeypressListener() {
|
142
|
-
if (this.hasToggleKeysValue) {
|
143
|
-
document.addEventListener("keydown", (event) => {
|
144
|
-
if (
|
145
|
-
!this.toggleKeysValue.split(",").includes(event.key.toLowerCase())
|
146
|
-
) {
|
147
|
-
return;
|
148
|
-
}
|
149
|
-
|
150
|
-
event.stopPropagation();
|
151
|
-
|
152
|
-
this.toggle(event);
|
153
|
-
});
|
154
|
-
}
|
155
|
-
}
|
156
|
-
|
157
|
-
/**
|
158
|
-
* @private
|
159
|
-
*/
|
160
|
-
_initShowKeypressListener() {
|
161
|
-
if (this.hasShowKeysValue) {
|
162
|
-
document.addEventListener("keydown", (event) => {
|
163
|
-
if (this.openValue) return;
|
164
|
-
if (!this.showKeysValue.split(",").includes(event.key.toLowerCase())) {
|
165
|
-
return;
|
166
|
-
}
|
167
|
-
|
168
|
-
event.stopPropagation();
|
169
|
-
|
170
|
-
this.toggle(event);
|
171
|
-
});
|
172
|
-
}
|
173
|
-
}
|
174
|
-
|
175
|
-
/**
|
176
|
-
* @private
|
177
|
-
*/
|
178
|
-
_awayHandler(event) {
|
179
|
-
if (!this.element.contains(event.target)) {
|
180
|
-
document.removeEventListener("click", this._awayHandler);
|
181
|
-
this.hide(event);
|
182
|
-
}
|
183
|
-
return true;
|
184
|
-
}
|
185
|
-
|
186
|
-
/**
|
187
|
-
* @private
|
188
|
-
* @param {DOMElement} target
|
189
|
-
* @param {boolean} openState
|
190
|
-
*/
|
191
|
-
_doInitTransition(target, openState) {
|
192
|
-
this._debug("init transition", `${openState ? "open" : "closed"}`, target);
|
193
|
-
this._debug(
|
194
|
-
"dispatching event",
|
195
|
-
`reveal:${openState ? "show" : "hide"}`,
|
196
|
-
target
|
197
|
-
);
|
198
|
-
target.dispatchEvent(
|
199
|
-
new Event(`reveal:${openState ? "show" : "hide"}`, {
|
200
|
-
bubbles: true,
|
201
|
-
cancelable: false,
|
202
|
-
})
|
203
|
-
);
|
204
|
-
|
205
|
-
return new Promise((resolve, reject) => {
|
206
|
-
if (
|
207
|
-
"transition" in target.dataset &&
|
208
|
-
this.element.offsetParent !== null
|
209
|
-
) {
|
210
|
-
requestAnimationFrame(() => {
|
211
|
-
this._transitionSetup(target, openState);
|
212
|
-
const _didEndTransition = this._didEndTransition.bind(this);
|
213
|
-
|
214
|
-
target.addEventListener(
|
215
|
-
"transitionend",
|
216
|
-
function _didEndTransitionHandler() {
|
217
|
-
_didEndTransition(target, openState);
|
218
|
-
target.removeEventListener(
|
219
|
-
"transitionend",
|
220
|
-
_didEndTransitionHandler
|
221
|
-
);
|
222
|
-
resolve();
|
223
|
-
}
|
224
|
-
);
|
225
|
-
|
226
|
-
requestAnimationFrame(() => {
|
227
|
-
this._doStartTransition(target, openState);
|
228
|
-
});
|
229
|
-
});
|
230
|
-
} else {
|
231
|
-
if (openState) {
|
232
|
-
this._debug(
|
233
|
-
"force hidden - init",
|
234
|
-
`${openState ? "open" : "closed"}`,
|
235
|
-
target
|
236
|
-
);
|
237
|
-
target.hidden = !target.hidden;
|
238
|
-
}
|
239
|
-
this._doCompleteTransition(target, openState);
|
240
|
-
resolve();
|
241
|
-
}
|
242
|
-
});
|
243
|
-
}
|
244
|
-
|
245
|
-
/**
|
246
|
-
* @private
|
247
|
-
* @param {DOMElement} target
|
248
|
-
*/
|
249
|
-
_doStartTransition(target, openState) {
|
250
|
-
this._debug("start transition", `${openState ? "open" : "closed"}`, target);
|
251
|
-
this.transitioningValue = true;
|
252
|
-
if (target.dataset.useTransitionClasses === "true") {
|
253
|
-
const transitionClasses = this._transitionClasses(
|
254
|
-
target,
|
255
|
-
this.transitionType
|
256
|
-
);
|
257
|
-
target.classList.add(...transitionClasses.end.split(" "));
|
258
|
-
target.classList.remove(...transitionClasses.start.split(" "));
|
259
|
-
} else {
|
260
|
-
const transitions = this._transitionDefaults(openState);
|
261
|
-
target.style.transformOrigin = transitions.origin;
|
262
|
-
target.style.transitionProperty = "opacity transform";
|
263
|
-
target.style.transitionDuration = `${transitions.duration / 1000}s`;
|
264
|
-
target.style.transitionTimingFunction = "cubic-bezier(0.4, 0.0, 0.2, 1)";
|
265
|
-
|
266
|
-
target.style.opacity = transitions.to.opacity;
|
267
|
-
target.style.transform = `scale(${transitions.to.scale / 100})`;
|
268
|
-
}
|
269
|
-
}
|
270
|
-
|
271
|
-
/**
|
272
|
-
* @private
|
273
|
-
* @param {DOMElement} target
|
274
|
-
* @param {boolean} openState
|
275
|
-
*/
|
276
|
-
_didEndTransition(target, openState) {
|
277
|
-
this._debug("end transition", `${openState ? "open" : "closed"}`, target);
|
278
|
-
if (target.dataset.useTransitionClasses === "true") {
|
279
|
-
const transitionClasses = this._transitionClasses(
|
280
|
-
target,
|
281
|
-
this.transitionType
|
282
|
-
);
|
283
|
-
target.classList.remove(...transitionClasses.before.split(" "));
|
284
|
-
} else {
|
285
|
-
target.style.opacity = target.dataset.opacityCache;
|
286
|
-
target.style.transform = target.dataset.transformCache;
|
287
|
-
target.style.transformOrigin = target.dataset.transformOriginCache;
|
288
|
-
}
|
289
|
-
this._doCompleteTransition(target, openState);
|
290
|
-
}
|
291
|
-
|
292
|
-
/**
|
293
|
-
* @private
|
294
|
-
* @param {DOMElement} target
|
295
|
-
* @param {boolean} openState
|
296
|
-
*/
|
297
|
-
_doCompleteTransition(target, openState) {
|
298
|
-
this._debug(
|
299
|
-
"complete transition",
|
300
|
-
`${openState ? "open" : "closed"}`,
|
301
|
-
target
|
302
|
-
);
|
303
|
-
this.transitioningValue = false;
|
304
|
-
|
305
|
-
if (!openState) {
|
306
|
-
this._debug(
|
307
|
-
"force hidden - complete",
|
308
|
-
`${openState ? "open" : "closed"}`,
|
309
|
-
target
|
310
|
-
);
|
311
|
-
target.hidden = !target.hidden;
|
312
|
-
}
|
313
|
-
this.openValue = openState;
|
314
|
-
|
315
|
-
this._debug(
|
316
|
-
"dispatching event",
|
317
|
-
`reveal:${openState ? "shown" : "hidden"}`,
|
318
|
-
target
|
319
|
-
);
|
320
|
-
target.dispatchEvent(
|
321
|
-
new Event(`reveal:${openState ? "shown" : "hidden"}`, {
|
322
|
-
bubbles: true,
|
323
|
-
cancelable: false,
|
324
|
-
})
|
325
|
-
);
|
326
|
-
|
327
|
-
if (this.hasAwayValue && openState) {
|
328
|
-
document.addEventListener("click", this._awayHandler);
|
329
|
-
}
|
330
|
-
|
331
|
-
this._debug("dispatching event", "reveal:complete", target);
|
332
|
-
target.dispatchEvent(
|
333
|
-
new Event("reveal:complete", { bubbles: true, cancelable: false })
|
334
|
-
);
|
335
|
-
}
|
336
|
-
|
337
|
-
/**
|
338
|
-
* @private
|
339
|
-
* @param {DOMElement} target
|
340
|
-
* @param {boolean} openState
|
341
|
-
*/
|
342
|
-
_transitionSetup(target, openState) {
|
343
|
-
this.transitionType = openState ? "transitionEnter" : "transitionLeave";
|
344
|
-
|
345
|
-
if (this.transitionType in target.dataset) {
|
346
|
-
target.dataset.useTransitionClasses = true;
|
347
|
-
const transitionClasses = this._transitionClasses(
|
348
|
-
target,
|
349
|
-
this.transitionType
|
350
|
-
);
|
351
|
-
target.classList.add(...transitionClasses.before.split(" "));
|
352
|
-
target.classList.add(...transitionClasses.start.split(" "));
|
353
|
-
} else {
|
354
|
-
target.dataset.useTransitionClasses = false;
|
355
|
-
const transitions = this._transitionDefaults(openState);
|
356
|
-
target.dataset.opacityCache = target.style.opacity;
|
357
|
-
target.dataset.transformCache = target.style.transform;
|
358
|
-
target.dataset.transformOriginCache = target.style.transformOrigin;
|
359
|
-
|
360
|
-
target.style.opacity = transitions.from.opacity;
|
361
|
-
target.style.transform = `scale(${transitions.from.scale / 100})`;
|
362
|
-
}
|
363
|
-
if (openState) {
|
364
|
-
this._debug("opening with transition", target);
|
365
|
-
target.hidden = !target.hidden;
|
366
|
-
}
|
367
|
-
}
|
368
|
-
|
369
|
-
/**
|
370
|
-
* @private
|
371
|
-
* @param {boolean} openState
|
372
|
-
*/
|
373
|
-
_transitionDefaults(openState) {
|
374
|
-
return {
|
375
|
-
duration: openState ? 200 : 150,
|
376
|
-
origin: "center",
|
377
|
-
from: {
|
378
|
-
opacity: openState ? 0 : 1,
|
379
|
-
scale: openState ? 95 : 100,
|
380
|
-
},
|
381
|
-
to: {
|
382
|
-
opacity: openState ? 1 : 0,
|
383
|
-
scale: openState ? 100 : 95,
|
384
|
-
},
|
385
|
-
};
|
386
|
-
}
|
387
|
-
|
388
|
-
/**
|
389
|
-
* @private
|
390
|
-
* @param {DOMElement} target
|
391
|
-
* @param {string} transitionType
|
392
|
-
*/
|
393
|
-
_transitionClasses(target, transitionType) {
|
394
|
-
return {
|
395
|
-
before: target.dataset[transitionType],
|
396
|
-
start: target.dataset[`${transitionType}Start`],
|
397
|
-
end: target.dataset[`${transitionType}End`],
|
398
|
-
};
|
399
|
-
}
|
400
|
-
|
401
|
-
/**
|
402
|
-
* @private
|
403
|
-
* @param {String} selector
|
404
|
-
*/
|
405
|
-
_fetchElements(selector) {
|
406
|
-
return [this.element, ...this.element.querySelectorAll(selector)].filter(
|
407
|
-
(el) => el.matches(selector)
|
408
|
-
);
|
409
|
-
}
|
410
|
-
|
411
|
-
/**
|
412
|
-
* @private
|
413
|
-
* @param {Array} args
|
414
|
-
*/
|
415
|
-
_debug(...args) {
|
416
|
-
if (this.debugValue) console.log(...args);
|
417
|
-
}
|
418
|
-
|
419
|
-
get selector() {
|
420
|
-
return this.hasTargetSelectorValue
|
421
|
-
? this.targetSelectorValue
|
422
|
-
: "[data-reveal]";
|
423
|
-
}
|
424
|
-
}
|
File without changes
|