Add basic modal component; Add resultDetails to search
This commit is contained in:
parent
464aad9d34
commit
b1843c688c
|
@ -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>`,
|
||||
];
|
||||
}
|
|
@ -12,6 +12,7 @@ export class SearchController extends ViewController {
|
|||
series: [],
|
||||
works: [],
|
||||
},
|
||||
openModal: null,
|
||||
});
|
||||
|
||||
// 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() !== '';
|
||||
}
|
||||
|
||||
get openModal() {
|
||||
return this.state.openModal;
|
||||
}
|
||||
|
||||
set openModal(modalId) {
|
||||
this.state.openModal = modalId;
|
||||
}
|
||||
|
||||
search() {
|
||||
if (this.hasQuery) {
|
||||
this.state.done = false;
|
||||
|
|
|
@ -2,6 +2,7 @@ import html from 'choo/html';
|
|||
|
||||
import { I18n } from '../../i18n';
|
||||
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.
|
||||
export const searchView = (state, emit) => {
|
||||
|
@ -30,28 +31,28 @@ export const searchView = (state, emit) => {
|
|||
html`<h2>${i18n.__('search.books_header')}</h2>`,
|
||||
controller.results.works.map(result => {
|
||||
return html`<div class="flex search-result">
|
||||
<div class="half-500">
|
||||
<div class="two-third-500">
|
||||
<h3 class="title">${result.name}</h3>
|
||||
${result.description ? html`<h4 class="subtitle">${result.description}</h4>` : null}
|
||||
</div>
|
||||
<div class="third-500">
|
||||
<span data-tooltip=${i18n.__('interaction.heart')}>
|
||||
<button class="pseudo">
|
||||
<i class="pseudo icon-heart-outline"></i>
|
||||
</button>
|
||||
</span>
|
||||
<span data-tooltip=${i18n.__('interaction.add')}>
|
||||
<button class="pseudo">
|
||||
<i class="pseudo icon-plus"></i>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="sixth-500">
|
||||
<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>
|
||||
${resultDetails(
|
||||
controller,
|
||||
i18n,
|
||||
result,
|
||||
[
|
||||
html`<span data-tooltip=${i18n.__('interaction.heart')}>
|
||||
<button class="pseudo">
|
||||
<i class="pseudo icon-heart-outline"></i>
|
||||
</button>
|
||||
</span>`,
|
||||
html`<span data-tooltip=${i18n.__('interaction.add')}>
|
||||
<button class="pseudo">
|
||||
<i class="pseudo icon-plus"></i>
|
||||
</button>
|
||||
</span>`,
|
||||
]
|
||||
)}
|
||||
</div>
|
||||
</div>`;
|
||||
}),
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue