matestack-ui-core 1.2.0 → 1.5.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +27 -1
  3. data/app/concepts/matestack/ui/core/cable/cable.haml +1 -1
  4. data/app/concepts/matestack/ui/core/collection/helper.rb +7 -1
  5. data/app/concepts/matestack/ui/core/form/checkbox/base.rb +120 -0
  6. data/app/concepts/matestack/ui/core/form/checkbox/checkbox.js +15 -0
  7. data/app/concepts/matestack/ui/core/form/checkbox/checkbox.rb +7 -70
  8. data/app/concepts/matestack/ui/core/form/checkbox/mixin.js +80 -0
  9. data/app/concepts/matestack/ui/core/form/form.js +15 -46
  10. data/app/concepts/matestack/ui/core/form/input/base.rb +75 -0
  11. data/app/concepts/matestack/ui/core/form/input/input.js +15 -0
  12. data/app/concepts/matestack/ui/core/form/input/input.rb +8 -52
  13. data/app/concepts/matestack/ui/core/form/input/mixin.js +55 -0
  14. data/app/concepts/matestack/ui/core/form/radio/base.rb +90 -0
  15. data/app/concepts/matestack/ui/core/form/radio/mixin.js +62 -0
  16. data/app/concepts/matestack/ui/core/form/radio/radio.js +15 -0
  17. data/app/concepts/matestack/ui/core/form/radio/radio.rb +7 -62
  18. data/app/concepts/matestack/ui/core/form/select/base.rb +98 -0
  19. data/app/concepts/matestack/ui/core/form/select/mixin.js +58 -0
  20. data/app/concepts/matestack/ui/core/form/select/select.js +15 -0
  21. data/app/concepts/matestack/ui/core/form/select/select.rb +11 -60
  22. data/app/concepts/matestack/ui/core/form/submit/base.rb +12 -0
  23. data/app/concepts/matestack/ui/core/form/submit/submit.js +19 -0
  24. data/app/concepts/matestack/ui/core/form/submit/submit.rb +9 -6
  25. data/app/concepts/matestack/ui/core/form/textarea/base.rb +49 -0
  26. data/app/concepts/matestack/ui/core/form/textarea/mixin.js +41 -0
  27. data/app/concepts/matestack/ui/core/form/textarea/textarea.js +15 -0
  28. data/app/concepts/matestack/ui/core/form/textarea/textarea.rb +10 -21
  29. data/app/concepts/matestack/ui/core/js/core.js +11 -0
  30. data/app/concepts/matestack/ui/core/{form/submit/submit.haml → select/select.haml} +1 -1
  31. data/app/concepts/matestack/ui/core/select/select.rb +7 -0
  32. data/app/concepts/matestack/ui/core/view/view.rb +2 -2
  33. data/app/javascript/matestack-ui-core/index.js +12 -2
  34. data/lib/matestack/ui/core/components.rb +2 -0
  35. data/lib/matestack/ui/core/version.rb +1 -1
  36. data/vendor/assets/javascripts/dist/matestack-ui-core.js +611 -55
  37. data/vendor/assets/javascripts/dist/matestack-ui-core.js.map +1 -1
  38. data/vendor/assets/javascripts/dist/matestack-ui-core.min.js +1 -1
  39. data/vendor/assets/javascripts/dist/matestack-ui-core.min.js.LICENSE.txt +5 -12
  40. data/vendor/assets/javascripts/dist/matestack-ui-core.min.js.br +0 -0
  41. data/vendor/assets/javascripts/dist/matestack-ui-core.min.js.gz +0 -0
  42. data/vendor/assets/javascripts/dist/matestack-ui-core.min.js.map +1 -1
  43. data/vendor/assets/javascripts/dist/matestack-ui-core.min.js.map.br +0 -0
  44. data/vendor/assets/javascripts/dist/matestack-ui-core.min.js.map.gz +0 -0
  45. metadata +31 -16
  46. data/app/concepts/matestack/ui/core/form/select/select.haml +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9912bb90f1207c60c0e31895fabbe090e93bca8c63fe85a4c075b92386ed1a32
