webface_rails 0.1.6

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 (45) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +37 -0
  4. data/Rakefile +7 -0
  5. data/app/controllers/webface_error_report_controller.rb +12 -0
  6. data/app/helpers/webface_component_helper.rb +248 -0
  7. data/app/helpers/webface_form.rb +161 -0
  8. data/app/views/webface_component_templates/_button.html.haml +2 -0
  9. data/app/views/webface_component_templates/_dialog_window.html.haml +6 -0
  10. data/app/views/webface_component_templates/_modal_window.html.haml +5 -0
  11. data/app/views/webface_component_templates/_simple_notification.html.haml +4 -0
  12. data/app/views/webface_components/_button.html.haml +11 -0
  13. data/app/views/webface_components/_checkbox.html.haml +21 -0
  14. data/app/views/webface_components/_editable_select.html.haml +8 -0
  15. data/app/views/webface_components/_field_hints.html.haml +7 -0
  16. data/app/views/webface_components/_hidden_form_field.html.haml +1 -0
  17. data/app/views/webface_components/_hint.html.haml +9 -0
  18. data/app/views/webface_components/_hint_trigger.html.haml +15 -0
  19. data/app/views/webface_components/_numeric_form_field.html.haml +13 -0
  20. data/app/views/webface_components/_password_form_field.html.haml +12 -0
  21. data/app/views/webface_components/_post_button_link.html.haml +4 -0
  22. data/app/views/webface_components/_radio.html.haml +18 -0
  23. data/app/views/webface_components/_select.html.haml +34 -0
  24. data/app/views/webface_components/_text_form_field.html.haml +12 -0
  25. data/app/views/webface_components/_text_form_field_with_validation.html.haml +6 -0
  26. data/app/views/webface_components/_textarea_form_field.html.haml +11 -0
  27. data/app/views/webface_components/shared/_editable_select_base.html.haml +34 -0
  28. data/lib/generators/templates/application.js +53 -0
  29. data/lib/generators/templates/mocha.css +10 -0
  30. data/lib/generators/templates/mocha.pug +15 -0
  31. data/lib/generators/templates/my_component.test.js +15 -0
  32. data/lib/generators/templates/run_webface_test +11 -0
  33. data/lib/generators/templates/test_animator.js +66 -0
  34. data/lib/generators/templates/test_utils.js +6 -0
  35. data/lib/generators/templates/webface.test.js +2 -0
  36. data/lib/generators/templates/webface_init.js +10 -0
  37. data/lib/generators/templates/webface_test_server.js +68 -0
  38. data/lib/generators/webface_generator.rb +81 -0
  39. data/lib/tasks/webface_rails_tasks.rake +3 -0
  40. data/lib/webface_rails.rb +11 -0
  41. data/lib/webface_rails/version.rb +3 -0
  42. data/spec/helpers/webface_component_helper_spec.rb +133 -0
  43. data/spec/helpers/webface_form_spec.rb +58 -0
  44. data/spec/spec_helper.rb +15 -0
  45. metadata +147 -0
