satis 1.0.67 → 1.0.68

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e05287afa653b87170ae0c2ebccac7548323106ff6fb631a6126ada2b84a554
4
- data.tar.gz: 9e6c4450b2d7b7e0cca1ef42e2076eec116be0b871e0697c8fcf544c9adcb8a9
3
+ metadata.gz: a777d25ac010446a7e69a55f9d0468ce45a462e7d69df395673f3e1d7ad40e84
4
+ data.tar.gz: 1857b13676a3952e4afcc709df9eacfbea2d97c475837676ba9a4141ede7c2da
5
5
  SHA512:
6
- metadata.gz: 4a3d17b84eb5dd0fdc1b84bcc316a771355041a3d6de1463ce420bb4b757e0bc8b97c2f476fa416aff09d999e1a73fc0d4eda12c200b0a4905dc2a5b40dba9d9
7
- data.tar.gz: d67f4cafd6e6b07c92cb626c3c95d843c1e02f8b4af4136c1cf462a1a3ff3fd15bb54340caa060750ff47ca2351c4cb64443b18cac61cd17e9cde5183de77b89
6
+ metadata.gz: 8b739ca26a38df0d714339b0c09ad14ed5c25b0a352fd4a0a8a6a8c26292e314da3388d81f0a63d92a39c7808d4d2c9a2c20837579fcd2256b50aec4ad8f41ea
7
+ data.tar.gz: 98ec19c8bfb70f0ce66875b9ad15a2ca3233e1b576971a5cce7c4bd7f7eb1ddff9dedf14a3d7b93ca92ff85f1441f1dec6e995f5889e69be022da1063658ec87
@@ -7,6 +7,9 @@ module Satis
7
7
 
8
8
  attr_accessor :original_view_context
9
9
 
10
+ delegate :add_helper, to: :class
11
+
12
+
10
13
  #
11
14
  # This provides us with a translation helper which scopes into the original view
12
15
  # and thereby conveniently scopes the translations.
@@ -46,5 +49,29 @@ module Satis
46
49
  def i18n_scope
47
50
  self.class.name.split('::').second.underscore.to_sym
48
51
  end
52
+
53
+ def component_name
54
+ self.class.name.sub(/::Component$/, "").sub(/^Satis::/, "").underscore
55
+ end
56
+
57
+ def self.add_helper(name, component)
58
+ if respond_to?(name)
59
+ Satis.config.logger.warn("Helper #{name} already defined, skipping.")
60
+ return
61
+ end
62
+ define_method(name) do |*args, **kwargs, &block|
63
+ original_args = args.dup
64
+ options = args.extract_options!
65
+ instance = if options.key? :variant
66
+ variant_component = component.to_s.sub(/::Component$/, "::#{options[:variant].to_s.camelize}::Component").safe_constantize
67
+ (variant_component || component).new(*original_args, **kwargs)
68
+ else
69
+ kwargs[component_name.to_sym] = self
70
+ component.new(*original_args, **kwargs)
71
+ end
72
+ original_view_context.render(instance, &block)
73
+ end
74
+ end
75
+
49
76
  end
50
77
  end
@@ -1,4 +1,4 @@
1
- .sts-card.sts-tabs data-controller="satis-tabs" data-satis-tabs-persist-value="false"
1
+ .sts-card.sts-tabs data-controller="satis-tabs" data-satis-tabs-persist-value="false" data-satis-tabs-key-value="#{key}"
2
2
  - if header?
3
3
  .sts-card__header class="#{tabs? ? '' : 'border-b border-gray-200'} #{header_background_color[:light]} dark:#{header_background_color[:dark]}"
4
4
  .-ml-4.-mt-4.flex.justify-between.items-center.flex-wrap.sm:flex-nowrap
@@ -33,19 +33,27 @@
33
33
  option selected=tab.selected? = t(tab.name, scope: [:tabs])
34
34
  .hidden.sm:block
35
35
  nav.-mb-px.flex.space-x-8.overflow-x-auto aria-label="Tabs"
