spina 2.18.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.

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