@@ -0,0 +1,2 @@
1
+ = component_block :button, :button, { type: 'button', class: "button", data: { component_template: "ButtonComponent", component_attribute_properties: "lockable:data-lockable" }} do
2
+ = property :caption, "span"
@@ -0,0 +1,6 @@
1
+ .modalWindow{ data: { component_template: "DialogWindowComponent", component_attribute_properties: "close_on_escape,close_on_background_click,show_close_button" }}
2
+ .modal{ aria: { hidden: "true" } }
3
+ .modalDialog
4
+ = image_tag "close_modal_window.svg", class: "close", data: { component_part: "close" }
5
+ .content{ data: { component_part: "content"} }
6
+ .buttonContainer{ data: { component_part: "button_container"}}
@@ -0,0 +1,5 @@
1
+ .modalWindow{ data: { component_template: "ModalWindowComponent", component_attribute_properties: "close_on_escape,close_on_background_click,show_close_button" }}
2
+
3
+ .modal{ aria: { hidden: "true" } }
4
+ .modalDialog{ data: { component_part: "content"} }
5
+ = image_tag "close_modal_window.svg", class: "close", data: { component_part: "close" }
@@ -0,0 +1,4 @@
1
+ .simpleNotification{ data: { component_template: "SimpleNotificationComponent", component_attribute_properties: "autohide_delay,permanent,container_selector", component_roles: "simple_notification" }}
2
+ .container
3
+ .message{ data: { component_property: "message", component_part: "message" }}
4
+ = image_tag "close_simple_notification.svg", class: "close", data: { component_part: "close" }
@@ -0,0 +1,11 @@
1
+ - attrs[:data] ||= {}
2
+ - attrs[:data][:confirmation] = attrs[:confirmation]
3
+ - attrs[:data][:disabled] = options[:disabled]
4
+ - attrs[:data][:prevent_native_click_event] = "false"
5
+
6
+ = component_block (attrs[:confirmation] ? "confirmable_button" : "button"), :button, attrs.merge(type: (options[:type] || "submit"), name: options[:name], class: "button #{attrs[:class]}", property_attr: "lockable,disabled,confirmation,prevent_native_click_event") do
7
+ - if block
8
+ = capture(&block)
9
+ - else
10
+ = attrs[:caption] || attrs[:value]
11
+
@@ -0,0 +1,21 @@
1
+ - val = get_value_for_field_name(options)
2
+
3
+ %div.checkboxContainer
4
+ = hidden_field_tag options[:name], 0
5
+ = component_block :checkbox, "input",
6
+ name: options[:name],
7
+ id: options[:name],
8
+ type: "checkbox",
9
+ class: "#{attrs.delete(:class)} checkbox",
10
+ roles: attrs.delete(:roles),
11
+ tabindex: options[:tabindex] || 1,
12
+ property_attr: "name,disabled,checked",
13
+ data: { disabled: attrs[:disabled], checked: attrs[:checked], component_attribute_properties: "name:name,disabled:disabled,checked:checked" },
14
+ checked: (val.nil? ? attrs[:checked] : val),
15
+ disabled: attrs[:disabled]
16
+
17
+ %label{ for: options[:name] }
18
+ = options[:label]
19
+ - unless options[:popup_hint].blank?
20
+ = component :hint_trigger, i18n: options[:popup_hint]
21
+ = render partial: "components/field_hints", locals: { options: options }
@@ -0,0 +1,8 @@
1
+ = component_block :editable_select, "div",
2
+ class: (attrs.delete(:class) || "") + " field editableSelectComponent#{" errors" if options[:model] && options[:name] && !get_error_for_field_name(options).blank?}",
3
+ tabindex: options[:tabindex] || 1,
4
+ property_attr: 'disabled,name,fetch_url,allow_custom_value,query_param_name',
5
+ roles: attrs.delete(:roles),
6
+ data: { disabled: (attrs.delete(:disabled) ? "disabled" : nil), fetch_url: options[:fetch_url], allow_custom_value: options[:allow_custom_value], query_param_name: options[:query_param_name] } do
7
+
8
+ = render partial: "components/shared/editable_select_base", locals: { options: options, attrs: attrs }
@@ -0,0 +1,7 @@
1
+ .hintsContainer
2
+ .errorHint
3
+ = property :validation_errors_summary, "div", class: "validationErrors", style: ("display: block;" unless get_error_for_field_name(options).blank?) do
4
+ - if options[:model] && options[:attr_name] && errors = get_error_for_field_name(options)
5
+ = errors.join("; ")
6
+ .hint
7
+ = options[:hint]
@@ -0,0 +1 @@
1
+ = component_block (attrs[:as_property] ? nil : :form_field), "input", { type: "hidden", name: options[:name], value: get_value_for_field_name(options) }.merge(attrs)
@@ -0,0 +1,9 @@
1
+ = component_block :hint, "div",
2
+ { class: 'helpHint', property_attr: "anchor,show_events,force_show_events,autoshow_delay,autohide_delay,display_limit,keep_open_when_hover,hint_id", roles: attrs.delete(:roles),
3
+ data: data_attrs_and_values!(:attrs, binding, [:anchor,:show_events,:force_show_events,:autoshow_delay,:autohide_delay,:display_limit,:keep_open_when_hover,:hint_id]) } do
4
+
5
+ .arrowUp
6
+ .content
7
+ = image_tag "close_hint.svg", class: "close", data: { component_part: "close" }
8
+ = capture(&block)
9
+ .arrowDown
@@ -0,0 +1,15 @@
1
+ = image_tag "hint_trigger.svg", class: "hintTrigger", id: (id = "hint_#{attrs.delete(:id)}_#{attrs[:i18n].to_s.sha512_hash(Rails.application.class.parent_name.to_s, 15)}")
2
+
3
+ - attrs.merge!({anchor: id, show_events: "mouseover", force_show_events: "click,touchend", autohide_delay: 10, roles: "hint", display_limit: 1, hint_id: id})
4
+
5
+ = component :hint, attrs do
6
+ - if attrs.has_key?(:ma_term)
7
+ = MultiAsset.t("hints.#{attrs[:ma_term]}")
8
+ - elsif attrs.has_key?(:title)
9
+ = attrs[:title]
10
+ - else
11
+ = I18n.t("hints.#{attrs[:i18n]}")
12
+
13
+ - attrs.delete(:ma_term)
14
+ - attrs.delete(:title)
15
+ - attrs.delete(:i18n)
@@ -0,0 +1,13 @@
1
+ = component_block :numeric_form_field, "div",
2
+ { class: (attrs.delete(:class) || "") + " field numeric#{" errors" if options[:model] && !get_error_for_field_name(options).blank?}", property_attr: "max_length", data: { max_length: attrs.delete(:max_length) }}.merge(attrs) do
3
+
4
+ - if options[:label]
5
+ %label
6
+ = options[:label]
7
+ - unless options[:popup_hint].blank?
8
+ = component :hint_trigger, i18n: options[:popup_hint]
9
+
10
+ .fieldAndHint
11
+ %input{ type: "text", tabindex: options[:tabindex], name: options[:name], value: get_value_for_field_name(options), maxlength: attrs.delete(:maxlength), data: { component_part: 'value_holder', component_attribute_properties: "name:name,disabled:disabled"}}
12
+ = options[:suffix]
13
+ = render partial: "components/field_hints", locals: { options: options }
@@ -0,0 +1,12 @@
1
+ = component_block :form_field, "div",
2
+ { class: (attrs.delete(:class) || "") + " field text#{" errors" if options[:model] && !get_error_for_field_name(options).blank?}", property_attr: "disabled,max_length", data: { max_length: attrs.delete(:max_length) }}.merge(attrs) do
3
+
4
+ - if options[:label]
5
+ %label
6
+ = options[:label]
7
+ - unless options[:popup_hint].blank?
8
+ = component :hint_trigger, i18n: options[:popup_hint]
9
+
10
+ %input{ type: (attrs.delete(:type) || "password"), tabindex: options[:tabindex], name: options[:name], value: get_value_for_field_name(options), autocomplete: attrs.delete(:autocomplete), data: { component_part: 'value_holder', component_attribute_properties: "name:name" }}
11
+ = options[:suffix]
12
+ = render partial: "components/field_hints", locals: { options: options }
@@ -0,0 +1,4 @@
1
+ %form{ action: path, method: "POST" }
2
+ %input{ name: "_method", type: "hidden", value: verb }
3
+ %input{ name: "authenticity_token", type: "hidden", value: form_authenticity_token }
4
+ = component :button, { value: caption }.merge(options)
@@ -0,0 +1,18 @@
1
+ = component_block :radio_button, "div",
2
+ class: (attrs.delete(:class) || "") + " radio #{"errors" if options[:model] && !get_error_for_field_name(options).blank? }",
3
+ property_attr: "disabled,value",
4
+ roles: attrs.delete(:roles),
5
+ data: { max_length: attrs.delete(:max_length), disabled: options[:disabled], value: get_value_for_field_name(options) }.merge(attrs) do
6
+
7
+ .container
8
+ - prepare_select_collection(options[:collection] || get_value_for_field_name(options)).each do |k,v|
9
+ .item
10
+ - if options[:selected] == k
11
+ %input{ type: "radio", name: options[:name], id: "radio_#{options[:radio_options_id]}_#{k}", disabled: options[:disabled], value: k, checked: "checked", data: { component_part: 'option', component_attribute_properties: "name:name" }}
12
+ - else
13
+ %input{ type: "radio", name: options[:name], id: "radio_#{options[:radio_options_id]}_#{k}", disabled: options[:disabled], value: k, data: { component_part: 'option', component_attribute_properties: "name:name" }}
14
+ %label{ for: "radio_#{options[:radio_options_id]}_#{k}" }
15
+ = v
16
+ - unless options[:popup_hint].blank?
17
+ = component :hint_trigger, i18n: options[:popup_hint].delete_at(0)
18
+ = render partial: "components/field_hints", locals: { options: options }
@@ -0,0 +1,34 @@
1
+ = component_block (attrs[:roles] == "timezone_selector" ? "timezone_select" : "select"), :div,
2
+ class: (attrs.delete(:class) || "") + " field selectComponent#{" errors" if options[:model] && !get_error_for_field_name(options).blank?}",
3
+ tabindex: options[:tabindex] || 1,
4
+ property_attr: 'disabled,name,fetch_url,separators_below,top_values',
5
+ roles: attrs.delete(:roles),
6
+ data: { disabled: attrs.delete(:disabled) ? 'disabled' : nil, fetch_url: options[:fetch_url], name: options[:name], separators_below: options[:separators_below], top_values: options[:top_values] } do
7
+
8
+ - if options[:label]
9
+ %label
10
+ = options[:label]
11
+ - unless options[:popup_hint].blank?
12
+ = component :hint_trigger, i18n: options[:popup_hint]
13
+
14
+ = property "input_value", "input",
15
+ type: 'hidden',
16
+ data: { component_attribute_properties: 'input_value:value' },
17
+ name: options[:name], value: get_value_for_field_name(options) || select_component_get_selected_value(options, type=:input)
18
+
19
+ .selectBoxAndOptions
20
+
21
+ = component_part :selectbox, "div", class: "selectBox" do
22
+ = property "display_value", "div", class: "value" do
23
+ = (select_component_get_selected_value(options, :display) if select_component_get_selected_value(options).blank?)
24
+ = image_tag "arrow_down.svg", class: "arrow"
25
+ = image_tag "ajax_indicators/rolling_dark.svg", class: 'ajaxIndicator'
26
+
27
+ = component_part :options_container, "div", class: "optionsContainer" do
28
+ - options[:has_blank] = true if options[:has_blank].nil?
29
+ - prepare_select_collection(options[:collection], selected: options[:selected] || params[options[:name]], blank_option: options[:has_blank]).each do |k,v|
30
+ = component_part :option, "div", class: "option", data: { option_value: k } do
31
+ = v
32
+
33
+ = component_part :option_template, "div", class: "option"
34
+ = render partial: "components/field_hints", locals: { options: options }
@@ -0,0 +1,12 @@
1
+ = component_block :form_field, "div",
2
+ { class: (attrs.delete(:class) || "") + " field text#{" errors" if options[:model] && !get_error_for_field_name(options).blank?}", property_attr: "disabled", data: { disabled: options[:disabled] }}.merge(attrs) do
3
+
4
+ - if options[:label]
5
+ %label
6
+ = options[:label]
7
+ - unless options[:popup_hint].blank?
8
+ = component :hint_trigger, i18n: options[:popup_hint]
9
+
10
+ %input{ type: (attrs.delete(:type) || "text"), tabindex: options[:tabindex], name: options[:name], value: attrs[:value] || get_value_for_field_name(options), autofocus: attrs[:autofocus], disabled: options[:disabled], maxlength: attrs.delete(:maxlength), autocomplete: attrs.delete(:autocomplete), data: { component_part: 'value_holder', component_attribute_properties: "name:name" }}
11
+ = options[:suffix]
12
+ = render partial: "components/field_hints", locals: { options: options }
@@ -0,0 +1,6 @@
1
+ = component_block :form_field_with_validation, "div",
2
+ { class: (attrs.delete(:class) || "") + " field text#{" errors" if options[:model] && !get_error_for_field_name(options).blank?}", property_attr: "disabled,label", data: { disabled: options[:disabled], label: options[:label] } }.merge(attrs) do
3
+
4
+ %input{ type: (attrs.delete(:type) || "text"), tabindex: options[:tabindex], name: options[:name], value: attrs[:value] || get_value_for_field_name(options), autofocus: options[:autofocus], disabled: options[:disabled], maxlength: attrs.delete(:maxlength), autocomplete: attrs.delete(:autocomplete), data: { component_part: 'value_holder', component_attribute_properties: "name:name" } }
5
+ = options[:suffix]
6
+ = render partial: "components/field_hints", locals: { options: options }
@@ -0,0 +1,11 @@
1
+ = component_block :form_field, "div",
2
+ { class: (attrs.delete(:class) || "") + " field textarea#{" errors" if options[:model] && !get_error_for_field_name(options).blank?}", property_attr: "disabled"}.merge(attrs) do
3
+
4
+ - if options[:label]
5
+ %label
6
+ = options[:label]
7
+ - unless options[:popup_hint].blank?
8
+ = component :hint_trigger, i18n: options[:popup_hint]
9
+
10
+ %textarea{ name: options[:name], maxlength: attrs.delete(:maxlength), tabindex: options[:tabindex], data: { component_part: 'value_holder', component_attribute_properties: "name:name" }}= get_value_for_field_name(options)
11
+ = render partial: "components/field_hints", locals: { options: options }
@@ -0,0 +1,34 @@
1
+ - if options[:label]
2
+ %label
3
+ = options[:label]
4
+ - unless options[:popup_hint].blank?
5
+ = component :hint_trigger, i18n: options[:popup_hint]
6
+
7
+ .selectBoxAndOptions
8
+
9
+ = component_part :selectbox, "div", class: "selectBox" do
10
+ = property "display_value", "input",
11
+ type: 'text',
12
+ class: 'editableSelect',
13
+ placeholder: t("views.start_typing"),
14
+ autocomplete: "off",
15
+ maxlength: attrs.delete(:maxlength),
16
+ value: (select_component_get_selected_value(options, :display) if select_component_get_selected_value(options).blank?),
17
+ data: { component_attribute_properties: 'display_value:value', component_part: "display_input" }
18
+ = image_tag "ajax_indicators/rolling_dark.svg", class: 'ajaxIndicator'
19
+ = component_part :arrow, "span" do
20
+ = image_tag "arrow_down.svg", class: "arrow"
21
+ = property "input_value", "input", type: 'hidden', name: options[:name], value: get_value_for_field_name(options), data: { component_part: "input", component_attribute_properties: 'input_value:value' }
22
+
23
+ = component_part :options_container, "div", class: "optionsContainer" do
24
+ - prepare_select_collection(options[:collection] || [], selected: options[:selected] || params[options[:name]], blank_option: false).each do |k,v|
25
+ = component_part :option, "div", class: "option", data: { option_value: k } do
26
+ = v
27
+
28
+ .noOptionsFoundMessage
29
+ %p.main= t("views.components.editable_select.no_matching_items")
30
+ - if options[:allow_custom_value]
31
+ %p.customValue= t("views.components.editable_select.you_can_leave_typed")
32
+
33
+ = component_part :option_template, "div", class: "option"
34
+ = render partial: "components/field_hints", locals: { options: options }
@@ -0,0 +1,53 @@
1
+ // Loading Webface components one by one. We could load webface.js instead (in that case, we wouldn't need to define Webface variable below
2
+ // but we wanted to give more flexibility here. Remove components you don't actually intend to use. Less to load!
3
+ import { Logmaster } from './path_to_webface/logmaster.js'
4
+ import { Animator } from './path_to_webface/animator.js'
5
+ import { AjaxRequest } from './path_to_webface/ajax_request.js'
6
+ import { Component } from './path_to_webface/component.js'
7
+ import { RootComponent } from './path_to_webface/components/root_component.js'
8
+ import { ButtonComponent } from './path_to_webface/components/button_component.js'
9
+ import { CheckboxComponent } from './path_to_webface/components/checkbox_component.js'
10
+ import { FormFieldComponent } from './path_to_webface/components/form_field_component.js'
11
+ import { NumericFormFieldComponent } from './path_to_webface/components/numeric_form_field_component.js'
12
+ import { ModalWindowComponent } from './path_to_webface/components/modal_window_component.js'
13
+ import { DialogWindowComponent } from './path_to_webface/components/dialog_window_component.js'
14
+ import { RadioButtonComponent } from './path_to_webface/components/radio_button_component.js'
15
+ import { HintComponent } from './path_to_webface/components/hint_component.js'
16
+ import { SimpleNotificationComponent } from './path_to_webface/components/simple_notification_component.js'
17
+ import { SelectComponent } from './path_to_webface/components/select_component.js'
18
+ import { ConfirmableButtonComponent } from './path_to_webface/components/confirmable_button_component.js'
19
+ import { ContextMenuComponent } from './path_to_webface/components/context_menu_component.js'
20
+
21
+ // Import your own components like this:
22
+ // import { MyComponent } from "./components/my_component.js";
23
+
24
+ window.webface.substitute_classes = { "Animator": Animator }
25
+
26
+ // Change these settings to your liking
27
+ AjaxRequest.display_40x = true;
28
+ AjaxRequest.log_request = true;
29
+ AjaxRequest.log_response = true;
30
+ AjaxRequest.user_notification = (message) => {
31
+ // Put code that informs user of a bad ajax-request here
32
+ // By default if does nothing.
33
+ };
34
+
35
+ export var Webface = {
36
+
37
+ "init": (root_el=document.querySelector("body")) => {
38
+
39
+ window.webface["logger"] = new Logmaster({test_env: false, reporters: { "console" : "DEBUG", "http" : "ERROR" }, throw_errors: true, http_report_url: "/report_webface_error"})
40
+
41
+ try {
42
+ var root = new RootComponent();
43
+ root.dom_element = root_el;
44
+ root.initChildComponents();
45
+ } catch(e) {
46
+ window.webface.logger.capture(e, { log_level: "ERROR" });
47
+ }
48
+
49
+ },
50
+
51
+ }
52
+
53
+ Webface.init();
@@ -0,0 +1,10 @@
1
+ body {
2
+ font-family: "Ubuntu Mono", Arial; font-size: 70%;
3
+ }
4
+
5
+ li h2 { font-weight: normal; cursor: pointer; }
6
+ li span.duration { padding-left: 2em; font-weight: normal; }
7
+ li pre { font-size: 150%; margin-left: 2em; background-color: #ddd; border-left: solid 4px #aaa; padding: 1em; }
8
+
9
+ li.fail h2 { color: #D11111; padding: 0.2em; font-weight: bold; }
10
+ li.fail pre { background-color: #FFCFCF; color: #AD0000; border-left: solid 4px #FF6161; }
@@ -0,0 +1,15 @@
1
+ doctype html
2
+ html
3
+ head
4
+ meta(charset="utf-8")
5
+ link(rel="stylesheet" href="mocha.css")
6
+ body
7
+ #mocha
8
+ script(src="node_modules/chai/chai.js")
9
+ script(src="node_modules/chai-spies/chai-spies.js")
10
+ script(src="node_modules/mocha/mocha.js")
11
+ script.
12
+ mocha.setup('bdd')
13
+ script(src=file, type="module")
14
+ script(type="module").
15
+ mocha.checkLeaks(); mocha.run();
@@ -0,0 +1,15 @@
1
+ import '../webface_init.js'
2
+
3
+ // Uncomment and replace with the component you're testing
4
+ //
5
+ import { MyComponent } from '../source/components/my_component.js'
6
+
7
+ describe("MyComponent", async function() {
8
+
9
+ it("test", function() {
10
+ // Uncomment to see if a new component instance can be created and component class has been loaded sucessfully.
11
+ //
12
+ new MyComponent();
13
+ });
14
+
15
+ });
@@ -0,0 +1,11 @@
1
+ #!/bin/bash
2
+ DIRECTORY=$(cd `dirname $0` && pwd)
3
+ cd $DIRECTORY
4
+ ls
5
+ echo $DIRECTORY
6
+ node test_server.js &
7
+ server_pid=$!
8
+ echo Server id is $server_pid
9
+ npm test -- -f http://localhost:8080?file=$1
10
+ kill $server_pid
11
+ cd -
@@ -0,0 +1,66 @@
1
+ // This class exists to not apply animations when in unit tests, since we can't really
2
+ // wait for animation Promises to resovle. Instead, we use this custom TestAnimator
3
+ // to show/hide elements and resolve Promises immediately.
4
+ export var TestAnimator = {
5
+
6
+ // IMPORTANT: we have to use a manual Promise creation inside animation methods here
7
+ // because browsers don't yet support Animation.finished. See https://developer.mozilla.org/en-US/docs/Web/API/Animation/finished
8
+
9
+ "show": function(elements, ms, { display_value="block" }={}) {
10
+ return this._applyToCollection(elements, (el) => {
11
+ el.style.display = "block";
12
+ el.style.opacity = "1";
13
+ return new Promise((resolve, reject) => {
14
+ resolve("show behavior resolved");
15
+ });
16
+ });
17
+ },
18
+
19
+ "hide": function(elements, ms) {
20
+ return this._applyToCollection(elements, (el) => {
21
+ el.style.display = "none";
22
+ el.style.opacity = "0";
23
+ return new Promise((resolve, reject) => {
24
+ resolve("hide behavior resolved");
25
+ });
26
+ });
27
+ },
28
+
29
+ "scrollDown": function(elements, ms, { display_value="block" }={}) {
30
+ return this._applyToCollection(elements, (el) => {
31
+ el.style.display = "block";
32
+ el.style.opacity = "1";
33
+ return new Promise((resolve, reject) => {
34
+ resolve("scrollDown behavior resolved");
35
+ });
36
+ });
37
+ },
38
+
39
+ "scrollUp": function(elements, ms) {
40
+ return this._applyToCollection(elements, (el) => {
41
+ el.style.display = "none";
42
+ el.style.opacity = "0";
43
+ return new Promise((resolve, reject) => {
44
+ resolve("scrollUp behavior resolved");
45
+ });
46
+ });
47
+ },
48
+
49
+ "_applyToCollection": function(elements, func) {
50
+ if(!(elements instanceof Array))
51
+ elements = [elements];
52
+
53
+ elements = elements.map((el) => {
54
+ if(/Component$/.test(el.constructor.name))
55
+ return el.dom_element;
56
+ else
57
+ return el;
58
+ });
59
+
60
+ return Promise.all(elements.map((el) => {
61
+ return func(el);
62
+ }));
63
+
64
+ }
65
+
66
+ }