matestack-ui-bootstrap 1.4.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.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +8 -0
  3. data/README.md +26 -0
  4. data/Rakefile +43 -0
  5. data/app/assets/images/avatar-placeholder.png +0 -0
  6. data/app/assets/images/icons/bootstrap-icons.svg +1 -0
  7. data/app/concepts/matestack/ui/bootstrap/apps/admin_template.rb +85 -0
  8. data/app/concepts/matestack/ui/bootstrap/components/accordion.rb +53 -0
  9. data/app/concepts/matestack/ui/bootstrap/components/alert.js +53 -0
  10. data/app/concepts/matestack/ui/bootstrap/components/alert.rb +34 -0
  11. data/app/concepts/matestack/ui/bootstrap/components/avatar.rb +27 -0
  12. data/app/concepts/matestack/ui/bootstrap/components/badge.rb +30 -0
  13. data/app/concepts/matestack/ui/bootstrap/components/breadcrumb.rb +46 -0
  14. data/app/concepts/matestack/ui/bootstrap/components/button.rb +54 -0
  15. data/app/concepts/matestack/ui/bootstrap/components/button_group.rb +36 -0
  16. data/app/concepts/matestack/ui/bootstrap/components/card.rb +100 -0
  17. data/app/concepts/matestack/ui/bootstrap/components/carousel.js +79 -0
  18. data/app/concepts/matestack/ui/bootstrap/components/carousel.rb +85 -0
  19. data/app/concepts/matestack/ui/bootstrap/components/chart.js +232 -0
  20. data/app/concepts/matestack/ui/bootstrap/components/chart.rb +71 -0
  21. data/app/concepts/matestack/ui/bootstrap/components/close.rb +30 -0
  22. data/app/concepts/matestack/ui/bootstrap/components/collapse.js +84 -0
  23. data/app/concepts/matestack/ui/bootstrap/components/collapse.rb +43 -0
  24. data/app/concepts/matestack/ui/bootstrap/components/dropdown.js +14 -0
  25. data/app/concepts/matestack/ui/bootstrap/components/dropdown.rb +116 -0
  26. data/app/concepts/matestack/ui/bootstrap/components/icon.rb +19 -0
  27. data/app/concepts/matestack/ui/bootstrap/components/list_group.rb +83 -0
  28. data/app/concepts/matestack/ui/bootstrap/components/modal.js +90 -0
  29. data/app/concepts/matestack/ui/bootstrap/components/modal.rb +106 -0
  30. data/app/concepts/matestack/ui/bootstrap/components/navbar.rb +120 -0
  31. data/app/concepts/matestack/ui/bootstrap/components/page_heading.rb +28 -0
  32. data/app/concepts/matestack/ui/bootstrap/components/pagination.rb +40 -0
  33. data/app/concepts/matestack/ui/bootstrap/components/popover.js +26 -0
  34. data/app/concepts/matestack/ui/bootstrap/components/popover.rb +92 -0
  35. data/app/concepts/matestack/ui/bootstrap/components/progress.rb +65 -0
  36. data/app/concepts/matestack/ui/bootstrap/components/scrollspy.rb +33 -0
  37. data/app/concepts/matestack/ui/bootstrap/components/section_card.rb +31 -0
  38. data/app/concepts/matestack/ui/bootstrap/components/spinner.rb +31 -0
  39. data/app/concepts/matestack/ui/bootstrap/components/tab_nav.rb +81 -0
  40. data/app/concepts/matestack/ui/bootstrap/components/tab_nav_content.rb +32 -0
  41. data/app/concepts/matestack/ui/bootstrap/components/toast.js +79 -0
  42. data/app/concepts/matestack/ui/bootstrap/components/toast.rb +99 -0
  43. data/app/concepts/matestack/ui/bootstrap/components/tooltip.js +26 -0
  44. data/app/concepts/matestack/ui/bootstrap/components/tooltip.rb +82 -0
  45. data/app/concepts/matestack/ui/bootstrap/content/collection/collection.rb +112 -0
  46. data/app/concepts/matestack/ui/bootstrap/content/collection/collection.scss +10 -0
  47. data/app/concepts/matestack/ui/bootstrap/content/collection/content.rb +101 -0
  48. data/app/concepts/matestack/ui/bootstrap/content/collection/filter.rb +33 -0
  49. data/app/concepts/matestack/ui/bootstrap/content/collection/paginate.rb +92 -0
  50. data/app/concepts/matestack/ui/bootstrap/content/figure.rb +7 -0
  51. data/app/concepts/matestack/ui/bootstrap/form/checkbox.rb +90 -0
  52. data/app/concepts/matestack/ui/bootstrap/form/date.js +38 -0
  53. data/app/concepts/matestack/ui/bootstrap/form/date.rb +98 -0
  54. data/app/concepts/matestack/ui/bootstrap/form/input.rb +123 -0
  55. data/app/concepts/matestack/ui/bootstrap/form/radio.rb +65 -0
  56. data/app/concepts/matestack/ui/bootstrap/form/select.haml +11 -0
  57. data/app/concepts/matestack/ui/bootstrap/form/select.rb +74 -0
  58. data/app/concepts/matestack/ui/bootstrap/form/submit.rb +20 -0
  59. data/app/concepts/matestack/ui/bootstrap/form/switch.rb +90 -0
  60. data/app/concepts/matestack/ui/bootstrap/layout/column.rb +47 -0
  61. data/app/concepts/matestack/ui/bootstrap/layout/container.rb +25 -0
  62. data/app/concepts/matestack/ui/bootstrap/layout/row.rb +15 -0
  63. data/app/concepts/matestack/ui/bootstrap/layout/sidebar.js +64 -0
  64. data/app/concepts/matestack/ui/bootstrap/layout/sidebar.rb +45 -0
  65. data/app/concepts/matestack/ui/bootstrap/layout/sidebar.scss +57 -0
  66. data/app/concepts/matestack/ui/bootstrap/pages/devise/sign_in.rb +40 -0
  67. data/app/concepts/matestack/ui/bootstrap/registry.rb +63 -0
  68. data/app/helpers/matestack/ui/bootstrap/application_helper.rb +13 -0
  69. data/app/javascript/matestack-ui-bootstrap/index.js +26 -0
  70. data/app/javascript/matestack-ui-bootstrap/stylesheets/matestack-ui-bootstrap.scss +65 -0
  71. data/app/javascript/packs/matestack-ui-bootstrap.js +2 -0
  72. data/config/routes.rb +2 -0
  73. data/config/webpack/development.js +5 -0
  74. data/config/webpack/environment.js +29 -0
  75. data/config/webpack/production.js +33 -0
  76. data/config/webpack/test.js +5 -0
  77. data/config/webpacker.yml +96 -0
  78. data/lib/matestack/ui/bootstrap.rb +27 -0
  79. data/lib/matestack/ui/bootstrap/engine.rb +26 -0
  80. data/lib/matestack/ui/bootstrap/version.rb +7 -0
  81. data/lib/tasks/matestack/ui/bootstrap_tasks.rake +66 -0
  82. metadata +137 -0
