fdbq-rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +49 -0
- data/Rakefile +22 -0
- data/app/assets/config/fdbq_rails_manifest.js +2 -0
- data/app/assets/javascripts/fdbq.js +1 -0
- data/app/assets/stylesheets/fdbq.css +3 -0
- data/app/controllers/fdbq/feedback_controller.rb +20 -0
- data/app/models/fdbq/feedback.rb +20 -0
- data/app/views/fdbq/feedback/create.json.jbuilder +7 -0
- data/config/routes.rb +3 -0
- data/lib/fdbq/fdbq.rb +19 -0
- data/lib/fdbq/plugin.rb +42 -0
- data/lib/fdbq/question.rb +23 -0
- data/lib/fdbq/rails/engine.rb +17 -0
- data/lib/fdbq/rails/helpers.rb +13 -0
- data/lib/fdbq/rails/version.rb +5 -0
- data/lib/fdbq/rails.rb +29 -0
- data/lib/fdbq/railtie.rb +4 -0
- data/lib/fdbq/version.rb +3 -0
- data/lib/fdbq-rails.rb +3 -0
- data/lib/generators/fdbq/rails/install_generator.rb +37 -0
- data/lib/generators/templates/create_fdbq_feedback.rb +9 -0
- data/lib/generators/templates/fdbq.rb +5 -0
- data/lib/generators/templates/fdbq.yml +23 -0
- data/vendor/assets/javascripts/@softserveopensource/fdbq/README.md +120 -0
- data/vendor/assets/javascripts/@softserveopensource/fdbq/index.js +393 -0
- data/vendor/assets/javascripts/@softserveopensource/fdbq/package.json +33 -0
- data/vendor/assets/javascripts/@softserveopensource/fdbq/theme.css +228 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f9195b6626e0cf218de00b495a3617f1cb460f8a50fbee7add7c93b5e44ebccc
|
4
|
+
data.tar.gz: 2d8c3c1606629fa1ed0e934168edd8fba4a699fcc7dfd3a4cd5af0f203014784
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5c98ef23951c9f362f4da0c4a8eb0922615bae697f1b2cc6281277da879dc33ca016c2f1258ab3ae68f1d2c40805810ef881769f74cdc7bf96ef3466d73e0770
|
7
|
+
data.tar.gz: 69cc776c15caaa9acc52b9286be0706870c36fe4711f19e2c2acdc0cf443773c60dd7f00c7e81d7299673e215b59b2d842b5c5e397c25aeeb9379a571a62fa89
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2020 Roman Solomud
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Fdbq::Rails
|
2
|
+
|
3
|
+
A Rails integration of <TBD> with an ORM recording of submits.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
##### Terminal
|
8
|
+
|
9
|
+
`gem install fdbq-rails`
|
10
|
+
|
11
|
+
##### Gemfile
|
12
|
+
|
13
|
+
`gem 'fdbq-rails'`
|
14
|
+
|
15
|
+
##### Rails
|
16
|
+
|
17
|
+
Generate configuration files
|
18
|
+
+ `rails g fdbq:rails:install`
|
19
|
+
|
20
|
+
Mount into your application
|
21
|
+
+ update `config/routes.rb` with `mount Fdbq::Rails::Engine, at: '<your path>'`
|
22
|
+
|
23
|
+
Update configuration for fton-end
|
24
|
+
+ update `config/fdbq.yml` configuration file
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
|
29
|
+
Aadd to your layout or view plugin
|
30
|
+
+ `fdbq_render`
|
31
|
+
|
32
|
+
To fetch list of submitted responses use
|
33
|
+
+ `fdbq_responses` helper that works within controller, helper or view.
|
34
|
+
or
|
35
|
+
+ `Fdbq::Feedback.all` to query directly thru ActiveRecord model.
|
36
|
+
|
37
|
+
## Contributing
|
38
|
+
|
39
|
+
- fork it
|
40
|
+
- commit
|
41
|
+
- submit PR
|
42
|
+
|
43
|
+
## TODO
|
44
|
+
|
45
|
+
- [ ] Add JS plugin
|
46
|
+
- [ ] Add MongoDB support
|
47
|
+
|
48
|
+
## License
|
49
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Fdbq::Rails'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
load 'rails/tasks/statistics.rake'
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1 @@
|
|
1
|
+
//= require @softserveopensource/fdbq
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Fdbq
|
2
|
+
class FeedbackController < Fdbq::Rails.controller_parent.constantize
|
3
|
+
skip_before_action :verify_authenticity_token, only: :create
|
4
|
+
|
5
|
+
def create
|
6
|
+
@feedback = Fdbq::Feedback.new(fields: permitted_params.to_h)
|
7
|
+
@feedback.save
|
8
|
+
|
9
|
+
respond_to do |format|
|
10
|
+
format.json { render(status: @feedback.persisted? ? 201 : 422) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def permitted_params
|
17
|
+
params.require(Fdbq.param_key).permit(*params[Fdbq.param_key]&.keys)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Fdbq
|
2
|
+
class Feedback < Rails.model_parent.constantize
|
3
|
+
self.table_name = "fdbq_feedback"
|
4
|
+
|
5
|
+
serialize :fields, Hash
|
6
|
+
|
7
|
+
|
8
|
+
validate :required_fields
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def required_fields
|
13
|
+
Fdbq.questions.select(&:required?).each do |question|
|
14
|
+
next if question.answered?(fields.to_h)
|
15
|
+
|
16
|
+
self.errors.add(:fields, :invalid)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/config/routes.rb
ADDED
data/lib/fdbq/fdbq.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'fdbq/plugin'
|
2
|
+
|
3
|
+
module Fdbq
|
4
|
+
def self.param_key
|
5
|
+
Plugin.instance.param_key
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.questions
|
9
|
+
Plugin.instance.questions
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.configure(&block)
|
13
|
+
Plugin.instance.instance_eval(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.current_instance
|
17
|
+
Plugin.instance.settings
|
18
|
+
end
|
19
|
+
end
|
data/lib/fdbq/plugin.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
require 'fdbq/question'
|
5
|
+
|
6
|
+
module Fdbq
|
7
|
+
class Plugin
|
8
|
+
DEFAULT_PARAM_KEY = :feedback
|
9
|
+
|
10
|
+
attr_accessor :config_file_path
|
11
|
+
attr_accessor :param_key
|
12
|
+
|
13
|
+
include ::Singleton
|
14
|
+
|
15
|
+
instance.param_key = DEFAULT_PARAM_KEY
|
16
|
+
|
17
|
+
def reset!
|
18
|
+
self.param_key = DEFAULT_PARAM_KEY
|
19
|
+
self.config_file_path = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def questions
|
23
|
+
config.dig('questions').to_a.map(&Question.method(:new))
|
24
|
+
end
|
25
|
+
|
26
|
+
def settings
|
27
|
+
config.tap do |c|
|
28
|
+
c['questions']&.map! { |el| el.merge('name' => build_param_key(el['name'])) }
|
29
|
+
|
30
|
+
c['submit'] = { url: Rails::Engine.routes.url_helpers.feedback_path }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def config
|
35
|
+
YAML.load_file(config_file_path).to_h
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_param_key(question_name)
|
39
|
+
"#{param_key}[#{question_name}]"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Fdbq
|
2
|
+
class Question
|
3
|
+
attr_accessor :label, :name, :value, :placeholder, :type, :required, :hint
|
4
|
+
|
5
|
+
def initialize(attrs = {})
|
6
|
+
attrs.to_h.each_pair do |k, v|
|
7
|
+
self.send("#{k}=", v)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def param_key
|
12
|
+
name.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def answered?(answers)
|
16
|
+
answers.to_h.stringify_keys[param_key].present?
|
17
|
+
end
|
18
|
+
|
19
|
+
def required?
|
20
|
+
!!required
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Fdbq
|
2
|
+
module Rails
|
3
|
+
class Engine < ::Rails::Engine
|
4
|
+
isolate_namespace Fdbq
|
5
|
+
engine_name 'fdbq'
|
6
|
+
|
7
|
+
initializer "fdbq.assets.precompile" do |app|
|
8
|
+
app.config.assets.precompile += %w( fdbq.* )
|
9
|
+
end
|
10
|
+
|
11
|
+
initializer "fdbq.engine" do |app|
|
12
|
+
ActionController::Base.send :include, Fdbq::Rails::Helpers
|
13
|
+
ActionView::Base.send :include, Fdbq::Rails::Helpers
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/fdbq/rails.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'jbuilder'
|
2
|
+
|
3
|
+
require "fdbq/fdbq"
|
4
|
+
require "fdbq/rails/engine"
|
5
|
+
require 'fdbq/rails/helpers'
|
6
|
+
|
7
|
+
module Fdbq
|
8
|
+
module Rails
|
9
|
+
def self.controller_parent
|
10
|
+
if latest?
|
11
|
+
'ApplicationController'
|
12
|
+
else
|
13
|
+
'ActionController::Base'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.model_parent
|
18
|
+
if latest?
|
19
|
+
'ApplicationRecord'
|
20
|
+
else
|
21
|
+
'ActiveRecord::Base'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.latest?
|
26
|
+
::Rails.version.to_i >= 5
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/fdbq/railtie.rb
ADDED
data/lib/fdbq/version.rb
ADDED
data/lib/fdbq-rails.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rails/generators/migration'
|
2
|
+
|
3
|
+
module Fdbq
|
4
|
+
module Rails
|
5
|
+
module Generators
|
6
|
+
class InstallGenerator < ::Rails::Generators::Base
|
7
|
+
include ::Rails::Generators::Migration
|
8
|
+
|
9
|
+
source_root File.expand_path("../../../templates", __FILE__)
|
10
|
+
|
11
|
+
desc "This generator creates a plugin configuration files and a migration"
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def next_migration_number(dirname)
|
15
|
+
if ActiveRecord::Base.timestamped_migrations
|
16
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
17
|
+
else
|
18
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def copy_config
|
24
|
+
template "fdbq.yml", "config/fdbq.yml"
|
25
|
+
end
|
26
|
+
|
27
|
+
def copy_migration
|
28
|
+
migration_template "create_fdbq_feedback.rb", "db/migrate/create_fdbq_feedback.rb"
|
29
|
+
end
|
30
|
+
|
31
|
+
def copy_initializer
|
32
|
+
template "fdbq.rb", "config/initializers/fdbq.rb"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
placement: bottom right
|
2
|
+
modal:
|
3
|
+
title:
|
4
|
+
subHeader:
|
5
|
+
title:
|
6
|
+
description:
|
7
|
+
questions:
|
8
|
+
# -
|
9
|
+
# name: # [String]
|
10
|
+
# label: # [String]
|
11
|
+
# value: # [String]
|
12
|
+
# placeholder: # [String]
|
13
|
+
# type: # [String]
|
14
|
+
# required: # [Boolean]
|
15
|
+
# hint: # [String]
|
16
|
+
# -
|
17
|
+
# name: # [String]
|
18
|
+
# label: # [String]
|
19
|
+
# value: # [String]
|
20
|
+
# placeholder: # [String]
|
21
|
+
# type: # [String]
|
22
|
+
# required: # [Boolean]
|
23
|
+
# hint: # [String]
|
@@ -0,0 +1,120 @@
|
|
1
|
+
## Table of contents
|
2
|
+
|
3
|
+
- [Installation](#installation)
|
4
|
+
- [Configuration](#configuration)
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
* Add package to a project
|
9
|
+
|
10
|
+
* using Yarn
|
11
|
+
|
12
|
+
`yarn add fdbq`
|
13
|
+
|
14
|
+
* using NPM
|
15
|
+
|
16
|
+
`npm i -S fdbq`
|
17
|
+
|
18
|
+
* Add javascript file
|
19
|
+
|
20
|
+
`import "fdbq"`
|
21
|
+
|
22
|
+
* Include default theme (optional)
|
23
|
+
|
24
|
+
`import "fdbq/theme.css"`
|
25
|
+
|
26
|
+
* Enable on a page
|
27
|
+
|
28
|
+
```
|
29
|
+
var instance = new Fdbq(config);
|
30
|
+
instance.init();
|
31
|
+
```
|
32
|
+
|
33
|
+
## Configuration
|
34
|
+
|
35
|
+
```
|
36
|
+
{
|
37
|
+
mountNode: "body",
|
38
|
+
placement: "bottom right",
|
39
|
+
modal: {
|
40
|
+
title: "Help us get better",
|
41
|
+
},
|
42
|
+
submit: {
|
43
|
+
url: "http://localhost:3000/api/test-feedback"
|
44
|
+
},
|
45
|
+
subHeader: {
|
46
|
+
title: "Feedback",
|
47
|
+
description: "Please take a short survey"
|
48
|
+
},
|
49
|
+
questions: [
|
50
|
+
{
|
51
|
+
name: "feedback[usage]",
|
52
|
+
label: "Please share your feedback upon usage of Practice Dashboard",
|
53
|
+
value: "",
|
54
|
+
placeholder: "Type here",
|
55
|
+
type: "text",
|
56
|
+
required: true,
|
57
|
+
hint: "Please, describe your opinion about the service we provide."
|
58
|
+
}
|
59
|
+
]
|
60
|
+
}
|
61
|
+
```
|
62
|
+
|
63
|
+
##### Instance configuration
|
64
|
+
|
65
|
+
| Setting | Required | Type | Options | Default | Description |
|
66
|
+
| ------------------------ | -------- | ------------------- | -------------------------------- | --------- | ----------------------------- |
|
67
|
+
| `mountNode` | No | String | |`"body" ` | Root node to mount lugin into |
|
68
|
+
| `placement` | Yes | String | `bottom`, `top`, `left`, `right` | | Button placement on a screen |
|
69
|
+
| `modal` | No | Object | | | Modal settings |
|
70
|
+
| `submit` | Yes | Object | | `func` | Modal submit handler. Default handler submit on `url` |
|
71
|
+
| `subHeader` | No | String | | | Modal body sub-header |
|
72
|
+
| `questions` | No | Array[Object] | | `[]` | List of questions for a modal |
|
73
|
+
|
74
|
+
##### Modal configuration `modal`
|
75
|
+
|
76
|
+
| Setting | Required | Type | Options | Default | Description |
|
77
|
+
| -------- | -------- | ------------------- | -------------------------------- | --------- | ----------- |
|
78
|
+
| `title` | No | String | |`"" ` | Modal title |
|
79
|
+
|
80
|
+
##### Modal configuration `subHeader`
|
81
|
+
|
82
|
+
| Setting | Required | Type | Options | Default | Description |
|
83
|
+
| ------------- | -------- | ------------------- | -------------------------------- | --------- | ---------------------- |
|
84
|
+
| `title` | No | String | |`"" ` | Modal body title |
|
85
|
+
| `description` | No | String | |`"" ` | Modal body description |
|
86
|
+
|
87
|
+
##### Modal configuration `submit`
|
88
|
+
|
89
|
+
* as `Function`
|
90
|
+
|
91
|
+
```
|
92
|
+
{
|
93
|
+
// ...
|
94
|
+
submit: function(fields, instance) {
|
95
|
+
console.log(fields, instance);
|
96
|
+
},
|
97
|
+
// ...
|
98
|
+
}
|
99
|
+
```
|
100
|
+
|
101
|
+
* as `Object`
|
102
|
+
|
103
|
+
| Setting | Required | Type | Options | Default | Description |
|
104
|
+
| ------- | -------- | ------------------- | -------------------------------- | --------- | ---------------------------------------------- |
|
105
|
+
| `url` | Yes | String | |`"" ` | Submit url for the form. (Uses `POST` request) |
|
106
|
+
|
107
|
+
##### Modal configuration `questions`
|
108
|
+
|
109
|
+
| Setting | Required | Type | Options | Default | Description |
|
110
|
+
| ------------- | -------- | ------- | ---------------- | ------- | ---------------------------------------------- |
|
111
|
+
| `name` | Yes | String | | | Name for a input field. Used as submit param |
|
112
|
+
| `label` | Yes | String | | | Input field label |
|
113
|
+
| `value` | No | String | | | Default value for the input |
|
114
|
+
| `placeholder` | No | String | | | Input placeholder |
|
115
|
+
| `type` | Yes | String | `Text`, `String` | | Input field type. Rendered in different style |
|
116
|
+
| `required` | No | Boolean | | `false` | Input required flag |
|
117
|
+
| `hint` | No | String | | | Hint text for input field |
|
118
|
+
|
119
|
+
|
120
|
+
> Check out a configuration example in `examples/basic/index.html`
|
@@ -0,0 +1,393 @@
|
|
1
|
+
(function() {
|
2
|
+
var TOGGLE_REGEX = /(^show | show | show$)/i;
|
3
|
+
var SUCCESS_STATUS_CODE = /^20\d$/;
|
4
|
+
|
5
|
+
function parameterize(object) {
|
6
|
+
var parts = [];
|
7
|
+
|
8
|
+
for(var prop in object) {
|
9
|
+
parts.push(encodeURIComponent(prop)+"="+encodeURIComponent(object[prop]));
|
10
|
+
}
|
11
|
+
|
12
|
+
return parts.join("&");
|
13
|
+
}
|
14
|
+
|
15
|
+
function questionID(config) { return (config.name+"").replace(/[^\w\-]+/ig, ""); }
|
16
|
+
|
17
|
+
function queryField(fieldID, instance) {
|
18
|
+
return document.querySelector('#'+instance.modalSelector+' [data-id="'+fieldID+'"][data-fdbq="field-container"]');
|
19
|
+
}
|
20
|
+
|
21
|
+
function arrayFind(array, prop, value) {
|
22
|
+
for(var i = 0; i < array.length; i++) {
|
23
|
+
if(array[i] && array[i][prop] === value) return array[i];
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
function validField(field, config) {
|
28
|
+
var isValid = true;
|
29
|
+
|
30
|
+
if(!field) return false;
|
31
|
+
|
32
|
+
if(config.required && !field.value) isValid = false;
|
33
|
+
|
34
|
+
return isValid;
|
35
|
+
}
|
36
|
+
|
37
|
+
function buildField(type, options) {
|
38
|
+
var node = document.createElement("div");
|
39
|
+
node.className = FdbqClassName("fdbq-field", "-"+type, options.error ? "has-errors" : "");
|
40
|
+
node.dataset.fdbq = "field-container";
|
41
|
+
node.dataset.id = questionID(options);
|
42
|
+
|
43
|
+
return node;
|
44
|
+
}
|
45
|
+
|
46
|
+
function buildInput(type, props, options) {
|
47
|
+
var node = document.createElement(type);
|
48
|
+
|
49
|
+
for(var prop in props) {
|
50
|
+
node[prop] = props[prop];
|
51
|
+
}
|
52
|
+
|
53
|
+
node.className = FdbqClassName("fdbq-input", node.className);
|
54
|
+
node.dataset.fdbq = "field";
|
55
|
+
|
56
|
+
return node;
|
57
|
+
}
|
58
|
+
|
59
|
+
function httpSubmit(params, instance) {
|
60
|
+
if(!instance.config.submit.url) return;
|
61
|
+
|
62
|
+
var xmlHttp = new XMLHttpRequest();
|
63
|
+
|
64
|
+
xmlHttp.onreadystatechange = function() {
|
65
|
+
if(xmlHttp.readyState == 4 && SUCCESS_STATUS_CODE.test(xmlHttp.status)) {
|
66
|
+
instance.onCloseClick();
|
67
|
+
}
|
68
|
+
};
|
69
|
+
|
70
|
+
xmlHttp.open("POST", instance.config.submit.url, true);
|
71
|
+
xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
72
|
+
xmlHttp.send(parameterize(params));
|
73
|
+
}
|
74
|
+
|
75
|
+
function renderModalBody(node, instance) {
|
76
|
+
node.appendChild(FdbqModalHead(instance));
|
77
|
+
node.appendChild(FdbqModalBody(instance));
|
78
|
+
}
|
79
|
+
|
80
|
+
function renderModalFields(container, instance) {
|
81
|
+
for(var i = 0; i < instance.config.questions.length; i++) {
|
82
|
+
var field = FdbqField(instance.config.questions[i], instance);
|
83
|
+
|
84
|
+
if(!field) continue;
|
85
|
+
|
86
|
+
container.appendChild(field);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
function renderFields(node, instance) {
|
91
|
+
if(!Array.isArray(instance.config.questions) || !instance.config.questions.length) return;
|
92
|
+
|
93
|
+
child = document.createElement("div");
|
94
|
+
child.className = "fdbq-modal-fields";
|
95
|
+
child.id = instance.fieldsContainerSelector;
|
96
|
+
|
97
|
+
renderModalFields(child, instance);
|
98
|
+
|
99
|
+
node.appendChild(child);
|
100
|
+
}
|
101
|
+
|
102
|
+
function FdbqFieldsReplaceInvalid(instance) {
|
103
|
+
if(!instance.errors || !instance.config || !instance.config.questions) return;
|
104
|
+
|
105
|
+
var fields = document.querySelector('#'+instance.modalSelector+' .fdbq-modal-fields');
|
106
|
+
|
107
|
+
if(!fields) return;
|
108
|
+
|
109
|
+
for(var i = 0; i < instance.errors.length; i++) {
|
110
|
+
var fieldID = instance.errors[i];
|
111
|
+
var config = arrayFind(instance.config.questions, function(el) { return questionID(el) === fieldID; });
|
112
|
+
var field = queryField(fieldID, instance);
|
113
|
+
|
114
|
+
if(!config || !field) continue;
|
115
|
+
|
116
|
+
fields.replaceChild(FdbqField(config, instance), field);
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
function FdbqUUID(id) { return id+"-"+Math.random().toString(36).substr(2, 9); };
|
121
|
+
function FdbqClassName() {
|
122
|
+
var classList = [];
|
123
|
+
|
124
|
+
for(var i = 0; i < arguments.length; i++) {
|
125
|
+
if(arguments[i]) classList.push(arguments[i]);
|
126
|
+
}
|
127
|
+
|
128
|
+
return classList.join(" ");
|
129
|
+
}
|
130
|
+
|
131
|
+
function FdbqFieldsReset(instance) {
|
132
|
+
var fields = document.getElementById(instance.fieldsContainerSelector);
|
133
|
+
|
134
|
+
if(!fields) return;
|
135
|
+
|
136
|
+
fields.innerHTML = "";
|
137
|
+
renderModalFields(fields, instance);
|
138
|
+
}
|
139
|
+
|
140
|
+
function FdbqTextField(config, instance) {
|
141
|
+
var child;
|
142
|
+
var inputID = questionID(config);
|
143
|
+
var node = buildField("text", Object.assign({}, config, { error: instance.errors.indexOf(inputID) >= 0 }));
|
144
|
+
var fieldID = FdbqUUID("fdbq-field-text-"+inputID);
|
145
|
+
|
146
|
+
if(config.label) {
|
147
|
+
child = document.createElement("label");
|
148
|
+
child.innerHTML = (config.required ? '<abbr title="required">*</abbr>' : "")+config.label;
|
149
|
+
child.htmlFor = fieldID;
|
150
|
+
|
151
|
+
node.appendChild(child);
|
152
|
+
}
|
153
|
+
|
154
|
+
node.appendChild(buildInput("textarea", {
|
155
|
+
id: fieldID,
|
156
|
+
rows: config.rows ? parseInt(config.rows, 10) : 3,
|
157
|
+
value: config.value || "",
|
158
|
+
placeholder: config.placeholder
|
159
|
+
}, { type: "text", id: inputID }));
|
160
|
+
|
161
|
+
if(config.hint) {
|
162
|
+
child = document.createElement("div");
|
163
|
+
child.className = "fdbq-field-hint";
|
164
|
+
child.innerHTML = config.hint;
|
165
|
+
|
166
|
+
node.appendChild(child);
|
167
|
+
}
|
168
|
+
|
169
|
+
return node;
|
170
|
+
}
|
171
|
+
|
172
|
+
function FdbqStringField(config, instance) {
|
173
|
+
var child;
|
174
|
+
var inputID = questionID(config);
|
175
|
+
var node = buildField("string", Object.assign({}, config, { error: instance.errors.indexOf(inputID) >= 0 }));
|
176
|
+
var fieldID = FdbqUUID("fdbq-field-string-"+inputID);
|
177
|
+
|
178
|
+
if(config.label) {
|
179
|
+
child = document.createElement("label");
|
180
|
+
child.innerHTML = (config.required ? '<abbr title="required">*</abbr>' : "")+config.label;
|
181
|
+
child.htmlFor = fieldID;
|
182
|
+
|
183
|
+
node.appendChild(child);
|
184
|
+
}
|
185
|
+
|
186
|
+
node.appendChild(buildInput("input", {
|
187
|
+
id: fieldID,
|
188
|
+
type: "text",
|
189
|
+
rows: config.rows ? parseInt(config.rows, 10) : 3,
|
190
|
+
value: config.value || "",
|
191
|
+
placeholder: config.placeholder
|
192
|
+
}, { error: instance.errors.indexOf(config.id) >= 0, type: "string", id: config.id }));
|
193
|
+
|
194
|
+
if(config.hint) {
|
195
|
+
child = document.createElement("div");
|
196
|
+
child.className = "fdbq-field-hint";
|
197
|
+
child.innerHTML = config.hint;
|
198
|
+
|
199
|
+
node.appendChild(child);
|
200
|
+
}
|
201
|
+
|
202
|
+
return node;
|
203
|
+
}
|
204
|
+
|
205
|
+
function FdbqField(config, instance) {
|
206
|
+
switch(config.type) {
|
207
|
+
case "text":
|
208
|
+
return FdbqTextField(config, instance);
|
209
|
+
default:
|
210
|
+
return FdbqStringField(config, instance);
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
214
|
+
function FdbqModalHead(instance) {
|
215
|
+
var node = document.createElement("div");
|
216
|
+
node.className = "fdbq-modal-head";
|
217
|
+
|
218
|
+
// heading;
|
219
|
+
var heading = document.createElement("h4");
|
220
|
+
heading.innerText = instance.config.modal && instance.config.modal.title || "";
|
221
|
+
|
222
|
+
// close button;
|
223
|
+
var closeButton = document.createElement("button");
|
224
|
+
closeButton.innerHTML = "×";
|
225
|
+
closeButton.className = "close";
|
226
|
+
closeButton.id = instance.modalCloseSelector;
|
227
|
+
|
228
|
+
node.appendChild(heading);
|
229
|
+
node.appendChild(closeButton);
|
230
|
+
|
231
|
+
return node;
|
232
|
+
}
|
233
|
+
|
234
|
+
function FdbqModalBodyHeading(instance) {
|
235
|
+
if(!instance.config.subHeader) return null;
|
236
|
+
|
237
|
+
var node = document.createElement("div");
|
238
|
+
node.className = "fdbq-modal-heading";
|
239
|
+
|
240
|
+
// heading;
|
241
|
+
if(instance.config.subHeader.title) {
|
242
|
+
var child = document.createElement("h5");
|
243
|
+
child.className = "sub-heading";
|
244
|
+
child.innerText = instance.config.subHeader.title;
|
245
|
+
|
246
|
+
node.appendChild(child);
|
247
|
+
}
|
248
|
+
|
249
|
+
// sub-title;
|
250
|
+
if(instance.config.subHeader.description) {
|
251
|
+
child = document.createElement("div");
|
252
|
+
child.className = "sub-heading-description";
|
253
|
+
child.innerHTML = instance.config.subHeader.description;
|
254
|
+
|
255
|
+
node.appendChild(child);
|
256
|
+
}
|
257
|
+
|
258
|
+
return node;
|
259
|
+
}
|
260
|
+
|
261
|
+
function FdbqActions(instance) {
|
262
|
+
var node = document.createElement("div");
|
263
|
+
node.className = "fdbq-modal-actions";
|
264
|
+
|
265
|
+
var child = document.createElement("button");
|
266
|
+
child.className = "submit";
|
267
|
+
child.id = instance.modalSubmitSelector;
|
268
|
+
child.innerText = "Submit";
|
269
|
+
|
270
|
+
node.appendChild(child);
|
271
|
+
|
272
|
+
return node;
|
273
|
+
}
|
274
|
+
|
275
|
+
function FdbqModalBody(instance) {
|
276
|
+
var node = document.createElement("div");
|
277
|
+
node.className = "fdbq-modal-body";
|
278
|
+
|
279
|
+
var child = FdbqModalBodyHeading(instance);
|
280
|
+
|
281
|
+
if(child) node.appendChild(child);
|
282
|
+
|
283
|
+
renderFields(node, instance);
|
284
|
+
|
285
|
+
child = FdbqActions(instance);
|
286
|
+
|
287
|
+
if(child) node.appendChild(child);
|
288
|
+
|
289
|
+
return node;
|
290
|
+
}
|
291
|
+
|
292
|
+
function FdbqModal(instance) {
|
293
|
+
var overlay = document.createElement("div");
|
294
|
+
var node = document.createElement("div");
|
295
|
+
node.className = "fdbq-modal";
|
296
|
+
node.id = instance.modalContainerSelector;
|
297
|
+
|
298
|
+
renderModalBody(node, instance);
|
299
|
+
|
300
|
+
overlay.className = "fdbq-modal-overlay";
|
301
|
+
overlay.appendChild(node);
|
302
|
+
overlay.id = instance.modalSelector;
|
303
|
+
|
304
|
+
return overlay;
|
305
|
+
}
|
306
|
+
|
307
|
+
function FdbqAction(instance) {
|
308
|
+
var node = document.createElement("button");
|
309
|
+
node.className = FdbqClassName("fdbq-toggle", instance.config.placement);
|
310
|
+
node.id = instance.buttonSelector;
|
311
|
+
node.innerHTML = instance.config.actionHTML || instance.config.actionText || '<i class="fdbq-icon-action"></i>';
|
312
|
+
|
313
|
+
return node;
|
314
|
+
}
|
315
|
+
|
316
|
+
window.Fdbq = function(config) {
|
317
|
+
this.init = function() {
|
318
|
+
var modal = FdbqModal(this);
|
319
|
+
var action = FdbqAction(this);
|
320
|
+
|
321
|
+
this.mountNode().appendChild(modal);
|
322
|
+
this.mountNode().appendChild(action);
|
323
|
+
|
324
|
+
var button = document.getElementById(this.buttonSelector);
|
325
|
+
var closeButton = document.getElementById(this.modalCloseSelector);
|
326
|
+
var submitButton = document.getElementById(this.modalSubmitSelector);
|
327
|
+
|
328
|
+
if(button) button.addEventListener("click", this.onActionClick.bind(this));
|
329
|
+
if(closeButton) closeButton.addEventListener("click", this.onCloseClick.bind(this));
|
330
|
+
if(submitButton) submitButton.addEventListener("click", this.onSubmitClick.bind(this));
|
331
|
+
|
332
|
+
};
|
333
|
+
|
334
|
+
this.mountNode = function() { return document.querySelector(this.config.mountNode) || document.body; };
|
335
|
+
this.onActionClick = function(event) {
|
336
|
+
var modal = document.getElementById(this.modalSelector);
|
337
|
+
|
338
|
+
if(!modal || TOGGLE_REGEX.test(modal.className)) return;
|
339
|
+
|
340
|
+
modal.className = modal.className+" show";
|
341
|
+
};
|
342
|
+
this.onCloseClick = function() {
|
343
|
+
var modal = document.getElementById(this.modalSelector);
|
344
|
+
|
345
|
+
if(!modal || !TOGGLE_REGEX.test(modal.className)) return;
|
346
|
+
modal.className = modal.className.replace(TOGGLE_REGEX, "").trim();
|
347
|
+
this.errors = [];
|
348
|
+
FdbqFieldsReset(this);
|
349
|
+
};
|
350
|
+
this.onSubmitClick = function(event) {
|
351
|
+
this.errors = [];
|
352
|
+
if(!this.config.submit || !this.config.questions) return;
|
353
|
+
|
354
|
+
var callback = typeof(this.config.submit) === "function" ? this.config.submit : httpSubmit;
|
355
|
+
var params = {};
|
356
|
+
|
357
|
+
for(var i = 0; i < this.config.questions.length; i++) {
|
358
|
+
var q = this.config.questions[i];
|
359
|
+
var id = questionID(q);
|
360
|
+
var field = document.querySelector('.fdbq-modal-fields [data-id="'+id+'"] [data-fdbq="field"]');
|
361
|
+
|
362
|
+
if(!field) continue;
|
363
|
+
|
364
|
+
if(validField(field, q)) {
|
365
|
+
params[q.name] = field.value;
|
366
|
+
} else {
|
367
|
+
this.errors.push(id);
|
368
|
+
}
|
369
|
+
}
|
370
|
+
|
371
|
+
if(this.errors.length) {
|
372
|
+
FdbqFieldsReplaceInvalid(this);
|
373
|
+
|
374
|
+
return false;
|
375
|
+
}
|
376
|
+
|
377
|
+
callback(params, this);
|
378
|
+
|
379
|
+
return true;
|
380
|
+
};
|
381
|
+
|
382
|
+
// props;
|
383
|
+
this.config = config || {};
|
384
|
+
this.errors = [];
|
385
|
+
this.identifier = FdbqUUID("fdbq");
|
386
|
+
this.buttonSelector = this.identifier+"-button";
|
387
|
+
this.modalSelector = this.identifier+"-modal";
|
388
|
+
this.modalContainerSelector = this.identifier+"-modal-container";
|
389
|
+
this.fieldsContainerSelector = this.identifier+"-fields";
|
390
|
+
this.modalCloseSelector = this.modalSelector+"-close";
|
391
|
+
this.modalSubmitSelector = this.modalSelector+"-submit";
|
392
|
+
};
|
393
|
+
})();
|
@@ -0,0 +1,33 @@
|
|
1
|
+
{
|
2
|
+
"name": "@softserveopensource/fdbq",
|
3
|
+
"version": "0.1.0",
|
4
|
+
"description": "A simple lightweight survey for your website",
|
5
|
+
"main": "index.js",
|
6
|
+
"author": "SoftServe OpenSource",
|
7
|
+
"license": "MIT",
|
8
|
+
"homepage": "https://github.com/SoftServeInc/fdbq#readme",
|
9
|
+
"scripts": {
|
10
|
+
"test": "jest"
|
11
|
+
},
|
12
|
+
"devDependencies": {
|
13
|
+
"jest": "^24.9.0",
|
14
|
+
"serve": "^11.3.0"
|
15
|
+
},
|
16
|
+
"files": [
|
17
|
+
"index.js",
|
18
|
+
"theme.css"
|
19
|
+
],
|
20
|
+
"repository": {
|
21
|
+
"url": "git+https://github.com/SoftServeInc/fdbq.git",
|
22
|
+
"type": "git"
|
23
|
+
},
|
24
|
+
"bugs": {
|
25
|
+
"url": "https://github.com/SoftServeInc/fdbq/issues"
|
26
|
+
},
|
27
|
+
"directories": {
|
28
|
+
"example": "examples"
|
29
|
+
},
|
30
|
+
"keywords": [
|
31
|
+
"Feedback"
|
32
|
+
]
|
33
|
+
}
|
@@ -0,0 +1,228 @@
|
|
1
|
+
|
2
|
+
.fdbq-modal-overlay {
|
3
|
+
position: fixed;
|
4
|
+
z-index: 10000;
|
5
|
+
top: 0;
|
6
|
+
left: 0;
|
7
|
+
right: 0;
|
8
|
+
bottom: 0;
|
9
|
+
background-color: rgba(0, 0, 0, 0.3);
|
10
|
+
overflow-y: scroll;
|
11
|
+
display: none;
|
12
|
+
}
|
13
|
+
|
14
|
+
.fdbq-modal-overlay.show {
|
15
|
+
display: block;
|
16
|
+
}
|
17
|
+
|
18
|
+
.fdbq-modal {
|
19
|
+
width: 40%;
|
20
|
+
margin: 40px auto;
|
21
|
+
background-color: #ffffff;
|
22
|
+
}
|
23
|
+
|
24
|
+
.fdbq-modal .fdbq-modal-head {
|
25
|
+
display: flex;
|
26
|
+
margin: 0;
|
27
|
+
padding: 0;
|
28
|
+
align-items: center;
|
29
|
+
background-color: #000000;
|
30
|
+
}
|
31
|
+
|
32
|
+
.fdbq-modal .fdbq-modal-head h4 {
|
33
|
+
width: 100%;
|
34
|
+
color: #ffffff;
|
35
|
+
margin-top: 10px;
|
36
|
+
margin-bottom: 10px;
|
37
|
+
margin-left: 15px;
|
38
|
+
margin-right: 15px;
|
39
|
+
font-size: 18px;
|
40
|
+
text-transform: uppercase;
|
41
|
+
}
|
42
|
+
|
43
|
+
.fdbq-modal .fdbq-modal-head .close {
|
44
|
+
padding: 0 10px;
|
45
|
+
margin: 0 5px 0 0;
|
46
|
+
border: none;
|
47
|
+
background-color: transparent;
|
48
|
+
color: #ffffff;
|
49
|
+
font-size: 33px;
|
50
|
+
line-height: 100%;
|
51
|
+
}
|
52
|
+
|
53
|
+
.fdbq-modal .fdbq-modal-head .close:focus {
|
54
|
+
outline: none;
|
55
|
+
}
|
56
|
+
|
57
|
+
.fdbq-modal .fdbq-modal-head .close:hover {
|
58
|
+
cursor: pointer;
|
59
|
+
}
|
60
|
+
|
61
|
+
.fdbq-modal .fdbq-modal-body {
|
62
|
+
padding: 30px 15px;
|
63
|
+
}
|
64
|
+
|
65
|
+
.fdbq-modal .fdbq-modal-body .fdbq-modal-actions {
|
66
|
+
margin-top: 30px;
|
67
|
+
padding: 0;
|
68
|
+
}
|
69
|
+
|
70
|
+
.fdbq-modal .fdbq-modal-body .fdbq-modal-heading {
|
71
|
+
margin-bottom: 30px;
|
72
|
+
}
|
73
|
+
|
74
|
+
.fdbq-modal .fdbq-modal-body .fdbq-modal-heading h5 {
|
75
|
+
margin: 0;
|
76
|
+
font-size: 22px;
|
77
|
+
font-weight: bold;
|
78
|
+
text-transform: uppercase;
|
79
|
+
}
|
80
|
+
|
81
|
+
.fdbq-modal .fdbq-modal-body .fdbq-modal-heading .sub-heading-description {
|
82
|
+
padding-top: 10px;
|
83
|
+
color: #000000;
|
84
|
+
}
|
85
|
+
|
86
|
+
.fdbq-modal .fdbq-modal-body .fdbq-modal-heading h5:after {
|
87
|
+
display: block;
|
88
|
+
margin-top: 10px;
|
89
|
+
content: "";
|
90
|
+
width: 23%;
|
91
|
+
height: 2px;
|
92
|
+
background-color: #000000;
|
93
|
+
}
|
94
|
+
|
95
|
+
.fdbq-modal .fdbq-modal-body .fdbq-modal-actions .submit {
|
96
|
+
background-color: #ffffff;
|
97
|
+
border: 2px solid #000000;
|
98
|
+
color: #000000;
|
99
|
+
font-size: 16px;
|
100
|
+
text-transform: uppercase;
|
101
|
+
padding: 5px 30px;
|
102
|
+
font-weight: bold;
|
103
|
+
}
|
104
|
+
|
105
|
+
.fdbq-modal .fdbq-modal-body .fdbq-modal-actions .submit:hover,
|
106
|
+
.fdbq-modal .fdbq-modal-body .fdbq-modal-actions .submit:focus {
|
107
|
+
background-color: #000000;
|
108
|
+
color: #ffffff;
|
109
|
+
cursor: pointer;
|
110
|
+
transition: background-color .5s;
|
111
|
+
}
|
112
|
+
|
113
|
+
.fdbq-modal .fdbq-field {
|
114
|
+
display: flex;
|
115
|
+
flex-direction: column;
|
116
|
+
}
|
117
|
+
|
118
|
+
.fdbq-modal .fdbq-field abbr {
|
119
|
+
color: red;
|
120
|
+
padding-right: 3px;
|
121
|
+
}
|
122
|
+
|
123
|
+
.fdbq-modal .fdbq-field label {
|
124
|
+
display: block;
|
125
|
+
margin-bottom: 5px;
|
126
|
+
color: #909090;
|
127
|
+
font-weight: bold;
|
128
|
+
}
|
129
|
+
|
130
|
+
.fdbq-modal .fdbq-field.has-errors label {
|
131
|
+
color: red !important;
|
132
|
+
}
|
133
|
+
|
134
|
+
.fdbq-modal .fdbq-field.has-errors .fdbq-input {
|
135
|
+
border-color: red !important;
|
136
|
+
}
|
137
|
+
|
138
|
+
.fdbq-modal .fdbq-field .fdbq-input {
|
139
|
+
padding: 15px;
|
140
|
+
border: none;
|
141
|
+
display: flex;
|
142
|
+
border: 2px solid #000000;
|
143
|
+
font-size: 14px;
|
144
|
+
color: #000000;
|
145
|
+
line-height: 15px;
|
146
|
+
}
|
147
|
+
|
148
|
+
.fdbq-modal .fdbq-field .fdbq-input:focus {
|
149
|
+
outline: none;
|
150
|
+
}
|
151
|
+
|
152
|
+
.fdbq-modal .fdbq-field + .fdbq-field {
|
153
|
+
margin-top: 15px;
|
154
|
+
}
|
155
|
+
|
156
|
+
.fdbq-modal .fdbq-field .fdbq-field-hint {
|
157
|
+
padding-top: 5px;
|
158
|
+
font-size: 14px;
|
159
|
+
color: #909090;
|
160
|
+
}
|
161
|
+
|
162
|
+
.fdbq-toggle {
|
163
|
+
padding: 5px;
|
164
|
+
margin: 0;
|
165
|
+
background-color: #000000;
|
166
|
+
border: none;
|
167
|
+
}
|
168
|
+
|
169
|
+
.fdbq-toggle:hover, .fdbq-toggle:focus {
|
170
|
+
cursor: pointer;
|
171
|
+
outline: none;
|
172
|
+
background-color: #303030;
|
173
|
+
transition: background-color .5s;
|
174
|
+
}
|
175
|
+
|
176
|
+
.fdbq-toggle .fdbq-icon-action {
|
177
|
+
display: inline-block;
|
178
|
+
width: 45px;
|
179
|
+
height: 45px;
|
180
|
+
background: url("data:image/png;base64,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") center top no-repeat;
|
181
|
+
background-size: contain;
|
182
|
+
}
|
183
|
+
|
184
|
+
.fdbq-toggle.bottom {
|
185
|
+
position: fixed;
|
186
|
+
bottom: 5%;
|
187
|
+
}
|
188
|
+
|
189
|
+
.fdbq-toggle.top {
|
190
|
+
position: fixed;
|
191
|
+
top: 5%;
|
192
|
+
}
|
193
|
+
|
194
|
+
.fdbq-toggle.center {
|
195
|
+
position: fixed;
|
196
|
+
left: 50%;
|
197
|
+
transform: translateX(-50%);
|
198
|
+
}
|
199
|
+
|
200
|
+
|
201
|
+
.fdbq-toggle.middle {
|
202
|
+
position: fixed;
|
203
|
+
top: 50%;
|
204
|
+
transform: translate(0, -50%);
|
205
|
+
}
|
206
|
+
|
207
|
+
.fdbq-toggle.right {
|
208
|
+
position: fixed;
|
209
|
+
right: 5%;
|
210
|
+
}
|
211
|
+
|
212
|
+
.fdbq-toggle.left {
|
213
|
+
position: fixed;
|
214
|
+
left: 5%;
|
215
|
+
}
|
216
|
+
|
217
|
+
|
218
|
+
.fdbq-toggle.right.bottom,
|
219
|
+
.fdbq-toggle.left.bottom {
|
220
|
+
position: fixed;
|
221
|
+
bottom: 5%;
|
222
|
+
}
|
223
|
+
|
224
|
+
.fdbq-toggle.right.top,
|
225
|
+
.fdbq-toggle.left.top {
|
226
|
+
top: 5%;
|
227
|
+
position: fixed;
|
228
|
+
}
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fdbq-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- SoftServe OpenSource
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-02-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3'
|
20
|
+
- - "~>"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 6.0.2
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3'
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 6.0.2
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: jbuilder
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: sqlite3
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
description: Fdbq::Rails. A Rails integration for Fdbq JS plugin.
|
62
|
+
email:
|
63
|
+
- opensource@softserveinc.com
|
64
|
+
executables: []
|
65
|
+
extensions: []
|
66
|
+
extra_rdoc_files: []
|
67
|
+
files:
|
68
|
+
- MIT-LICENSE
|
69
|
+
- README.md
|
70
|
+
- Rakefile
|
71
|
+
- app/assets/config/fdbq_rails_manifest.js
|
72
|
+
- app/assets/javascripts/fdbq.js
|
73
|
+
- app/assets/stylesheets/fdbq.css
|
74
|
+
- app/controllers/fdbq/feedback_controller.rb
|
75
|
+
- app/models/fdbq/feedback.rb
|
76
|
+
- app/views/fdbq/feedback/create.json.jbuilder
|
77
|
+
- config/routes.rb
|
78
|
+
- lib/fdbq-rails.rb
|
79
|
+
- lib/fdbq/fdbq.rb
|
80
|
+
- lib/fdbq/plugin.rb
|
81
|
+
- lib/fdbq/question.rb
|
82
|
+
- lib/fdbq/rails.rb
|
83
|
+
- lib/fdbq/rails/engine.rb
|
84
|
+
- lib/fdbq/rails/helpers.rb
|
85
|
+
- lib/fdbq/rails/version.rb
|
86
|
+
- lib/fdbq/railtie.rb
|
87
|
+
- lib/fdbq/version.rb
|
88
|
+
- lib/generators/fdbq/rails/install_generator.rb
|
89
|
+
- lib/generators/templates/create_fdbq_feedback.rb
|
90
|
+
- lib/generators/templates/fdbq.rb
|
91
|
+
- lib/generators/templates/fdbq.yml
|
92
|
+
- vendor/assets/javascripts/@softserveopensource/fdbq/README.md
|
93
|
+
- vendor/assets/javascripts/@softserveopensource/fdbq/index.js
|
94
|
+
- vendor/assets/javascripts/@softserveopensource/fdbq/package.json
|
95
|
+
- vendor/assets/javascripts/@softserveopensource/fdbq/theme.css
|
96
|
+
homepage: https://github.com/SoftServeInc/fdbq-rails
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata:
|
100
|
+
allowed_push_host: https://rubygems.org
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubygems_version: 3.0.6
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: Fdbq::Rails. A Rails integration for Fdbq JS plugin.
|
120
|
+
test_files: []
|