matestack-ui-bootstrap 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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