4
- data.tar.gz: 768a1f1071275163d1a3870fb4d8401960b757fc803f3ab82b8bf17a3b4a22c2
3
+ metadata.gz: 355f4aad94fe47a7344717057594f5c540b323c054407da6333cd9a6c1b59235
4
+ data.tar.gz: 21c43c8fe8909738f8b1914c9f66b4085c19fa200eb177e07f47a1a865999398
5
5
  SHA512:
6
- metadata.gz: d79c8aa967c5f223ff69b211eaa36d715bb0615a4eec5c303ea62b5f13e53fa7b4945778cf143ba9098c42f9608bc1da1d89f43f3689cb3239bf7cc79903b316
7
- data.tar.gz: 8cff6d3684d75949299bce178f7c3114dbef3333fe794ef821df0422be2100f4f4c383e2f0c06348b03928518cd834729d4b9096b885c6f415615b2221a44f28
6
+ metadata.gz: fdeb8b0676fa23e2e8c1c38b0110c8b8ca334fd29f8ded46253fc067e893507624e6fdce349f07a54b44ea5f42d841b25348f5b5041038646986942cef1755cc
7
+ data.tar.gz: 5adf20510a4b1b243b9395b3028dbebedb38ddb87a94e80ea6911e69814031feae845181202192ac48483b9a4c0a0a440c8bb1df33541dd2a22d64304d99fe87
data/README.md CHANGED
@@ -15,6 +15,8 @@ Reactivity included if desired.
15
15
 
16
16
  You end up writing 50% less code while increasing productivity, maintainability and developer happiness. Work with pure Ruby. If necessary, extend with pure JavaScript. No Opal involved.
17
17
 
