├── test
├── controllers
│ ├── .keep
│ └── filepond
│ │ └── rails
│ │ └── ingress_controller_test.rb
├── dummy
│ ├── log
│ │ └── .keep
│ ├── lib
│ │ └── assets
│ │ │ └── .keep
│ ├── public
│ │ ├── favicon.ico
│ │ ├── apple-touch-icon.png
│ │ ├── apple-touch-icon-precomposed.png
│ │ ├── 500.html
│ │ ├── 422.html
│ │ └── 404.html
│ ├── app
│ │ ├── assets
│ │ │ ├── images
│ │ │ │ └── .keep
│ │ │ ├── config
│ │ │ │ └── manifest.js
│ │ │ └── stylesheets
│ │ │ │ └── application.css
│ │ ├── models
│ │ │ ├── concerns
│ │ │ │ └── .keep
│ │ │ ├── some_model.rb
│ │ │ └── application_record.rb
│ │ ├── controllers
│ │ │ ├── concerns
│ │ │ │ └── .keep
│ │ │ ├── application_controller.rb
│ │ │ └── home_controller.rb
│ │ ├── views
│ │ │ ├── layouts
│ │ │ │ ├── mailer.text.erb
│ │ │ │ ├── mailer.html.erb
│ │ │ │ └── application.html.erb
│ │ │ └── home
│ │ │ │ └── index.html.erb
│ │ ├── helpers
│ │ │ └── application_helper.rb
│ │ ├── channels
│ │ │ └── application_cable
│ │ │ │ ├── channel.rb
│ │ │ │ └── connection.rb
│ │ ├── mailers
│ │ │ └── application_mailer.rb
│ │ ├── javascript
│ │ │ └── application.js
│ │ └── jobs
│ │ │ └── application_job.rb
│ ├── bin
│ │ ├── rake
│ │ ├── rails
│ │ └── setup
│ ├── config
│ │ ├── environment.rb
│ │ ├── cable.yml
│ │ ├── routes.rb
│ │ ├── boot.rb
│ │ ├── importmap.rb
│ │ ├── initializers
│ │ │ ├── filter_parameter_logging.rb
│ │ │ ├── permissions_policy.rb
│ │ │ ├── assets.rb
│ │ │ ├── inflections.rb
│ │ │ └── content_security_policy.rb
│ │ ├── database.yml
│ │ ├── application.rb
│ │ ├── locales
│ │ │ └── en.yml
│ │ ├── storage.yml
│ │ ├── puma.rb
│ │ └── environments
│ │ │ ├── test.rb
│ │ │ ├── development.rb
│ │ │ └── production.rb
│ ├── db
│ │ ├── migrate
│ │ │ ├── 20221229032128_create_some_models.rb
│ │ │ └── 20221229033425_create_active_storage_tables.active_storage.rb
│ │ └── schema.rb
│ ├── config.ru
│ └── Rakefile
├── fixtures
│ └── files
│ │ ├── .keep
│ │ └── toronto.png
├── filepond
│ └── rails_test.rb
├── application_system_test_case.rb
├── test_helper.rb
└── system
│ └── filepond
│ └── rails
│ └── uploads_test.rb
├── app
├── assets
│ ├── config
│ │ └── filepond_rails_manifest.js
│ ├── javascripts
│ │ └── filepond-rails.js
│ └── stylesheets
│ │ ├── filepond.min.css
│ │ └── filepond.css
└── controllers
│ └── filepond
│ └── rails
│ ├── application_controller.rb
│ └── ingress_controller.rb
├── lib
├── filepond
│ ├── rails
│ │ ├── version.rb
│ │ └── engine.rb
│ └── rails.rb
└── tasks
│ └── filepond
│ └── rails_tasks.rake
├── Rakefile
├── config
├── importmap.rb
└── routes.rb
├── .gitignore
├── script
└── docker-dev-start.sh
├── Gemfile
├── CHANGELOG.md
├── bin
└── rails
├── docker-compose.yml
├── .github
└── workflows
│ └── ci.yml
├── MIT-LICENSE
├── Dockerfile
├── licenses
└── LICENSE-filepond.md
├── filepond-rails.gemspec
├── README.md
└── Gemfile.lock
/test/controllers/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/log/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/lib/assets/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/fixtures/files/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/images/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/app/models/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/app/assets/config/filepond_rails_manifest.js:
--------------------------------------------------------------------------------
1 | // = link_directory ../javascripts .js
2 |
--------------------------------------------------------------------------------
/test/dummy/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ApplicationHelper
4 | end
5 |
--------------------------------------------------------------------------------
/test/fixtures/files/toronto.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Code-With-Rails/filepond-rails/HEAD/test/fixtures/files/toronto.png
--------------------------------------------------------------------------------
/lib/filepond/rails/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Filepond
4 | module Rails
5 | VERSION = '1.0.1'
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/dummy/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationController < ActionController::Base
4 | end
5 |
--------------------------------------------------------------------------------
/test/dummy/app/models/some_model.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class SomeModel < ApplicationRecord
4 | has_one_attached :picture
5 | end
6 |
--------------------------------------------------------------------------------
/test/dummy/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationRecord < ActiveRecord::Base
4 | primary_abstract_class
5 | end
6 |
--------------------------------------------------------------------------------
/test/dummy/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | require_relative '../config/boot'
5 | require 'rake'
6 | Rake.application.run
7 |
--------------------------------------------------------------------------------
/lib/tasks/filepond/rails_tasks.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # desc "Explaining what the task does"
3 | # task :filepond_rails do
4 | # # Task goes here
5 | # end
6 |
--------------------------------------------------------------------------------
/app/controllers/filepond/rails/application_controller.rb:
--------------------------------------------------------------------------------
1 | module Filepond
2 | module Rails
3 | class ApplicationController < ActionController::Base
4 | end
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/test/dummy/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ApplicationCable
4 | class Channel < ActionCable::Channel::Base
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../stylesheets .css
3 | //= link_tree ../../javascript .js
4 | //= link filepond_rails_manifest.js
5 |
--------------------------------------------------------------------------------
/test/dummy/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ApplicationCable
4 | class Connection < ActionCable::Connection::Base
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/test/dummy/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationMailer < ActionMailer::Base
4 | default from: 'from@example.com'
5 | layout 'mailer'
6 | end
7 |
--------------------------------------------------------------------------------
/lib/filepond/rails.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'filepond/rails/version'
4 | require 'filepond/rails/engine'
5 |
6 | module Filepond
7 | module Rails
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/test/dummy/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | APP_PATH = File.expand_path('../config/application', __dir__)
5 | require_relative '../config/boot'
6 | require 'rails/commands'
7 |
--------------------------------------------------------------------------------
/test/dummy/config/environment.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Load the Rails application.
4 | require_relative 'application'
5 |
6 | # Initialize the Rails application.
7 | Rails.application.initialize!
8 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/setup"
2 |
3 | APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4 | load "rails/tasks/engine.rake"
5 |
6 | load "rails/tasks/statistics.rake"
7 |
8 | require "bundler/gem_tasks"
9 |
--------------------------------------------------------------------------------
/config/importmap.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | pin 'filepond', to: 'https://ga.jspm.io/npm:filepond@4.30.4/dist/filepond.js', preload: true
4 | pin_all_from File.expand_path("../app/assets/javascripts", __dir__)
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.bundle/
2 | /doc/
3 | /log/*.log
4 | /pkg/
5 | /tmp/
6 | /test/dummy/db/*.sqlite3
7 | /test/dummy/db/*.sqlite3-*
8 | /test/dummy/log/*.log
9 | /test/dummy/storage/
10 | /test/dummy/tmp/
11 |
12 | .DS_Store
13 |
--------------------------------------------------------------------------------
/test/dummy/db/migrate/20221229032128_create_some_models.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateSomeModels < ActiveRecord::Migration[7.0]
4 | def change
5 | create_table :some_models, &:timestamps
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/dummy/config.ru:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is used by Rack-based servers to start the application.
4 |
5 | require_relative 'config/environment'
6 |
7 | run Rails.application
8 | Rails.application.load_server
9 |
--------------------------------------------------------------------------------
/test/dummy/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: test
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: dummy_production
11 |
--------------------------------------------------------------------------------
/test/dummy/config/routes.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Rails.application.routes.draw do
4 | mount Filepond::Rails::Engine => '/filepond'
5 |
6 | post '/', to: 'home#update'
7 | delete '/', to: 'home#destroy'
8 | root to: 'home#index'
9 | end
10 |
--------------------------------------------------------------------------------
/test/filepond/rails_test.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'test_helper'
4 |
5 | module Filepond
6 | class RailsTest < ActiveSupport::TestCase
7 | test 'it has a version number' do
8 | assert Filepond::Rails::VERSION
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/test/dummy/app/javascript/application.js:
--------------------------------------------------------------------------------
1 | import { FilePondRails, FilePond } from 'filepond-rails'
2 |
3 | window.FilePond = FilePond
4 | window.FilePondRails = FilePondRails
5 |
6 | const input = document.querySelector('.filepond')
7 | window.filePondInstance = FilePondRails.create(input)
8 |
--------------------------------------------------------------------------------
/test/dummy/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Add your own tasks in files placed in lib/tasks ending in .rake,
4 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
5 |
6 | require_relative 'config/application'
7 |
8 | Rails.application.load_tasks
9 |
--------------------------------------------------------------------------------
/test/dummy/config/boot.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Set up gems listed in the Gemfile.
4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__)
5 |
6 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
7 | $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__)
8 |
--------------------------------------------------------------------------------
/script/docker-dev-start.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -xeuo pipefail
4 |
5 | bundle
6 |
7 | if [[ -f ./test/dummy/tmp/pids/server.pid ]]; then
8 | rm ./test/dummy/tmp/pids/server.pid
9 | fi
10 |
11 | cd test/dummy
12 |
13 | bin/rails db:setup
14 |
15 | bin/rails server --port 3000 --binding 0.0.0.0
16 |
--------------------------------------------------------------------------------
/test/dummy/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/test/application_system_test_case.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'test_helper'
4 |
5 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
6 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400], options: {
7 | browser: :remote,
8 | url: ENV['SELENIUM_REMOTE_URL']
9 | }
10 | end
11 |
--------------------------------------------------------------------------------
/test/dummy/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationJob < ActiveJob::Base
4 | # Automatically retry jobs that encountered a deadlock
5 | # retry_on ActiveRecord::Deadlocked
6 |
7 | # Most jobs are safe to ignore if the underlying records are no longer available
8 | # discard_on ActiveJob::DeserializationError
9 | end
10 |
--------------------------------------------------------------------------------
/test/dummy/config/importmap.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Pin npm packages by running ./bin/importmap
4 |
5 | pin 'application', preload: true
6 | pin 'filepond', to: 'https://ga.jspm.io/npm:filepond@4.30.4/dist/filepond.js', preload: true
7 | pin '@rails/activestorage', to: 'https://ga.jspm.io/npm:@rails/activestorage@7.0.4/app/assets/javascripts/activestorage.esm.js'
8 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source 'https://rubygems.org'
4 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
5 |
6 | # Specify your gem's dependencies in filepond-rails.gemspec.
7 | gemspec
8 |
9 | gem 'image_processing'
10 | gem 'importmap-rails'
11 | gem 'sprockets-rails'
12 | gem 'sqlite3'
13 |
14 | # Start debugger with binding.b [https://github.com/ruby/debug]
15 | # gem "debug", ">= 1.0.0"
16 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Filepond::Rails::Engine.routes.draw do
4 | # For details regarding FilePond endpoints, please refer to https://pqina.nl/filepond/docs/api/server
5 |
6 | # https://pqina.nl/filepond/docs/api/server/#fetch
7 | post 'active_storage/fetch', to: 'ingress#fetch'
8 |
9 | # https://pqina.nl/filepond/docs/api/server/#remove
10 | delete 'active_storage/remove', to: 'ingress#remove'
11 | end
12 |
--------------------------------------------------------------------------------
/test/dummy/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | filepond-rails Dummy App
5 |
6 | <%= csrf_meta_tags %>
7 | <%= csp_meta_tag %>
8 | <%= stylesheet_link_tag 'application', 'data-turbo-track': 'reload' %>
9 | <%= javascript_importmap_tags %>
10 |
11 |
12 |
13 | <%= yield %>
14 |
15 |
16 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Configure parameters to be filtered from the log file. Use this to limit dissemination of
6 | # sensitive information. See the ActiveSupport::ParameterFilter documentation for supported
7 | # notations and behaviors.
8 | Rails.application.config.filter_parameters += %i[
9 | passw secret token _key crypt salt certificate otp ssn
10 | ]
11 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/permissions_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # Define an application-wide HTTP permissions policy. For further
3 | # information see https://developers.google.com/web/updates/2018/06/feature-policy
4 | #
5 | # Rails.application.config.permissions_policy do |f|
6 | # f.camera :none
7 | # f.gyroscope :none
8 | # f.microphone :none
9 | # f.usb :none
10 | # f.fullscreen :self
11 | # f.payment :self, "https://secure.example.com"
12 | # end
13 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 1.0.1 (January 10, 2023)
4 | * Update license information
5 | * Update README with instructions on running system tests
6 |
7 | ## 1.0.0 (December 29, 2022)
8 | * Add Docker to allow consistent development environment
9 | * Remove unneeded files which were added during scaffolding
10 | * Declare importmap-rails as required
11 | * Add tests for IngressController, system tests and fixes discovered through those tests
12 | * Add GitHub action CI
13 |
14 | ## 0.1.0 (December 25, 2022)
15 | * Initial release
16 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails gems
3 | # installed from the root of your application.
4 |
5 | ENGINE_ROOT = File.expand_path("..", __dir__)
6 | ENGINE_PATH = File.expand_path("../lib/filepond/rails/engine", __dir__)
7 | APP_PATH = File.expand_path("../test/dummy/config/application", __dir__)
8 |
9 | # Set up gems listed in the Gemfile.
10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
11 | require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
12 |
13 | require "rails/all"
14 | require "rails/engine/commands"
15 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Version of your assets, change this if you want to expire all your assets.
6 | Rails.application.config.assets.version = '1.0'
7 |
8 | # Add additional assets to the asset load path.
9 | # Rails.application.config.assets.paths << Emoji.images_path
10 |
11 | # Precompile additional assets.
12 | # application.js, application.css, and all non-JS/CSS in the app/assets
13 | # folder are already added.
14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
15 |
--------------------------------------------------------------------------------
/lib/filepond/rails/engine.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Filepond
4 | module Rails
5 | class Engine < ::Rails::Engine
6 | isolate_namespace Filepond::Rails
7 |
8 | initializer 'filepond-rails.importmap', before: 'importmap' do |app|
9 | app.config.importmap.paths << Engine.root.join('config/importmap.rb')
10 | app.config.importmap.cache_sweepers << Engine.root.join('app/assets/javascripts')
11 | end
12 |
13 | initializer 'filepond-rails.assets.precompile' do |app|
14 | app.config.assets.precompile += %w( filepond-rails.js )
15 | end
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.8"
2 | networks:
3 | backend:
4 | selenium:
5 | services:
6 | chrome_server:
7 | image: seleniarm/standalone-chromium
8 | volumes:
9 | - /dev/shm:/dev/shm
10 | networks:
11 | - selenium
12 | app:
13 | build: .
14 | tty: true
15 | volumes:
16 | - .:/filepond-rails
17 | working_dir: /filepond-rails
18 | environment:
19 | SELENIUM_REMOTE_URL: http://chrome_server:4444/wd/hub
20 | command: script/docker-dev-start.sh
21 | networks:
22 | - backend
23 | - selenium
24 | ports:
25 | - "3000:3000"
26 | depends_on:
27 | - chrome_server
28 |
--------------------------------------------------------------------------------
/test/dummy/app/controllers/home_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class HomeController < ApplicationController
4 | before_action :setup_model
5 |
6 | def index; end
7 |
8 | def update
9 | @some_model.update(model_params)
10 | redirect_to root_path
11 | end
12 |
13 | def destroy
14 | @some_model.picture.purge
15 | redirect_to root_path
16 | end
17 |
18 | private
19 |
20 | def setup_model
21 | @some_model ||= SomeModel.first_or_create
22 | end
23 |
24 | def model_params
25 | params.require(:some_model).permit(:picture)
26 | rescue ActionController::ParameterMissing
27 | {}
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/test/dummy/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite. Versions 3.8.0 and up are supported.
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem "sqlite3"
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: db/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: db/test.sqlite3
22 |
23 | production:
24 | <<: *default
25 | database: db/production.sqlite3
26 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # Be sure to restart your server when you modify this file.
3 |
4 | # Add new inflection rules using the following format. Inflections
5 | # are locale specific, and you may define rules for as many different
6 | # locales as you wish. All of these examples are active by default:
7 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
8 | # inflect.plural /^(ox)$/i, "\\1en"
9 | # inflect.singular /^(ox)en/i, "\\1"
10 | # inflect.irregular "person", "people"
11 | # inflect.uncountable %w( fish sheep )
12 | # end
13 |
14 | # These inflection rules are supported but not enabled by default:
15 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
16 | # inflect.acronym "RESTful"
17 | # end
18 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10 | * files in this directory. Styles in this file should be added after the last require_* statement.
11 | * It is generally better to create a new file per style scope.
12 | *
13 | *= require_tree .
14 | *= require_self
15 | *= require 'filepond'
16 | */
17 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | # This workflow uses actions that are not certified by GitHub. They are
2 | # provided by a third-party and are governed by separate terms of service,
3 | # privacy policy, and support documentation.
4 | #
5 | # This workflow will install a prebuilt Ruby version, install dependencies, and
6 | # run tests and linters.
7 | name: 'CI'
8 | on:
9 | push:
10 | branches: [ 'main' ]
11 | pull_request:
12 | branches: [ 'main' ]
13 | jobs:
14 | test:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: Checkout code
18 | uses: actions/checkout@v3
19 | - name: Build container
20 | run: docker compose build
21 | - name: Boot containers
22 | run: docker-compose up -d app chrome_server
23 | - name: Run tests
24 | run: docker compose exec -T app bin/rails test
25 | - name: Run system tests
26 | run: docker compose exec -T app bin/rails app:test:system
27 |
--------------------------------------------------------------------------------
/test/dummy/config/application.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative 'boot'
4 |
5 | require 'rails/all'
6 |
7 | # Require the gems listed in Gemfile, including any gems
8 | # you've limited to :test, :development, or :production.
9 | Bundler.require(*Rails.groups)
10 | require 'filepond/rails'
11 |
12 | module Dummy
13 | class Application < Rails::Application
14 | config.load_defaults Rails::VERSION::STRING.to_f
15 |
16 | # For compatibility with applications that use this config
17 | config.action_controller.include_all_helpers = false
18 |
19 | # Configuration for the application, engines, and railties goes here.
20 | #
21 | # These settings can be overridden in specific environments using the files
22 | # in config/environments, which are processed later.
23 | #
24 | # config.time_zone = "Central Time (US & Canada)"
25 | # config.eager_load_paths << Rails.root.join("extras")
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Configure Rails Environment
4 | ENV['RAILS_ENV'] = 'test'
5 |
6 | require_relative '../test/dummy/config/environment'
7 | ActiveRecord::Migrator.migrations_paths = [File.expand_path('../test/dummy/db/migrate', __dir__)]
8 | ActiveRecord::Migrator.migrations_paths << File.expand_path('../db/migrate', __dir__)
9 | require 'rails/test_help'
10 | require 'capybara'
11 |
12 | # Load fixtures from the engine
13 | if ActiveSupport::TestCase.respond_to?(:fixture_path=)
14 | ActiveSupport::TestCase.fixture_path = File.expand_path('fixtures', __dir__)
15 | ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
16 | ActiveSupport::TestCase.file_fixture_path = "#{ActiveSupport::TestCase.fixture_path}/files"
17 | ActiveSupport::TestCase.fixtures :all
18 | end
19 |
20 | Capybara.server_host = '0.0.0.0'
21 | Capybara.app_host = "http://#{ENV.fetch('HOSTNAME')}:#{Capybara.server_port}"
22 |
--------------------------------------------------------------------------------
/test/dummy/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t "hello"
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t("hello") %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # "true": "foo"
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at https://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2022 Simon Chiu
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.
21 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ruby:3.2-bullseye
2 |
3 | SHELL ["/bin/bash", "--login", "-c"]
4 |
5 | # Install apt based dependencies required to run Rails as
6 | # well as RubyGems. As the Ruby image itself is based on a
7 | # Debian image, we use apt-get to install those.
8 | RUN apt-get update && apt-get install -y build-essential nano libvips
9 | RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
10 | RUN nvm install 18.12.1
11 |
12 | # Configure the main working directory. This is the base
13 | # directory used in any further RUN, COPY, and ENTRYPOINT
14 | # commands.
15 | RUN mkdir -p /filepond-rails
16 | WORKDIR /filepond-rails
17 |
18 | # Copy the Gemfile as well as the Gemfile.lock and install
19 | # the RubyGems. This is a separate step so the dependencies
20 | # will be cached unless changes to one of those two files
21 | # are made.
22 | COPY lib/filepond/rails/version.rb ./lib/filepond/rails/
23 | COPY filepond-rails.gemspec ./
24 | COPY Gemfile* ./
25 | RUN gem install bundler -v 2.3.3 && bundle install --jobs 20 --retry 5
26 |
27 | COPY . /filepond-rails
28 | RUN rm -rf tmp/*
29 |
30 | ADD . /filepond-rails
31 |
--------------------------------------------------------------------------------
/test/dummy/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | require 'fileutils'
5 |
6 | # path to your application root.
7 | APP_ROOT = File.expand_path('..', __dir__)
8 |
9 | def system!(*args)
10 | system(*args) || abort("\n== Command #{args} failed ==")
11 | end
12 |
13 | FileUtils.chdir APP_ROOT do
14 | # This script is a way to set up or update your development environment automatically.
15 | # This script is idempotent, so that you can run it at any time and get an expectable outcome.
16 | # Add necessary setup steps to this file.
17 |
18 | puts '== Installing dependencies =='
19 | system! 'gem install bundler --conservative'
20 | system('bundle check') || system!('bundle install')
21 |
22 | # puts "\n== Copying sample files =="
23 | # unless File.exist?("config/database.yml")
24 | # FileUtils.cp "config/database.yml.sample", "config/database.yml"
25 | # end
26 |
27 | puts "\n== Preparing database =="
28 | system! 'bin/rails db:prepare'
29 |
30 | puts "\n== Removing old logs and tempfiles =="
31 | system! 'bin/rails log:clear tmp:clear'
32 |
33 | puts "\n== Restarting application server =="
34 | system! 'bin/rails restart'
35 | end
36 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # Be sure to restart your server when you modify this file.
3 |
4 | # Define an application-wide content security policy.
5 | # See the Securing Rails Applications Guide for more information:
6 | # https://guides.rubyonrails.org/security.html#content-security-policy-header
7 |
8 | # Rails.application.configure do
9 | # config.content_security_policy do |policy|
10 | # policy.default_src :self, :https
11 | # policy.font_src :self, :https, :data
12 | # policy.img_src :self, :https, :data
13 | # policy.object_src :none
14 | # policy.script_src :self, :https
15 | # policy.style_src :self, :https
16 | # # Specify URI for violation reports
17 | # # policy.report_uri "/csp-violation-report-endpoint"
18 | # end
19 | #
20 | # # Generate session nonces for permitted importmap and inline scripts
21 | # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
22 | # config.content_security_policy_nonce_directives = %w(script-src)
23 | #
24 | # # Report violations without enforcing the policy.
25 | # # config.content_security_policy_report_only = true
26 | # end
27 |
--------------------------------------------------------------------------------
/test/dummy/app/views/home/index.html.erb:
--------------------------------------------------------------------------------
1 | filepond-rails development
2 |
3 |
4 |
5 | This is the "dummy" test app for the filepond-rails gem. Manually testing the dummy app here should be done before any new releases.
6 |
7 | Steps to test:
8 |
9 |
10 | - Drag and drop a file and ensure it can be uploaded
11 | - Drag and drop a URL of an image from a browser bar and ensure it can be uploaded
12 | - Ensure that cancelling an upload removes the blob from the server
13 |
14 |
15 |
16 | Picture:
17 | <% if @some_model.picture.attached? %>
18 |
19 | <%= image_tag(@some_model.picture.representation(resize_to_limit: [100, 100])) %>
20 | <%= button_to 'Delete', root_path, method: :delete %>
21 | <% else %>
22 | No picture
23 | <% end %>
24 |
25 |
26 | <%= form_with model: @some_model, url: root_path, method: :post do |f| %>
27 | <%= field_set_tag 'Upload Test Form' do %>
28 | <%= f.file_field :picture, class: 'filepond', direct_upload: true %>
29 | <%= f.button 'Save' %>
30 | <% end %>
31 | <% end %>
32 |
33 |
34 | Versions:
35 | Rails <%= Rails::VERSION::STRING %>
36 | filepond-rails <%= Filepond::Rails::VERSION %>
37 |
38 |
--------------------------------------------------------------------------------
/test/dummy/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket-<%= Rails.env %>
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket-<%= Rails.env %>
23 |
24 | # Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name-<%= Rails.env %>
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/licenses/LICENSE-filepond.md:
--------------------------------------------------------------------------------
1 | The following files are copied from https://github.com/pqina/filepond/blob/4.30.4:
2 | * filepond.css
3 | * filepond.min.css
4 |
5 | ---
6 |
7 | Original: https://github.com/pqina/filepond/blob/4.30.4/LICENSE
8 |
9 | MIT License
10 |
11 | Copyright (c) 2019 PQINA | Rik Schennink
12 |
13 | Permission is hereby granted, free of charge, to any person obtaining a copy
14 | of this software and associated documentation files (the "Software"), to deal
15 | in the Software without restriction, including without limitation the rights
16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 | copies of the Software, and to permit persons to whom the Software is
18 | furnished to do so, subject to the following conditions:
19 |
20 | The above copyright notice and this permission notice shall be included in all
21 | copies or substantial portions of the Software.
22 |
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 | SOFTWARE.
30 |
--------------------------------------------------------------------------------
/filepond-rails.gemspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative 'lib/filepond/rails/version'
4 |
5 | Gem::Specification.new do |spec|
6 | spec.name = 'filepond-rails'
7 | spec.version = Filepond::Rails::VERSION
8 | spec.authors = ['Simon Chiu']
9 | spec.email = ['simon@furvur.com']
10 | spec.homepage = 'https://codewithrails.com/filepond-rails'
11 | spec.summary = 'FilePond integration for Rails'
12 | spec.description = 'Add FilePond server endpoints and form helpers for Rails'
13 | spec.license = 'MIT'
14 |
15 | # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
16 | # to allow pushing to a single host or delete this section to allow pushing to any host.
17 | spec.metadata['allowed_push_host'] = 'https://rubygems.org'
18 |
19 | spec.metadata['homepage_uri'] = spec.homepage
20 | spec.metadata['source_code_uri'] = 'https://github.com/Code-With-Rails/filepond-rails'
21 | spec.metadata['changelog_uri'] = 'https://github.com/Code-With-Rails/filepond-rails/blob/main/CHANGELOG.md'
22 |
23 | spec.files = Dir.chdir(File.expand_path(__dir__)) do
24 | Dir['{app,config,db,lib}/**/*', 'MIT-LICENSE', 'Rakefile', 'README.md']
25 | end
26 |
27 | spec.add_dependency 'rails', '>= 7.0', '< 8'
28 | spec.add_development_dependency 'puma', '~> 6.0'
29 | spec.add_development_dependency 'rubocop', '~> 1.1'
30 | spec.add_development_dependency 'webmock', '~> 3.18'
31 | spec.add_development_dependency 'capybara'
32 | spec.add_development_dependency 'dropybara'
33 | spec.add_development_dependency 'selenium-webdriver'
34 | end
35 |
--------------------------------------------------------------------------------
/test/system/filepond/rails/uploads_test.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'application_system_test_case'
4 | require 'dropybara'
5 |
6 | class UploadsTest < ApplicationSystemTestCase
7 | setup do
8 | ActionController::Base.allow_forgery_protection = true
9 | visit(root_url)
10 | end
11 |
12 | teardown do
13 | ActionController::Base.allow_forgery_protection = false
14 | end
15 |
16 | test 'goes to homepage' do
17 | assert_selector 'h1', text: 'filepond-rails development'
18 | end
19 |
20 | test 'FilePond is loaded' do
21 | assert page.evaluate_script("typeof(FilePond) !== 'undefined'")
22 | end
23 |
24 | test 'FilePondRails is loaded' do
25 | assert page.evaluate_script("typeof(FilePondRails) !== 'undefined'")
26 | end
27 |
28 | test 'file upload via file upload' do
29 | assert_difference -> { ActiveStorage::Blob.count } do
30 | page.execute_script <<~JS
31 | window.filePondInstance.addFile("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==")
32 | window.filePondInstance.processFile()
33 | JS
34 | sleep(5) # There is a delay in FilePond due to the UI providing feedback
35 | end
36 | end
37 |
38 | test 'file upload via fetch' do
39 | assert_difference -> { ActiveStorage::Blob.count }, 2 do
40 | page.execute_script <<~JS
41 | window.filePondInstance.addFile(
42 | 'https://user-images.githubusercontent.com/16937/209497677-a0ace476-a04f-4efb-be8c-6d263ad5d0e0.png'
43 | )
44 | window.filePondInstance.processFile()
45 | JS
46 | sleep(5) # There is a delay in FilePond due to the UI providing feedback
47 | end
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/test/dummy/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/test/dummy/config/puma.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Puma can serve each request in a thread from an internal thread pool.
4 | # The `threads` method setting takes two numbers: a minimum and maximum.
5 | # Any libraries that use thread pools should be configured to match
6 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
7 | # and maximum; this matches the default thread size of Active Record.
8 | #
9 | max_threads_count = ENV.fetch('RAILS_MAX_THREADS', 5)
10 | min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count }
11 | threads min_threads_count, max_threads_count
12 |
13 | # Specifies the `worker_timeout` threshold that Puma will use to wait before
14 | # terminating a worker in development environments.
15 | #
16 | worker_timeout 3600 if ENV.fetch('RAILS_ENV', 'development') == 'development'
17 |
18 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
19 | #
20 | port ENV.fetch('PORT', 3000)
21 |
22 | # Specifies the `environment` that Puma will run in.
23 | #
24 | environment ENV.fetch('RAILS_ENV', 'development')
25 |
26 | # Specifies the `pidfile` that Puma will use.
27 | pidfile ENV.fetch('PIDFILE', 'tmp/pids/server.pid')
28 |
29 | # Specifies the number of `workers` to boot in clustered mode.
30 | # Workers are forked web server processes. If using threads and workers together
31 | # the concurrency of the application would be max `threads` * `workers`.
32 | # Workers do not work on JRuby or Windows (both of which do not support
33 | # processes).
34 | #
35 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
36 |
37 | # Use the `preload_app!` method when specifying a `workers` number.
38 | # This directive tells Puma to first boot the application and load code
39 | # before forking the application. This takes advantage of Copy On Write
40 | # process behavior so workers use less memory.
41 | #
42 | # preload_app!
43 |
44 | # Allow puma to be restarted by `bin/rails restart` command.
45 | plugin :tmp_restart
46 |
--------------------------------------------------------------------------------
/test/dummy/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/test/dummy/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/test/controllers/filepond/rails/ingress_controller_test.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'test_helper'
4 |
5 | module Filepond
6 | module Rails
7 | class IngressControllerTest < ActionDispatch::IntegrationTest
8 | include Engine.routes.url_helpers
9 |
10 | # We're savages and will not stub this out
11 | def url
12 | 'https://user-images.githubusercontent.com/16937/209497677-a0ace476-a04f-4efb-be8c-6d263ad5d0e0.png'
13 | end
14 |
15 | def fetch_request(url:)
16 | post active_storage_fetch_url, env: { 'RAW_POST_DATA' => url }
17 | end
18 |
19 | def blob
20 | @blob ||= ActiveStorage::Blob.create_and_upload!(
21 | io: File.open(file_fixture('toronto.png')),
22 | filename: 'toronto.png'
23 | )
24 | end
25 |
26 | def remove_request(signed_id:)
27 | delete active_storage_remove_url, env: { 'RAW_POST_DATA' => signed_id }
28 | end
29 |
30 | test '#fetch creates blob' do
31 | assert_difference -> { ActiveStorage::Blob.count } do
32 | fetch_request(url:)
33 | end
34 | end
35 |
36 | test '#fetch redirects to blob' do
37 | fetch_request(url:)
38 | blob = ActiveStorage::Blob.last
39 | assert_redirected_to "/rails/active_storage/blobs/redirect/#{blob.signed_id}/#{blob.filename}"
40 | end
41 |
42 | test '#fetch responds with 422 if something goes wrong' do
43 | fetch_request(url: 'http://nonexistent/404')
44 | assert_response :unprocessable_entity
45 | end
46 |
47 | test '#remove purges blob' do
48 | signed_id = blob.signed_id
49 |
50 | assert_difference -> { ActiveStorage::Blob.count }, -1 do
51 | remove_request(signed_id:)
52 | end
53 | end
54 |
55 | test '#remove returns 200 if blob purged' do
56 | remove_request(signed_id: blob.signed_id)
57 | assert_response :ok
58 | end
59 |
60 | test '#remove returns 404 if blob not found' do
61 | signed_id = 'abcdefg'
62 |
63 | remove_request(signed_id:)
64 | assert_response :not_found
65 | end
66 | end
67 | end
68 | end
69 |
--------------------------------------------------------------------------------
/app/assets/javascripts/filepond-rails.js:
--------------------------------------------------------------------------------
1 | import { DirectUpload } from '@rails/activestorage'
2 | import * as FilePond from 'filepond'
3 |
4 | let FilePondRails = {
5 | directUploadUrl: null,
6 | input: null,
7 | default_options: {
8 | server: {
9 | process: (fieldName, file, metadata, load, error, progress, abort, transfer, options) => {
10 | const uploader = new DirectUpload(file, FilePondRails.directUploadUrl, {
11 | directUploadWillStoreFileWithXHR: (request) => {
12 | request.upload.addEventListener(
13 | 'progress',
14 | event => progress(event.lengthComputable, event.loaded, event.total)
15 | )
16 | }
17 | })
18 | uploader.create((errorResponse, blob) => {
19 | if (errorResponse) {
20 | error(`Something went wrong: ${errorResponse}`)
21 | } else {
22 | const hiddenField = document.createElement('input')
23 | hiddenField.setAttribute('type', 'hidden')
24 | hiddenField.setAttribute('value', blob.signed_id)
25 | hiddenField.name = FilePondRails.input.name
26 | document.querySelector('form').appendChild(hiddenField)
27 | load(blob.signed_id)
28 | }
29 | })
30 |
31 | return {
32 | abort: () => abort()
33 | }
34 | },
35 | fetch: {
36 | url: './filepond/active_storage/fetch',
37 | method: 'POST',
38 | onload: (response) => {
39 | console.log(response)
40 | return response
41 | },
42 | ondata: (response) => {
43 | console.log(response)
44 | return response
45 | }
46 | },
47 | revert: {
48 | url: './filepond/active_storage/remove'
49 | },
50 | headers: {
51 | 'X-CSRF-Token': document.head.querySelector("[name='csrf-token']").content
52 | }
53 | }
54 | },
55 |
56 | // Convenience method to initialize FilePond based on the way this gem expects things to work
57 | create: function(input) {
58 | FilePondRails.directUploadUrl = input.dataset.directUploadUrl
59 | FilePondRails.input = input
60 |
61 | // Initialize FilePond on our element
62 | return FilePond.create(input, FilePondRails.default_options)
63 | }
64 | }
65 |
66 | export {
67 | FilePond as FilePond,
68 | FilePondRails as FilePondRails
69 | }
70 |
--------------------------------------------------------------------------------
/app/controllers/filepond/rails/ingress_controller.rb:
--------------------------------------------------------------------------------
1 | require 'open-uri'
2 |
3 | module Filepond
4 | module Rails
5 | class IngressController < ApplicationController
6 | # FilePond calls this endpoint when a URL is dropped onto the
7 | # upload widget. The server acts as a "proxy" for the client in
8 | # order to load a file. We then redirect to the actual file
9 | # which is now served from our servers, and proceed through the
10 | # usual route.
11 | #
12 | # Note that the implementation below may not be the most efficient.
13 | # The alternative way would be to directly proxy the request to
14 | # the URL where the file is originally hosted.
15 | def fetch
16 | begin
17 | # We explicitly declare this for clarity of what is in the
18 | # raw_post value as sent by FilePond
19 | uri = URI.parse(raw_post)
20 | url = uri.to_s
21 |
22 | blob = ActiveStorage::Blob.create_and_upload!(
23 | io: URI.open(uri),
24 | filename: URI.parse(url).path.parameterize
25 | )
26 |
27 | redirect_to ::Rails.application.routes.url_helpers.rails_service_blob_path(
28 | blob.signed_id,
29 | blob.filename
30 | )
31 | # Why we casting such a wide net with StandardError (TL;DR: too many errors to catch):
32 | # See https://stackoverflow.com/a/46979718/20551849
33 | rescue StandardError
34 | head :unprocessable_entity
35 | end
36 | end
37 |
38 | # FilePond calls this endpoint when a user removes (ie. undos)
39 | # a file upload. This ensures that the blob is removed.
40 | def remove
41 | # We explicitly declare this for clarity of what is in the
42 | # raw_post value, as sent by FilePond
43 | signed_id = raw_post
44 |
45 | blob = ActiveStorage::Blob.find_signed(signed_id)
46 | if blob && blob.purge
47 | head :ok
48 | else
49 | # If we cannot find the blob, then we'll just return 404
50 | head :not_found
51 | end
52 | end
53 |
54 | private
55 |
56 | # FilePond sends the value (eg. file ID, URL, etc) and it comes
57 | # through as the POST body. We can retrieve that value with this
58 | # helper.
59 | def raw_post
60 | request.raw_post
61 | end
62 | end
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/test/dummy/db/schema.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is auto-generated from the current state of the database. Instead
4 | # of editing this file, please use the migrations feature of Active Record to
5 | # incrementally modify your database, and then regenerate this schema definition.
6 | #
7 | # This file is the source Rails uses to define your schema when running `bin/rails
8 | # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
9 | # be faster and is potentially less error prone than running all of your
10 | # migrations from scratch. Old migrations may fail to apply correctly if those
11 | # migrations use external dependencies or application code.
12 | #
13 | # It's strongly recommended that you check this file into your version control system.
14 |
15 | ActiveRecord::Schema[7.0].define(version: 20_221_229_033_425) do
16 | create_table 'active_storage_attachments', force: :cascade do |t|
17 | t.string 'name', null: false
18 | t.string 'record_type', null: false
19 | t.bigint 'record_id', null: false
20 | t.bigint 'blob_id', null: false
21 | t.datetime 'created_at', null: false
22 | t.index ['blob_id'], name: 'index_active_storage_attachments_on_blob_id'
23 | t.index %w[record_type record_id name blob_id], name: 'index_active_storage_attachments_uniqueness',
24 | unique: true
25 | end
26 |
27 | create_table 'active_storage_blobs', force: :cascade do |t|
28 | t.string 'key', null: false
29 | t.string 'filename', null: false
30 | t.string 'content_type'
31 | t.text 'metadata'
32 | t.string 'service_name', null: false
33 | t.bigint 'byte_size', null: false
34 | t.string 'checksum'
35 | t.datetime 'created_at', null: false
36 | t.index ['key'], name: 'index_active_storage_blobs_on_key', unique: true
37 | end
38 |
39 | create_table 'active_storage_variant_records', force: :cascade do |t|
40 | t.bigint 'blob_id', null: false
41 | t.string 'variation_digest', null: false
42 | t.index %w[blob_id variation_digest], name: 'index_active_storage_variant_records_uniqueness', unique: true
43 | end
44 |
45 | create_table 'some_models', force: :cascade do |t|
46 | t.datetime 'created_at', null: false
47 | t.datetime 'updated_at', null: false
48 | end
49 |
50 | add_foreign_key 'active_storage_attachments', 'active_storage_blobs', column: 'blob_id'
51 | add_foreign_key 'active_storage_variant_records', 'active_storage_blobs', column: 'blob_id'
52 | end
53 |
--------------------------------------------------------------------------------
/test/dummy/db/migrate/20221229033425_create_active_storage_tables.active_storage.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This migration comes from active_storage (originally 20170806125915)
4 | class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
5 | def change
6 | # Use Active Record's configured type for primary and foreign keys
7 | primary_key_type, foreign_key_type = primary_and_foreign_key_types
8 |
9 | create_table :active_storage_blobs, id: primary_key_type do |t|
10 | t.string :key, null: false
11 | t.string :filename, null: false
12 | t.string :content_type
13 | t.text :metadata
14 | t.string :service_name, null: false
15 | t.bigint :byte_size, null: false
16 | t.string :checksum
17 |
18 | if connection.supports_datetime_with_precision?
19 | t.datetime :created_at, precision: 6, null: false
20 | else
21 | t.datetime :created_at, null: false
22 | end
23 |
24 | t.index [:key], unique: true
25 | end
26 |
27 | create_table :active_storage_attachments, id: primary_key_type do |t|
28 | t.string :name, null: false
29 | t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
30 | t.references :blob, null: false, type: foreign_key_type
31 |
32 | if connection.supports_datetime_with_precision?
33 | t.datetime :created_at, precision: 6, null: false
34 | else
35 | t.datetime :created_at, null: false
36 | end
37 |
38 | t.index %i[record_type record_id name blob_id], name: :index_active_storage_attachments_uniqueness,
39 | unique: true
40 | t.foreign_key :active_storage_blobs, column: :blob_id
41 | end
42 |
43 | create_table :active_storage_variant_records, id: primary_key_type do |t|
44 | t.belongs_to :blob, null: false, index: false, type: foreign_key_type
45 | t.string :variation_digest, null: false
46 |
47 | t.index %i[blob_id variation_digest], name: :index_active_storage_variant_records_uniqueness, unique: true
48 | t.foreign_key :active_storage_blobs, column: :blob_id
49 | end
50 | end
51 |
52 | private
53 |
54 | def primary_and_foreign_key_types
55 | config = Rails.configuration.generators
56 | setting = config.options[config.orm][:primary_key_type]
57 | primary_key_type = setting || :primary_key
58 | foreign_key_type = setting || :bigint
59 | [primary_key_type, foreign_key_type]
60 | end
61 | end
62 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'active_support/core_ext/integer/time'
4 |
5 | # The test environment is used exclusively to run your application's
6 | # test suite. You never need to work with it otherwise. Remember that
7 | # your test database is "scratch space" for the test suite and is wiped
8 | # and recreated between test runs. Don't rely on the data there!
9 |
10 | Rails.application.configure do
11 | # Settings specified here will take precedence over those in config/application.rb.
12 |
13 | # Turn false under Spring and add config.action_view.cache_template_loading = true.
14 | config.cache_classes = true
15 |
16 | # Eager loading loads your whole application. When running a single test locally,
17 | # this probably isn't necessary. It's a good idea to do in a continuous integration
18 | # system, or in some way before deploying your code.
19 | config.eager_load = ENV['CI'].present?
20 |
21 | # Configure public file server for tests with Cache-Control for performance.
22 | config.public_file_server.enabled = true
23 | config.public_file_server.headers = {
24 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
25 | }
26 |
27 | # Show full error reports and disable caching.
28 | config.consider_all_requests_local = true
29 | config.action_controller.perform_caching = false
30 | config.cache_store = :null_store
31 |
32 | # Raise exceptions instead of rendering exception templates.
33 | config.action_dispatch.show_exceptions = false
34 |
35 | # Disable request forgery protection in test environment.
36 | config.action_controller.allow_forgery_protection = false
37 |
38 | # Store uploaded files on the local file system in a temporary directory.
39 | config.active_storage.service = :test
40 |
41 | config.action_mailer.perform_caching = false
42 |
43 | # Tell Action Mailer not to deliver emails to the real world.
44 | # The :test delivery method accumulates sent emails in the
45 | # ActionMailer::Base.deliveries array.
46 | config.action_mailer.delivery_method = :test
47 |
48 | # Print deprecation notices to the stderr.
49 | config.active_support.deprecation = :stderr
50 |
51 | # Raise exceptions for disallowed deprecations.
52 | config.active_support.disallowed_deprecation = :raise
53 |
54 | # Tell Active Support which deprecation messages to disallow.
55 | config.active_support.disallowed_deprecation_warnings = []
56 |
57 | # Raises error for missing translations.
58 | # config.i18n.raise_on_missing_translations = true
59 |
60 | # Annotate rendered view with file names.
61 | # config.action_view.annotate_rendered_view_with_filenames = true
62 | end
63 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'active_support/core_ext/integer/time'
4 |
5 | Rails.application.configure do
6 | # Settings specified here will take precedence over those in config/application.rb.
7 |
8 | # In the development environment your application's code is reloaded any time
9 | # it changes. This slows down response time but is perfect for development
10 | # since you don't have to restart the web server when you make code changes.
11 | config.cache_classes = false
12 |
13 | # Do not eager load code on boot.
14 | config.eager_load = false
15 |
16 | # Show full error reports.
17 | config.consider_all_requests_local = true
18 |
19 | # Enable server timing
20 | config.server_timing = true
21 |
22 | # Enable/disable caching. By default caching is disabled.
23 | # Run rails dev:cache to toggle caching.
24 | if Rails.root.join('tmp/caching-dev.txt').exist?
25 | config.action_controller.perform_caching = true
26 | config.action_controller.enable_fragment_cache_logging = true
27 |
28 | config.cache_store = :memory_store
29 | config.public_file_server.headers = {
30 | 'Cache-Control' => "public, max-age=#{2.days.to_i}"
31 | }
32 | else
33 | config.action_controller.perform_caching = false
34 |
35 | config.cache_store = :null_store
36 | end
37 |
38 | # Store uploaded files on the local file system (see config/storage.yml for options).
39 | config.active_storage.service = :local
40 |
41 | # Don't care if the mailer can't send.
42 | config.action_mailer.raise_delivery_errors = false
43 |
44 | config.action_mailer.perform_caching = false
45 |
46 | # Print deprecation notices to the Rails logger.
47 | config.active_support.deprecation = :log
48 |
49 | # Raise exceptions for disallowed deprecations.
50 | config.active_support.disallowed_deprecation = :raise
51 |
52 | # Tell Active Support which deprecation messages to disallow.
53 | config.active_support.disallowed_deprecation_warnings = []
54 |
55 | # Raise an error on page load if there are pending migrations.
56 | config.active_record.migration_error = :page_load
57 |
58 | # Highlight code that triggered database queries in logs.
59 | config.active_record.verbose_query_logs = true
60 |
61 | # Suppress logger output for asset requests.
62 | config.assets.quiet = true
63 |
64 | # Raises error for missing translations.
65 | # config.i18n.raise_on_missing_translations = true
66 |
67 | # Annotate rendered view with file names.
68 | # config.action_view.annotate_rendered_view_with_filenames = true
69 |
70 | # Uncomment if you wish to allow Action Cable access from any origin.
71 | # config.action_cable.disable_request_forgery_protection = true
72 | end
73 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # filepond-rails
2 |
3 | [](https://badge.fury.io/rb/filepond-rails)
4 | 
5 |
6 | This gem allows you to quickly integrate [FilePond](https://github.com/pqina/filepond) with your Ruby on Rails app.
7 |
8 | ## Requirements
9 | * Rails 7.0.x and above
10 | * Use of [importmap-rails](https://github.com/rails/importmap-rails)
11 |
12 | ## Installation
13 | Add this line to your application's Gemfile:
14 |
15 | ```ruby
16 | gem 'filepond-rails'
17 | ```
18 |
19 | And then run:
20 | ```bash
21 | $ bundle
22 | ```
23 |
24 | Add the following to your `application.css`:
25 | ```css
26 | *= require 'filepond'
27 | ```
28 |
29 | Add `javascript_importmap_tags` in the `head` section of your layout:
30 | ```erb
31 | <%= javascript_importmap_tags %>
32 | ```
33 |
34 | Mount `filepond-rails` routes:
35 | ```ruby
36 | Rails.application.routes.draw do
37 | mount Filepond::Rails::Engine, at: '/filepond'
38 | end
39 | ```
40 |
41 | Note: You must mount the routes at `/filepond`.
42 |
43 | ## Usage
44 |
45 | This gem compromises of two parts:
46 | 1. An ingress controller for the FilePond `fetch` and `remove` server calls. The JavaScript code leverages Active Storage's existing direct upload endpoint.
47 | 2. JavaScript ESM module to automatically connect configured elements with the above endpoints.
48 |
49 | At the present moment, the above parts are designed (and assumed) to work together.
50 |
51 | The gem assumes that you set up your models and Active Storage configuration in a standard Rails way. Assuming you have a form that looks like this:
52 |
53 | ```erb
54 | <%= form_with model: @user, url: update_avatar_path, method: :post do |f| %>
55 | <%= f.label :avatar, 'Update your avatar' %>
56 | <%= f.file_field :avatar, class: 'filepond', direct_upload: true %>
57 | <%= f.button 'Update' %>
58 | <% end %>
59 | ```
60 |
61 | You can instantiate the `file_field` component like so:
62 |
63 | ```js
64 | // app/javascript/application.js
65 | import { FilePondRails, FilePond } from 'filepond-rails'
66 |
67 | window.FilePond = FilePond
68 | window.FilePondRails = FilePondRails
69 |
70 | const input = document.querySelector('.filepond')
71 | FilePondRails.create(input)
72 | ```
73 |
74 | The gem's JavaScript provide two available exports:
75 | 1. `FilePond` (which corresponds to the original FilePond library)
76 | 2. `FilePondRails` (which is a convenience helper that integrates and sets up FilePond to work with our `FilePond::Rails::IngressController`
77 |
78 | ## Development
79 |
80 | filepond-rails is configured with Docker to help developer-contributors be able to easily work on this gem locally.
81 |
82 | To get started:
83 |
84 | 1. Fork this repository
85 | 2. git clone it to your local machine and cd into it
86 | 3. Run `docker compose build`
87 |
88 | To run the gem through the "dummy" app, run:
89 |
90 | ```bash
91 | docker compose up
92 | ```
93 |
94 | To enter the development environment where you can run `bin/rails g controller` and other commands:
95 |
96 | ```bash
97 | docker compose run app bash
98 | ```
99 |
100 | You should now be able to run tests locally within this bash container:
101 |
102 | ```bash
103 | bin/rails test && bin/rails app:test:system
104 | ```
105 |
106 | ## Reference
107 | * Read [How to create a Ruby on Rails gem from your existing code](https://codewithrails.com/create-rails-gem) for a background on this gem
108 |
109 | ## License
110 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
111 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'active_support/core_ext/integer/time'
4 |
5 | Rails.application.configure do
6 | # Settings specified here will take precedence over those in config/application.rb.
7 |
8 | # Code is not reloaded between requests.
9 | config.cache_classes = true
10 |
11 | # Eager load code on boot. This eager loads most of Rails and
12 | # your application in memory, allowing both threaded web servers
13 | # and those relying on copy on write to perform better.
14 | # Rake tasks automatically ignore this option for performance.
15 | config.eager_load = true
16 |
17 | # Full error reports are disabled and caching is turned on.
18 | config.consider_all_requests_local = false
19 | config.action_controller.perform_caching = true
20 |
21 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
22 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
23 | # config.require_master_key = true
24 |
25 | # Disable serving static files from the `/public` folder by default since
26 | # Apache or NGINX already handles this.
27 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
28 |
29 | # Compress CSS using a preprocessor.
30 | # config.assets.css_compressor = :sass
31 |
32 | # Do not fallback to assets pipeline if a precompiled asset is missed.
33 | config.assets.compile = false
34 |
35 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
36 | # config.asset_host = "http://assets.example.com"
37 |
38 | # Specifies the header that your server uses for sending files.
39 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
40 | # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX
41 |
42 | # Store uploaded files on the local file system (see config/storage.yml for options).
43 | config.active_storage.service = :local
44 |
45 | # Mount Action Cable outside main process or domain.
46 | # config.action_cable.mount_path = nil
47 | # config.action_cable.url = "wss://example.com/cable"
48 | # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]
49 |
50 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
51 | # config.force_ssl = true
52 |
53 | # Include generic and useful information about system operation, but avoid logging too much
54 | # information to avoid inadvertent exposure of personally identifiable information (PII).
55 | config.log_level = :info
56 |
57 | # Prepend all log lines with the following tags.
58 | config.log_tags = [:request_id]
59 |
60 | # Use a different cache store in production.
61 | # config.cache_store = :mem_cache_store
62 |
63 | # Use a real queuing backend for Active Job (and separate queues per environment).
64 | # config.active_job.queue_adapter = :resque
65 | # config.active_job.queue_name_prefix = "dummy_production"
66 |
67 | config.action_mailer.perform_caching = false
68 |
69 | # Ignore bad email addresses and do not raise email delivery errors.
70 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
71 | # config.action_mailer.raise_delivery_errors = false
72 |
73 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
74 | # the I18n.default_locale when a translation cannot be found).
75 | config.i18n.fallbacks = true
76 |
77 | # Don't log any deprecations.
78 | config.active_support.report_deprecations = false
79 |
80 | # Use default logging formatter so that PID and timestamp are not suppressed.
81 | config.log_formatter = ::Logger::Formatter.new
82 |
83 | # Use a different logger for distributed setups.
84 | # require "syslog/logger"
85 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name")
86 |
87 | if ENV['RAILS_LOG_TO_STDOUT'].present?
88 | logger = ActiveSupport::Logger.new($stdout)
89 | logger.formatter = config.log_formatter
90 | config.logger = ActiveSupport::TaggedLogging.new(logger)
91 | end
92 |
93 | # Do not dump schema after migrations.
94 | config.active_record.dump_schema_after_migration = false
95 | end
96 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | filepond-rails (1.0.1)
5 | rails (>= 7.0, < 8)
6 |
7 | GEM
8 | remote: https://rubygems.org/
9 | specs:
10 | actioncable (7.0.4.1)
11 | actionpack (= 7.0.4.1)
12 | activesupport (= 7.0.4.1)
13 | nio4r (~> 2.0)
14 | websocket-driver (>= 0.6.1)
15 | actionmailbox (7.0.4.1)
16 | actionpack (= 7.0.4.1)
17 | activejob (= 7.0.4.1)
18 | activerecord (= 7.0.4.1)
19 | activestorage (= 7.0.4.1)
20 | activesupport (= 7.0.4.1)
21 | mail (>= 2.7.1)
22 | net-imap
23 | net-pop
24 | net-smtp
25 | actionmailer (7.0.4.1)
26 | actionpack (= 7.0.4.1)
27 | actionview (= 7.0.4.1)
28 | activejob (= 7.0.4.1)
29 | activesupport (= 7.0.4.1)
30 | mail (~> 2.5, >= 2.5.4)
31 | net-imap
32 | net-pop
33 | net-smtp
34 | rails-dom-testing (~> 2.0)
35 | actionpack (7.0.4.1)
36 | actionview (= 7.0.4.1)
37 | activesupport (= 7.0.4.1)
38 | rack (~> 2.0, >= 2.2.0)
39 | rack-test (>= 0.6.3)
40 | rails-dom-testing (~> 2.0)
41 | rails-html-sanitizer (~> 1.0, >= 1.2.0)
42 | actiontext (7.0.4.1)
43 | actionpack (= 7.0.4.1)
44 | activerecord (= 7.0.4.1)
45 | activestorage (= 7.0.4.1)
46 | activesupport (= 7.0.4.1)
47 | globalid (>= 0.6.0)
48 | nokogiri (>= 1.8.5)
49 | actionview (7.0.4.1)
50 | activesupport (= 7.0.4.1)
51 | builder (~> 3.1)
52 | erubi (~> 1.4)
53 | rails-dom-testing (~> 2.0)
54 | rails-html-sanitizer (~> 1.1, >= 1.2.0)
55 | activejob (7.0.4.1)
56 | activesupport (= 7.0.4.1)
57 | globalid (>= 0.3.6)
58 | activemodel (7.0.4.1)
59 | activesupport (= 7.0.4.1)
60 | activerecord (7.0.4.1)
61 | activemodel (= 7.0.4.1)
62 | activesupport (= 7.0.4.1)
63 | activestorage (7.0.4.1)
64 | actionpack (= 7.0.4.1)
65 | activejob (= 7.0.4.1)
66 | activerecord (= 7.0.4.1)
67 | activesupport (= 7.0.4.1)
68 | marcel (~> 1.0)
69 | mini_mime (>= 1.1.0)
70 | activesupport (7.0.4.1)
71 | concurrent-ruby (~> 1.0, >= 1.0.2)
72 | i18n (>= 1.6, < 2)
73 | minitest (>= 5.1)
74 | tzinfo (~> 2.0)
75 | addressable (2.8.1)
76 | public_suffix (>= 2.0.2, < 6.0)
77 | ast (2.4.2)
78 | builder (3.2.4)
79 | capybara (3.38.0)
80 | addressable
81 | matrix
82 | mini_mime (>= 0.1.3)
83 | nokogiri (~> 1.8)
84 | rack (>= 1.6.0)
85 | rack-test (>= 0.6.3)
86 | regexp_parser (>= 1.5, < 3.0)
87 | xpath (~> 3.2)
88 | concurrent-ruby (1.1.10)
89 | crack (0.4.5)
90 | rexml
91 | crass (1.0.6)
92 | date (3.3.3)
93 | dropybara (1.0.0)
94 | erubi (1.12.0)
95 | ffi (1.15.5)
96 | globalid (1.0.1)
97 | activesupport (>= 5.0)
98 | hashdiff (1.0.1)
99 | i18n (1.12.0)
100 | concurrent-ruby (~> 1.0)
101 | image_processing (1.12.2)
102 | mini_magick (>= 4.9.5, < 5)
103 | ruby-vips (>= 2.0.17, < 3)
104 | importmap-rails (1.1.5)
105 | actionpack (>= 6.0.0)
106 | railties (>= 6.0.0)
107 | json (2.6.3)
108 | loofah (2.19.1)
109 | crass (~> 1.0.2)
110 | nokogiri (>= 1.5.9)
111 | mail (2.8.0.1)
112 | mini_mime (>= 0.1.1)
113 | net-imap
114 | net-pop
115 | net-smtp
116 | marcel (1.0.2)
117 | matrix (0.4.2)
118 | method_source (1.0.0)
119 | mini_magick (4.12.0)
120 | mini_mime (1.1.2)
121 | mini_portile2 (2.8.1)
122 | minitest (5.17.0)
123 | net-imap (0.3.4)
124 | date
125 | net-protocol
126 | net-pop (0.1.2)
127 | net-protocol
128 | net-protocol (0.2.1)
129 | timeout
130 | net-smtp (0.3.3)
131 | net-protocol
132 | nio4r (2.5.8)
133 | nokogiri (1.14.0-aarch64-linux)
134 | racc (~> 1.4)
135 | nokogiri (1.14.0-arm64-darwin)
136 | racc (~> 1.4)
137 | nokogiri (1.14.0-x86_64-linux)
138 | racc (~> 1.4)
139 | parallel (1.22.1)
140 | parser (3.1.3.0)
141 | ast (~> 2.4.1)
142 | public_suffix (5.0.1)
143 | puma (6.0.1)
144 | nio4r (~> 2.0)
145 | racc (1.6.2)
146 | rack (2.2.6.2)
147 | rack-test (2.0.2)
148 | rack (>= 1.3)
149 | rails (7.0.4.1)
150 | actioncable (= 7.0.4.1)
151 | actionmailbox (= 7.0.4.1)
152 | actionmailer (= 7.0.4.1)
153 | actionpack (= 7.0.4.1)
154 | actiontext (= 7.0.4.1)
155 | actionview (= 7.0.4.1)
156 | activejob (= 7.0.4.1)
157 | activemodel (= 7.0.4.1)
158 | activerecord (= 7.0.4.1)
159 | activestorage (= 7.0.4.1)
160 | activesupport (= 7.0.4.1)
161 | bundler (>= 1.15.0)
162 | railties (= 7.0.4.1)
163 | rails-dom-testing (2.0.3)
164 | activesupport (>= 4.2.0)
165 | nokogiri (>= 1.6)
166 | rails-html-sanitizer (1.5.0)
167 | loofah (~> 2.19, >= 2.19.1)
168 | railties (7.0.4.1)
169 | actionpack (= 7.0.4.1)
170 | activesupport (= 7.0.4.1)
171 | method_source
172 | rake (>= 12.2)
173 | thor (~> 1.0)
174 | zeitwerk (~> 2.5)
175 | rainbow (3.1.1)
176 | rake (13.0.6)
177 | regexp_parser (2.6.1)
178 | rexml (3.2.5)
179 | rubocop (1.41.1)
180 | json (~> 2.3)
181 | parallel (~> 1.10)
182 | parser (>= 3.1.2.1)
183 | rainbow (>= 2.2.2, < 4.0)
184 | regexp_parser (>= 1.8, < 3.0)
185 | rexml (>= 3.2.5, < 4.0)
186 | rubocop-ast (>= 1.23.0, < 2.0)
187 | ruby-progressbar (~> 1.7)
188 | unicode-display_width (>= 1.4.0, < 3.0)
189 | rubocop-ast (1.24.0)
190 | parser (>= 3.1.1.0)
191 | ruby-progressbar (1.11.0)
192 | ruby-vips (2.1.4)
193 | ffi (~> 1.12)
194 | rubyzip (2.3.2)
195 | selenium-webdriver (4.7.1)
196 | rexml (~> 3.2, >= 3.2.5)
197 | rubyzip (>= 1.2.2, < 3.0)
198 | websocket (~> 1.0)
199 | sprockets (4.2.0)
200 | concurrent-ruby (~> 1.0)
201 | rack (>= 2.2.4, < 4)
202 | sprockets-rails (3.4.2)
203 | actionpack (>= 5.2)
204 | activesupport (>= 5.2)
205 | sprockets (>= 3.0.0)
206 | sqlite3 (1.5.4)
207 | mini_portile2 (~> 2.8.0)
208 | thor (1.2.1)
209 | timeout (0.3.1)
210 | tzinfo (2.0.5)
211 | concurrent-ruby (~> 1.0)
212 | unicode-display_width (2.3.0)
213 | webmock (3.18.1)
214 | addressable (>= 2.8.0)
215 | crack (>= 0.3.2)
216 | hashdiff (>= 0.4.0, < 2.0.0)
217 | websocket (1.2.9)
218 | websocket-driver (0.7.5)
219 | websocket-extensions (>= 0.1.0)
220 | websocket-extensions (0.1.5)
221 | xpath (3.2.0)
222 | nokogiri (~> 1.8)
223 | zeitwerk (2.6.6)
224 |
225 | PLATFORMS
226 | aarch64-linux
227 | arm64-darwin-22
228 | x86_64-linux
229 |
230 | DEPENDENCIES
231 | capybara
232 | dropybara
233 | filepond-rails!
234 | image_processing
235 | importmap-rails
236 | puma (~> 6.0)
237 | rubocop (~> 1.1)
238 | selenium-webdriver
239 | sprockets-rails
240 | sqlite3
241 | webmock (~> 3.18)
242 |
243 | BUNDLED WITH
244 | 2.4.1
245 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/filepond.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * FilePond 4.30.4
3 | * Licensed under MIT, https://opensource.org/licenses/MIT/
4 | * Please visit https://pqina.nl/filepond/ for details.
5 | */
6 |
7 | /* eslint-disable */
8 | .filepond--assistant{position:absolute;overflow:hidden;height:1px;width:1px;padding:0;border:0;clip:rect(1px,1px,1px,1px);-webkit-clip-path:inset(50%);clip-path:inset(50%);white-space:nowrap}.filepond--browser.filepond--browser{position:absolute;margin:0;padding:0;left:1em;top:1.75em;width:calc(100% - 2em);opacity:0;font-size:0}.filepond--data{position:absolute;width:0;height:0;padding:0;margin:0;border:none;visibility:hidden;pointer-events:none;contain:strict}.filepond--drip{position:absolute;top:0;left:0;right:0;bottom:0;overflow:hidden;opacity:.1;pointer-events:none;border-radius:.5em;background:rgba(0,0,0,.01)}.filepond--drip-blob{-webkit-transform-origin:center center;transform-origin:center center;width:8em;height:8em;margin-left:-4em;margin-top:-4em;background:#292625;border-radius:50%}.filepond--drip-blob,.filepond--drop-label{position:absolute;top:0;left:0;will-change:transform,opacity}.filepond--drop-label{right:0;margin:0;color:#4f4f4f;display:flex;justify-content:center;align-items:center;height:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.filepond--drop-label.filepond--drop-label label{display:block;margin:0;padding:.5em}.filepond--drop-label label{cursor:default;font-size:.875em;font-weight:400;text-align:center;line-height:1.5}.filepond--label-action{text-decoration:underline;-webkit-text-decoration-skip:ink;text-decoration-skip-ink:auto;-webkit-text-decoration-color:#a7a4a4;text-decoration-color:#a7a4a4;cursor:pointer}.filepond--root[data-disabled] .filepond--drop-label label{opacity:.5}.filepond--file-action-button.filepond--file-action-button{font-size:1em;width:1.625em;height:1.625em;font-family:inherit;line-height:inherit;margin:0;padding:0;border:none;outline:none;will-change:transform,opacity}.filepond--file-action-button.filepond--file-action-button span{position:absolute;overflow:hidden;height:1px;width:1px;padding:0;border:0;clip:rect(1px,1px,1px,1px);-webkit-clip-path:inset(50%);clip-path:inset(50%);white-space:nowrap}.filepond--file-action-button.filepond--file-action-button svg{width:100%;height:100%}.filepond--file-action-button.filepond--file-action-button:after{position:absolute;left:-.75em;right:-.75em;top:-.75em;bottom:-.75em;content:""}.filepond--file-action-button{cursor:auto;color:#fff;border-radius:50%;background-color:rgba(0,0,0,.5);background-image:none;box-shadow:0 0 0 0 hsla(0,0%,100%,0);transition:box-shadow .25s ease-in}.filepond--file-action-button:focus,.filepond--file-action-button:hover{box-shadow:0 0 0 .125em hsla(0,0%,100%,.9)}.filepond--file-action-button[disabled]{color:hsla(0,0%,100%,.5);background-color:rgba(0,0,0,.25)}.filepond--file-action-button[hidden]{display:none}.filepond--action-edit-item.filepond--action-edit-item{width:2em;height:2em;padding:.1875em}.filepond--action-edit-item.filepond--action-edit-item[data-align*=center]{margin-left:-.1875em}.filepond--action-edit-item.filepond--action-edit-item[data-align*=bottom]{margin-bottom:-.1875em}.filepond--action-edit-item-alt{border:none;line-height:inherit;background:transparent;font-family:inherit;color:inherit;outline:none;padding:0;margin:0 0 0 .25em;pointer-events:all;position:absolute}.filepond--action-edit-item-alt svg{width:1.3125em;height:1.3125em}.filepond--action-edit-item-alt span{font-size:0;opacity:0}.filepond--file-info{position:static;display:flex;flex-direction:column;align-items:flex-start;flex:1;margin:0 .5em 0 0;min-width:0;will-change:transform,opacity;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.filepond--file-info *{margin:0}.filepond--file-info .filepond--file-info-main{font-size:.75em;line-height:1.2;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;width:100%}.filepond--file-info .filepond--file-info-sub{font-size:.625em;opacity:.5;transition:opacity .25s ease-in-out;white-space:nowrap}.filepond--file-info .filepond--file-info-sub:empty{display:none}.filepond--file-status{position:static;display:flex;flex-direction:column;align-items:flex-end;flex-grow:0;flex-shrink:0;margin:0;min-width:2.25em;text-align:right;will-change:transform,opacity;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.filepond--file-status *{margin:0;white-space:nowrap}.filepond--file-status .filepond--file-status-main{font-size:.75em;line-height:1.2}.filepond--file-status .filepond--file-status-sub{font-size:.625em;opacity:.5;transition:opacity .25s ease-in-out}.filepond--file-wrapper.filepond--file-wrapper{border:none;margin:0;padding:0;min-width:0;height:100%}.filepond--file-wrapper.filepond--file-wrapper>legend{position:absolute;overflow:hidden;height:1px;width:1px;padding:0;border:0;clip:rect(1px,1px,1px,1px);-webkit-clip-path:inset(50%);clip-path:inset(50%);white-space:nowrap}.filepond--file{position:static;display:flex;height:100%;align-items:flex-start;padding:.5625em;color:#fff;border-radius:.5em}.filepond--file .filepond--file-status{margin-left:auto;margin-right:2.25em}.filepond--file .filepond--processing-complete-indicator{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:3}.filepond--file .filepond--file-action-button,.filepond--file .filepond--processing-complete-indicator,.filepond--file .filepond--progress-indicator{position:absolute}.filepond--file [data-align*=left]{left:.5625em}.filepond--file [data-align*=right]{right:.5625em}.filepond--file [data-align*=center]{left:calc(50% - .8125em)}.filepond--file [data-align*=bottom]{bottom:1.125em}.filepond--file [data-align=center]{top:calc(50% - .8125em)}.filepond--file .filepond--progress-indicator{margin-top:.1875em}.filepond--file .filepond--progress-indicator[data-align*=right]{margin-right:.1875em}.filepond--file .filepond--progress-indicator[data-align*=left]{margin-left:.1875em}[data-filepond-item-state*=error] .filepond--file-info,[data-filepond-item-state*=invalid] .filepond--file-info,[data-filepond-item-state=cancelled] .filepond--file-info{margin-right:2.25em}[data-filepond-item-state~=processing] .filepond--file-status-sub{opacity:0}[data-filepond-item-state~=processing] .filepond--action-abort-item-processing~.filepond--file-status .filepond--file-status-sub{opacity:.5}[data-filepond-item-state=processing-error] .filepond--file-status-sub{opacity:0}[data-filepond-item-state=processing-error] .filepond--action-retry-item-processing~.filepond--file-status .filepond--file-status-sub{opacity:.5}[data-filepond-item-state=processing-complete] .filepond--action-revert-item-processing svg{-webkit-animation:fall .5s linear .125s both;animation:fall .5s linear .125s both}[data-filepond-item-state=processing-complete] .filepond--file-status-sub{opacity:.5}[data-filepond-item-state=processing-complete] .filepond--file-info-sub,[data-filepond-item-state=processing-complete] .filepond--processing-complete-indicator:not([style*=hidden])~.filepond--file-status .filepond--file-status-sub{opacity:0}[data-filepond-item-state=processing-complete] .filepond--action-revert-item-processing~.filepond--file-info .filepond--file-info-sub{opacity:.5}[data-filepond-item-state*=error] .filepond--file-wrapper,[data-filepond-item-state*=error] .filepond--panel,[data-filepond-item-state*=invalid] .filepond--file-wrapper,[data-filepond-item-state*=invalid] .filepond--panel{-webkit-animation:shake .65s linear both;animation:shake .65s linear both}[data-filepond-item-state*=busy] .filepond--progress-indicator svg{-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@-webkit-keyframes shake{10%,90%{-webkit-transform:translateX(-.0625em);transform:translateX(-.0625em)}20%,80%{-webkit-transform:translateX(.125em);transform:translateX(.125em)}30%,50%,70%{-webkit-transform:translateX(-.25em);transform:translateX(-.25em)}40%,60%{-webkit-transform:translateX(.25em);transform:translateX(.25em)}}@keyframes shake{10%,90%{-webkit-transform:translateX(-.0625em);transform:translateX(-.0625em)}20%,80%{-webkit-transform:translateX(.125em);transform:translateX(.125em)}30%,50%,70%{-webkit-transform:translateX(-.25em);transform:translateX(-.25em)}40%,60%{-webkit-transform:translateX(.25em);transform:translateX(.25em)}}@-webkit-keyframes fall{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}70%{opacity:1;-webkit-transform:scale(1.1);transform:scale(1.1);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}to{-webkit-transform:scale(1);transform:scale(1);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes fall{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}70%{opacity:1;-webkit-transform:scale(1.1);transform:scale(1.1);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}to{-webkit-transform:scale(1);transform:scale(1);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.filepond--hopper[data-hopper-state=drag-over]>*{pointer-events:none}.filepond--hopper[data-hopper-state=drag-over]:after{content:"";position:absolute;left:0;top:0;right:0;bottom:0;z-index:100}.filepond--progress-indicator{z-index:103}.filepond--file-action-button{z-index:102}.filepond--file-status{z-index:101}.filepond--file-info{z-index:100}.filepond--item{position:absolute;top:0;left:0;right:0;z-index:1;padding:0;margin:.25em;will-change:transform,opacity}.filepond--item>.filepond--panel{z-index:-1}.filepond--item>.filepond--panel .filepond--panel-bottom{box-shadow:0 .0625em .125em -.0625em rgba(0,0,0,.25)}.filepond--item>.filepond--file-wrapper,.filepond--item>.filepond--panel{transition:opacity .15s ease-out}.filepond--item[data-drag-state]{cursor:-webkit-grab;cursor:grab}.filepond--item[data-drag-state]>.filepond--panel{transition:box-shadow .125s ease-in-out;box-shadow:0 0 0 transparent}.filepond--item[data-drag-state=drag]{cursor:-webkit-grabbing;cursor:grabbing}.filepond--item[data-drag-state=drag]>.filepond--panel{box-shadow:0 .125em .3125em rgba(0,0,0,.325)}.filepond--item[data-drag-state]:not([data-drag-state=idle]){z-index:2}.filepond--item-panel{background-color:#64605e}[data-filepond-item-state=processing-complete] .filepond--item-panel{background-color:#369763}[data-filepond-item-state*=error] .filepond--item-panel,[data-filepond-item-state*=invalid] .filepond--item-panel{background-color:#c44e47}.filepond--item-panel{border-radius:.5em;transition:background-color .25s}.filepond--list-scroller{position:absolute;top:0;left:0;right:0;margin:0;will-change:transform}.filepond--list-scroller[data-state=overflow] .filepond--list{bottom:0;right:0}.filepond--list-scroller[data-state=overflow]{overflow-y:scroll;overflow-x:hidden;-webkit-overflow-scrolling:touch;-webkit-mask:linear-gradient(180deg,#000 calc(100% - .5em),transparent);mask:linear-gradient(180deg,#000 calc(100% - .5em),transparent)}.filepond--list-scroller::-webkit-scrollbar{background:transparent}.filepond--list-scroller::-webkit-scrollbar:vertical{width:1em}.filepond--list-scroller::-webkit-scrollbar:horizontal{height:0}.filepond--list-scroller::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.3);border-radius:99999px;border:.3125em solid transparent;background-clip:content-box}.filepond--list.filepond--list{position:absolute;top:0;margin:0;padding:0;list-style-type:none;will-change:transform}.filepond--list{left:.75em;right:.75em}.filepond--root[data-style-panel-layout~=integrated]{width:100%;height:100%;max-width:none;margin:0}.filepond--root[data-style-panel-layout~=circle] .filepond--panel-root,.filepond--root[data-style-panel-layout~=integrated] .filepond--panel-root{border-radius:0}.filepond--root[data-style-panel-layout~=circle] .filepond--panel-root>*,.filepond--root[data-style-panel-layout~=integrated] .filepond--panel-root>*{display:none}.filepond--root[data-style-panel-layout~=circle] .filepond--drop-label,.filepond--root[data-style-panel-layout~=integrated] .filepond--drop-label{bottom:0;height:auto;display:flex;justify-content:center;align-items:center;z-index:7}.filepond--root[data-style-panel-layout~=circle] .filepond--item-panel,.filepond--root[data-style-panel-layout~=integrated] .filepond--item-panel{display:none}.filepond--root[data-style-panel-layout~=compact] .filepond--list-scroller,.filepond--root[data-style-panel-layout~=integrated] .filepond--list-scroller{overflow:hidden;height:100%;margin-top:0;margin-bottom:0}.filepond--root[data-style-panel-layout~=compact] .filepond--list,.filepond--root[data-style-panel-layout~=integrated] .filepond--list{left:0;right:0;height:100%}.filepond--root[data-style-panel-layout~=compact] .filepond--item,.filepond--root[data-style-panel-layout~=integrated] .filepond--item{margin:0}.filepond--root[data-style-panel-layout~=compact] .filepond--file-wrapper,.filepond--root[data-style-panel-layout~=integrated] .filepond--file-wrapper{height:100%}.filepond--root[data-style-panel-layout~=compact] .filepond--drop-label,.filepond--root[data-style-panel-layout~=integrated] .filepond--drop-label{z-index:7}.filepond--root[data-style-panel-layout~=circle]{border-radius:99999rem;overflow:hidden}.filepond--root[data-style-panel-layout~=circle]>.filepond--panel{border-radius:inherit}.filepond--root[data-style-panel-layout~=circle]>.filepond--panel>*{display:none}.filepond--root[data-style-panel-layout~=circle] .filepond--file-info,.filepond--root[data-style-panel-layout~=circle] .filepond--file-status{display:none}.filepond--root[data-style-panel-layout~=circle] .filepond--action-edit-item{opacity:1!important;visibility:visible!important}@media not all and (min-resolution:0.001dpcm){@supports (-webkit-appearance:none) and (stroke-color:transparent){.filepond--root[data-style-panel-layout~=circle]{will-change:transform}}}.filepond--panel-root{border-radius:.5em;background-color:#f1f0ef}.filepond--panel{position:absolute;left:0;top:0;right:0;margin:0;height:100%!important;pointer-events:none}.filepond-panel:not([data-scalable=false]){height:auto!important}.filepond--panel[data-scalable=false]>div{display:none}.filepond--panel[data-scalable=true]{-webkit-transform-style:preserve-3d;transform-style:preserve-3d;background-color:transparent!important;border:none!important}.filepond--panel-bottom,.filepond--panel-center,.filepond--panel-top{position:absolute;left:0;top:0;right:0;margin:0;padding:0}.filepond--panel-bottom,.filepond--panel-top{height:.5em}.filepond--panel-top{border-bottom-left-radius:0!important;border-bottom-right-radius:0!important;border-bottom:none!important}.filepond--panel-top:after{content:"";position:absolute;height:2px;left:0;right:0;bottom:-1px;background-color:inherit}.filepond--panel-bottom,.filepond--panel-center{will-change:transform;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform-origin:left top;transform-origin:left top;-webkit-transform:translate3d(0,.5em,0);transform:translate3d(0,.5em,0)}.filepond--panel-bottom{border-top-left-radius:0!important;border-top-right-radius:0!important;border-top:none!important}.filepond--panel-bottom:before{content:"";position:absolute;height:2px;left:0;right:0;top:-1px;background-color:inherit}.filepond--panel-center{height:100px!important;border-top:none!important;border-bottom:none!important;border-radius:0!important}.filepond--panel-center:not([style]){visibility:hidden}.filepond--progress-indicator{position:static;width:1.25em;height:1.25em;color:#fff;margin:0;pointer-events:none;will-change:transform,opacity}.filepond--progress-indicator svg{width:100%;height:100%;vertical-align:top;transform-box:fill-box}.filepond--progress-indicator path{fill:none;stroke:currentColor}.filepond--list-scroller{z-index:6}.filepond--drop-label{z-index:5}.filepond--drip{z-index:3}.filepond--root>.filepond--panel{z-index:2}.filepond--browser{z-index:1}.filepond--root{box-sizing:border-box;position:relative;margin-bottom:1em;font-size:1rem;line-height:normal;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;font-weight:450;text-align:left;text-rendering:optimizeLegibility;direction:ltr;contain:layout style size}.filepond--root *{box-sizing:inherit;line-height:inherit}.filepond--root :not(text){font-size:inherit}.filepond--root[data-disabled]{pointer-events:none}.filepond--root[data-disabled] .filepond--list-scroller{pointer-events:all}.filepond--root[data-disabled] .filepond--list{pointer-events:none}.filepond--root .filepond--drop-label{min-height:4.75em}.filepond--root .filepond--list-scroller{margin-top:1em;margin-bottom:1em}.filepond--root .filepond--credits{position:absolute;right:0;opacity:.175;line-height:.85;font-size:11px;color:inherit;text-decoration:none;z-index:3;bottom:-14px}.filepond--root .filepond--credits[style]{top:0;bottom:auto;margin-top:14px}
--------------------------------------------------------------------------------
/app/assets/stylesheets/filepond.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * FilePond 4.30.4
3 | * Licensed under MIT, https://opensource.org/licenses/MIT/
4 | * Please visit https://pqina.nl/filepond/ for details.
5 | */
6 |
7 | /* eslint-disable */
8 | .filepond--assistant {
9 | position: absolute;
10 | overflow: hidden;
11 | height: 1px;
12 | width: 1px;
13 | padding: 0;
14 | border: 0;
15 | clip: rect(1px, 1px, 1px, 1px);
16 | -webkit-clip-path: inset(50%);
17 | clip-path: inset(50%);
18 | white-space: nowrap;
19 | }
20 | /* Hard to override styles */
21 | .filepond--browser.filepond--browser {
22 | /* is positioned absolute so it is focusable for form validation errors */
23 | position: absolute;
24 | margin: 0;
25 | padding: 0;
26 |
27 | /* is positioned ~behind drop label */
28 | left: 1em;
29 | top: 1.75em;
30 | width: calc(100% - 2em);
31 |
32 | /* hide visually */
33 | opacity: 0;
34 | font-size: 0; /* removes text cursor in Internet Explorer 11 */
35 | }
36 | .filepond--data {
37 | position: absolute;
38 | width: 0;
39 | height: 0;
40 | padding: 0;
41 | margin: 0;
42 | border: none;
43 | visibility: hidden;
44 | pointer-events: none;
45 | contain: strict;
46 | }
47 | .filepond--drip {
48 | position: absolute;
49 | top: 0;
50 | left: 0;
51 | right: 0;
52 | bottom: 0;
53 | overflow: hidden;
54 | opacity: 0.1;
55 |
56 | /* can't interact with this element */
57 | pointer-events: none;
58 |
59 | /* inherit border radius from parent (needed for drip-blob cut of) */
60 | border-radius: 0.5em;
61 |
62 | /* this seems to prevent Chrome from redrawing this layer constantly */
63 | background: rgba(0, 0, 0, 0.01);
64 | }
65 | .filepond--drip-blob {
66 | position: absolute;
67 | -webkit-transform-origin: center center;
68 | transform-origin: center center;
69 | top: 0;
70 | left: 0;
71 | width: 8em;
72 | height: 8em;
73 | margin-left: -4em;
74 | margin-top: -4em;
75 | background: #292625;
76 | border-radius: 50%;
77 |
78 | /* will be animated */
79 | will-change: transform, opacity;
80 | }
81 | .filepond--drop-label {
82 | position: absolute;
83 | left: 0;
84 | right: 0;
85 | top: 0;
86 | margin: 0;
87 | color: #4f4f4f;
88 |
89 | /* center contents */
90 | display: flex;
91 | justify-content: center;
92 | align-items: center;
93 |
94 | /* fixes IE11 centering problems (is overruled by label min-height) */
95 | height: 0px;
96 |
97 | /* dont allow selection */
98 | -webkit-user-select: none;
99 | -moz-user-select: none;
100 | -ms-user-select: none;
101 | user-select: none;
102 |
103 | /* will be animated */
104 | will-change: transform, opacity;
105 | }
106 | /* Hard to override styles on purpose */
107 | .filepond--drop-label.filepond--drop-label label {
108 | display: block;
109 | margin: 0;
110 | padding: 0.5em; /* use padding instead of margin so click area is not impacted */
111 | }
112 | .filepond--drop-label label {
113 | cursor: default;
114 | font-size: 0.875em;
115 | font-weight: normal;
116 | text-align: center;
117 | line-height: 1.5;
118 | }
119 | .filepond--label-action {
120 | text-decoration: underline;
121 | -webkit-text-decoration-skip: ink;
122 | text-decoration-skip-ink: auto;
123 | -webkit-text-decoration-color: #a7a4a4;
124 | text-decoration-color: #a7a4a4;
125 | cursor: pointer;
126 | }
127 | .filepond--root[data-disabled] .filepond--drop-label label {
128 | opacity: 0.5;
129 | }
130 | /* Hard to override styles */
131 | .filepond--file-action-button.filepond--file-action-button {
132 | font-size: 1em;
133 | width: 1.625em;
134 | height: 1.625em;
135 |
136 | font-family: inherit;
137 | line-height: inherit;
138 |
139 | margin: 0;
140 | padding: 0;
141 | border: none;
142 | outline: none;
143 |
144 | will-change: transform, opacity;
145 |
146 | /* hidden label */
147 | }
148 | .filepond--file-action-button.filepond--file-action-button span {
149 | position: absolute;
150 | overflow: hidden;
151 | height: 1px;
152 | width: 1px;
153 | padding: 0;
154 | border: 0;
155 | clip: rect(1px, 1px, 1px, 1px);
156 | -webkit-clip-path: inset(50%);
157 | clip-path: inset(50%);
158 | white-space: nowrap;
159 | }
160 | .filepond--file-action-button.filepond--file-action-button {
161 | /* scale SVG to fill button */
162 | }
163 | .filepond--file-action-button.filepond--file-action-button svg {
164 | width: 100%;
165 | height: 100%;
166 | }
167 | .filepond--file-action-button.filepond--file-action-button {
168 | /* bigger touch area */
169 | }
170 | .filepond--file-action-button.filepond--file-action-button::after {
171 | position: absolute;
172 | left: -0.75em;
173 | right: -0.75em;
174 | top: -0.75em;
175 | bottom: -0.75em;
176 | content: '';
177 | }
178 | /* Soft styles */
179 | .filepond--file-action-button {
180 | /* use default arrow cursor */
181 | cursor: auto;
182 |
183 | /* reset default button styles */
184 | color: #fff;
185 |
186 | /* set default look n feel */
187 | border-radius: 50%;
188 | background-color: rgba(0, 0, 0, 0.5);
189 | background-image: none;
190 |
191 | /* we animate box shadow on focus */
192 | /* it's only slightly slower than animating */
193 | /* a pseudo-element with transforms and renders */
194 | /* a lot better on chrome */
195 | box-shadow: 0 0 0 0 rgba(255, 255, 255, 0);
196 | transition: box-shadow 0.25s ease-in;
197 | }
198 | .filepond--file-action-button:hover,
199 | .filepond--file-action-button:focus {
200 | box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.9);
201 | }
202 | .filepond--file-action-button[disabled] {
203 | color: rgba(255, 255, 255, 0.5);
204 | background-color: rgba(0, 0, 0, 0.25);
205 | }
206 | .filepond--file-action-button[hidden] {
207 | display: none;
208 | }
209 | /* edit button */
210 | .filepond--action-edit-item.filepond--action-edit-item {
211 | width: 2em;
212 | height: 2em;
213 | padding: 0.1875em;
214 | }
215 | .filepond--action-edit-item.filepond--action-edit-item[data-align*='center'] {
216 | margin-left: -0.1875em;
217 | }
218 | .filepond--action-edit-item.filepond--action-edit-item[data-align*='bottom'] {
219 | margin-bottom: -0.1875em;
220 | }
221 | .filepond--action-edit-item-alt {
222 | border: none;
223 | line-height: inherit;
224 | background: transparent;
225 | font-family: inherit;
226 | color: inherit;
227 | outline: none;
228 | padding: 0;
229 | margin: 0 0 0 0.25em;
230 | pointer-events: all;
231 | position: absolute;
232 | }
233 | .filepond--action-edit-item-alt svg {
234 | width: 1.3125em;
235 | height: 1.3125em;
236 | }
237 | .filepond--action-edit-item-alt span {
238 | font-size: 0;
239 | opacity: 0;
240 | }
241 | .filepond--file-info {
242 | position: static;
243 | display: flex;
244 | flex-direction: column;
245 | align-items: flex-start;
246 | flex: 1;
247 | margin: 0 0.5em 0 0;
248 | min-width: 0;
249 |
250 | /* will be animated */
251 | will-change: transform, opacity;
252 |
253 | /* can't do anything with this info */
254 | pointer-events: none;
255 | -webkit-user-select: none;
256 | -moz-user-select: none;
257 | -ms-user-select: none;
258 | user-select: none;
259 |
260 | /* no margins on children */
261 | }
262 | .filepond--file-info * {
263 | margin: 0;
264 | }
265 | .filepond--file-info {
266 | /* we don't want to have these overrules so these selectors are a bit more specific */
267 | }
268 | .filepond--file-info .filepond--file-info-main {
269 | font-size: 0.75em;
270 | line-height: 1.2;
271 |
272 | /* we want ellipsis if this bar gets too wide */
273 | text-overflow: ellipsis;
274 | overflow: hidden;
275 | white-space: nowrap;
276 | width: 100%;
277 | }
278 | .filepond--file-info .filepond--file-info-sub {
279 | font-size: 0.625em;
280 | opacity: 0.5;
281 | transition: opacity 0.25s ease-in-out;
282 | white-space: nowrap;
283 | }
284 | .filepond--file-info .filepond--file-info-sub:empty {
285 | display: none;
286 | }
287 | .filepond--file-status {
288 | position: static;
289 | display: flex;
290 | flex-direction: column;
291 | align-items: flex-end;
292 | flex-grow: 0;
293 | flex-shrink: 0;
294 |
295 | margin: 0;
296 | min-width: 2.25em;
297 | text-align: right;
298 |
299 | /* will be animated */
300 | will-change: transform, opacity;
301 |
302 | /* can't do anything with this info */
303 | pointer-events: none;
304 | -webkit-user-select: none;
305 | -moz-user-select: none;
306 | -ms-user-select: none;
307 | user-select: none;
308 |
309 | /* no margins on children */
310 | }
311 | .filepond--file-status * {
312 | margin: 0;
313 | white-space: nowrap;
314 | }
315 | .filepond--file-status {
316 | /* font sizes */
317 | }
318 | .filepond--file-status .filepond--file-status-main {
319 | font-size: 0.75em;
320 | line-height: 1.2;
321 | }
322 | .filepond--file-status .filepond--file-status-sub {
323 | font-size: 0.625em;
324 | opacity: 0.5;
325 | transition: opacity 0.25s ease-in-out;
326 | }
327 | /* Hard to override styles */
328 | .filepond--file-wrapper.filepond--file-wrapper {
329 | border: none;
330 | margin: 0;
331 | padding: 0;
332 | min-width: 0;
333 | height: 100%;
334 |
335 | /* hide legend for visual users */
336 | }
337 | .filepond--file-wrapper.filepond--file-wrapper > legend {
338 | position: absolute;
339 | overflow: hidden;
340 | height: 1px;
341 | width: 1px;
342 | padding: 0;
343 | border: 0;
344 | clip: rect(1px, 1px, 1px, 1px);
345 | -webkit-clip-path: inset(50%);
346 | clip-path: inset(50%);
347 | white-space: nowrap;
348 | }
349 | .filepond--file {
350 | position: static;
351 | display: flex;
352 | height: 100%;
353 | align-items: flex-start;
354 |
355 | padding: 0.5625em 0.5625em;
356 |
357 | color: #fff;
358 | border-radius: 0.5em;
359 |
360 | /* control positions */
361 | }
362 | .filepond--file .filepond--file-status {
363 | margin-left: auto;
364 | margin-right: 2.25em;
365 | }
366 | .filepond--file .filepond--processing-complete-indicator {
367 | pointer-events: none;
368 | -webkit-user-select: none;
369 | -moz-user-select: none;
370 | -ms-user-select: none;
371 | user-select: none;
372 | z-index: 3;
373 | }
374 | .filepond--file .filepond--processing-complete-indicator,
375 | .filepond--file .filepond--progress-indicator,
376 | .filepond--file .filepond--file-action-button {
377 | position: absolute;
378 | }
379 | .filepond--file {
380 | /* .filepond--file-action-button */
381 | }
382 | .filepond--file [data-align*='left'] {
383 | left: 0.5625em;
384 | }
385 | .filepond--file [data-align*='right'] {
386 | right: 0.5625em;
387 | }
388 | .filepond--file [data-align*='center'] {
389 | left: calc(50% - 0.8125em); /* .8125 is half of button width */
390 | }
391 | .filepond--file [data-align*='bottom'] {
392 | bottom: 1.125em;
393 | }
394 | .filepond--file [data-align='center'] {
395 | top: calc(50% - 0.8125em);
396 | }
397 | .filepond--file .filepond--progress-indicator {
398 | margin-top: 0.1875em;
399 | }
400 | .filepond--file .filepond--progress-indicator[data-align*='right'] {
401 | margin-right: 0.1875em;
402 | }
403 | .filepond--file .filepond--progress-indicator[data-align*='left'] {
404 | margin-left: 0.1875em;
405 | }
406 | /* make sure text does not overlap */
407 | [data-filepond-item-state='cancelled'] .filepond--file-info,
408 | [data-filepond-item-state*='invalid'] .filepond--file-info,
409 | [data-filepond-item-state*='error'] .filepond--file-info {
410 | margin-right: 2.25em;
411 | }
412 | [data-filepond-item-state~='processing'] .filepond--file-status-sub {
413 | opacity: 0;
414 | }
415 | [data-filepond-item-state~='processing']
416 | .filepond--action-abort-item-processing
417 | ~ .filepond--file-status
418 | .filepond--file-status-sub {
419 | opacity: 0.5;
420 | }
421 | [data-filepond-item-state='processing-error'] .filepond--file-status-sub {
422 | opacity: 0;
423 | }
424 | [data-filepond-item-state='processing-error']
425 | .filepond--action-retry-item-processing
426 | ~ .filepond--file-status
427 | .filepond--file-status-sub {
428 | opacity: 0.5;
429 | }
430 | [data-filepond-item-state='processing-complete'] {
431 | /* busy state */
432 | }
433 | [data-filepond-item-state='processing-complete'] .filepond--action-revert-item-processing svg {
434 | -webkit-animation: fall 0.5s 0.125s linear both;
435 | animation: fall 0.5s 0.125s linear both;
436 | }
437 | [data-filepond-item-state='processing-complete'] {
438 | /* hide details by default, only show when can revert */
439 | }
440 | [data-filepond-item-state='processing-complete'] .filepond--file-status-sub {
441 | opacity: 0.5;
442 | }
443 | [data-filepond-item-state='processing-complete']
444 | .filepond--processing-complete-indicator:not([style*='hidden'])
445 | ~ .filepond--file-status
446 | .filepond--file-status-sub {
447 | opacity: 0;
448 | }
449 | [data-filepond-item-state='processing-complete'] .filepond--file-info-sub {
450 | opacity: 0;
451 | }
452 | [data-filepond-item-state='processing-complete']
453 | .filepond--action-revert-item-processing
454 | ~ .filepond--file-info
455 | .filepond--file-info-sub {
456 | opacity: 0.5;
457 | }
458 | /* file state can be invalid or error, both are visually similar but */
459 | /* having them as separate states might be useful */
460 | [data-filepond-item-state*='invalid'] .filepond--panel,
461 | [data-filepond-item-state*='invalid'] .filepond--file-wrapper,
462 | [data-filepond-item-state*='error'] .filepond--panel,
463 | [data-filepond-item-state*='error'] .filepond--file-wrapper {
464 | -webkit-animation: shake 0.65s linear both;
465 | animation: shake 0.65s linear both;
466 | }
467 | /* spins progress indicator when file is marked as busy */
468 | [data-filepond-item-state*='busy'] .filepond--progress-indicator svg {
469 | -webkit-animation: spin 1s linear infinite;
470 | animation: spin 1s linear infinite;
471 | }
472 | /**
473 | * States
474 | */
475 | @-webkit-keyframes spin {
476 | 0% {
477 | -webkit-transform: rotateZ(0deg);
478 | transform: rotateZ(0deg);
479 | }
480 |
481 | 100% {
482 | -webkit-transform: rotateZ(360deg);
483 | transform: rotateZ(360deg);
484 | }
485 | }
486 | @keyframes spin {
487 | 0% {
488 | -webkit-transform: rotateZ(0deg);
489 | transform: rotateZ(0deg);
490 | }
491 |
492 | 100% {
493 | -webkit-transform: rotateZ(360deg);
494 | transform: rotateZ(360deg);
495 | }
496 | }
497 | @-webkit-keyframes shake {
498 | 10%,
499 | 90% {
500 | -webkit-transform: translateX(-0.0625em);
501 | transform: translateX(-0.0625em);
502 | }
503 |
504 | 20%,
505 | 80% {
506 | -webkit-transform: translateX(0.125em);
507 | transform: translateX(0.125em);
508 | }
509 |
510 | 30%,
511 | 50%,
512 | 70% {
513 | -webkit-transform: translateX(-0.25em);
514 | transform: translateX(-0.25em);
515 | }
516 |
517 | 40%,
518 | 60% {
519 | -webkit-transform: translateX(0.25em);
520 | transform: translateX(0.25em);
521 | }
522 | }
523 | @keyframes shake {
524 | 10%,
525 | 90% {
526 | -webkit-transform: translateX(-0.0625em);
527 | transform: translateX(-0.0625em);
528 | }
529 |
530 | 20%,
531 | 80% {
532 | -webkit-transform: translateX(0.125em);
533 | transform: translateX(0.125em);
534 | }
535 |
536 | 30%,
537 | 50%,
538 | 70% {
539 | -webkit-transform: translateX(-0.25em);
540 | transform: translateX(-0.25em);
541 | }
542 |
543 | 40%,
544 | 60% {
545 | -webkit-transform: translateX(0.25em);
546 | transform: translateX(0.25em);
547 | }
548 | }
549 | @-webkit-keyframes fall {
550 | 0% {
551 | opacity: 0;
552 | -webkit-transform: scale(0.5);
553 | transform: scale(0.5);
554 | -webkit-animation-timing-function: ease-out;
555 | animation-timing-function: ease-out;
556 | }
557 |
558 | 70% {
559 | opacity: 1;
560 | -webkit-transform: scale(1.1);
561 | transform: scale(1.1);
562 | -webkit-animation-timing-function: ease-in-out;
563 | animation-timing-function: ease-in-out;
564 | }
565 |
566 | 100% {
567 | -webkit-transform: scale(1);
568 | transform: scale(1);
569 | -webkit-animation-timing-function: ease-out;
570 | animation-timing-function: ease-out;
571 | }
572 | }
573 | @keyframes fall {
574 | 0% {
575 | opacity: 0;
576 | -webkit-transform: scale(0.5);
577 | transform: scale(0.5);
578 | -webkit-animation-timing-function: ease-out;
579 | animation-timing-function: ease-out;
580 | }
581 |
582 | 70% {
583 | opacity: 1;
584 | -webkit-transform: scale(1.1);
585 | transform: scale(1.1);
586 | -webkit-animation-timing-function: ease-in-out;
587 | animation-timing-function: ease-in-out;
588 | }
589 |
590 | 100% {
591 | -webkit-transform: scale(1);
592 | transform: scale(1);
593 | -webkit-animation-timing-function: ease-out;
594 | animation-timing-function: ease-out;
595 | }
596 | }
597 | /* ignore all other interaction elements while dragging a file */
598 | .filepond--hopper[data-hopper-state='drag-over'] > * {
599 | pointer-events: none;
600 | }
601 | /* capture all hit tests using a hidden layer, this speeds up the event flow */
602 | .filepond--hopper[data-hopper-state='drag-over']::after {
603 | content: '';
604 | position: absolute;
605 | left: 0;
606 | top: 0;
607 | right: 0;
608 | bottom: 0;
609 | z-index: 100;
610 | }
611 | .filepond--progress-indicator {
612 | z-index: 103;
613 | }
614 | .filepond--file-action-button {
615 | z-index: 102;
616 | }
617 | .filepond--file-status {
618 | z-index: 101;
619 | }
620 | .filepond--file-info {
621 | z-index: 100;
622 | }
623 | .filepond--item {
624 | position: absolute;
625 | top: 0;
626 | left: 0;
627 | right: 0;
628 | z-index: 1;
629 |
630 | padding: 0;
631 | margin: 0.25em;
632 |
633 | will-change: transform, opacity;
634 |
635 | /* item children order */
636 | }
637 | .filepond--item > .filepond--panel {
638 | z-index: -1;
639 | }
640 | /* has a slight shadow */
641 | .filepond--item > .filepond--panel .filepond--panel-bottom {
642 | box-shadow: 0 0.0625em 0.125em -0.0625em rgba(0, 0, 0, 0.25);
643 | }
644 | .filepond--item {
645 | /* drag related */
646 | }
647 | .filepond--item > .filepond--file-wrapper,
648 | .filepond--item > .filepond--panel {
649 | transition: opacity 0.15s ease-out;
650 | }
651 | .filepond--item[data-drag-state] {
652 | cursor: -webkit-grab;
653 | cursor: grab;
654 | }
655 | .filepond--item[data-drag-state] > .filepond--panel {
656 | transition: box-shadow 0.125s ease-in-out;
657 | box-shadow: 0 0 0 rgba(0, 0, 0, 0);
658 | }
659 | .filepond--item[data-drag-state='drag'] {
660 | cursor: -webkit-grabbing;
661 | cursor: grabbing;
662 | }
663 | .filepond--item[data-drag-state='drag'] > .filepond--panel {
664 | box-shadow: 0 0.125em 0.3125em rgba(0, 0, 0, 0.325);
665 | }
666 | .filepond--item[data-drag-state]:not([data-drag-state='idle']) {
667 | z-index: 2;
668 | }
669 | /* states */
670 | .filepond--item-panel {
671 | background-color: #64605e;
672 | }
673 | [data-filepond-item-state='processing-complete'] .filepond--item-panel {
674 | background-color: #369763;
675 | }
676 | [data-filepond-item-state*='invalid'] .filepond--item-panel,
677 | [data-filepond-item-state*='error'] .filepond--item-panel {
678 | background-color: #c44e47;
679 | }
680 | /* style of item panel */
681 | .filepond--item-panel {
682 | border-radius: 0.5em;
683 | transition: background-color 0.25s;
684 | }
685 | /* normal mode */
686 | .filepond--list-scroller {
687 | position: absolute;
688 | top: 0;
689 | left: 0;
690 | right: 0;
691 | margin: 0;
692 | will-change: transform;
693 | }
694 | /* scroll mode */
695 | .filepond--list-scroller[data-state='overflow'] .filepond--list {
696 | bottom: 0;
697 | right: 0;
698 | }
699 | .filepond--list-scroller[data-state='overflow'] {
700 | overflow-y: scroll;
701 | overflow-x: hidden;
702 | -webkit-overflow-scrolling: touch;
703 | -webkit-mask: linear-gradient(to bottom, #000 calc(100% - 0.5em), transparent 100%);
704 | mask: linear-gradient(to bottom, #000 calc(100% - 0.5em), transparent 100%);
705 | }
706 | /* style scrollbar */
707 | .filepond--list-scroller::-webkit-scrollbar {
708 | background: transparent;
709 | }
710 | .filepond--list-scroller::-webkit-scrollbar:vertical {
711 | width: 1em;
712 | }
713 | .filepond--list-scroller::-webkit-scrollbar:horizontal {
714 | height: 0;
715 | }
716 | .filepond--list-scroller::-webkit-scrollbar-thumb {
717 | background-color: rgba(0, 0, 0, 0.3);
718 | border-radius: 99999px;
719 | border: 0.3125em solid transparent;
720 | background-clip: content-box;
721 | }
722 | /* hard to overide styles on purpose */
723 | .filepond--list.filepond--list {
724 | position: absolute;
725 | top: 0;
726 | margin: 0;
727 | padding: 0;
728 | list-style-type: none;
729 |
730 | /* prevents endless paint calls on filepond--list-scroller */
731 | will-change: transform;
732 | }
733 | /* used for padding so allowed to be restyled */
734 | .filepond--list {
735 | left: 0.75em;
736 | right: 0.75em;
737 | }
738 | .filepond--root[data-style-panel-layout~='integrated'] {
739 | width: 100%;
740 | height: 100%;
741 | max-width: none;
742 | margin: 0;
743 | }
744 | .filepond--root[data-style-panel-layout~='circle'] .filepond--panel-root,
745 | .filepond--root[data-style-panel-layout~='integrated'] .filepond--panel-root {
746 | border-radius: 0;
747 | }
748 | .filepond--root[data-style-panel-layout~='circle'] .filepond--panel-root > *,
749 | .filepond--root[data-style-panel-layout~='integrated'] .filepond--panel-root > * {
750 | display: none;
751 | }
752 | .filepond--root[data-style-panel-layout~='circle'] .filepond--drop-label,
753 | .filepond--root[data-style-panel-layout~='integrated'] .filepond--drop-label {
754 | bottom: 0;
755 | height: auto;
756 | display: flex;
757 | justify-content: center;
758 | align-items: center;
759 | z-index: 7;
760 | }
761 | .filepond--root[data-style-panel-layout~='circle'],
762 | .filepond--root[data-style-panel-layout~='integrated'] {
763 | /* we're only loading one item, this makes the intro animation a bit nicer */
764 | }
765 | .filepond--root[data-style-panel-layout~='circle'] .filepond--item-panel,
766 | .filepond--root[data-style-panel-layout~='integrated'] .filepond--item-panel {
767 | display: none;
768 | }
769 | .filepond--root[data-style-panel-layout~='compact'] .filepond--list-scroller,
770 | .filepond--root[data-style-panel-layout~='integrated'] .filepond--list-scroller {
771 | overflow: hidden;
772 | height: 100%;
773 | margin-top: 0;
774 | margin-bottom: 0;
775 | }
776 | .filepond--root[data-style-panel-layout~='compact'] .filepond--list,
777 | .filepond--root[data-style-panel-layout~='integrated'] .filepond--list {
778 | left: 0;
779 | right: 0;
780 | height: 100%;
781 | }
782 | .filepond--root[data-style-panel-layout~='compact'] .filepond--item,
783 | .filepond--root[data-style-panel-layout~='integrated'] .filepond--item {
784 | margin: 0;
785 | }
786 | .filepond--root[data-style-panel-layout~='compact'] .filepond--file-wrapper,
787 | .filepond--root[data-style-panel-layout~='integrated'] .filepond--file-wrapper {
788 | height: 100%;
789 | }
790 | .filepond--root[data-style-panel-layout~='compact'] .filepond--drop-label,
791 | .filepond--root[data-style-panel-layout~='integrated'] .filepond--drop-label {
792 | z-index: 7;
793 | }
794 | .filepond--root[data-style-panel-layout~='circle'] {
795 | border-radius: 99999rem;
796 | overflow: hidden;
797 | }
798 | .filepond--root[data-style-panel-layout~='circle'] > .filepond--panel {
799 | border-radius: inherit;
800 | }
801 | .filepond--root[data-style-panel-layout~='circle'] > .filepond--panel > * {
802 | display: none;
803 | }
804 | .filepond--root[data-style-panel-layout~='circle'] {
805 | /* circle cuts of this info, so best to hide it */
806 | }
807 | .filepond--root[data-style-panel-layout~='circle'] .filepond--file-info {
808 | display: none;
809 | }
810 | .filepond--root[data-style-panel-layout~='circle'] .filepond--file-status {
811 | display: none;
812 | }
813 | .filepond--root[data-style-panel-layout~='circle'] .filepond--action-edit-item {
814 | opacity: 1 !important;
815 | visibility: visible !important;
816 | }
817 | /* dirfty way to fix circular overflow issue on safari 11+ */
818 | @media not all and (min-resolution: 0.001dpcm) {
819 | @supports (-webkit-appearance: none) and (stroke-color: transparent) {
820 | .filepond--root[data-style-panel-layout~='circle'] {
821 | will-change: transform;
822 | }
823 | }
824 | }
825 | .filepond--panel-root {
826 | border-radius: 0.5em;
827 | background-color: #f1f0ef;
828 | }
829 | .filepond--panel {
830 | position: absolute;
831 | left: 0;
832 | top: 0;
833 | right: 0;
834 | margin: 0;
835 |
836 | /* defaults to 100% height (fixed height mode) this fixes problem with panel height in IE11 */
837 | height: 100% !important;
838 |
839 | /* no interaction possible with panel */
840 | pointer-events: none;
841 | }
842 | .filepond-panel:not([data-scalable='false']) {
843 | height: auto !important;
844 | }
845 | .filepond--panel[data-scalable='false'] > div {
846 | display: none;
847 | }
848 | .filepond--panel[data-scalable='true'] {
849 | /* this seems to fix Chrome performance issues */
850 | /* - when box-shadow is enabled */
851 | /* - when multiple ponds are active on the same page */
852 | -webkit-transform-style: preserve-3d;
853 | transform-style: preserve-3d;
854 |
855 | /* prevent borders and backgrounds */
856 | background-color: transparent !important;
857 | border: none !important;
858 | }
859 | .filepond--panel-top,
860 | .filepond--panel-bottom,
861 | .filepond--panel-center {
862 | position: absolute;
863 | left: 0;
864 | top: 0;
865 | right: 0;
866 | margin: 0;
867 | padding: 0;
868 | }
869 | .filepond--panel-top,
870 | .filepond--panel-bottom {
871 | height: 0.5em;
872 | }
873 | .filepond--panel-top {
874 | border-bottom-left-radius: 0 !important;
875 | border-bottom-right-radius: 0 !important;
876 | border-bottom: none !important;
877 |
878 | /* fixes tiny transparant line between top and center panel */
879 | }
880 | .filepond--panel-top::after {
881 | content: '';
882 | position: absolute;
883 | height: 2px;
884 | left: 0;
885 | right: 0;
886 | bottom: -1px;
887 | background-color: inherit;
888 | }
889 | .filepond--panel-center,
890 | .filepond--panel-bottom {
891 | will-change: transform;
892 | -webkit-backface-visibility: hidden;
893 | backface-visibility: hidden;
894 | -webkit-transform-origin: left top;
895 | transform-origin: left top;
896 | -webkit-transform: translate3d(0, 0.5em, 0);
897 | transform: translate3d(0, 0.5em, 0);
898 | }
899 | .filepond--panel-bottom {
900 | border-top-left-radius: 0 !important;
901 | border-top-right-radius: 0 !important;
902 | border-top: none !important;
903 |
904 | /* fixes tiny transparant line between bottom and center of panel */
905 | }
906 | .filepond--panel-bottom::before {
907 | content: '';
908 | position: absolute;
909 | height: 2px;
910 | left: 0;
911 | right: 0;
912 | top: -1px;
913 | background-color: inherit;
914 | }
915 | .filepond--panel-center {
916 | /* the center panel is scaled using scale3d to fit the correct height */
917 | /* we use 100px instead of 1px as scaling 1px to a huge height is really laggy on chrome */
918 | height: 100px !important;
919 | border-top: none !important;
920 | border-bottom: none !important;
921 | border-radius: 0 !important;
922 |
923 | /* hide if not transformed, prevents a little flash when the panel is at 100px height while attached for first time */
924 | }
925 | .filepond--panel-center:not([style]) {
926 | visibility: hidden;
927 | }
928 | .filepond--progress-indicator {
929 | position: static;
930 | width: 1.25em;
931 | height: 1.25em;
932 |
933 | color: #fff;
934 |
935 | /* can't have margins */
936 | margin: 0;
937 |
938 | /* no interaction possible with progress indicator */
939 | pointer-events: none;
940 |
941 | /* will be animated */
942 | will-change: transform, opacity;
943 | }
944 | .filepond--progress-indicator svg {
945 | width: 100%;
946 | height: 100%;
947 | vertical-align: top;
948 | transform-box: fill-box; /* should center the animation correctly when zoomed in */
949 | }
950 | .filepond--progress-indicator path {
951 | fill: none;
952 | stroke: currentColor;
953 | }
954 | .filepond--list-scroller {
955 | z-index: 6;
956 | }
957 | .filepond--drop-label {
958 | z-index: 5;
959 | }
960 | .filepond--drip {
961 | z-index: 3;
962 | }
963 | .filepond--root > .filepond--panel {
964 | z-index: 2;
965 | }
966 | .filepond--browser {
967 | z-index: 1;
968 | }
969 | .filepond--root {
970 | /* layout*/
971 | box-sizing: border-box;
972 | position: relative;
973 | margin-bottom: 1em;
974 |
975 | /* base font size for whole component */
976 | font-size: 1rem;
977 |
978 | /* base line height */
979 | line-height: normal;
980 |
981 | /* up uses default system font family */
982 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
983 | 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
984 |
985 | /* will increase font weight a bit on Safari */
986 | font-weight: 450;
987 |
988 | /* default text alignment */
989 | text-align: left;
990 |
991 | /* better text rendering on Safari */
992 | text-rendering: optimizeLegibility;
993 |
994 | /* text direction is ltr for now */
995 | direction: ltr;
996 |
997 | /* optimize rendering */
998 | /* https://developer.mozilla.org/en-US/docs/Web/CSS/contain */
999 | contain: layout style size;
1000 |
1001 | /* correct box sizing, line-height and positioning on child elements */
1002 | }
1003 | .filepond--root * {
1004 | box-sizing: inherit;
1005 | line-height: inherit;
1006 | }
1007 | .filepond--root *:not(text) {
1008 | font-size: inherit;
1009 | }
1010 | .filepond--root {
1011 | /* block everything */
1012 | }
1013 | .filepond--root[data-disabled] {
1014 | pointer-events: none;
1015 | }
1016 | .filepond--root[data-disabled] .filepond--list-scroller {
1017 | pointer-events: all;
1018 | }
1019 | .filepond--root[data-disabled] .filepond--list {
1020 | pointer-events: none;
1021 | }
1022 | /**
1023 | * Root element children layout
1024 | */
1025 | .filepond--root .filepond--drop-label {
1026 | min-height: 4.75em;
1027 | }
1028 | .filepond--root .filepond--list-scroller {
1029 | margin-top: 1em;
1030 | margin-bottom: 1em;
1031 | }
1032 | .filepond--root .filepond--credits {
1033 | position: absolute;
1034 | right: 0;
1035 | opacity: 0.175;
1036 | line-height: 0.85;
1037 | font-size: 11px;
1038 | color: inherit;
1039 | text-decoration: none;
1040 | z-index: 3;
1041 | bottom: -14px;
1042 | }
1043 | .filepond--root .filepond--credits[style] {
1044 | top: 0;
1045 | bottom: auto;
1046 | margin-top: 14px;
1047 | }
1048 |
--------------------------------------------------------------------------------