36
- - tabs.each do |tab|
37
- a.tab id="#{tab.name}" href="#" aria-current="#{tab.selected? ? "page" : ''}" data-satis-tabs-target="tab" data-action="click->satis-tabs#select"
36
+ - tabs.each_with_index do |tab, index|
37
+ - id = tab.id.present? ? tab.id : tab.name
38
+ .flex
39
+ a.tab id="#{id}" class="#{index == (tab.selected_tab_index) ? 'selected' : ''}" href="#" aria-current="#{tab.selected? ? "page" : ''}" data-satis-tabs-target="tab" data-action="click->satis-tabs#select"
38
40
  - if tab.icon
39
41
  i.mr-2 class=tab.icon
40
- = t(tab.name, scope: [:tabs], default: tab.title || tab.name)
42
+ span id="tab_label_#{tab.id}"
43
+ = t(tab.name, scope: [:tabs], default: tab.title || tab.name)
41
44
  i.fal.fa-triangle-exclamation.ml-2.hidden
42
45
  - if tab.badge
43
- span.badge
46
+ span.badge id="tab_badge_#{tab.id}"
44
47
  = tab.badge
48
+ - if tab.tab_menu
49
+ = render(Satis::Menu::Component.new(tab.tab_menu, icon: 'fa-thin fa-square-chevron-down'))
50
+ - if custom_tabs_link_html.present?
51
+ = custom_tabs_link_html
45
52
 
46
- - tabs.each do |tab|
47
- div id="#{tab.name}-content" class="tab-content #{tab.options[:padding] == true ? 'px-6 py-6' : ''}" data-satis-tabs-target="content"
53
+ - tabs.each_with_index do |tab, index|
54
+ div id="#{tab.name}-content" class="tab-content #{tab.options[:padding] == true ? 'px-6 py-6' : ''} #{index == (tab.selected_tab_index) ? 'selected' : ''}" data-satis-tabs-target="content"
48
55
  = tab.to_s
56
+
49
57
  - else
50
58
  div class="#{content_padding ? 'px-6 py-6' : ''}"
51
59
  = content
@@ -7,7 +7,7 @@ module Satis
7
7
  renders_many :tabs, Tab::Component
8
8
  renders_one :footer
9
9
 
10
- attr_reader :icon, :title, :description, :menu, :content_padding, :header_background_color, :initial_actions
10
+ attr_reader :icon, :title, :description, :menu, :content_padding, :header_background_color, :initial_actions, :key, :custom_tabs_link_html
11
11
 
12
12
  def initialize(icon: nil,
13
13
  title: nil,
@@ -17,7 +17,8 @@ module Satis
17
17
  header_background_color: {
18
18
  dark: 'bg-gray-800', light: 'bg-white'
19
19
  },
20
- actions: [])
20
+ actions: [],
21
+ key: nil)
21
22
  super
22
23
  @title = title
23
24
  @title = @title.reject(&:blank?).compact.join(' ') if @title.is_a?(Array)
@@ -27,6 +28,12 @@ module Satis
27
28
  @content_padding = content_padding
28
29
  @header_background_color = header_background_color
29
30
  @initial_actions = actions
31
+ @key = key
32
+ end
33
+
34
+ def custom_tabs_link(&block)
35
+ return unless block_given?
36
+ @custom_tabs_link_html = block.call.html_safe
30
37
  end
31
38
 
32
39
  def tabs?
@@ -27,8 +27,8 @@ module Satis
27
27
  options[:input_html].delete(:autofocus)
28
28
  end
29
29
 
30
- actions = [options[:input_html]['data-action'], 'change->satis-dropdown#display',
31
- 'focus->satis-dropdown#focus'].join(' ')
30
+ actions = [options[:input_html]['data-action'], 'change->satis-dropdown#display',
31
+ 'focus->satis-dropdown#focus'].join(' ') unless options[:input_html]['data-reflex']
32
32
 
33
33
  options[:input_html].merge!('data-satis-dropdown-target' => 'hiddenInput',
34
34
  'data-action' => actions)
@@ -5,6 +5,16 @@
5
5
  }
6
6
 
7
7
  input {
8
- @apply dark:text-gray-300
8
+ @apply dark:text-gray-300;
9
+
10
+ }
11
+
12
+ .warning
13
+ {
14
+ @apply border-orange-500;
15
+ input {
16
+ @apply text-gray-400;
17
+
18
+ }
9
19
  }
10
20
  }
@@ -184,6 +184,12 @@ export default class extends ApplicationController {
184
184
  } else {
185
185
  this.debouncedLocalResults(event)
186
186
  }
187
+
188
+ if (this.searchInputTarget.value) {
189
+ this.searchInputTarget.closest(".bg-white").classList.add("warning")
190
+ } else {
191
+ this.searchInputTarget.closest(".bg-white").classList.remove("warning")
192
+ }
187
193
  }
188
194
 
189
195
  // User presses reset button
@@ -211,6 +217,10 @@ export default class extends ApplicationController {
211
217
  event.preventDefault()
212
218
  }
213
219
 
220
+ if (this.searchInputTarget.closest(".bg-white").classList.contains("warning")) {
221
+ this.searchInputTarget.closest(".bg-white").classList.remove("warning")
222
+ }
223
+
214
224
  return false
215
225
  }
216
226
 
@@ -245,6 +255,10 @@ export default class extends ApplicationController {
245
255
  this.lastSearch = this.searchInputTarget.value
246
256
 
247
257
  this.hiddenInputTarget.dispatchEvent(new Event("change"))
258
+
259
+ if (this.searchInputTarget.closest(".bg-white").classList.contains("warning")) {
260
+ this.searchInputTarget.closest(".bg-white").classList.remove("warning")
261
+ }
248
262
  }
249
263
 
250
264
  // --- Helpers
@@ -257,11 +271,17 @@ export default class extends ApplicationController {
257
271
  this.searchInputTarget.value = currentItem.getAttribute("data-satis-dropdown-item-text")
258
272
 
259
273
  Array.prototype.slice.call(currentItem.attributes).forEach((attr) => {
260
- if (attr.name.startsWith("data") && !attr.name.startsWith("data-satis") && !attr.name.startsWith("data-action")) {
274
+ if (
275
+ attr.name.startsWith("data") &&
276
+ !attr.name.startsWith("data-satis") &&
277
+ !attr.name.startsWith("data-action")
278
+ ) {
261
279
  this.hiddenInputTarget.setAttribute(attr.name, attr.value)
262
280
  }
263
281
  })
264
- this.hiddenInputTarget.dispatchEvent(new CustomEvent("change", { detail: { src: "satis-dropdown" } }))
282
+ if (!this.hiddenInputTarget.getAttribute("data-reflex")) {
283
+ this.hiddenInputTarget.dispatchEvent(new CustomEvent("change", { detail: { src: "satis-dropdown" } }))
284
+ }
265
285
  }
266
286
  }
267
287
 
@@ -362,7 +382,10 @@ export default class extends ApplicationController {
362
382
  this.hiddenInputTarget.value = this.lastSearch
363
383
  }
364
384
 
