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: [],
|
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;
|
||||||
|
|
|
@ -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>`;
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -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