From 1adb22be7124d0f4cbf30df7072ec9f4552142cf Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bellet Date: Tue, 22 Mar 2022 10:42:36 +0100 Subject: [PATCH] Create SelectorWithFilter component --- app/components/selector_component.rb | 1 + app/components/super_selector_component.rb | 24 ++++ .../super_selector_component.html.haml | 22 +++ .../super_selector_component.scss | 126 ++++++++++++++++++ .../controllers/super_selector_controller.js | 18 +++ app/webpacker/css/admin/all.scss | 1 + 6 files changed, 192 insertions(+) create mode 100644 app/components/super_selector_component.rb create mode 100644 app/components/super_selector_component/super_selector_component.html.haml create mode 100644 app/components/super_selector_component/super_selector_component.scss create mode 100644 app/webpacker/controllers/super_selector_controller.js diff --git a/app/components/selector_component.rb b/app/components/selector_component.rb index 884f143c2f..4837abb318 100644 --- a/app/components/selector_component.rb +++ b/app/components/selector_component.rb @@ -11,6 +11,7 @@ class SelectorComponent < ViewComponentReflex::Component selected: selected.include?(item[:value]) } end + @selected = selected @state = :close @data = data end diff --git a/app/components/super_selector_component.rb b/app/components/super_selector_component.rb new file mode 100644 index 0000000000..bd78d4c550 --- /dev/null +++ b/app/components/super_selector_component.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class SuperSelectorComponent < SelectorComponent + def initialize(title:, selected:, items:, data: {}) + super(title: title, selected: selected, items: items, data: data) + @query = "" + @selected_items = items.select { |item| @selected.include?(item[:value]) } + + filter_items + end + + def search + @query = element.value + filter_items + end + + def filter_items + @filtered_items = if @query.empty? + @items + else + @items.select { |item| item[:label].downcase.include?(@query.downcase) } + end + end +end diff --git a/app/components/super_selector_component/super_selector_component.html.haml b/app/components/super_selector_component/super_selector_component.html.haml new file mode 100644 index 0000000000..203dd023d0 --- /dev/null +++ b/app/components/super_selector_component/super_selector_component.html.haml @@ -0,0 +1,22 @@ += component_controller do + .super-selector{ class: ("super-selector-close" if @state == :close) } + .super-selector-main + .super-selector-label + = @title + .super-selector-selected-items + - case @selected_items.length + - when 1, 2 + - @selected_items.each do |item| + .super-selector-selected-item + = item[:label] + - else + .super-selector-selected-item + = "#{@selected_items.length} categories selected" + .super-selector-arrow{data: reflex_data_attributes(:toggle)} + .super-selector-wrapper + .super-selector-search + %input{type: "text", placeholder: "Search", data: reflex_data_attributes("debounced:input->search"), value: @query} + .super-selector-items + - @filtered_items.each do |item| + .super-selector-item{ class: ("selected" if item[:selected]), data: @data, "data-value": item[:value] } + = item[:label] diff --git a/app/components/super_selector_component/super_selector_component.scss b/app/components/super_selector_component/super_selector_component.scss new file mode 100644 index 0000000000..81944f5875 --- /dev/null +++ b/app/components/super_selector_component/super_selector_component.scss @@ -0,0 +1,126 @@ + +.super-selector { + margin-top: 5px; + position: relative; + + .super-selector-main { + border: 1px solid #d2d2d2; + height: 3em; + position: relative; + + .super-selector-label { + padding-left: 5px; + padding-right: 5px; + margin-left: 10px; + position: absolute; + top: -1em; + background-color: white; + } + + .super-selector-arrow { + position: absolute; + right: 0px; + height: 3em; + width: 1.5em; + top: -1px; + cursor: pointer; + + &:after { + content: ""; + position: absolute; + top: 50%; + right: 5px; + margin-top: -5px; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid #d2d2d2; + } + } + } + + .super-selector-selected-items { + margin-left: 5px; + margin-right: 2em; + margin-top: 7px; + display: flex; + + .super-selector-selected-item { + border: 1px solid #cee1f4; + background-color: #eff5fc; + border-radius: 20px; + height: 2em; + padding-left: 10px; + padding-right: 10px; + display: inline-block; + margin-right: 5px; + padding-top: 2px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + + .super-selector-wrapper { + position: absolute; + left: 0px; + right: 0px; + z-index: 1; + background-color: white; + margin-top: -1px; + border: 1px solid #d2d2d2; + + .super-selector-search { + border-bottom: 1px solid #d2d2d2; + padding: 10px 5px; + + input { + border: 1px solid #d2d2d2; + box-sizing: border-box; + border-radius: 4px; + width: 100%; + } + } + + .super-selector-items { + overflow-y: auto; + min-height: 6em; + + .super-selector-item { + padding-left: 10px; + padding-right: 10px; + border-bottom: 1px solid #d2d2d2; + position: relative; + height: 3em; + line-height: 3em; + + &:hover { + background-color: #eee; + cursor: pointer; + } + + &:last-child { + border-bottom: none; + } + + &.selected { + &:after { + content: "✓"; + display: inline-block; + position: absolute; + right: 10px; + } + } + } + } + } +} + +.super-selector-close { + &.super-selector { + .super-selector-wrapper { + display: none; + } + } +} diff --git a/app/webpacker/controllers/super_selector_controller.js b/app/webpacker/controllers/super_selector_controller.js new file mode 100644 index 0000000000..918862e1f6 --- /dev/null +++ b/app/webpacker/controllers/super_selector_controller.js @@ -0,0 +1,18 @@ +import ApplicationController from "./application_controller"; + +export default class extends ApplicationController { + connect() { + super.connect(); + window.addEventListener("click", this.handleClick); + } + disconnect() { + super.disconnect(); + window.removeEventListener("click", this.handleClick); + } + + handleClick = (event) => { + if (!this.element.contains(event.target)) { + this.stimulate("SuperSelectorComponent#close", this.element); + } + }; +} diff --git a/app/webpacker/css/admin/all.scss b/app/webpacker/css/admin/all.scss index 75d5e4a88e..bbc4b6ee73 100644 --- a/app/webpacker/css/admin/all.scss +++ b/app/webpacker/css/admin/all.scss @@ -123,5 +123,6 @@ @import "app/components/product_component/product_component"; @import "app/components/selector_component/selector_component"; @import "app/components/products_table_component/products_table_component"; +@import "app/components/super_selector_component/super_selector_component"; @import "v2/main.scss";