18
+ [<img src="https://img.youtube.com/vi/Mue5gs6Wtq4/0.jpg" width="350">](https://www.youtube.com/watch?v=Mue5gs6Wtq4)
19
+
18
20
  The main goals are:
19
21
 
20
22
  - More maintainable UI code, using a component-based structure written in Ruby
@@ -25,10 +27,34 @@ The main goals are:
25
27
  it alongside your classic views and incrementally turn your Rails-App into a
26
28
  dynamic Web-App.
27
29
 
30
+ ## Compatibility
31
+
32
+ ### Ruby/Rails
33
+
34
+ `matestack-ui-core` is tested against:
35
+
36
+ - Rails 6.1.1 + Ruby 3.0.0
37
+ - Rails 6.1.1 + Ruby 2.7.2
38
+ - Rails 6.0.3.4 + Ruby 2.6.6
39
+ - Rails 5.2.4.4 + Ruby 2.6.6
40
+
41
+ Rails versions below 5.2 are not supported.
42
+
43
+ ### Vue.js
44
+
45
+ `matestack-ui-core` currently uses Vue.js 2.6.12 and Vuex 3.6.2 for its reactivity features.
46
+ Custom reactive components are bound to these versions as well.
47
+
48
+ Vue 3 / Vuex 4 update is planned for Q2 2021.
49
+
28
50
  ## Documentation/Installation
29
51
 
30
52
  Documentation can be found [here](https://docs.matestack.io)
31
53
 
54
+ ## Getting started
55
+
56
+ A getting started guide can be found [here](https://docs.matestack.io/start/150-getting_started)
57
+
32
58
  ## Changelog
33
59
 
34
60
  Changelog can be found [here](./CHANGELOG.md)
@@ -342,7 +368,7 @@ class Components::Card < Matestack::Ui::Component
342
368
  heading size: 5, text: title if title.present?
343
369
  end
344
370
  end
345
-
371
+
346
372
  def body_slot
347
373
  slot do
348
374
  paragraph class: "card-text", text: body
@@ -1,4 +1,4 @@
1
- %component{dynamic_tag_attributes.merge('initial-template': "#{render_content}")}
1
+ %component{dynamic_tag_attributes.merge('v-bind:initial-template': "#{render_content.to_json}")}
2
2
  %div{class: "matestack-cable-component-container", "v-bind:class": "{ 'loading': loading === true }"}
3
3
  %div{class: "matestack-cable-component-wrapper", "v-if": "cableTemplate != null", "v-bind:class": "{ 'loading': loading === true }"}
4
4
  %v-runtime-template{":template":"cableTemplate"}
@@ -91,7 +91,13 @@ module Matestack::Ui::Core::Collection
91
91
  end
92
92
  end
93
93
 
94
- def set_collection id: nil, init_offset: 0, init_limit: nil, base_count: nil, filtered_count: nil, data: nil
94
+ # since ruby 3 changed hash <-> keyword argument transformation, we need to
95
+ # adjust this method call in order to stay compatible with ruby 2.x and ruby 3.x
96
+ def set_collection options_hash
97
+ _set_collection **options_hash
98
+ end
99
+
100
+ def _set_collection id: nil, init_offset: 0, init_limit: nil, base_count: nil, filtered_count: nil, data: nil
95
101
  @collections = {} if @collections.nil?
96
102
 
97
103
  collection_config = CollectionConfig.new(
@@ -0,0 +1,120 @@
1
+ require_relative '../utils'
2
+ require_relative '../has_errors'
3
+ module Matestack::Ui::Core::Form::Checkbox
4
+ class Base < Matestack::Ui::Core::Component::Dynamic
5
+ include Matestack::Ui::Core::Form::Utils
6
+ include Matestack::Ui::Core::Form::HasInputHtmlAttributes
7
+ include Matestack::Ui::Core::Form::HasErrors
8
+
9
+ requires :key
10
+ optional :value, :false_value, :multiple, :init, for: { as: :input_for }, label: { as: :input_label }, options: { as: :checkbox_options }
11
+
12
+ def component_id
13
+ "checkbox-component-for-#{attr_key}"
14
+ end
15
+
16
+ def input_key
17
+ "$parent.data[\"#{key}\"]"
18
+ end
19
+
20
+ def error_key
21
+ "$parent.errors[\"#{key}\"]"
22
+ end
23
+
24
+ def change_event
25
+ "inputChanged('#{attr_key}')"
26
+ end
27
+
28
+ def render_options
29
+ # multiple
30
+ if checkbox_options
31
+ checkbox_options.to_a.each do |item|
32
+ input html_attributes.merge(
33
+ attributes: vue_attributes,
34
+ type: :checkbox,
35
+ id: "#{id_for_item(item_value(item))}",
36
+ name: item_name(item),
37
+ value: item_value(item)
38
+ )
39
+ label text: item_name(item), for: id_for_item(item_value(item))
40
+ end
41
+ # checked/unchecked checkbox (true/false checkbox)
42
+ else
43
+ input html_attributes.merge(
44
+ attributes: vue_attributes_for_single_input,
45
+ type: :hidden,
46
+ id: id_for_item(value),
47
+ value: (false_value || 0)
48
+ )
49
+
50
+ input html_attributes.merge(
51
+ attributes: vue_attributes_for_single_input,
52
+ type: :checkbox,
53
+ id: id_for_item(value),
54
+ value: checked_value
55
+ )
56
+
57
+ label text: input_label, for: id_for_item(value)
58
+ end
59
+ end
60
+
61
+ def vue_attributes
62
+ (options[:attributes] || {}).merge({
63
+ "@change": change_event,
64
+ ref: "select.multiple.#{attr_key}",
65
+ 'init-value': init_value,
66
+ 'v-bind:class': "{ '#{input_error_class}': #{error_key} }",
67
+ 'value-type': value_type,
68
+ "#{v_model_type}": input_key,
69
+ })
70
+ end
71
+
72
+ def vue_attributes_for_single_input
73
+ (options[:attributes] || {}).merge({
74
+ "@change": change_event,
75
+ ref: "input.#{attr_key}",
76
+ 'init-value': init_value_for_single_input,
77
+ 'v-bind:class': "{ '#{input_error_class}': #{error_key} }",
78
+ "#{v_model_type}": input_key
79
+ })
80
+ end
81
+
82
+ def init_value_for_single_input
83
+ if init_value == true || init_value == 1
84
+ return "true"
85
+ end
86
+ if init_value == false || init_value == 0
87
+ return "false"
88
+ end
89
+ end
90
+
91
+ def value_type
92
+ item_value(checkbox_options.first).is_a?(Integer) ? Integer : nil
93
+ end
94
+
95
+ def item_value(item)
96
+ item.is_a?(Array) ? item.last : item
97
+ end
98
+
99
+ def item_name(item)
100
+ item.is_a?(Array) ? item.first : item
101
+ end
102
+
103
+ def checked_value
104
+ value || 1
105
+ end
106
+
107
+ def v_model_type
108
+ if checkbox_options && checkbox_options.first.is_a?(Integer)
109
+ 'v-model.number'
110
+ else
111
+ 'v-model'
112
+ end
113
+ end
114
+
115
+ def id_for_item(value)
116
+ "#{html_attributes[:id]}_#{value}"
117
+ end
118
+
119
+ end
120
+ end
@@ -0,0 +1,15 @@
1
+ import Vue from "vue/dist/vue.esm";
2
+
3
+ import formCheckboxMixin from "./mixin";
4
+ import componentMixin from "../../component/component";
5
+
6
+ const componentDef = {
7
+ mixins: [componentMixin, formCheckboxMixin],
8
+ data() {
9
+ return {};
10
+ }
11
+ }
12
+
13
+ let component = Vue.component("matestack-ui-core-form-checkbox", componentDef);
14
+
15
+ export default componentDef;
@@ -1,79 +1,16 @@
1
- require_relative '../utils'
2
- require_relative '../has_input_html_attributes'
3
- require_relative '../has_errors'
1
+ require_relative './base'
2
+
4
3
  module Matestack::Ui::Core::Form::Checkbox
5
- class Checkbox < Matestack::Ui::Core::Component::Static
6
- include Matestack::Ui::Core::Form::Utils
7
- include Matestack::Ui::Core::Form::HasInputHtmlAttributes
8
- include Matestack::Ui::Core::Form::HasErrors
4
+ class Checkbox < Base
9
5
 
10
- requires :key
11
- optional :value, :false_value, :multiple, :init, for: { as: :input_for }, label: { as: :input_label }, options: { as: :checkbox_options }
6
+ vue_js_component_name "matestack-ui-core-form-checkbox"
12
7
 
13
8
  def response
14
- # multiple values
15
- if checkbox_options
16
- checkbox_options.to_a.each do |item|
17
- input html_attributes.merge(
18
- attributes: vue_attributes,
19
- type: :checkbox,
20
- id: "#{id_for_item(item_value(item))}",
21
- name: item_name(item),
22
- value: item_value(item)
23
- )
24
- label text: item_name(item), for: id_for_item(item_value(item))
25
- end
26
- # checked/unchecked checkbox
27
- else
28
- form_input type: :hidden, key: key, value: (false_value || 0), errors: false
29
- form_input type: :checkbox, key: key, value: checked_value, id: id_for_item(value), errors: false
30
- label text: input_label, for: id_for_item(value)
31
- end
32
- render_errors
33
- end
34
-
35
- def vue_attributes
36
- (options[:attributes] || {}).merge({
37
- "@change": change_event,
38
- ref: "select.multiple.#{attr_key}",
39
- 'init-value': init_value,
40
- 'v-bind:class': "{ '#{input_error_class}': #{error_key} }",
41
- 'value-type': value_type,
42
- "#{v_model_type}": input_key,
43
- })
44
- end
45
-
46
- def value_type
47
- item_value(checkbox_options.first).is_a?(Integer) ? Integer : nil
48
- end
49
-
50
- def item_value(item)
51
- item.is_a?(Array) ? item.last : item
52
- end
53
-
54
- def item_name(item)
55
- item.is_a?(Array) ? item.first : item
56
- end
57
-
58
- def checked_value
59
- value || 1
60
- end
61
-
62
- def v_model_type
63
- if checkbox_options && checkbox_options.first.is_a?(Integer)
64
- 'v-model.number'
65
- else
66
- 'v-model'
9
+ div class: "matestack-ui-core-form-checkbox" do
10
+ render_options
11
+ render_errors
67
12
  end
68
13
  end
69
14
 
70
- def change_event
71
- "inputChanged('#{attr_key}')"
72
- end
73
-
74
- def id_for_item(value)
75
- "#{html_attributes[:id]}_#{value}"
76
- end
77
-
78
15
  end
79
16
  end
@@ -0,0 +1,80 @@
1
+ const formCheckboxMixin = {
2
+ methods: {
3
+ initialize: function(){
4
+ const self = this
5
+ let data = {};
6
+
7
+ for (let key in self.$refs) {
8
+ let initValue = self.$refs[key]["attributes"]["init-value"];
9
+ let valueType = self.$refs[key]["attributes"]["value-type"];
10
+
11
+ if (key.startsWith("select.")) {
12
+ if (key.startsWith("select.multiple.")) {
13
+ if (initValue) {
14
+ data[key.replace("select.multiple.", "")] = JSON.parse(initValue["value"]);
15
+ Object.assign(self.$parent.data, data);
16
+ self.afterInitialize(JSON.parse(initValue["value"]))
17
+ } else {
18
+ data[key.replace("select.multiple.", "")] = [];
19
+ Object.assign(self.$parent.data, data);
20
+ self.afterInitialize([])
21
+ }
22
+ } else {
23
+ if (initValue) {
24
+ if (valueType && valueType["value"] == "Integer") {
25
+ data[key.replace("select.", "")] = parseInt(initValue["value"]);
26
+ Object.assign(self.$parent.data, data);
27
+ self.afterInitialize(parseInt(initValue["value"]))
28
+ } else {
29
+
30
+ data[key.replace("select.", "")] = initValue["value"];
31
+ Object.assign(self.$parent.data, data);
32
+ self.afterInitialize(initValue["value"])
33
+ }
34
+ } else {
35
+ data[key.replace("select.", "")] = null;
36
+ Object.assign(self.$parent.data, data);
37
+ self.afterInitialize(null)
38
+ }
39
+ }
40
+ } else {
41
+ if (initValue) {
42
+ if(initValue["value"] === "true"){
43
+ data[key.replace("input.", "")] = true;
44
+ Object.assign(self.$parent.data, data);
45
+ self.afterInitialize(true)
46
+ }
47
+ if(initValue["value"] === "false"){
48
+ data[key.replace("input.", "")] = false;
49
+ Object.assign(self.$parent.data, data);
50
+ self.afterInitialize(false)
51
+ }
52
+ } else {
53
+ data[key.replace("input.", "")] = null;
54
+ Object.assign(self.$parent.data, data);
55
+ self.afterInitialize(null)
56
+ }
57
+ }
58
+ }
59
+
60
+ //without the timeout it's somehow not working
61
+ setTimeout(function () {
62
+ self.$forceUpdate()
63
+ }, 1);
64
+ },
65
+ inputChanged: function (key) {
66
+ this.$parent.resetErrors(key);
67
+ this.$forceUpdate();
68
+ },
69
+ afterInitialize: function(value){
70
+ // can be used in the main component for further initialization steps
71
+ },
72
+ setValue: function (value){
73
+ this.$parent.data[this.componentConfig["key"]] = value
74
+ this.$forceUpdate();
75
+ }
76
+ }
77
+
78
+ }
79
+
80
+ export default formCheckboxMixin
@@ -18,9 +18,6 @@ const componentDef = {
18
18
  initDataKey: function (key, initValue) {
19
19
  this.data[key] = initValue;
20
20
  },
21
- inputChanged: function (key) {
22
- this.resetErrors(key);
23
- },
24
21
  updateFormValue: function (key, value) {
25
22
  this.data[key] = value;
26
23
  },
@@ -44,11 +41,11 @@ const componentDef = {
44
41
  flat[i] = newVal;
45
42
  } else if (flat[i] instanceof File){
46
43
  flat[i] = newVal;
47
- this.$refs["input."+i].value = newVal
44
+ this.$refs["input-component-for-"+i].value = newVal
48
45
  } else if (flat[i] instanceof Array) {
49
46
  if(flat[i][0] instanceof File){
50
47
  flat[i] = newVal
51
- this.$refs["input."+i].value = newVal
48
+ this.$refs["input-component-for-"+i].value = newVal
52
49
  }
53
50
  } else if (typeof flat[i] === "object" && !(flat[i] instanceof Array)) {
54
51
  setProps(flat[i], newVal);
@@ -57,54 +54,26 @@ const componentDef = {
57
54
  }
58
55
  }
59
56
  },
60
- filesAdded: function (key) {
61
- const dataTransfer = event.dataTransfer || event.target;
62
- const files = dataTransfer.files;
63
- if (event.target.attributes.multiple) {
64
- this.data[key] = [];
65
- for (let index in files) {
66
- if (files[index] instanceof File) {
67
- this.data[key].push(files[index]);
68
- }
69
- }
70
- } else {
71
- this.data[key] = files[0];
72
- }
73
- },
74
57
  initValues: function () {
75
58
  let self = this;
76
59
  let data = {};
77
60
  for (let key in self.$refs) {
78
- let initValue = self.$refs[key]["attributes"]["init-value"];
79
- let valueType = self.$refs[key]["attributes"]["value-type"];
80
-
81
- if (key.startsWith("input.")) {
82
- if (initValue) {
83
- data[key.replace("input.", "")] = initValue["value"];
84
- } else {
85
- data[key.replace("input.", "")] = null;
86
- }
61
+ if (key.startsWith("input-component")) {
62
+ self.$refs[key].initialize()
87
63
  }
88
- if (key.startsWith("select.")) {
89
- if (key.startsWith("select.multiple.")) {
90
- if (initValue) {
91
- data[key.replace("select.multiple.", "")] = JSON.parse(initValue["value"]);
92
- } else {
93
- data[key.replace("select.multiple.", "")] = [];
94
- }
95
- } else {
96
- if (initValue) {
97
- if (valueType && valueType["value"] == "Integer") data[key.replace("select.", "")] = parseInt(initValue["value"]);
98
- else {
99
- data[key.replace("select.", "")] = initValue["value"];
100
- }
101
- } else {
102
- data[key.replace("select.", "")] = null;
103
- }
104
- }
64
+ if (key.startsWith("textarea-component")) {
65
+ self.$refs[key].initialize()
66
+ }
67
+ if (key.startsWith("select-component")) {
68
+ self.$refs[key].initialize()
69
+ }
70
+ if (key.startsWith("radio-component")) {
71
+ self.$refs[key].initialize()
72
+ }
73
+ if (key.startsWith("checkbox-component")) {
74
+ self.$refs[key].initialize()
105
75
  }
106
76
  }
107
- self.data = data;
108
77
  },
109
78
  shouldResetFormOnSuccessfulSubmit() {
110
79
  const self = this;