spina 2.18.0 → 2.19.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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/assets/javascripts/spina/controllers/data_binding_controller.js +1 -0
  4. data/app/assets/javascripts/spina/controllers/page_select_controller.js +2 -37
  5. data/app/assets/javascripts/spina/controllers/reveal_controller.js +1 -1
  6. data/app/assets/javascripts/spina/controllers/select_controller.js +44 -0
  7. data/app/assets/javascripts/spina/libraries/stimulus-data-bindings@1.3.2.js +234 -0
  8. data/app/assets/javascripts/spina/libraries/stimulus-reveal@1.4.2.js +424 -0
  9. data/app/controllers/spina/admin/resource_select_options_controller.rb +20 -0
  10. data/app/models/spina/navigation_item.rb +5 -1
  11. data/app/models/spina/parts/image_variant.rb +3 -3
  12. data/app/models/spina/parts/page_link.rb +3 -2
  13. data/app/models/spina/parts/resource_link.rb +13 -0
  14. data/app/views/spina/admin/page_select_options/index.html.erb +3 -4
  15. data/app/views/spina/admin/page_select_options/show.html.erb +1 -1
  16. data/app/views/spina/admin/parts/page_links/_form.html.erb +17 -14
  17. data/app/views/spina/admin/parts/resource_links/_form.html.erb +32 -0
  18. data/app/views/spina/admin/resource_select_options/index.html.erb +13 -0
  19. data/app/views/spina/admin/resource_select_options/show.html.erb +3 -0
  20. data/config/locales/da.yml +418 -0
  21. data/config/locales/en.yml +8 -5
  22. data/config/routes.rb +4 -1
  23. data/lib/spina/engine.rb +2 -1
  24. data/lib/spina/theme.rb +8 -6
  25. data/lib/spina/version.rb +1 -1
  26. data/lib/tasks/tailwind.rake +12 -4
  27. metadata +25 -10
  28. data/app/assets/javascripts/spina/libraries/stimulus-reveal@1.2.4.js +0 -388
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a3f9ec18d2a07a7aedd9c210abd460a3e0098b92342087d4103a7315921692ad
4
- data.tar.gz: fa6abb6f02ec21bbe17d8f004dffe8154e36dc762be8e8d7f9c8284e4d33e5df
3
+ metadata.gz: '086e323d25a5656407e2aa82ddc38880377fbc9b17c133cf871960b93048dbe8'
4
+ data.tar.gz: db63f3e045d380e2e780123384458b256ed4d2a03c0eaf55ad84ff2f500f4565
5
5
  SHA512:
6
- metadata.gz: a40c89a073e8b3355cd7b36269c1406b8a05ddd7e8d7ff6d7aa143e324daaf6b575fe673e730217eee7bc8b5219bbbd8bc3b36e64c32eb25f9c302d3cc8f1fb4
7
- data.tar.gz: 5269cfc9d62772413beb28f28f49045ec845695308a65a72b0f000408694bce3fc3c7b4345e61cca33bce02ed17f3198c7d841410a41343eed8f162563e789fa
6
+ metadata.gz: e65a55997c54d92fef58a5e69eb27f5f2e2e642ea0070862b65a718d63e61cfc53622c1c62a2e4d187f9511a8b8adea77f1ae4308447943e17b4ba6c35e73a67
7
+ data.tar.gz: b42c96e25a501760af09946823032406af80a47b7af0842efc42c0ebdc29d8ed92707b03bd2fbc2484dd6154e2186d0e72880b5ac4dbb41e781189d6a4b95d5f
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <img src="https://spinacms.com/spinacms.png" alt="Spina CMS" width="225"/>
2
2
 
3
- [Spina CMS](https://spinacms.com) is an easy to use CMS that features a clean interface without distractions. [Live demo](http://spinacms-demo.herokuapp.com/admin/pages)
3
+ [Spina CMS](https://spinacms.com) is an easy to use CMS that features a clean interface without distractions.
4
4
 
5
5
  [![Ruby](https://github.com/SpinaCMS/Spina/actions/workflows/ruby.yml/badge.svg)](https://github.com/SpinaCMS/Spina/actions/workflows/ruby.yml)
6
6
  [![Code Climate](https://codeclimate.com/github/SpinaCMS/Spina/badges/gpa.svg)](https://codeclimate.com/github/SpinaCMS/Spina)
@@ -0,0 +1 @@
1
+ //= require ../libraries/stimulus-data-bindings@1.3.2.js
@@ -1,38 +1,3 @@
1
- import { Controller } from "@hotwired/stimulus"
1
+ import SelectController from 'controllers/select_controller'
2
2
 
3
- export default class extends Controller {
4
-
5
- static get targets() {
6
- return ['input', 'label', 'search']
7
- }
8
-
9
- connect() {
10
- // Show placeholder if there is no page selected yet
11
- if (this.labelTarget.querySelector("turbo-frame") == undefined) {
12
- this.clear()
13
- }
14
- }
15
-
16
- select(event) {
17
- let button = event.currentTarget
18
-
19
- this.inputTarget.value = button.dataset.id
20
- this.labelTarget.innerText = button.dataset.title
21
- }
22
-
23
- clear() {
24
- this.inputTarget.value = ""
25
- this.labelTarget.innerHTML = `
26
- <span class="text-gray-400">
27
- ${this.element.dataset.placeholder}
28
- </span>
29
- `
30
- }
31
-
32
- autofocus() {
33
- setTimeout(function() {
34
- this.searchTarget.focus()
35
- }.bind(this), 100)
36
- }
37
-
38
- }
3
+ export default class extends SelectController { }
@@ -1 +1 @@
1
- //= require ../libraries/stimulus-reveal@1.2.4.js
1
+ //= require ../libraries/stimulus-reveal@1.4.2.js
@@ -0,0 +1,44 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+
5
+ static get targets() {
6
+ return ['input', 'label', 'search']
7
+ }
8
+
9
+ connect() {
10
+ // Show placeholder if there is no page selected yet
11
+ if (this.labelTarget.querySelector("turbo-frame") == undefined) {
12
+ this.clear()
13
+ }
14
+ }
15
+
16
+ select(event) {
17
+ let button = event.currentTarget
18
+
19
+ this.inputTarget.value = button.dataset.id
20
+ this.labelTarget.innerText = button.dataset.title
21
+ this._dispatchChange()
22
+ }
23
+
24
+ clear() {
25
+ this.inputTarget.value = ""
26
+ this.labelTarget.innerHTML = `
27
+ <span class="text-gray-400">
28
+ ${this.element.dataset.placeholder}
29
+ </span>
30
+ `
31
+ this._dispatchChange()
32
+ }
33
+
34
+ autofocus() {
35
+ setTimeout(function () {
36
+ this.searchTarget.focus()
37
+ }.bind(this), 100)
38
+ }
39
+
40
+ _dispatchChange() {
41
+ this.inputTarget.dispatchEvent(new Event('change', { bubbles: true, cancelable: false }))
42
+ }
43
+
44
+ }
@@ -0,0 +1,234 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ /**
4
+ * One way data and visibility bindings for inputs
5
+ * @extends Controller
6
+ */
7
+ export default class DataBindingController extends Controller {
8
+ /**
9
+ * Initialize bindings on connection to the DOM
10
+ */
11
+ connect() {
12
+ if (this.element.dataset.bindingDebug === "true") {
13
+ this.debugMode = true
14
+ }
15
+
16
+ this._debug("stimulus-data-binding: connecting to wrapper:", this.element)
17
+
18
+ const sourceElements = Array.from(this.element.querySelectorAll('[data-binding-target]'))
19
+ if (this.element.dataset.bindingTarget) sourceElements.unshift(this.element)
20
+
21
+ if (sourceElements.length === 0) this._debug("No source elements found. Did you set data-binding-target on your source elements?")
22
+
23
+ for (const sourceElement of sourceElements) {
24
+ if (this.debugMode) console.group("stimulus-data-binding: Source element")
25
+ this._debug("Source element found", sourceElement)
26
+
27
+ if (sourceElement.dataset.bindingInitial !== 'false') {
28
+ this._debug("Running initial binding on source element")
29
+ this._runBindings(sourceElement)
30
+ } else {
31
+ this._debug("%cNot running initial binding on source element as binding-initial is set to false", "color: rgba(150,150,150,0.8);")
32
+ }
33
+
34
+ if (this.debugMode) console.groupEnd()
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Updates bindings for the current element.
40
+ * @param {Event} e - an event with a currentTarget DOMElement
41
+ */
42
+ update(e) {
43
+ this._runBindings(e.currentTarget)
44
+ }
45
+
46
+ /**
47
+ * @private
48
+ * @param {DOMElement} source
49
+ */
50
+ _runBindings(source) {
51
+ this._debug("Searching for targets for source: ", source)
52
+ for (const targetRef of source.dataset.bindingTarget.split(' ')) {
53
+ const targetElements = this._bindingElements(targetRef)
54
+
55
+ if (targetElements.length === 0) this._debug(`Could not find any target elements for ref ${targetRef}. Have you set data-target-ref="${targetRef}" on your target elements?`)
56
+
57
+ for (const target of targetElements) {
58
+ if (this.debugMode) console.group("stimulus-data-binding: Target Element")
59
+ this._debug("Target found. Running bindings for target: ", target)
60
+
61
+ const bindingCondition = this._getDatum('bindingCondition', source, target)
62
+
63
+ if (bindingCondition) {
64
+ this._debug(`Evaluating binding condition: '${bindingCondition}'`)
65
+ } else {
66
+ this._debug(`%cNo binding condition set. Evaluating as true. To add a condition set 'data-binding-condition="..."'`, "color: rgba(150,150,150,0.8);")
67
+ }
68
+
69
+ const conditionPassed = this._evaluate(
70
+ bindingCondition,
71
+ {
72
+ source,
73
+ target
74
+ }
75
+ )
76
+
77
+ if (conditionPassed) {
78
+ this._debug(`Condition evaluated to: `, conditionPassed)
79
+ } else {
80
+ this._debug(`Condition evaluated to: `, conditionPassed)
81
+ }
82
+
83
+ const bindingValue = this._getDatum('bindingValue', source, target)
84
+
85
+ if (bindingValue) {
86
+ this._debug(`Evaluating binding value: '${bindingValue}'`)
87
+ } else {
88
+ this._debug(`%cNo binding value set, evaluating as true. to set a value for the attribute / property on your target elements set 'data-binding-value="..."'`, "color: rgba(150,150,150,0.8);")
89
+ }
90
+
91
+ const value = this._evaluate(
92
+ bindingValue,
93
+ {
94
+ source,
95
+ target
96
+ }
97
+ )
98
+
99
+ this._debug(`Value evaluated to: '${value}'`)
100
+
101
+ const bindingAttribute = this._getDatum(
102
+ 'bindingAttribute',
103
+ source,
104
+ target
105
+ )
106
+
107
+ if (!bindingAttribute) {
108
+ this._debug(`%cNo binding attribute set. To add attributes to your target element set 'data-binding-attribute="..."'`, "color: rgba(150,150,150,0.8);")
109
+ }
110
+
111
+ if (bindingAttribute) {
112
+ for (const attribute of bindingAttribute.split(' ')) {
113
+ if (conditionPassed) {
114
+ this._debug(`Condition passed so setting attribute '${attribute}' to '${value}'`)
115
+ target.setAttribute(attribute, value)
116
+ } else {
117
+ this._debug(`Condition failed so removing attribute '${attribute}'`)
118
+ target.removeAttribute(attribute)
119
+ }
120
+ }
121
+ }
122
+
123
+ const bindingProperty = this._getDatum(
124
+ 'bindingProperty',
125
+ source,
126
+ target
127
+ )
128
+
129
+ if (!bindingProperty) {
130
+ this._debug(`%cNo binding property set. To add properties to your target element set 'data-binding-property="..."'`, "color: rgba(150,150,150,0.8);")
131
+ }
132
+
133
+ if (bindingProperty) {
134
+ for (const prop of bindingProperty.split(' ')) {
135
+ const propertyValue = conditionPassed ? value : ''
136
+ if (target[prop] != propertyValue) { target.dataset.hasChanged = true }
137
+
138
+ if (conditionPassed) {
139
+ this._debug(`Condition passed so setting property '${prop}' from ${target[prop]} to '${value}'`)
140
+ } else {
141
+ this._debug(`Condition failed so setting property '${prop}' from ${target[prop]} to '' (empty string)`)
142
+ }
143
+
144
+ target[prop] = propertyValue
145
+ }
146
+ }
147
+
148
+ const bindingClass = this._getDatum(
149
+ 'bindingClass',
150
+ source,
151
+ target
152
+ )
153
+
154
+ if (!bindingClass) {
155
+ this._debug(`%cNo binding class set. To add classes to your target element set 'data-binding-class="..."'`, "color: rgba(150,150,150,0.8);")
156
+ }
157
+
158
+ if (bindingClass) {
159
+ for (const klass of bindingClass.split(' ')) {
160
+ if (conditionPassed) {
161
+ this._debug(`Condition passed so adding class '${klass}'`)
162
+ target.classList.add(klass)
163
+ } else {
164
+ this._debug(`Condition failed so removing class '${klass}'`)
165
+ target.classList.remove(klass)
166
+ }
167
+ }
168
+ }
169
+
170
+ const bindingEvent = this._getDatum('bindingEvent', source, target)
171
+
172
+ if (!bindingEvent) {
173
+ this._debug(`%cNo binding event set. To dispatch events on property change to your target element set 'data-binding-event="..."'`, "color: rgba(150,150,150,0.8);")
174
+ }
175
+
176
+ if (bindingEvent) {
177
+ for (const event of bindingEvent.split(' ')) {
178
+ if (target.dataset.hasChanged) {
179
+ this._debug(`Target has changed so dispatching event ${event}`)
180
+ target.dispatchEvent(new Event(event, { cancelable: true, bubbles: true }))
181
+ delete target.dataset.hasChanged
182
+ } else {
183
+ this._debug(`No changes to target properties so not dispatching '${event}'`)
184
+ }
185
+ }
186
+ }
187
+
188
+ if (this.debugMode) console.groupEnd()
189
+ }
190
+ }
191
+ }
192
+
193
+ /**
194
+ * @private
195
+ * @param {String} name - the name of the binding reference
196
+ */
197
+ _bindingElements(name) {
198
+ return this.element.querySelectorAll(`[data-binding-ref="${name}"]`)
199
+ }
200
+
201
+ /**
202
+ * @private
203
+ * @param {String} attribute - the attribute to fetch from the source / target dataaset
204
+ * @param {String} source - The source element to get the attribute from, only loads if target doesnt have it
205
+ * @param {String} target - The target element to get the attribute from, has precedence over the source
206
+ */
207
+ _getDatum(attribute, source, target) {
208
+ return target.dataset[attribute] || source.dataset[attribute]
209
+ }
210
+
211
+ /**
212
+ * @private
213
+ * @param {String} expression - expression to safe eval
214
+ * @param {Object} variables - variables to be present when evaluating the given expression
215
+ */
216
+ _evaluate(expression, variables = {}) {
217
+ if (!expression) return true
218
+ return new Function(
219
+ Object.keys(variables).map((v) => `$${v}`),
220
+ `return ${expression.trim()}`
221
+ )(...Object.values(variables))
222
+ }
223
+
224
+ /**
225
+ * @private
226
+ * @param {String} expression - expression to safe eval
227
+ * @param {Object} variables - variables to be present when evaluating the given expression
228
+ */
229
+ _debug(...args) {
230
+ if (this.debugMode) {
231
+ console.log(...args)
232
+ }
233
+ }
234
+ }