decidim-homepage_proposals 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +32 -0
  4. data/Rakefile +30 -0
  5. data/app/cells/decidim/homepage_proposals/content_blocks/proposals_slider/show.erb +37 -0
  6. data/app/cells/decidim/homepage_proposals/content_blocks/proposals_slider_cell.rb +56 -0
  7. data/app/cells/decidim/homepage_proposals/content_blocks/proposals_slider_settings_form/show.erb +13 -0
  8. data/app/cells/decidim/homepage_proposals/content_blocks/proposals_slider_settings_form_cell.rb +36 -0
  9. data/app/controllers/decidim/proposals_slider_controller.rb +74 -0
  10. data/app/packs/entrypoints/decidim_homepage_proposals.js +5 -0
  11. data/app/packs/entrypoints/decidim_homepage_proposals_admin.js +1 -0
  12. data/app/packs/images/decidim/homepage_proposals/icon.svg +1 -0
  13. data/app/packs/images/decidim/homepage_proposals/slider_proposal_image.jpeg +0 -0
  14. data/app/packs/src/decidim/homepage_proposals/admin.js +16 -0
  15. data/app/packs/src/decidim/homepage_proposals/glideBuilder.js +49 -0
  16. data/app/packs/src/decidim/homepage_proposals/glidejs/Manager.js +130 -0
  17. data/app/packs/src/decidim/homepage_proposals/glidejs/glideItems/GlideItem.js +17 -0
  18. data/app/packs/src/decidim/homepage_proposals/glidejs/glideItems/NotFound.js +23 -0
  19. data/app/packs/src/decidim/homepage_proposals/glidejs/glideItems/Proposal.js +51 -0
  20. data/app/packs/src/decidim/homepage_proposals/main.js +19 -0
  21. data/app/packs/stylesheets/decidim/homepage_proposals/homepage_proposals.scss +45 -0
  22. data/app/views/decidim/shared/homepage_proposals/_filters.erb +47 -0
  23. data/app/views/decidim/shared/homepage_proposals/_filters_small_view.erb +18 -0
  24. data/config/assets.rb +10 -0
  25. data/config/i18n-tasks.yml +10 -0
  26. data/config/locales/en.yml +33 -0
  27. data/config/routes.rb +5 -0
  28. data/lib/decidim/homepage_proposals/admin.rb +10 -0
  29. data/lib/decidim/homepage_proposals/admin_engine.rb +24 -0
  30. data/lib/decidim/homepage_proposals/engine.rb +41 -0
  31. data/lib/decidim/homepage_proposals/test/factories.rb +17 -0
  32. data/lib/decidim/homepage_proposals/version.rb +13 -0
  33. data/lib/decidim/homepage_proposals.rb +12 -0
  34. data/lib/tasks/homepage_proposals.rake +25 -0
  35. data/lib/tasks/homepage_proposals_webpacker_tasks.rake +14 -0
  36. metadata +92 -0
