element-book
Version:
An [`element-vir`](https://npmjs.com/package/element-vir) drop-in element for building, testing, and demonstrating a collection of elements (or, in other words, a design system).
173 lines (167 loc) • 6.04 kB
JavaScript
import { check } from '@augment-vir/assert';
import { extractEventTarget } from '@augment-vir/web';
import { css, defineElementEvent, html, listen, renderIf } from 'element-vir';
import { Options24Icon, ViraIcon, ViraInput } from 'vira';
import { BookPageControlType, isControlInitType, } from '../../../../data/book-entry/book-page/book-page-controls.js';
import { colorThemeCssVars } from '../../../color-theme/color-theme.js';
import { defineBookElement } from '../../define-book-element.js';
export const BookPageControls = defineBookElement()({
tagName: 'book-page-controls',
events: {
controlValueChange: defineElementEvent(),
},
hostClasses: {
'book-page-controls-has-controls': ({ inputs }) => !!Object.keys(inputs.config).length,
},
styles: ({ hostClasses }) => css `
:host {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
padding-left: 36px;
align-content: flex-start;
gap: 16px;
row-gap: 10px;
color: ${colorThemeCssVars['element-book-page-foreground-faint-level-1-color'].value};
}
${hostClasses['book-page-controls-has-controls'].selector} {
margin-top: 8px;
}
.control-wrapper {
position: relative;
display: flex;
gap: 4px;
flex-direction: column;
}
.error {
font-weight: bold;
color: red;
}
${ViraInput} {
height: 24px;
max-width: 128px;
}
${ViraIcon}.options-icon {
position: absolute;
left: 0;
bottom: 0;
margin-left: -32px;
}
`,
render({ inputs, dispatch, events }) {
if (!Object.entries(inputs.config).length) {
return '';
}
return Object.entries(inputs.config).map(([controlName, controlInit,], index) => {
if (controlInit.controlType === BookPageControlType.Hidden) {
return '';
}
const controlInputTemplate = createControlInput(inputs.currentValues[controlName], controlInit, (newValue) => {
const fullUrlBreadcrumbs = check.isArray(inputs.fullUrlBreadcrumbs)
? inputs.fullUrlBreadcrumbs
: inputs.fullUrlBreadcrumbs[controlName];
if (!fullUrlBreadcrumbs) {
throw new Error(`Failed to find breadcrumbs from given control name: '${controlName}'`);
}
dispatch(new events.controlValueChange({
fullUrlBreadcrumbs,
newValues: {
...inputs.currentValues,
[controlName]: newValue,
},
}));
});
return html `
<div class="control-wrapper">
${renderIf(index === 0, html `
<${ViraIcon.assign({ icon: Options24Icon })}
class="options-icon"
></${ViraIcon}>
`)}
<label class="control-wrapper">
<span>${controlName}</span>
${controlInputTemplate}
</label>
</div>
`;
});
},
});
function createControlInput(value, controlInit, valueChange) {
if (isControlInitType(controlInit, BookPageControlType.Hidden)) {
return '';
}
else if (isControlInitType(controlInit, BookPageControlType.Checkbox)) {
return html `
<input
type="checkbox"
?checked=${value}
${listen('input', (event) => {
const inputElement = extractEventTarget(event, HTMLInputElement);
valueChange(inputElement.checked);
})}
/>
`;
}
else if (isControlInitType(controlInit, BookPageControlType.Color)) {
return html `
<input
type="color"
.value=${value}
${listen('input', (event) => {
const inputElement = extractEventTarget(event, HTMLInputElement);
valueChange(inputElement.value);
})}
/>
`;
}
else if (isControlInitType(controlInit, BookPageControlType.Text)) {
return html `
<${ViraInput.assign({
value,
showClearButton: true,
disableBrowserHelps: true,
})}
${listen(ViraInput.events.valueChange, (event) => {
valueChange(event.detail);
})}
></${ViraInput}>
`;
}
else if (isControlInitType(controlInit, BookPageControlType.Number)) {
return html `
<input
type="number"
.value=${value}
${listen('input', (event) => {
const inputElement = extractEventTarget(event, HTMLInputElement);
valueChange(inputElement.value);
})}
/>
`;
}
else if (isControlInitType(controlInit, BookPageControlType.Dropdown)) {
return html `
<select
.value=${value}
${listen('input', (event) => {
const selectElement = extractEventTarget(event, HTMLSelectElement);
valueChange(selectElement.value);
})}
>
${controlInit.options.map((optionLabel) => {
return html `
<option ?selected=${optionLabel === value} value=${optionLabel}>
${optionLabel}
</option>
`;
})}
</select>
`;
}
else {
return html `
<p class="error">${controlInit.controlType} controls are not implemented yet.</p>
`;
}
}