diff --git a/src/preview/assets/index.html b/src/preview/assets/index.html index 36376bf5..c0112d1d 100644 --- a/src/preview/assets/index.html +++ b/src/preview/assets/index.html @@ -8,32 +8,33 @@ -
-
- -
+ + -
-
+ -
-
+
diff --git a/src/preview/assets/preview.css b/src/preview/assets/preview.css index b8fe29c6..8b8fc7d7 100644 --- a/src/preview/assets/preview.css +++ b/src/preview/assets/preview.css @@ -37,6 +37,11 @@ iframe { flex-grow: 1; } -.hide { +fieldset { + border: none; + padding: 0; +} + +fieldset[disabled] { display: none; } diff --git a/src/preview/assets/preview.js b/src/preview/assets/preview.js index 80178e16..34b32753 100644 --- a/src/preview/assets/preview.js +++ b/src/preview/assets/preview.js @@ -1,192 +1,136 @@ // @ts-nocheck /* global SOCKET_SERVER_PORT */ -/** @type {HTMLSelectElement} */ -const collectionSelect = document.getElementById('collection'); - -/** @type {HTMLSelectElement} */ -const fragmentSelect = document.getElementById('fragment'); - -/** @type {HTMLSelectElement} */ -const pageTemplateSelect = document.getElementById('pageTemplate'); - -/** @type {HTMLSelectElement} */ -const previewTypeSelect = document.getElementById('previewType'); - -/** @type {HTMLElement} */ -const fragmentsPreview = document.getElementById('fragmentsPreview'); - -/** @type {HTMLElement} */ -const pageTemplatesPreview = document.getElementById('pageTemplatesPreview'); - -/** @type {HTMLIFrameElement} */ +const form = document.getElementById('form'); const preview = document.getElementById('preview'); - const socket = new WebSocket(`ws://${location.hostname}:${SOCKET_SERVER_PORT}`); +let currentCollection = ''; let projectContent = {}; +let initialized = false; -/** - * @param {HTMLSelectElement} selectElement Select element - */ -function syncSelectFieldURL(selectElement) { - const url = new URL(location.href); - url.searchParams.set(selectElement.id, selectElement.value); - history.pushState(null, null, url.href); +function isDisabled(element) { + return element + ? element.disabled || isDisabled(element.parentElement) + : false; } -collectionSelect.addEventListener('change', () => { - const collectionId = collectionSelect.value; - - if (collectionId) { - syncSelectFieldURL(collectionSelect); - - const collection = projectContent.collections.find( - (collection) => collection.slug === collectionId - ); - - renderSelect( - fragmentSelect, - collection.fragments - .map((fragment) => ({ - value: fragment.slug, - label: fragment.metadata.name, - type: 'fragment', - })) - .concat( - collection.fragmentCompositions.map((composition) => ({ - value: composition.slug, - label: composition.metadata.name, - type: 'composition', - })) - ) - ); - } -}); - -fragmentSelect.addEventListener('change', () => { - if (fragmentSelect.value) { - syncSelectFieldURL(fragmentSelect); +function updateOptions(list, select) { + const previousValue = select.value; + select.innerHTML = ''; - const type = fragmentSelect.selectedOptions[0].dataset.type; + for (const item of list) { + const option = document.createElement('option'); + option.value = item.slug; + option.innerHTML = item.metadata.name; + select.appendChild(option); + } - preview.src = `/fragment-preview?collection=${collectionSelect.value}&fragment=${fragmentSelect.value}&type=${type}`; + if (list.some((item) => item.slug === previousValue)) { + select.value = previousValue; } -}); +} -pageTemplateSelect.addEventListener('change', () => { - if (pageTemplateSelect.value) { - syncSelectFieldURL(pageTemplateSelect); +function updateCurrentCollection() { + currentCollection = form.elements.collection.value; - const type = 'page-template'; + const collection = projectContent.collections.find( + (collection) => collection.slug === currentCollection + ); - preview.src = `/fragment-preview?pageTemplate=${pageTemplateSelect.value}&type=${type}`; + if (!collection) { + return; } -}); -previewTypeSelect.addEventListener('change', () => { - const changeEvent = new Event('change'); - const url = new URL(location.href); + updateOptions( + [...collection.fragments, ...collection.fragmentCompositions], + form.elements.fragment + ); +} - url.searchParams.forEach((_, key) => { - url.searchParams.delete(key); - }); +function handleFormChange() { + const fieldsetId = form.elements.fieldsetId.value; - url.searchParams.set(previewTypeSelect.id, previewTypeSelect.value); + for (const fieldset of form.querySelectorAll('fieldset')) { + if (fieldset.id === fieldsetId) { + fieldset.disabled = false; + } else { + fieldset.disabled = true; + } + } - history.pushState(null, null, url.href); + if (form.elements.collection.value !== currentCollection) { + updateCurrentCollection(); + } - if (previewTypeSelect.value === 'page-template') { - fragmentsPreview.className = 'hide'; - pageTemplatesPreview.className = ''; + const url = new URL(location.href); - renderSelect( - pageTemplateSelect, - projectContent.pageTemplates.map((pageTemplate) => ({ - value: pageTemplate.slug, - label: pageTemplate.metadata.name, - })) - ); + for (const element of form.elements) { + if (element.name) { + if (element.value && !isDisabled(element)) { + if (url.searchParams.get(element.name) !== element.value) { + url.searchParams.set(element.name, element.value); + history.pushState(null, null, url.href); + } + } else if (url.searchParams.has(element.name)) { + url.searchParams.delete(element.name); + history.pushState(null, null, url.href); + } + } + } - pageTemplateSelect.dispatchEvent(changeEvent); + if ( + fieldsetId === 'fragmentsPreview' && + form.elements.collection.value && + form.elements.fragment.value + ) { + const collectionSlug = form.elements.collection.value; + const fragmentSlug = form.elements.fragment.value; + + preview.src = `/fragment-preview?collection=${collectionSlug}&fragment=${fragmentSlug}`; + } else if ( + fieldsetId === 'pageTemplatesPreview' && + form.elements.pageTemplate.value + ) { + const pageTemplateSlug = form.elements.pageTemplate.value; + preview.src = `/fragment-preview?pageTemplate=${pageTemplateSlug}`; } else { - fragmentsPreview.className = ''; - pageTemplatesPreview.className = 'hide'; + preview.src = '/fragment-preview'; + } +} - renderSelect(fragmentSelect, []); +function handleMessage(event) { + projectContent = JSON.parse(event.data); - renderSelect( - collectionSelect, - projectContent.collections.map((collection) => ({ - value: collection.slug, - label: collection.metadata.name, - })) - ); + updateOptions(projectContent.collections, form.elements.collection); + updateOptions(projectContent.pageTemplates, form.elements.pageTemplate); - collectionSelect.dispatchEvent(changeEvent); - } -}); + if (!initialized) { + initialized = true; + preview.src = '/fragment-preview'; -function renderSelect(selectElement, options) { - const selectedOption = new URL(location.href).searchParams.get( - selectElement.id - ); + const url = new URL(window.location.href); - selectElement.innerHTML = ''; + form.elements.fieldsetId.value = + url.searchParams.get('fieldsetId') || form.elements.fieldsetId.value; - options.forEach((option) => { - const optionElement = document.createElement('option'); + form.elements.pageTemplate.value = + url.searchParams.get('pageTemplate') || form.elements.pageTemplate.value; - optionElement.value = option.value; - optionElement.innerHTML = option.label; - optionElement.setAttribute('data-type', option.type); + form.elements.collection.value = + url.searchParams.get('collection') || form.elements.collection.value; - selectElement.appendChild(optionElement); - }); + updateCurrentCollection(); - if (options.length) { - selectElement.removeAttribute('disabled'); - } else { - selectElement.setAttribute('disabled', 'disabled'); - } + form.elements.fragment.value = + url.searchParams.get('fragment') || form.elements.fragment.value; - if (selectedOption) { - selectElement.value = selectedOption; - } else if (options.length === 1) { - selectElement.value = options[0].value; + handleFormChange(); + } else if (preview.src) { + // eslint-disable-next-line no-self-assign + preview.src = preview.src; } - - const changeEvent = new Event('change'); - selectElement.dispatchEvent(changeEvent); } -socket.addEventListener('message', (event) => { - projectContent = JSON.parse(event.data); - - preview.src = '/fragment-preview'; - - renderSelect(previewTypeSelect, [ - { value: 'fragment', label: 'Fragments' }, - { value: 'page-template', label: 'Page Templates' }, - ]); - - if (previewTypeSelect.value === 'fragment') { - renderSelect(fragmentSelect, []); - - renderSelect( - collectionSelect, - projectContent.collections.map((collection) => ({ - value: collection.slug, - label: collection.metadata.name, - })) - ); - } else { - renderSelect( - pageTemplateSelect, - projectContent.pageTemplates.map((pageTemplate) => ({ - value: pageTemplate.slug, - label: pageTemplate.metadata.name, - })) - ); - } -}); +socket.addEventListener('message', handleMessage); +form.addEventListener('change', handleFormChange); diff --git a/src/preview/index.ts b/src/preview/index.ts index 22492b81..cee69937 100644 --- a/src/preview/index.ts +++ b/src/preview/index.ts @@ -36,7 +36,13 @@ export default class extends AuthGenerator { this.logMessage(`Group ID: ${this.getGroupId()}`); this.logMessage(`Preview URL: http://localhost:${DEV_SERVER_PORT}`); - await watchPromise; + try { + await watchPromise; + } catch (error) { + if (error instanceof Error) { + this.logMessage(error.toString(), { level: 'error' }); + } + } } else { this.logMessage( '\nYour Liferay Server cannot generate fragment previews.' + @@ -131,24 +137,23 @@ export default class extends AuthGenerator { pageTemplateId = request.query.pageTemplate?.toString() ?? ''; const projectContent = getProjectContent(this.destinationPath()); - const type = request.query.type; const collection = projectContent.collections.find( (collection) => collection.slug === collectionId ); + let preview = ''; + if (collection) { - const fragment = - type === 'fragment' && - collection.fragments.find((fragment) => fragment.slug === fragmentId); + const fragment = collection.fragments.find( + (fragment) => fragment.slug === fragmentId + ); - const fragmentComposition = - type !== 'fragment' && - collection.fragmentCompositions?.find( - (composition) => composition.slug === fragmentId - ); + const fragmentComposition = collection.fragmentCompositions?.find( + (composition) => composition.slug === fragmentId + ); - if (fragment && type === 'fragment') { + if (fragment) { if (fragment.metadata.type === 'react') { this.logMessage( 'React based fragments do not support preview command (yet)', @@ -156,36 +161,48 @@ export default class extends AuthGenerator { ); } - this._getFragmentPreview( - fragment.css, - fragment.html, - fragment.js, - fragment.configuration - ).then((preview) => { - response.send(this._replaceLinks(preview)); - }); - } else if (fragmentComposition && type === 'composition') { - this._getCompositionPreview( - JSON.parse(fragmentComposition.definitionData) - ).then((preview) => { - response.send(this._replaceLinks(preview)); - }); + try { + preview = await this._getFragmentPreview( + fragment.css, + fragment.html, + fragment.js, + fragment.configuration + ); + } catch (error) { + if (error instanceof Error) { + this.logMessage(error.toString(), { level: 'error' }); + } + } + } else if (fragmentComposition) { + try { + preview = await this._getCompositionPreview( + JSON.parse(fragmentComposition.definitionData) + ); + } catch (error) { + if (error instanceof Error) { + this.logMessage(error.toString(), { level: 'error' }); + } + } } - } else if (pageTemplateId && type === 'page-template') { + } else if (pageTemplateId) { const pageTemplate = projectContent.pageTemplates?.find( (pageTemplate) => pageTemplate.slug === pageTemplateId ); if (pageTemplate) { - this._getPageTemplatePreview( - JSON.parse(pageTemplate.definitionData) - ).then((preview) => { - response.send(this._replaceLinks(preview as string)); - }); + try { + preview = await this._getPageTemplatePreview( + JSON.parse(pageTemplate.definitionData) + ); + } catch (error) { + if (error instanceof Error) { + this.logMessage(error.toString(), { level: 'error' }); + } + } } - } else { - response.send(''); } + + response.send(this._replaceLinks(preview)); }); app.get('/preview-constants.js', (request, response) => {