Add basic modal component; Add resultDetails to search

This commit is contained in:
Robbie Antenesse 2019-09-11 17:34:11 -06:00
parent 464aad9d34
commit b1843c688c
4 changed files with 113 additions and 18 deletions

View File

@ -0,0 +1,66 @@
import html from 'choo/html';
export const modal = (modalId, controller, contentHTML, options = {}) => {
/* Options:
* controller <class>: Pass the controller class with state; Requires get/set for openModal in state.
* buttonHTML <choo/html>: Displayed in place of the default button to open the modal
* buttonText <string>: Displayed if no buttonHTML is specified
* noHeader <bool>: Set to `true` and exclude headerHTML to not include a modal header
* headerHTML <choo/html>: Displayed in place of the default header; Recommended to use `<header>` tag
* headerText <string>: Displayed in an `<h3>` if no header is specified
* noFooter <bool>: Set to `true` and exclude footerHTML to not include a modal footer
* footerHTML <choo/html>: Displayed in place of the default footer; Recommended to use `<footer>` tag
*/
const isOpen = controller.openModal === modalId;
return [
(
typeof options.buttonHTML === 'undefined'
? html`<label for=${modalId} class="button">${options.buttonText}</label>`
: options.buttonHTML
),
// Modals in Picnic CSS uses pure CSS with clever usage of invisible checkboxes and labels
html`<div class="modal">
<input id=${modalId} type="checkbox" ${!isOpen ? null : 'checked'}
onchange=${() => controller.openModal = isOpen ? modalId : null }/>
<label for=${modalId} class="overlay"></label>
<article>
${typeof options.headerHTML === 'undefined'
? (
options.noHeader
? null
: html`<header>
<h3>${options.headerText}</h3>
<label for=${modalId} class="close">${'\u00d7'}</label>
</header>`
)
: options.headerHTML
}
<section class="content">
${typeof contentHTML === 'undefined'
? null
: contentHTML
}
</section>
${typeof options.footerHTML === 'undefined'
? (
options.noFooter
? null
: html`<footer>
<label for=${modalId} class="button dangerous">
Close
</label>
</footer>`
)
: options.footerHTML
}
</article>
</div>`,
];
}

View File

@ -12,6 +12,7 @@ export class SearchController extends ViewController {
series: [], series: [],
works: [], works: [],
}, },
openModal: null,
}); });
// If using controller methods in an input's onchange or onclick instance, // If using controller methods in an input's onchange or onclick instance,
@ -31,6 +32,14 @@ export class SearchController extends ViewController {
return this.appState.query.hasOwnProperty('for') && this.appState.query.for.trim() !== ''; return this.appState.query.hasOwnProperty('for') && this.appState.query.for.trim() !== '';
} }
get openModal() {
return this.state.openModal;
}
set openModal(modalId) {
this.state.openModal = modalId;
}
search() { search() {
if (this.hasQuery) { if (this.hasQuery) {
this.state.done = false; this.state.done = false;

View File

@ -2,6 +2,7 @@ import html from 'choo/html';
import { I18n } from '../../i18n'; import { I18n } from '../../i18n';
import { SearchController } from './controller'; // The controller for this view, where processing should happen. import { SearchController } from './controller'; // The controller for this view, where processing should happen.
import { resultDetails } from './resultDetails';
// This is the view function that is exported and used in the view manager. // This is the view function that is exported and used in the view manager.
export const searchView = (state, emit) => { export const searchView = (state, emit) => {
@ -30,28 +31,28 @@ export const searchView = (state, emit) => {
html`<h2>${i18n.__('search.books_header')}</h2>`, html`<h2>${i18n.__('search.books_header')}</h2>`,
controller.results.works.map(result => { controller.results.works.map(result => {
return html`<div class="flex search-result"> return html`<div class="flex search-result">
<div class="half-500"> <div class="two-third-500">
<h3 class="title">${result.name}</h3> <h3 class="title">${result.name}</h3>
${result.description ? html`<h4 class="subtitle">${result.description}</h4>` : null} ${result.description ? html`<h4 class="subtitle">${result.description}</h4>` : null}
</div> </div>
<div class="third-500"> <div class="third-500">
<span data-tooltip=${i18n.__('interaction.heart')}> ${resultDetails(
<button class="pseudo"> controller,
<i class="pseudo icon-heart-outline"></i> i18n,
</button> result,
</span> [
<span data-tooltip=${i18n.__('interaction.add')}> html`<span data-tooltip=${i18n.__('interaction.heart')}>
<button class="pseudo"> <button class="pseudo">
<i class="pseudo icon-plus"></i> <i class="pseudo icon-heart-outline"></i>
</button> </button>
</span> </span>`,
</div> html`<span data-tooltip=${i18n.__('interaction.add')}>
<div class="sixth-500"> <button class="pseudo">
<span class="tooltip-left" data-tooltip=${i18n.__('search.see_details_tooltip')}> <i class="pseudo icon-plus"></i>
<a class="small pseudo button" href=${result.link} target="_blank"> </button>
${i18n.__('search.see_details')} </span>`,
</a> ]
</span> )}
</div> </div>
</div>`; </div>`;
}), }),

View File

@ -0,0 +1,19 @@
import html from 'choo/html';
import { modal } from '../partials/modal';
export const resultDetails = (searchController, i18n, result, buttonHTML) => {
const modalId = `result_${result.uri}}`;
const modalContent = html`<article>
<span class="tooltip-left" data-tooltip=${i18n.__('search.see_details_tooltip')}>
<a class="small pseudo button" href=${result.link} target="_blank">
${i18n.__('search.see_details')}
</a>
</span>
</article>`;
return modal(modalId, searchController, modalContent, {
buttonText: i18n.__('search.see_details'), // This should be replaced with buttonHTML containing the ratings and number of reviews etc.
headerText: result.name,
});
}