@@ -0,0 +1,26 @@
1
+ import * as bootstrap from 'bootstrap'
2
+
3
+ MatestackUiCore.Vue.component('matestack-ui-bootstrap-tooltip', {
4
+
5
+ mixins: [MatestackUiCore.componentMixin],
6
+ data() {
7
+ return {
8
+ // tooltipInstance: undefined
9
+
10
+ };
11
+ },
12
+ methods: {
13
+
14
+ },
15
+ mounted: function() {
16
+ const self = this;
17
+ var tooltip = self.$el
18
+ self.tooltipInstance = new bootstrap.Tooltip(tooltip)
19
+ }
20
+ // created: function() {
21
+ // },
22
+
23
+ // beforeDestroy: function() {
24
+
25
+ // },
26
+ });
@@ -0,0 +1,82 @@
1
+ class Matestack::Ui::Bootstrap::Components::Tooltip < Matestack::Ui::VueJsComponent
2
+ vue_js_component_name "matestack-ui-bootstrap-tooltip"
3
+
4
+ DATA_ALIAS_ATTRIBUTES = %i[container delay selector html template fallback_placement]
5
+
6
+ DATA_ALIAS_ATTRIBUTES.each do |attribute|
7
+ optional "#{attribute}": { as: :"bs_#{attribute}"}
8
+ end
9
+
10
+ # TODO:
11
+ # for security reasons the sanitize, sanitizeFn and whiteList options cannot be supplied using data attributes.
12
+ # sanitize sanitize_fn white_list
13
+ # optional :content
14
+ optional :tag
15
+ optional class: { as: :bs_class }
16
+ optional id: { as: :bs_id }
17
+ DATA_ATTRIBUTES = %i[title text variant animation placement tabindex trigger boundary offset popper_config]
18
+ optional *DATA_ATTRIBUTES
19
+
20
+ def response
21
+ case tag
22
+ when :div
23
+ div tooltip_attributes do
24
+ element_partial
25
+ end
26
+ when :span
27
+ span tooltip_attributes do
28
+ element_partial
29
+ end
30
+ when :link
31
+ link tooltip_attributes
32
+ else
33
+ bs_btn tooltip_attributes
34
+ end
35
+ end
36
+
37
+ protected
38
+
39
+ def element_partial
40
+ if options[:slots] && options[:slots][:element]
41
+ slot options[:slots][:element]
42
+ else
43
+ bs_btn variant: variant, attributes: { 'style': "pointer-events: none;" }, text: text
44
+ end
45
+ end
46
+
47
+ def tooltip_attributes
48
+ attributes = {}.tap do |hash|
49
+ hash[:class] = tooltip_classes
50
+ hash[:style] = variant if (tag == :button or !tag.present?)
51
+
52
+ hash[:attributes] = { role: "button", title: "#{title}", tabindex: "#{tabindex}" } if (tag == :link or tag == :a)
53
+
54
+ hash[:text] = text if text.present?
55
+
56
+ hash[:data] = {}.tap do |hash|
57
+ DATA_ALIAS_ATTRIBUTES.each do |attribute|
58
+ hash["bs-#{attribute}"] = self.send(:"bs_#{attribute}") if self.send(:"bs_#{attribute}")
59
+ end
60
+ DATA_ATTRIBUTES.each do |attribute|
61
+ hash["bs-#{attribute}"] = self.send(:"#{attribute}") if self.send(:"#{attribute}")
62
+ end
63
+ hash["bs-toggle"] = "tooltip"
64
+ hash["bs-type"] = tag
65
+ # hash[:'original-title'] = content
66
+ end
67
+ end
68
+ html_attributes.merge(
69
+ attributes
70
+ )
71
+ end
72
+
73
+ def tooltip_classes
74
+ [].tap do |classes|
75
+ classes << "d-inline-block" if (tag == :span or tag == :div)
76
+ classes << "btn btn-#{variant || 'link'}" if (tag == :link or !tag.present?)
77
+
78
+ classes << bs_class
79
+ end.join(' ').strip
80
+ end
81
+
82
+ end
@@ -0,0 +1,112 @@
1
+ class Matestack::Ui::Bootstrap::Content::Collection::Collection < Matestack::Ui::Component
2
+ include Matestack::Ui::Core::Collection::Helper
3
+ include Matestack::Ui::Bootstrap::Content::Collection::Content
4
+ include Matestack::Ui::Bootstrap::Content::Collection::Filter
5
+ include Matestack::Ui::Bootstrap::Content::Collection::Paginate
6
+
7
+ # html attributes
8
+ optional id: { as: :bs_id }
9
+
10
+ # table configuration
11
+ optional :items
12
+ optional :columns
13
+ optional :filters
14
+ optional :footer
15
+ optional :paginate
16
+ optional :rerender_on
17
+ optional :item_actions_proc
18
+ optional :collection_rendering_proc
19
+ optional :slots
20
+
21
+ # bootstrap settings
22
+ optional :responsive
23
+ optional :variant
24
+ optional :striped
25
+ optional :hoverable
26
+ optional :border_variant
27
+ optional :borderless
28
+
29
+ attr_accessor :processed_filters
30
+
31
+ def response
32
+ div class: "smart-collection" do
33
+ filter_partial
34
+ content
35
+ end
36
+ end
37
+
38
+
39
+ private
40
+
41
+ def collection
42
+ return @collection if @collection
43
+ settings = {}.tap do |h|
44
+ h[:id] = bs_id || "smartcollection"
45
+ h[:data] = filtered_query
46
+ h[:base_count] = items.count
47
+ h[:init_limit] = paginate if paginate
48
+ h[:filtered_count] = filtered_query.count if paginate
49
+ end
50
+ @collection = set_collection(settings)
51
+ end
52
+
53
+ def collection_id
54
+ collection.config[:id]
55
+ end
56
+
57
+ def filtered_query
58
+ return @filtered_query if @filtered_query
59
+ @filtered_query = items
60
+ filters.select { |key, value| '.'.in? key.to_s }.each do |key, value|
61
+ associated_name = key.to_s.split(".").first
62
+ @filtered_query = @filtered_query.joins(associated_name.to_sym).all
63
+ if value.is_a?(Hash)
64
+ processed_filters[key] = value
65
+ @filtered_query = add_query_filter(@filtered_query, associated_name, key, value)
66
+ end
67
+ end
68
+ filters.reject { |key, value| '.'.in? key.to_s }.each do |key, value|
69
+ if value.is_a?(Hash)
70
+ processed_filters[key] = value
71
+ @filtered_query = add_query_filter(@filtered_query, nil, key, value)
72
+ end
73
+ end
74
+ @filtered_query
75
+ end
76
+
77
+ def add_query_filter(query, associated_name, key, filter_config)
78
+ value = get_collection_filter(collection_id)[key.to_sym]
79
+ if value.present?
80
+ if associated_name.present?
81
+ table_name = items.klass.reflections[associated_name].table_name
82
+ key = key.to_s.gsub(associated_name, table_name)
83
+ else
84
+ table_name = items.klass.table_name
85
+ key = key.to_s
86
+ end
87
+ case filter_config[:match]
88
+ when :equals
89
+ query = query.where("#{key}": value)
90
+ when :starts_with
91
+ query = query.where("lower(#{key}) LIKE ?", "#{value.downcase}%")
92
+ when :ends_with
93
+ query = query.where("lower(#{key}) LIKE ?", "%#{value.downcase}")
94
+ when :like
95
+ query = query.where("lower(#{key}) LIKE ?", "%#{value.downcase}%")
96
+ else
97
+ query = query.where("#{key}": value)
98
+ end
99
+ end
100
+ query
101
+ end
102
+
103
+ def head_columns
104
+ columns.map { |key, value| value.is_a?(Hash) ? value[:heading] : value }
105
+ end
106
+
107
+ def processed_filters
108
+ @filters ||= {}
109
+ end
110
+
111
+
112
+ end
@@ -0,0 +1,10 @@
1
+ .smart-collection{
2
+ .pagination{
3
+ .page-item{
4
+ cursor: pointer;
5
+ }
6
+ .page-item.disabled{
7
+ cursor: initial;
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,101 @@
1
+ module Matestack::Ui::Bootstrap::Content::Collection::Content
2
+
3
+ def content
4
+ bs_row id: 'content' do
5
+ bs_col do
6
+ async id: collection_id, rerender_on: "#{collection_id}-update, #{rerender_on} " do
7
+ collection_content collection.config do
8
+ div class: responsive_class do
9
+ if slots && slots[:collection_rendering]
10
+ slot slots[:collection_rendering].call(collection.paginated_data)
11
+ elsif columns
12
+ div class: "table-responsive" do
13
+ table table_attributes do
14
+ table_head
15
+ table_body
16
+ table_footer
17
+ end
18
+ end
19
+ end
20
+ end
21
+ paginate_partial if paginate.present?
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def table_head
29
+ thead do
30
+ tr do
31
+ columns&.each do |key, value|
32
+ th text: value.is_a?(Hash) ? value[:heading] : value, class: cell_class(value), attributes: { scope: :col }
33
+ end
34
+ th if slots && slots[:table_item_actions]
35
+ end
36
+ end
37
+ end
38
+
39
+ def table_body
40
+ tbody do
41
+ collection.paginated_data.each_with_index do |data, index|
42
+ tr class: 'align-middle' do
43
+ columns.each do |key, value|
44
+ cell(data, key, value)
45
+ end
46
+ if slots && slots[:table_item_actions]
47
+ td class: 'text-end' do
48
+ slot slots[:table_item_actions].call(data)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ def table_footer
57
+ tfoot do
58
+ tr do
59
+ footer&.each do |value|
60
+ td text: value
61
+ end
62
+ end
63
+ end if footer
64
+ end
65
+
66
+ private
67
+
68
+ def cell(data, key, value)
69
+ td text: cell_text(data, key, value), class: cell_class(value)
70
+ end
71
+
72
+ def cell_class(value)
73
+ [].tap do |classes|
74
+ classes << "text-#{value[:text]}" if value[:text]
75
+ classes << "align-#{value[:align]}" if value[:align]
76
+ end.join(' ') if value.is_a? Hash
77
+ end
78
+
79
+ def cell_text(data, key, value)
80
+ text = data.instance_eval(key.to_s)
81
+ text = value[:format].call(text) if value.is_a?(Hash) && value[:format]
82
+ text
83
+ end
84
+
85
+ def table_attributes
86
+ klass = ['table'].tap do |classes|
87
+ classes << "table-#{variant}" if variant
88
+ classes << "table-striped" if striped
89
+ classes << "table-hover" if hoverable
90
+ classes << "table-bordered border-#{border_variant}" if border_variant
91
+ classes << "table-borderless" if borderless
92
+ end.join(' ').strip
93
+ { id: collection_id, class: klass }
94
+ end
95
+
96
+ def responsive_class
97
+ return unless responsive
98
+ responsive === true ? 'table-responsive' : "table-responsive-#{responsive}"
99
+ end
100
+
101
+ end
@@ -0,0 +1,33 @@
1
+ module Matestack::Ui::Bootstrap::Content::Collection::Filter
2
+
3
+ def filter_partial
4
+ collection_filter collection.config do
5
+ bs_row class: 'mt-2 mb-4' do
6
+ div class: 'col-auto' do
7
+ bs_row do
8
+ processed_filters.each do |key, config|
9
+ bs_col do
10
+ filter_input key, config
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ def filter_input(key, config)
20
+ attributes = {
21
+ key: key,
22
+ type: :text,
23
+ placeholder: config[:placeholder] || key.to_s,
24
+ }
25
+ case config[:type]
26
+ when :select
27
+ collection_filter_select attributes.merge(class: 'form-select', options: config[:options])
28
+ else
29
+ collection_filter_input attributes.merge(class: 'form-control')
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,92 @@
1
+ module Matestack::Ui::Bootstrap::Content::Collection::Paginate
2
+
3
+ def paginate_partial
4
+ div class: "current-pagination-state ps-2" do
5
+ small do
6
+ plain "showing #{@collection.from}"
7
+ plain "to #{@collection.to}"
8
+ plain "of #{@collection.filtered_count}"
9
+ if (@collection.base_count - @collection.filtered_count) > 0
10
+ plain "(#{@collection.base_count - @collection.filtered_count} hidden by filter)"
11
+ end
12
+ end
13
+ end
14
+ pagination_nav_partial
15
+ end
16
+
17
+ def pagination_nav_partial
18
+ nav class: "table-responsive", attributes: { style: "display: -webkit-box;" } do
19
+ ul class: ul_classes do
20
+ li class: "page-item #{ 'disabled' if current_page == 1 }" do
21
+ collection_content_previous class: 'page-link' do
22
+ bs_icon name: "chevron-left", size: 10
23
+ end
24
+ end
25
+ if @collection.pages.count >= 9 && current_page > 5
26
+ li class: "page-item" do
27
+ collection_content_page_link class: 'page-link', page: 1 do
28
+ plain 1
29
+ end
30
+ end
31
+ unless current_page == 6
32
+ li class: "page-item disabled" do
33
+ link class: 'page-link', path: "#" do
34
+ plain "..."
35
+ end
36
+ end
37
+ end
38
+ end
39
+ @collection.pages.each do |page|
40
+ if (current_page-page).abs < 5
41
+ li class: "page-item #{ 'active' if current_page == page }" do
42
+ collection_content_page_link class: 'page-link', page: page do
43
+ plain page
44
+ end
45
+ end
46
+ end
47
+ end
48
+ if @collection.pages.count >= 9 && last_page-current_page > 4
49
+ unless current_page == last_page-5
50
+ li class: "page-item disabled" do
51
+ link class: 'page-link', path: "#" do
52
+ plain "..."
53
+ end
54
+ end
55
+ end
56
+ li class: "page-item" do
57
+ collection_content_page_link class: 'page-link', page: last_page do
58
+ plain last_page
59
+ end
60
+ end
61
+ end
62
+ li class: "page-item #{ 'disabled' if current_page == last_page }" do
63
+ collection_content_next class: 'page-link' do
64
+ bs_icon name: "chevron-right", size: 10
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ def ul_classes
72
+ [].tap do |classes|
73
+ classes << "pagination"
74
+ classes << "justify-content-end"
75
+ classes << "mt-3"
76
+ end.join(' ').strip
77
+ end
78
+
79
+ def current_page
80
+ current_offset = params["#{bs_id}-offset"].try(:to_i)
81
+ (current_offset/paginate)+1 if current_offset && paginate.present?
82
+ end
83
+
84
+ def last_page
85
+ if @collection.filtered_count%paginate == 0
86
+ (@collection.filtered_count/paginate)
87
+ else
88
+ (@collection.filtered_count/paginate)+1
89
+ end
90
+ end
91
+
92
+ end