data/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # Decidim::HomepageProposals
2
+
3
+ Homepage slider for proposals.
4
+
5
+ ## Usage
6
+
7
+ HomepageProposals will be available as a Component for a Participatory
8
+ Space.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem "decidim-homepage_proposals"
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ ```bash
21
+ bundle
22
+ bundle exec rake decidim_module_homepage_proposals:webpacker:install
23
+ bundle exec rake assets:precompile
24
+ ```
25
+
26
+ ## Contributing
27
+
28
+ See [Decidim](https://github.com/decidim/decidim).
29
+
30
+ ## License
31
+
32
+ This engine is distributed under the GNU AFFERO GENERAL PUBLIC LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/dev/common_rake"
4
+
5
+ desc "Generates a dummy app for testing"
6
+ task test_app: "decidim:generate_external_test_app"
7
+
8
+ desc "Generates a development app."
9
+ task :development_app do
10
+ Bundler.with_original_env do
11
+ generate_decidim_app(
12
+ "development_app",
13
+ "--app_name",
14
+ "#{base_app_name}_development_app",
15
+ "--path",
16
+ "..",
17
+ "--recreate_db",
18
+ "--seed_db",
19
+ "--demo",
20
+ "--profiling"
21
+ )
22
+ end
23
+ seed_slider("development_app")
24
+ end
25
+
26
+ def seed_slider(path)
27
+ Dir.chdir(path) do
28
+ system("bundle exec rake decidim:homepage_proposals:seed")
29
+ end
30
+ end
@@ -0,0 +1,37 @@
1
+ <section class="wrapper-home home-section" xmlns="http://www.w3.org/1999/xhtml">
2
+ <div class="row collapse">
3
+ <h3 class="section-heading"><%= t("decidim.homepage_proposals.proposal_at_a_glance.title") %></h3>
4
+
5
+
6
+ <div class="filters_container" <%= 'style="display: none"' unless content_block_settings.activate_filters %>>
7
+ <%= render partial: "decidim/shared/homepage_proposals/filters_small_view" %>
8
+ <div class="show-for-mediumlarge">
9
+ <%= render partial: "decidim/shared/homepage_proposals/filters" %>
10
+ </div>
11
+ </div>
12
+
13
+
14
+ <div id="proposals_slider">
15
+ <div class="glide margin-top-3">
16
+ <div class="column small-12">
17
+ <p class="loading text-center"><%= t(".loading") %></p>
18
+ </div>
19
+ <div class="glide__track row small-12 column" data-glide-el="track">
20
+ <ul class="glide__slides" id="proposals_glide_items">
21
+ <%# Content loaded by 'app/packs/src/decidim/homepage_proposals/main.js' %>
22
+ </ul>
23
+ </div>
24
+ <div class="glide__bullets" data-glide-el="controls[nav]">
25
+ <div class="glide__bullet" data-glide-dir="<">
26
+ <%= icon "chevron-left", class: "icon--large" %>
27
+ </div>
28
+ <div class="glide__bullet" data-glide-dir=">">
29
+ <%= icon "chevron-right", class: "icon--large" %>
30
+ </div>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </section>
36
+
37
+ <%= javascript_pack_tag 'decidim_homepage_proposals' %>
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module HomepageProposals
5
+ module ContentBlocks
6
+ class ProposalsSliderCell < Decidim::ViewModel
7
+ attr_accessor :glanced_proposals
8
+
9
+ include Cell::ViewModel::Partial
10
+ include Core::Engine.routes.url_helpers
11
+ include Decidim::IconHelper
12
+ include ActionView::Helpers::FormOptionsHelper
13
+ include Decidim::FiltersHelper
14
+ include Decidim::FilterResource
15
+
16
+ private
17
+
18
+ def content_block_settings
19
+ @content_block_settings ||= Decidim::ContentBlock.find_by(
20
+ manifest_name: "proposals_slider",
21
+ organization: current_organization
22
+ ).settings
23
+ end
24
+
25
+ def options_for_default_component
26
+ components = Decidim::Component.where(id: content_block_settings.linked_components_id.compact)
27
+ options = components.map do |component|
28
+ ["#{translated_attribute(component.name)} (#{translated_attribute(component.participatory_space.title)})", component.id]
29
+ end
30
+
31
+ options_for_select(options, selected: selected_component_id)
32
+ end
33
+
34
+ def linked_components
35
+ @linked_components ||= Decidim::Component.where(id: content_block_settings.linked_components_id.compact)
36
+ end
37
+
38
+ def default_filter_params
39
+ {
40
+ scope_id: nil,
41
+ category_id: nil,
42
+ component_id: nil
43
+ }
44
+ end
45
+
46
+ def categories_filter
47
+ @categories_filter ||= Decidim::Category.where(id: linked_components.map(&:categories).flatten)
48
+ end
49
+
50
+ def selected_component_id
51
+ @selected_component_id ||= params.dig(:filter, :component_id) || content_block_settings.default_linked_component
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,13 @@
1
+ <% form.fields_for :settings, form.object.settings do |settings_fields| %>
2
+ <%= settings_fields.check_box :activate_filters, label: t(".activate_filters") %>
3
+ <%= settings_fields.select :linked_components_id,
4
+ options_for_proposals_components,
5
+ { include_blank: true, label: t(".linked_components_id") },
6
+ { multiple: true, class: "chosen-select" } %>
7
+ <%= settings_fields.select :default_linked_component,
8
+ options_for_default_component,
9
+ { include_blank: false, label: t(".default_linked_component") },
10
+ class: "chosen-select" %>
11
+ <% end %>
12
+
13
+ <%= javascript_pack_tag "decidim_homepage_proposals_admin" %>
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module HomepageProposals
5
+ module ContentBlocks
6
+ class ProposalsSliderSettingsFormCell < Decidim::ViewModel
7
+ include ActionView::Helpers::FormOptionsHelper
8
+
9
+ alias form model
10
+
11
+ def content_block
12
+ options[:content_block]
13
+ end
14
+
15
+ def options_for_proposals_components
16
+ options = proposals_components.map do |proposal_component|
17
+ ["#{translated_attribute(proposal_component.name)} (#{translated_attribute(proposal_component.participatory_space.title)})", proposal_component.id]
18
+ end
19
+ options_for_select(options, selected: content_block.settings.linked_components_id)
20
+ end
21
+
22
+ def options_for_default_component
23
+ components = Decidim::Component.where(id: content_block.settings.linked_components_id.compact)
24
+ options = components.map do |component|
25
+ ["#{translated_attribute(component.name)} (#{translated_attribute(component.participatory_space.title)})", component.id]
26
+ end
27
+ options_for_select(options, selected: content_block.settings.default_linked_component)
28
+ end
29
+
30
+ def proposals_components
31
+ @proposals_components ||= Decidim::Component.where(manifest_name: "proposals").published
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ class ProposalsSliderController < Decidim::ApplicationController
5
+ include Decidim::FilterResource
6
+ include Decidim::TranslatableAttributes
7
+ include Decidim::Core::Engine.routes.url_helpers
8
+ include Decidim::ComponentPathHelper
9
+
10
+ def refresh_proposals
11
+ render json: build_proposals_api
12
+ end
13
+
14
+ private
15
+
16
+ def build_proposals_api
17
+ return component_url unless glanced_proposals.any?
18
+
19
+ glanced_proposals.flat_map do |proposal|
20
+ {
21
+ id: proposal.id,
22
+ title: translated_attribute(proposal.title).truncate(40),
23
+ body: translated_attribute(proposal.body).truncate(150),
24
+ url: proposal_path(proposal),
25
+ image: image_for(proposal),
26
+ state: proposal.state
27
+ }
28
+ end
29
+ end
30
+
31
+ def glanced_proposals
32
+ if params[:filter].present?
33
+ category = Decidim::Category.find(params.dig(:filter, :category_id)) if params.dig(:filter, :category_id).present?
34
+ scopes = Decidim::Scope.find(params.dig(:filter, :scope_id)) if params.dig(:filter, :scope_id).present?
35
+ end
36
+
37
+ @glanced_proposals ||= Decidim::Proposals::Proposal.published
38
+ .where(component: params.dig(:filter, :component_id))
39
+ .where(filter_by_scopes(scopes))
40
+ .select do |proposal|
41
+ if category.present?
42
+ proposal.category == category
43
+ else
44
+ true
45
+ end
46
+ end
47
+ .sample(12)
48
+ end
49
+
50
+ def filter_by_scopes(scopes)
51
+ { scope: scopes } if scopes.present?
52
+ end
53
+
54
+ def proposal_path(proposal)
55
+ Decidim::ResourceLocatorPresenter.new(proposal).path
56
+ end
57
+
58
+ def image_for(proposal)
59
+ return view_context.image_pack_url("media/images/slider_proposal_image.jpeg") unless proposal.attachments.select(&:image?).any?
60
+
61
+ proposal.attachments.select(&:image?).first&.url
62
+ end
63
+
64
+ def component_url
65
+ return { url: "/" } if params.dig(:filter, :component_id).blank?
66
+
67
+ begin
68
+ { url: main_component_path(Decidim::Component.find(params.dig(:filter, :component_id))) }
69
+ rescue ActiveRecord::RecordNotFound
70
+ { url: "/" }
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,5 @@
1
+ // Images
2
+ require.context("../images", true)
3
+
4
+ import "src/decidim/homepage_proposals/main"
5
+ import "src/decidim/form_filter.js"
@@ -0,0 +1 @@
1
+ import "src/decidim/homepage_proposals/admin"
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 35 35"><path d="M17.5 35A17.5 17.5 0 1 1 35 17.5 17.52 17.52 0 0 1 17.5 35zm0-33.06A15.56 15.56 0 1 0 33.06 17.5 15.57 15.57 0 0 0 17.5 1.94zm9.5 13.7H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zm0 3.68H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zM22.26 23H8a1 1 0 0 1 0-1.94h14.26a1 1 0 0 1 0 1.94z"/></svg>
@@ -0,0 +1,16 @@
1
+ $(document).ready(function() {
2
+ $('#content_block_settings_linked_components_id').on('change', function() {
3
+ var selectedOptions = $(this).val(); // Get the selected options from the multiselect field
4
+ var $selectedOptionsField = $('#content_block_settings_default_linked_component'); // Get the select field for the selected options
5
+
6
+ $selectedOptionsField.empty(); // Clear the select field
7
+
8
+ // Add the selected options to the select field
9
+ if (selectedOptions && selectedOptions.length > 0) {
10
+ selectedOptions.forEach(function(option) {
11
+ var optionText = $('#content_block_settings_linked_components_id option[value="' + option + '"]').text();
12
+ $selectedOptionsField.append($('<option>', { value: option, text: optionText }));
13
+ });
14
+ }
15
+ });
16
+ });
@@ -0,0 +1,49 @@
1
+ import Glide from "@glidejs/glide";
2
+
3
+ export default class GlideBuilder {
4
+ constructor(selector = '.glide', type = 'carousel') {
5
+ this.type = type
6
+ this.setOpts()
7
+ this.glide = new Glide(selector, this.options)
8
+
9
+ this.bindings()
10
+ }
11
+
12
+ static pervView() {
13
+ return 4;
14
+ }
15
+
16
+ destroy() {
17
+ this.glide.destroy();
18
+ }
19
+
20
+ disable() {
21
+ this.glide.disable();
22
+ }
23
+
24
+ mount() {
25
+ this.glide.mount()
26
+ }
27
+
28
+ bindings() {
29
+ this.glide.on("run", () => {
30
+ let bulletNumber = this.glide.index;
31
+ $($(".glide__bullets").children()).css("color", "lightgrey");
32
+ $($(".glide__bullets").children().get(bulletNumber + 1)).css("color", "grey");
33
+ });
34
+ }
35
+
36
+ setOpts() {
37
+ this.options = {
38
+ type: this.type,
39
+ startAt: 0,
40
+ autoplay: 0,
41
+ perView: GlideBuilder.pervView(),
42
+ hoverpause: true,
43
+ breakpoints: {
44
+ 1024: { perView: 3 }, 768: { perView: 2 }, 480: { perView: 1 }
45
+ },
46
+ perTouch: 1
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,130 @@
1
+ import FormFilterComponents from "src/decidim/form_filter.js";
2
+ import GlideBuilder from "../glideBuilder";
3
+ import GlideItem from "./glideItems/GlideItem";
4
+ import Proposal from "./glideItems/Proposal";
5
+ import NotFound from "./glideItems/NotFound";
6
+
7
+
8
+ // Manager communicates with API and build the HTML for the Glide.js carousel
9
+ // Example :
10
+ // let manager = new Manager($proposalsSlider, $proposalsGlideItems, $glideBullets, $formFilter)
11
+ // manager.start().then(() => manager.glide.mount())
12
+ //
13
+ // @return - Instance of Manager
14
+ export default class Manager {
15
+ constructor($proposalsSlider, $proposalsGlideItems, $glideBullets, $formFilter) {
16
+ this.$proposalSlider = $proposalsSlider;
17
+ this.$proposalsGlideItems = $proposalsGlideItems;
18
+ this.formFilterComponent = new FormFilterComponents($formFilter);
19
+ this.$loading = this.$proposalSlider.find(".loading");
20
+ }
21
+
22
+ // @return String - API URL with filter params
23
+ APIUrl() {
24
+ return '/proposals_slider/refresh_proposals' + this.filterURIParams();
25
+ }
26
+
27
+ // @return String - Filter params query string
28
+ filterURIParams() {
29
+ return this.formFilterComponent._currentStateAndPath()[0];
30
+ }
31
+
32
+ // Clears Glide carousel and display loader
33
+ // @return void
34
+ startLoading() {
35
+ let height = $(".glide__slides").css("height");
36
+ this.clearGlideItems();
37
+ this.$loading.css("height", height);
38
+ this.$loading.show();
39
+ }
40
+
41
+ // Hide loader
42
+ // @return void
43
+ endLoading() {
44
+ this.$loading.hide();
45
+ }
46
+
47
+ // Clears glide items and glide bullets
48
+ // @return void
49
+ clearGlideItems() {
50
+ this.$proposalsGlideItems.empty();
51
+ $(".glide__bullet.glide__bullet_idx").remove()
52
+ }
53
+
54
+ // We must disable existing glide carousel before refresh
55
+ // @return void
56
+ disableGlide() {
57
+ if (this.glide !== undefined) {
58
+ this.glide.disable()
59
+ }
60
+ }
61
+
62
+ // Send request to API and create items received in Glide.js carousel
63
+ // @return this.glide
64
+ start() {
65
+ this.startLoading();
66
+ this.disableGlide()
67
+
68
+ $.get(this.APIUrl())
69
+ .done((res) => {
70
+ this.generateGlides(res)
71
+ })
72
+ .fail(() => {
73
+ this.generateGlides([])
74
+ })
75
+ .always((res) => {
76
+ this.glide = new GlideBuilder('.glide', 'carousel');
77
+
78
+ if (res.length <= 1 || res.status === 500) {
79
+ this.glide.disable()
80
+ }
81
+ this.endLoading();
82
+
83
+ this.glide.mount()
84
+ })
85
+ }
86
+
87
+ // Creates Glide Items
88
+ // @return void
89
+ generateGlides(res) {
90
+ if (res.url) {
91
+ this.createProposalsNotFound(res.url);
92
+ return;
93
+ }
94
+
95
+ this.createProposals(res)
96
+ }
97
+
98
+ // Create Glide Items with:
99
+ // - A Not Found GlideItem
100
+ // - 3 placeholders
101
+ // @return void
102
+ createProposalsNotFound(url) {
103
+ const notFoundGlide = new NotFound(url)
104
+ this.$proposalsGlideItems.append(notFoundGlide.render())
105
+ $(".glide__bullets > .glide__bullet:last").before(notFoundGlide.bullet(0));
106
+
107
+ for (let i = 0; i < GlideBuilder.pervView() - 1; i++) {
108
+ this.$proposalsGlideItems.append(notFoundGlide.placeholder());
109
+ }
110
+ }
111
+
112
+ // Create Glide Items proposals, if proposals length is smaller than GlideBuilder.pervView() (default: 4)
113
+ // Then it creates placeholders until reaching 4 Glide Items
114
+ // @return void
115
+ createProposals(proposals) {
116
+ for (let i = 0; i < proposals.length; i++) {
117
+ let proposalGlide = new Proposal(proposals[i])
118
+ this.$proposalsGlideItems.append(proposalGlide.render());
119
+ $(".glide__bullets > .glide__bullet:last").before(proposalGlide.bullet(i));
120
+ }
121
+
122
+ if (proposals.length < GlideBuilder.pervView()) {
123
+ let missingCount = GlideBuilder.pervView() - proposals.length
124
+
125
+ for (let i = 0; i < missingCount; i++) {
126
+ this.$proposalsGlideItems.append(new GlideItem(null).placeholder());
127
+ }
128
+ }
129
+ }
130
+ }
@@ -0,0 +1,17 @@
1
+ export default class GlideItem {
2
+ constructor(url) {
3
+ this.url = url;
4
+ }
5
+
6
+ placeholder() {
7
+ return `<div class="column glide__slide"></div>`
8
+ }
9
+
10
+ bullet(idx) {
11
+ return `<div class="glide__bullet glide__bullet_idx" data-glide-dir="=${idx}">
12
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-circle-fill" viewBox="0 0 16 16">
13
+ <circle cx="7" cy="7" r="7"/>
14
+ </svg>
15
+ </div>`
16
+ }
17
+ }
@@ -0,0 +1,23 @@
1
+ import GlideItem from "./GlideItem";
2
+
3
+ export default class NotFound extends GlideItem {
4
+ render() {
5
+ return `<div class="column glide__slide">
6
+ <div class="card card--proposal card--stack">
7
+ <div class="card--header"></div>
8
+ <div class="card--content text-center margin-top-1">
9
+ <h3 class="card__title">No match</h3>
10
+ <div class="card__text--paragraph padding-top-1">
11
+ <p>No proposals found for this request</p>
12
+ </div>
13
+ <a href="${this.url}" class="button--clear-filters">
14
+ <div class="card__button align-bottom">
15
+ <span class="button button--secondary">Visit proposals</span>
16
+ </div>
17
+ </a>
18
+ </div>
19
+ </div>
20
+ </div>`
21
+ }
22
+
23
+ }
@@ -0,0 +1,51 @@
1
+ import GlideItem from "./GlideItem";
2
+
3
+ export default class Proposal extends GlideItem {
4
+ constructor(obj) {
5
+ super();
6
+ this.title = obj.title;
7
+ this.body = obj.body;
8
+ this.image = obj.image;
9
+ this.url = obj.url;
10
+ this.state = obj.state || 'not answered' ;
11
+ this.color = this.colorFromState(this.state);
12
+ }
13
+
14
+ colorFromState(state) {
15
+ switch(state){
16
+ case 'accepted':
17
+ return 'success';
18
+ case 'rejected':
19
+ return 'alert';
20
+ case 'evaluating':
21
+ return 'warning';
22
+ default:
23
+ return 'muted';
24
+ }
25
+ }
26
+
27
+ render() {
28
+ return `<div class="column glide__slide">
29
+ <div class="card card--proposal card--stack">
30
+ <a href="${this.url}">
31
+ <div class="proposal-glance card--header">
32
+ <img src="${this.image}" class="proposal-glance__img" alt="slider_img">
33
+ </div>
34
+ </a>
35
+ <div class="card--process__small text-center padding-1">
36
+ <span class="${this.color} card__text--status status_slider"> ${this.state.charAt(0).toUpperCase() + this.state.slice(1)} </span>
37
+ <a href="${this.url}"><h3 class="proposal-glance card__title">${this.title}</h3></a>
38
+ <div class="card__text--paragraph padding-top-1">
39
+ <p>${this.body}</p>
40
+ </div>
41
+ <a href="${this.url}">
42
+ <div class="card__button align-bottom padding-top-1">
43
+ <span class="button small button--secondary">Visit</span>
44
+ </div>
45
+ </a>
46
+ </div>
47
+ </div>
48
+ </div>`
49
+ }
50
+
51
+ }
@@ -0,0 +1,19 @@
1
+ import Manager from "./glidejs/Manager"
2
+
3
+ $(() => {
4
+ const $proposalsSlider = $("#proposals_slider");
5
+ const $proposalsGlideItems = $("#proposals_glide_items");
6
+ const $filterForm = $("#filters-form");
7
+ const $smallFilterForm = $("#filter-box form");
8
+ const slider = new Manager($proposalsSlider, $proposalsGlideItems, $(".glide__bullet.glide__bullet_idx"), $filterForm);
9
+ const smallSlider = new Manager($proposalsSlider, $proposalsGlideItems, $(".glide__bullet.glide__bullet_idx"), $smallFilterForm);
10
+ slider.start()
11
+
12
+ $filterForm.on("change", () => {
13
+ slider.start()
14
+ });
15
+
16
+ $smallFilterForm.on("change", () => {
17
+ smallSlider.start()
18
+ });
19
+ });
@@ -0,0 +1,45 @@
1
+ @import "@glidejs/glide/src/assets/sass/glide.core";
2
+
3
+ .card__text--paragraph {
4
+ height: 150px;
5
+ }
6
+
7
+ // glide's relative position prevents filters from being clicked
8
+ .glide {
9
+ position: inherit !important;
10
+ margin-top: 0 !important;
11
+ }
12
+
13
+ .glide__bullets {
14
+ display: flex;
15
+ justify-content: center;
16
+ align-items: center;
17
+ }
18
+
19
+ .glide__bullet {
20
+ margin-right: 0.5vw;
21
+ margin-left: 0.5vw;
22
+ color: lightgrey;
23
+ }
24
+
25
+ .glide__bullet:nth-of-type(2) {
26
+ color: grey;
27
+ }
28
+
29
+ .glide__bullet:hover {
30
+ color: grey;
31
+ }
32
+
33
+ .proposal-glance.card--header {
34
+ height: 120px;
35
+ }
36
+
37
+ .proposal-glance__img {
38
+ width:100%;
39
+ height:100%;
40
+ object-fit:cover;
41
+ }
42
+
43
+ .proposal-glance.card__title {
44
+ max-height: 30px;
45
+ }