365
- if (matches.length == 1 && matches[0].getAttribute("data-satis-dropdown-item-text").toLowerCase().indexOf(this.lastSearch.toLowerCase()) >= 0) {
385
+ if (
386
+ matches.length == 1 &&
387
+ matches[0].getAttribute("data-satis-dropdown-item-text").toLowerCase().indexOf(this.lastSearch.toLowerCase()) >= 0
388
+ ) {
366
389
  this.selectItem(matches[0].closest('[data-satis-dropdown-target="item"]'))
367
390
  } else if (matches.length > 1) {
368
391
  this.showResultsList(event)
@@ -372,7 +395,11 @@ export default class extends ApplicationController {
372
395
  // Remote search
373
396
  fetchResults(event) {
374
397
  const promise = new Promise((resolve, reject) => {
375
- if ((this.searchInputTarget.value == this.lastSearch && (this.currentPage == this.lastPage || this.currentPage == this.endPage)) || !this.hasUrlValue) {
398
+ if (
399
+ (this.searchInputTarget.value == this.lastSearch &&
400
+ (this.currentPage == this.lastPage || this.currentPage == this.endPage)) ||
401
+ !this.hasUrlValue
402
+ ) {
376
403
  return
377
404
  }
378
405
 
@@ -401,7 +428,13 @@ export default class extends ApplicationController {
401
428
  this.highLightSelected()
402
429
  this.showResultsList()
403
430
 
404
- if (this.nrOfItems == 1 && this.itemTargets[0].getAttribute("data-satis-dropdown-item-text").toLowerCase().indexOf(this.searchInputTarget.value.toLowerCase()) >= 0) {
431
+ if (
432
+ this.nrOfItems == 1 &&
433
+ this.itemTargets[0]
434
+ .getAttribute("data-satis-dropdown-item-text")
435
+ .toLowerCase()
436
+ .indexOf(this.searchInputTarget.value.toLowerCase()) >= 0
437
+ ) {
405
438
  this.selectItem(this.itemTargets[0].closest('[data-satis-dropdown-target="item"]'))
406
439
  } else if (this.nrOfItems == 1) {
407
440
  this.moveDown()
@@ -6,8 +6,8 @@ module Satis
6
6
  attr_reader :classes
7
7
 
8
8
  def initialize(classes: nil, colored: true)
9
- @classes = classes || ''
10
- @classes += ' colored' if colored
9
+ @classes = classes || ""
10
+ @classes += " colored" if colored
11
11
  end
12
12
  end
13
13
 
@@ -27,11 +27,11 @@ module Satis
27
27
  end
28
28
 
29
29
  def input_class
30
- [@options.fetch(:input_html, {}).fetch(:class, ''), 'sts-input__input', form.has_error?(attribute) && 'is-invalid'].join(' ')
30
+ [@options.fetch(:input_html, {}).fetch(:class, ""), "sts-input__input", form.has_error?(attribute) ? "is-invalid" : ""].join(" ")
31
31
  end
32
32
 
33
33
  def input_container_class
34
- form.has_error?(attribute) && 'is-invalid'
34
+ form.has_error?(attribute) && "is-invalid"
35
35
  end
36
36
  end
37
37
  end
@@ -20,6 +20,14 @@
20
20
  &:focus {
21
21
  box-shadow: none;
22
22
  }
23
+
24
+ &.white {
25
+ background: rgb(255, 255, 255);
26
+
27
+ .dark & {
28
+ background: rgba(255, 255, 255, 0.1);
29
+ }
30
+ }
23
31
  }
24
32
 
25
33
  &__element {
@@ -33,6 +41,14 @@
33
41
  }
34
42
  }
35
43
 
44
+ &.white {
45
+ background: rgb(255, 255, 255);
46
+
47
+ .dark & {
48
+ background: rgba(255, 255, 255, 0.1);
49
+ }
50
+ }
51
+
36
52
  input {
37
53
  @apply -ml-3 -mr-3 -mt-2 -mb-2 w-full;
38
54
 
@@ -1,9 +1,9 @@
1
- div data-controller="satis-menu" data-action="mouseover->satis-menu#show mouseleave->satis-menu#hide"
1
+ div data-controller="satis-menu" data-action="#{menu.event}->satis-menu#show mouseleave->satis-menu#hide"
2
2
  - if content
3
3
  = content
4
4
  - else
5
5
  button.inline-flex.items-center.justify-center.h-8.w-8.rounded-full.focus:outline-none.focus:ring-2.focus:ring-offset-2.focus:ring-primary-500.dark:text-gray-500 aria-expanded="false" aria-haspopup="true" type="button"
6
- span.font-semibold.flex-1 class="#{menu.items.present? ? '' : 'text-gray-200'}"
6
+ span.font-semibold.flex-1 id="#{icon_id}" class="#{menu.items.present? ? '' : 'text-gray-200'}"
7
7
  i class=icon
8
8
 
9
9
  - if menu.items.present?
@@ -4,12 +4,13 @@ module Satis
4
4
  module Menu
5
5
  class Component < Satis::ApplicationComponent
6
6
  # renders_many :tabs, Tab::Component
7
- attr_reader :menu, :icon
7
+ attr_reader :menu, :icon, :icon_id
8
8
 
9
- def initialize(menu, icon: nil)
9
+ def initialize(menu, icon: nil, icon_id: nil)
10
10
  super
11
11
  @menu = menu
12
12
  @icon = icon || 'fa-solid fa-ellipsis-vertical'
13
+ @icon_id = icon_id
13
14
  end
14
15
  end
15
16
  end
@@ -1,9 +1,13 @@
1
1
  li.rounded-sm.px-3.py-1.hover:bg-gray-100.dark:hover:bg-gray-200.flex.items-center data-controller="satis-menu" data-action="mouseover->satis-menu#show mouseleave->satis-menu#hide"
2
2
  a.cursor-pointer.py-1.w-full.text-left.flex.items-center.outline-none.focus:outline-none href=item.link *item.link_attributes
3
+ - if item.type == :custom
4
+ = render(partial: item.link_attributes[:data][:path], locals: item.link_attributes[:data][:parameters])
3
5
  span.pr-1.flex-shrink-0.w-6
4
6
  - if item.icon.present?
5
7
  i class=item.icon
6
- span.pr-1.flex-1 = item.label
8
+ span.pr-1.flex-1
9
+ - unless item.type == :custom
10
+ = item.label
7
11
  span.pr-1.flex-shrink-0.w-6
8
12
  - if item.type == :toggle
9
13
  i.fal.fa-check.hidden data-satis-menu-target="toggle" id="#{item.id}"
@@ -3,7 +3,7 @@
3
3
  module Satis
4
4
  module Tab
5
5
  class Component < Satis::ApplicationComponent
6
- attr_reader :options, :name, :icon, :badge
6
+ attr_reader :options, :name, :icon, :badge, :id, :tab_menu, :selected_tab_index
7
7
 
8
8
  def initialize(name, *args, &block)
9
9
  super
@@ -11,8 +11,11 @@ module Satis
11
11
  @options = args.extract_options!
12
12
  @args = args
13
13
  @icon = options[:icon]
14
+ @id = options[:id]
14
15
  @badge = options[:badge]
16
+ @tab_menu = options[:tab_menu]
15
17
  @block = block
18
+ @selected_tab_index = options[:selected_tab_index]
16
19
  end
17
20
 
18
21
  def responsive?
@@ -1,4 +1,4 @@
1
- .sts-tabs id="#{group}" data-controller="satis-tabs" data-satis-tabs-persist-value="#{persist}"
1
+ .sts-tabs id="#{group}" data-controller="satis-tabs" data-satis-tabs-persist-value="#{persist}" data-satis-tabs-key-value="#{key}"
2
2
  .sm:hidden
3
3
  label.sr-only for="tabs" Select a tab
4
4
  select#tabs.block.w-full.pl-3.pr-10.py-2.text-base.border-gray-300.focus:outline-none.focus:ring-primary-500.focus:border-primary-500.sm:text-sm.rounded-md name="tabs" data-action="change->satis-tabs#select" data-satis-tabs-target="select"
@@ -20,4 +20,4 @@
20
20
  div
21
21
  - tabs.each do |tab|
22
22
  div id="#{tab.name}-content" class="tab-content #{tab.options[:padding] == false ? '' : 'mt-4'}" data-satis-tabs-target="content"
23
- = tab.to_s
23
+ = tab.to_s
@@ -4,12 +4,13 @@ module Satis
4
4
  module Tabs
5
5
  class Component < Satis::ApplicationComponent
6
6
  renders_many :tabs, Tab::Component
7
- attr_reader :group, :persist
7
+ attr_reader :group, :persist, :key
8
8
 
9
- def initialize(group: :main, persist: false)
9
+ def initialize(group: :main, persist: false, key: nil)
10
10
  super
11
11
  @group = group
12
12
  @persist = persist
13
+ @key = key
13
14
  end
14
15
  end
15
16
  end
@@ -5,7 +5,7 @@ import ApplicationController from "../../../../frontend/controllers/application_
5
5
  */
6
6
  export default class extends ApplicationController {
7
7
  static targets = ["tab", "content", "select"]
8
- static values = { persist: Boolean }
8
+ static values = { persist: Boolean, key: String }
9
9
 
10
10
  static keyBindings = [
11
11
  {
@@ -23,9 +23,6 @@ export default class extends ApplicationController {
23
23
  connect() {
24
24
  super.connect()
25
25
 
26
- const ourUrl = new URL(window.location.href)
27
- this.keyBase = ourUrl.pathname.substring(1, ourUrl.pathname.length).replace(/\//, "_") + "_tabs_" + this.context.scope.element.id
28
-
29
26
  let firstErrorIndex
30
27
  this.tabTargets.forEach((tab, index) => {
31
28
  let hasErrors = this.contentTargets[index].querySelectorAll(".is-invalid")
@@ -37,7 +34,13 @@ export default class extends ApplicationController {
37
34
  }
38
35
  })
39
36
 
40
- this.open(firstErrorIndex || this.tabToOpen())
37
+ if (this.keyValue) {
38
+ this.getUserData(this.keyValue).then((data) => {
39
+ this.open(firstErrorIndex || data?.tab_index || 0)
40
+ })
41
+ } else {
42
+ this.open(firstErrorIndex || 0)
43
+ }
41
44
  }
42
45
 
43
46
  select(event) {
@@ -51,7 +54,12 @@ export default class extends ApplicationController {
51
54
  })
52
55
  }
53
56
  this.open(index)
54
- this.storeValue("openTab", index)
57
+
58
+ if (this.keyValue) {
59
+ this.setUserData(this.keyValue, { tab_index: index }).then((data) => {
60
+ //console.log(data)
61
+ })
62
+ }
55
63
 
56
64
  // Cancel the this event (dont show the browser context menu)
57
65
  event.preventDefault()
@@ -75,49 +83,5 @@ export default class extends ApplicationController {
75
83
  this.selectTarget.selectedIndex = index
76
84
  }
77
85
 
78
- storeValue(key, value) {
79
- if (!this.persistValue) {
80
- return
81
- }
82
-
83
- if (typeof Storage !== "undefined") {
84
- sessionStorage.setItem(this.keyBase + "_" + key, value)
85
- }
86
- }
87
-
88
- getValue(key) {
89
- if (!this.persistValue) {
90
- return
91
- }
92
-
93
- if (typeof Storage !== "undefined") {
94
- return sessionStorage.getItem(this.keyBase + "_" + key)
95
- }
96
- }
97
-
98
86
  disconnect() {}
99
-
100
- tabToOpen() {
101
- let urlValue = this.getUrlVar(this.context.scope.element.id + "Tab")
102
-
103
- if (typeof urlValue !== "undefined") {
104
- return urlValue
105
- }
106
-
107
- return this.getValue("openTab") || 0
108
- }
109
-
110
- getUrlVar(name) {
111
- return this.getUrlVars()[name]
112
- }
113
-
114
- getUrlVars() {
115
- let vars = {}
116
-
117
- window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) {
118
- vars[key] = value
119
- })
120
-
121
- return vars
122
- }
123
87
  }
@@ -0,0 +1,30 @@
1
+ module Satis
2
+ class UserDataController < ApplicationController
3
+ def show
4
+ key = request.url.split('/user_data/').last
5
+ data = Satis.config.current_user.user_data.keyed(key)
6
+
7
+ if data.id.present?
8
+ render json: data.data, status: 200
9
+ else
10
+ render json: {}, status: 404
11
+ end
12
+ end
13
+
14
+ def update
15
+ key = request.url.split('/user_data/').last
16
+ data = Satis.config.current_user.user_data.keyed(key)
17
+
18
+ data.data = user_data_update_params.as_json
19
+ data.save!
20
+
21
+ render json: data.data, status: 200
22
+ end
23
+
24
+ private
25
+
26
+ def user_data_update_params
27
+ params.require(:user_datum)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ module Satis
2
+ class UserData < ApplicationRecord
3
+ belongs_to :user, optional: true
4
+
5
+ validates :key, presence: true, uniqueness: { scope: :user_id, allow_nil: true }
6
+
7
+ def self.keyed(key)
8
+ find_or_create_by(key: key)
9
+ end
10
+ end
11
+ end
data/config/routes.rb CHANGED
@@ -1,5 +1,3 @@
1
1
  Satis::Engine.routes.draw do
2
- resources :tables, param: :table_name do
3
- get 'filter_collection', on: :collection
4
- end
2
+ resources :user_data, only: %i[show update]
5
3
  end
@@ -0,0 +1,13 @@
1
+ class CreateSatisUserData < ActiveRecord::Migration[6.1]
2
+ def change
3
+ create_table :satis_user_data, id: :uuid do |t|
4
+ t.string :key, null: false
5
+ t.jsonb :data, null: false, default: {}
6
+ t.belongs_to :user, null: false, foreign_key: true, type: :uuid
7
+
8
+ t.timestamps
9
+ end
10
+ add_index :satis_user_data, :key, unique: true
11
+ add_index :satis_user_data, :data, using: :gin
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ class ChangeSatisUserData < ActiveRecord::Migration[7.0]
2
+ disable_ddl_transaction!
3
+ def change
4
+ safety_assured { change_column :satis_user_data, :data, :jsonb, null: true, default: {} }
5
+ safety_assured { change_column :satis_user_data, :user_id, :uuid, null: true, foreign_key: true }
6
+
7
+ remove_index :satis_user_data, [:key]
8
+ add_index :satis_user_data, [:user_id, :key], unique: true, algorithm: :concurrently
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ require_relative 'satisfied'
2
+
3
+ module Satis::ActiveRecordHelpers
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ delegate :satisfied?, to: :class
8
+ end
9
+
10
+ class_methods do
11
+ def satisfied(options = {})
12
+ @_satis_satisfied_options = options
13
+ include Satis::Satisfied
14
+ end
15
+
16
+ def satisfied?
17
+ included_modules.include?(Satis::Satisfied)
18
+ end
19
+ end
20
+ end
@@ -3,13 +3,14 @@
3
3
  module Satis
4
4
  class Configuration
5
5
  attr_accessor :submit_on_enter, :confirm_before_leave
6
- attr_writer :default_help_text
6
+ attr_writer :default_help_text, :current_user
7
7
  attr_writer :logger
8
8
 
9
9
  def initialize
10
10
  @logger = Logger.new(STDOUT)
11
11
  @submit_on_enter = true
12
12
  @confirm_before_leave = false
13
+ @current_user = -> {}
13
14
 
14
15
  @default_help_text = lambda do |_template, _object, key, _additional_scope|
15
16
  scope = help_scope(template, object, additional_scope)
@@ -57,5 +58,11 @@ module Satis
57
58
 
58
59
  actions.map { |action| help_scope(template, object, additional_scope, action: action) }
59
60
  end
61
+
62
+ def current_user
63
+ raise 'current_user should be a Proc' unless @current_user.is_a? Proc
64
+
65
+ instance_exec(&@current_user)
66
+ end
60
67
  end
61
68
  end
data/lib/satis/engine.rb CHANGED
@@ -23,5 +23,13 @@ module Satis
23
23
  end
24
24
  end
25
25
  end
26
+
27
+ initializer :append_migrations do |app|
28
+ unless app.root.to_s.match? root.to_s
29
+ config.paths['db/migrate'].expanded.each do |expanded_path|
30
+ app.config.paths['db/migrate'] << expanded_path
31
+ end
32
+ end
33
+ end
26
34
  end
27
35
  end
@@ -3,13 +3,13 @@
3
3
  module Satis
4
4
  module Menus
5
5
  class Menu
6
- attr_reader :items, :level
7
-
6
+ attr_reader :items, :level, :event
8
7
  def initialize(*args, **kwargs)
9
8
  @options = args.extract_options!
10
9
  @items = []
11
10
  @scope = Array.wrap(args.first)
12
11
  @level = kwargs[:level] || 0
12
+ @event = kwargs[:event] || 'mouseover'
13
13
  yield self if block_given?
14
14
  end
15
15
 
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Satis
4
+ module Satisfied
5
+ extend ActiveSupport::Concern
6
+
7
+ class_methods do
8
+ def satisfied_options
9
+ @_satis_satisfied_options || {}
10
+ end
11
+ end
12
+
13
+ included do
14
+ has_many :user_data, class_name: 'Satis::UserData'
15
+ end
16
+ end
17
+ end
data/lib/satis/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Satis
2
- VERSION = "1.0.67"
2
+ VERSION = "1.0.68"
3
3
  end
data/lib/satis.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'satis/version'
2
2
  require 'satis/engine'
3
3
  require 'satis/configuration'
4
+ require 'satis/active_record_helpers'
4
5
 
5
6
  require 'view_component'
6
7
  require 'browser'
@@ -32,5 +33,16 @@ module Satis
32
33
  def add_helper(name, component)
33
34
  Satis::Helpers::Container.add_helper(name, component)
34
35
  end
36
+
37
+ def add_component_helper(component_name, name, component)
38
+ klass = "Satis::#{component_name.to_s.classify}::Component".safe_constantize
39
+ return if klass.blank?
40
+ klass.add_helper name, component
41
+ end
42
+ end
43
+
44
+ # Include helpers
45
+ ActiveSupport.on_load(:active_record) do
46
+ include Satis::ActiveRecordHelpers
35
47
  end
36
48
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: satis
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.67
4
+ version: 1.0.68
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom de Grunt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-10 00:00:00.000000000 Z
11
+ date: 2022-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: browser
@@ -164,14 +164,19 @@ files:
164
164
  - app/components/satis/tabs/component.scss
165
165
  - app/components/satis/tabs/component_controller.js
166
166
  - app/controllers/satis/application_controller.rb
167
+ - app/controllers/satis/user_data_controller.rb
167
168
  - app/helpers/satis/application_helper.rb
168
169
  - app/jobs/satis/application_job.rb
169
170
  - app/mailers/satis/application_mailer.rb
170
171
  - app/models/satis/application_record.rb
172
+ - app/models/satis/user_data.rb
171
173
  - app/views/shared/_fields_for.html.slim
172
174
  - config/routes.rb
175
+ - db/migrate/20220929142147_create_satis_user_data.rb
176
+ - db/migrate/20221212083110_change_satis_user_data.rb
173
177
  - lib/satis.rb
174
178
  - lib/satis/action_controller_helpers.rb
179
+ - lib/satis/active_record_helpers.rb
175
180
  - lib/satis/configuration.rb
176
181
  - lib/satis/engine.rb
177
182
  - lib/satis/forms/builder.rb
@@ -184,6 +189,7 @@ files:
184
189
  - lib/satis/menus/builder.rb
185
190
  - lib/satis/menus/item.rb
186
191
  - lib/satis/menus/menu.rb
192
+ - lib/satis/satisfied.rb
187
193
  - lib/satis/version.rb
188
194
  - lib/tasks/satis_tasks.rake
189
195
  homepage: https://github.com/entdec/satis