From 33b197add898cc48979db829630231f6888e6846 Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Tue, 18 Oct 2022 13:40:40 -0300 Subject: [PATCH] refactor(demo): refactor gallery as web component --- .../fontawesome/FontAwesomeReflect.java | 2 +- .../addons/fontawesome/IconsGalleryView.java | 101 ++++++++---------- .../frontend/fc-font-awesome-gallery.ts | 101 ++++++++++++++++++ .../styles/font-awesome/demo-styles.css | 28 +---- 4 files changed, 146 insertions(+), 86 deletions(-) create mode 100644 src/test/resources/META-INF/resources/frontend/fc-font-awesome-gallery.ts diff --git a/src/test/java/com/flowingcode/vaadin/addons/fontawesome/FontAwesomeReflect.java b/src/test/java/com/flowingcode/vaadin/addons/fontawesome/FontAwesomeReflect.java index 56e33ed..35d2d7e 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/fontawesome/FontAwesomeReflect.java +++ b/src/test/java/com/flowingcode/vaadin/addons/fontawesome/FontAwesomeReflect.java @@ -40,7 +40,7 @@ private FontAwesomeReflect() {} /**Return a list of all the {@code FontAwesome} icon types defined in the addon.*/ public static List> getIconTypes() { - return Arrays.asList(Brands.class, Regular.class, Solid.class); + return Arrays.asList(Solid.class, Regular.class, Brands.class); } /**Return the iconset name of the given {@code FontAwesome} icon type.*/ diff --git a/src/test/java/com/flowingcode/vaadin/addons/fontawesome/IconsGalleryView.java b/src/test/java/com/flowingcode/vaadin/addons/fontawesome/IconsGalleryView.java index 2cd4bbf..efbeb80 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/fontawesome/IconsGalleryView.java +++ b/src/test/java/com/flowingcode/vaadin/addons/fontawesome/IconsGalleryView.java @@ -20,85 +20,83 @@ package com.flowingcode.vaadin.addons.fontawesome; import com.vaadin.flow.component.Component; -import com.vaadin.flow.component.UI; +import com.vaadin.flow.component.Tag; import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.dependency.JsModule; +import com.vaadin.flow.component.dependency.Uses; import com.vaadin.flow.component.dialog.Dialog; import com.vaadin.flow.component.html.Div; -import com.vaadin.flow.component.html.H4; import com.vaadin.flow.component.html.Span; -import com.vaadin.flow.component.orderedlayout.FlexLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.data.value.ValueChangeMode; +import com.vaadin.flow.function.SerializableConsumer; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; @PageTitle("Icons Gallery") @SuppressWarnings("serial") @Route(value = "font-awesome-iron-iconset/icons", layout = FontawesomeDemoView.class) +@JsModule("./fc-font-awesome-gallery.ts") +@Uses(FontAwesome.Solid.Icon.class) +@Uses(FontAwesome.Regular.Icon.class) +@Uses(FontAwesome.Brands.Icon.class) public class IconsGalleryView extends Div { - // this demo uses reflection, for a simple example that does not use reflection - // see SimpleDemoView - private static final Map searchString = new WeakHashMap<>(); + @Tag("fc-font-awesome-gallery-demo") + public static class IconsGallery extends Component { + + private static final String EVENT_DETAIL = "event.detail"; + + public IconsGallery(Class type, SerializableConsumer iconClickListener) { + String caption = "FontAwesome " + type.getSimpleName(); + String family = FontAwesomeReflect.getIconset(type); + getElement().setProperty("caption", caption); + getElement().setProperty("family", family); + getElement().addEventListener("iconClick", ev -> { + iconClickListener.accept(ev.getEventData().getString(EVENT_DETAIL)); + }).addEventData(EVENT_DETAIL); + } + + public void filter(String filterString) { + filterString = StringUtils.lowerCase(filterString); + filterString = StringUtils.trimToNull(filterString); + getElement().setProperty("filterString", filterString); + } + + } - private final List> icons = new ArrayList<>(); - private final List> layouts = new ArrayList<>(); - private final Div noResults = new Div(new Span("Your search did not match any icons.")); { TextField filter = new TextField(); filter.setValueChangeMode(ValueChangeMode.EAGER); - filter.setWidth("100%"); + filter.setWidth("calc(100vw - 56px)"); filter.addValueChangeListener(ev -> applyFilter(filter.getValue())); filter.setPlaceholder("Search icons"); + add(filter); setSizeUndefined(); addClassName("main-icon-view"); - getIconTypes().forEach(type -> { - FlexLayout layout = new FlexLayout(); - layout.addClassName("flex"); - H4 h4 = new H4("FontAwesome " + type.getSimpleName()); - add(h4, layout); - layouts.add(Pair.of(h4, layout)); - - Map icons = new HashMap<>(); - this.icons.add(icons); - for (Object e : type.getEnumConstants()) { - String name = ((Enum) e).name().toLowerCase().replace('_', '-').replaceFirst("^-", ""); - Button btn = new Button(name, create(type, e)); - btn.setClassName("text-align-left"); - btn.addClickListener(ev -> showDetails(e)); - layout.add(btn); - layout.setFlexGrow(0, btn); - icons.put(name, btn); - } + FontAwesomeReflect.getIconTypes().forEach(type -> { + add(new IconsGallery(type, this::showDetails)); }); - add(noResults); + Div noResults = new Div(new Span("Your search did not match any icons.")); noResults.addClassName("no-results"); - noResults.setVisible(false); - - addAttachListener(ev -> getUI().map(searchString::get).ifPresent(filter::setValue)); + noResults.getElement().setAttribute("hidden", "true"); + add(noResults); } - private void showDetails(Object icon) { + private void showDetails(String icon) { DemoViewSingle view = new DemoViewSingle(); Button closeButton = new Button(FontAwesome.Solid.XMARK.create()); closeButton.addClassName("close-button"); HorizontalLayout top = new HorizontalLayout(closeButton); - view.setParameter(null, FontAwesomeReflect.getIconName(icon).replace(':', '/')); + view.setParameter(null, icon.replace(':', '/')); Div div = new Div(top, view); div.addClassName("details-dialog"); Dialog dlg = new Dialog(div); @@ -107,24 +105,9 @@ private void showDetails(Object icon) { closeButton.addClickListener(ev -> dlg.close()); } - private Component create(Class type, Object e) { - try { - return (Component) type.getMethod("create").invoke(e); - } catch (Exception ex) { - throw new RuntimeException(ex); - } + private void applyFilter(String filterString) { + getChildren().filter(IconsGallery.class::isInstance).map(IconsGallery.class::cast) + .forEach(gallery -> gallery.filter(filterString)); } - private static Stream> getIconTypes() { - return Stream.of(FontAwesome.Regular.class, FontAwesome.Solid.class, FontAwesome.Brands.class); - } - - private void applyFilter(String value) { - searchString.put(getUI().get(), value); - icons.forEach(map -> map.forEach((name, icon) -> icon.setVisible( - StringUtils.isBlank(value) || name.toLowerCase().contains(value.toLowerCase())))); - layouts.forEach( - p -> p.getLeft().setVisible(p.getRight().getChildren().anyMatch(Component::isVisible))); - noResults.setVisible(layouts.stream().noneMatch(p -> p.getLeft().isVisible())); - } } diff --git a/src/test/resources/META-INF/resources/frontend/fc-font-awesome-gallery.ts b/src/test/resources/META-INF/resources/frontend/fc-font-awesome-gallery.ts new file mode 100644 index 0000000..d314f1a --- /dev/null +++ b/src/test/resources/META-INF/resources/frontend/fc-font-awesome-gallery.ts @@ -0,0 +1,101 @@ +import { html, css, LitElement } from 'lit'; +import { property } from 'lit/decorators.js'; + +import '@vaadin/button'; +import '@vaadin/text-field'; + +class FontAwesomeGalleryDemo extends LitElement { + + @property() + family: string; + + @property() + caption: string; + + @property() + filterString: string; + + @property() + _icons: string[]; + + static styles = css` + :host([empty]) { + display: none; + } + + h4 { + margin-top: 2ex; + margin-bottom: 1ex; + } + + .flex { + display: flex; + flex-wrap: wrap; + } + + .icon { + border-radius: 4px + } + + .icon:hover { + cursor : pointer; + background: #f6f6f6; + } + + .icon:active { + background: #f0f0f0; + } + + .icon vaadin-icon { + margin: 8px; + padding-bottom: 4px; + } + `; + + render() { + return html` +

${this.caption}

+
+ ${this._icons && this._icons.map(this.createButton.bind(this))} +
+ `; + } + + updated(changedProps : any) { + if (changedProps.has('family')) { + const iconset = document.querySelector("fc-iconset[name='"+this.family+"']") as any; + let icons = []; + if (iconset) { + (iconset as any).applyIcon(null); + icons = Object.keys(iconset._icons).concat(Object.keys(iconset._alias)).sort(); + } + this._icons = icons; + } + + let hasVisibleIcons = !!this!.shadowRoot.querySelector(".icon:not([hidden])"); + this.toggleAttribute('empty', !hasVisibleIcons); + + let globalHasVisibleIcons = !!document.querySelector("fc-font-awesome-gallery-demo:not([empty])"); + document.querySelector('.no-results').toggleAttribute('hidden', globalHasVisibleIcons); + } + + createButton(icon: String) { + return html`
this.__handleClick(this.family+":"+icon)}> + ${icon} +
`; + } + + __testFilterString(icon: String) { + if (!this.filterString) return true; + if (this.filterString.includes('-')) return icon.includes(this.filterString); + return icon.split('-').some(word=>word.startsWith(this.filterString)); + } + + __handleClick(detail: String) { + this.dispatchEvent(new CustomEvent('iconClick', {detail})); + } + +} + +customElements.define('fc-font-awesome-gallery-demo', FontAwesomeGalleryDemo); \ No newline at end of file diff --git a/src/test/resources/META-INF/resources/frontend/styles/font-awesome/demo-styles.css b/src/test/resources/META-INF/resources/frontend/styles/font-awesome/demo-styles.css index e13646d..3742a6c 100644 --- a/src/test/resources/META-INF/resources/frontend/styles/font-awesome/demo-styles.css +++ b/src/test/resources/META-INF/resources/frontend/styles/font-awesome/demo-styles.css @@ -37,36 +37,12 @@ vaadin-dialog-overlay { .main-icon-view { margin: 8px; + align-self: stretch; } -.main-icon-view .flex { - flex-wrap: wrap; -} - -.main-icon-view vaadin-button { - margin: 4px; - margin-bottom: 8px; - background: white; - color: black; - cursor: pointer; - min-width: 240px; - flex-grow: 0; -} - -.main-icon-view vaadin-button vaadin-icon { - position: absolute; - top: 2px; - left: 2px; -} - -.main-icon-view vaadin-button span { - position: absolute; - top: 8px; - left: 34px; - } - .main-icon-view .no-results { margin-left: 16px; + margin-top: 2ex; } .single-icon-view vaadin-icon {