fdbq-rails 0.1.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.
- 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: []
|