diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/content/dependencies/generatePageLayout.js | 53 | ||||
| -rw-r--r-- | src/content/dependencies/generatePageTitleText.js | 67 | ||||
| -rw-r--r-- | src/content/dependencies/generateSearchSidebarBox.js | 5 | ||||
| -rw-r--r-- | src/data/things/track.js | 2 | ||||
| -rw-r--r-- | src/data/yaml.js | 52 | ||||
| -rw-r--r-- | src/static/css/site.css | 6 | ||||
| -rw-r--r-- | src/static/js/client/sidebar-search.js | 74 | ||||
| -rw-r--r-- | src/strings-default.yaml | 2 |
8 files changed, 200 insertions, 61 deletions
diff --git a/src/content/dependencies/generatePageLayout.js b/src/content/dependencies/generatePageLayout.js index 23d5932d..fec3bd78 100644 --- a/src/content/dependencies/generatePageLayout.js +++ b/src/content/dependencies/generatePageLayout.js @@ -1,5 +1,3 @@ -import striptags from 'striptags'; - import {openAggregate} from '#aggregate'; import {atOffset, empty, repeat} from '#sugar'; @@ -27,6 +25,9 @@ export default { relations.stickyHeadingContainer = relation('generateStickyHeadingContainer'); + relations.titleText = + relation('generatePageTitleText'); + relations.sidebar = relation('generatePageSidebar'); @@ -629,6 +630,12 @@ export default { footerHTML, ]; + relations.titleText.setSlots({ + title: slots.title, + showWikiNameInTitle: slots.showWikiNameInTitle, + subtitle: slots.subtitle, + }); + const pageHTML = html.tags([ `<!DOCTYPE html>`, html.tag('html', @@ -653,44 +660,12 @@ export default { html.tag('head', [ html.tag('title', - language.encapsulate('misc.pageTitle', workingCapsule => { - const workingOptions = {}; - - // Slightly jank: The output of striptags is, of course, a string, - // and as far as language.formatString() is concerned, that means - // it needs to be sanitized - including turning ampersands into - // &'s. But the title is already HTML that has implicitly been - // sanitized, however it got here, and includes HTML entities that - // are properly escaped. Those need to get included as they are, - // so we wrap the title in a tag and pass it off as good to go. - workingOptions.title = - html.tags([ - striptags(slots.title.toString()), - ]); - - if (!html.isBlank(slots.subtitle)) { - // Same shenanigans here, as far as wrapping striptags goes. - workingCapsule += '.withSubtitle'; - workingOptions.subtitle = - html.tags([ - striptags(slots.subtitle.toString()), - ]); - } + {'data-without-wiki-name': + relations.titleText.clone() + .slot('showWikiNameInTitle', false) + .toString()}, - const showWikiName = - (slots.showWikiNameInTitle === true - ? true - : slots.showWikiNameInTitle === 'auto' - ? html.isBlank(slots.subtitle) - : false); - - if (showWikiName) { - workingCapsule += '.withWikiName'; - workingOptions.wikiName = data.wikiName; - } - - return language.$(workingCapsule, workingOptions); - })), + relations.titleText), html.tag('meta', {charset: 'utf-8'}), html.tag('meta', { diff --git a/src/content/dependencies/generatePageTitleText.js b/src/content/dependencies/generatePageTitleText.js new file mode 100644 index 00000000..5482ca91 --- /dev/null +++ b/src/content/dependencies/generatePageTitleText.js @@ -0,0 +1,67 @@ +import striptags from 'striptags'; + +export default { + sprawl: ({wikiInfo}) => ({wikiInfo}), + + data: (sprawl) => ({ + wikiName: + sprawl.wikiInfo.nameShort, + }), + + slots: { + title: { + type: 'html', + mutable: false, + }, + + showWikiNameInTitle: { + validate: v => v.is(true, false, 'auto'), + default: 'auto', + }, + + subtitle: { + type: 'html', + mutable: false, + }, + }, + + generate: (data, slots, {html, language}) => + language.encapsulate('misc.pageTitle', workingCapsule => { + const workingOptions = {}; + + // Slightly jank: The output of striptags is, of course, a string, + // and as far as language.formatString() is concerned, that means + // it needs to be sanitized - including turning ampersands into + // &'s. But the title is already HTML that has implicitly been + // sanitized, however it got here, and includes HTML entities that + // are properly escaped. Those need to get included as they are, + // so we wrap the title in a tag and pass it off as good to go. + workingOptions.title = + html.tags([ +striptags(slots.title.toString()), + ]); + + if (!html.isBlank(slots.subtitle)) { + // Same shenanigans here, as far as wrapping striptags goes. + workingCapsule += '.withSubtitle'; + workingOptions.subtitle = + html.tags([ + striptags(slots.subtitle.toString()), + ]); + } + + const showWikiName = + (slots.showWikiNameInTitle === true +? true + : slots.showWikiNameInTitle === 'auto' +? html.isBlank(slots.subtitle) +: false); + + if (showWikiName) { + workingCapsule += '.withWikiName'; + workingOptions.wikiName = data.wikiName; + } + + return language.$(workingCapsule, workingOptions); + }), +}; \ No newline at end of file diff --git a/src/content/dependencies/generateSearchSidebarBox.js b/src/content/dependencies/generateSearchSidebarBox.js index 701a01ac..0d760773 100644 --- a/src/content/dependencies/generateSearchSidebarBox.js +++ b/src/content/dependencies/generateSearchSidebarBox.js @@ -35,6 +35,11 @@ export default { html.tag('template', {class: 'wiki-search-no-results-string'}, language.$(capsule, 'noResults')), + html.tag('template', {class: 'wiki-search-back-string'}, + language.$(capsule, 'back', { + page: html.tag('slot', {name: 'page'}), + })), + html.tag('template', {class: 'wiki-search-current-result-string'}, language.$(capsule, 'currentResult')), diff --git a/src/data/things/track.js b/src/data/things/track.js index 1095cce9..ab7511a8 100644 --- a/src/data/things/track.js +++ b/src/data/things/track.js @@ -1241,7 +1241,7 @@ export class Track extends Thing { tracksWhichAreFollowingProductionsOf: { bindTo: 'trackData', - referencing: track => track, + referencing: track => track.isMainRelease ? [track] : [], referenced: track => track.previousProductionTracks, }, }; diff --git a/src/data/yaml.js b/src/data/yaml.js index 4ba766c4..fbb4e5d6 100644 --- a/src/data/yaml.js +++ b/src/data/yaml.js @@ -44,28 +44,38 @@ function inspect(value, opts = {}) { return nodeInspect(value, {colors: ENABLE_COLOR, ...opts}); } -function pushWikiData(a, b) { - for (const key of Object.keys(b)) { - if (Object.hasOwn(a, key)) { - if (Array.isArray(a[key])) { - if (Array.isArray(b[key])) { - a[key].push(...b[key]); - } else { - throw new Error(`${key} already present, expected array of items to push`); - } +function makeEmptyWikiData() { + const wikiData = {}; + + for (const thingConstructor of Object.values(thingConstructors)) { + if (thingConstructor[Thing.wikiData]) { + if (thingConstructor[Thing.oneInstancePerWiki]) { + wikiData[thingConstructor[Thing.wikiData]] = null; } else { - if (Array.isArray(a[key])) { - throw new Error(`${key} already present and not an array, refusing to overwrite`); - } else { - throw new Error(`${key} already present, refusing to overwrite`); - } + wikiData[thingConstructor[Thing.wikiData]] = []; } - } else { + } + } + + return wikiData; +} + +function pushWikiData(a, b) { + for (const key of Object.keys(b)) { + if (!Object.hasOwn(a, key)) { + throw new Error(`${key} not present`); + } + + if (Array.isArray(a[key])) { if (Array.isArray(b[key])) { - a[key] = [...b[key]]; + a[key].push(...b[key]); } else { - a[key] = b[key]; + throw new Error(`${key} is an array, expected array of items to push`); } + } else if (a[key] === null) { + a[key] = b[key]; + } else if (b[key] !== null) { + throw new Error(`${key} already has a value: ${inspect(a[key])}`); } } } @@ -187,7 +197,7 @@ function makeProcessDocument(thingConstructor, { const thing = Reflect.construct(thingConstructor, []); - const wikiData = {}; + const wikiData = makeEmptyWikiData(); const flat = [thing]; if (thingConstructor[Thing.wikiData]) { if (thingConstructor[Thing.oneInstancePerWiki]) { @@ -1357,7 +1367,7 @@ export function processThingsFromDataStep(documents, dataStep) { case documentModes.allTogether: { const things = []; const flat = []; - const wikiData = {}; + const wikiData = makeEmptyWikiData(); const aggregate = openAggregate({message: `Errors processing documents`}); documents.forEach( @@ -1417,7 +1427,7 @@ export function processThingsFromDataStep(documents, dataStep) { throw new Error(`Missing header document (empty file or erroneously starting with "---"?)`); const aggregate = openAggregate({message: `Errors processing documents`}); - const wikiData = {}; + const wikiData = makeEmptyWikiData(); const {result: headerResult, aggregate: headerAggregate} = processDocument(headerDocument, dataStep.headerDocumentThing); @@ -1688,7 +1698,7 @@ export function connectThingsFromDataSteps(processThingResultLists, dataSteps) { } export function makeWikiDataFromDataSteps(processThingResultLists, _dataSteps) { - const wikiData = {}; + const wikiData = makeEmptyWikiData(); for (const result of processThingResultLists.flat(2)) { pushWikiData(wikiData, result.wikiData); diff --git a/src/static/css/site.css b/src/static/css/site.css index e1654e6d..73b732d8 100644 --- a/src/static/css/site.css +++ b/src/static/css/site.css @@ -750,6 +750,12 @@ summary.underline-white > span:hover a:not(:hover) { margin: 0; } +.wiki-search-context-container { + padding: 2px 12px 4px; + font-size: 0.9em; + border-bottom: 1px solid var(--dim-color); +} + .wiki-search-results-container { margin-bottom: 0; padding: 2px; diff --git a/src/static/js/client/sidebar-search.js b/src/static/js/client/sidebar-search.js index b4356a0f..d63b9708 100644 --- a/src/static/js/client/sidebar-search.js +++ b/src/static/js/client/sidebar-search.js @@ -39,6 +39,9 @@ export const info = { failedRule: null, failedContainer: null, + contextContainer: null, + contextBackLink: null, + filterContainer: null, albumFilterLink: null, artistFilterLink: null, @@ -66,6 +69,8 @@ export const info = { currentResultString: null, endSearchString: null, + backString: null, + albumResultKindString: null, artistResultKindString: null, groupResultKindString: null, @@ -109,6 +114,10 @@ export const info = { type: 'string', }, + activeQueryContextPageName: {type: 'string'}, + activeQueryContextPagePathname: {type: 'string'}, + activeQueryContextPageColor: {type: 'string'}, + activeQueryResults: { type: 'json', maxLength: settings => settings.maxActiveResultsStorage, @@ -180,6 +189,9 @@ export function getPageReferences() { info.noResultsString = findString('no-results'); + info.backString = + findString('back'); + info.currentResultString = findString('current-result'); @@ -313,6 +325,25 @@ export function mutatePageContent() { info.searchBox.appendChild(info.failedRule); info.searchBox.appendChild(info.failedContainer); + // Context section + + info.contextContainer = + document.createElement('div'); + + info.contextContainer.classList.add('wiki-search-context-container'); + + info.contextBackLink = + document.createElement('a'); + + info.contextContainer.appendChild( + templateContent(info.backString, { + page: info.contextBackLink, + })); + + cssProp(info.contextContainer, 'display', 'none'); + + info.searchBox.appendChild(info.contextContainer); + // Filter section info.filterContainer = @@ -652,6 +683,17 @@ async function activateSidebarSearch(query) { state.searchStage = 'complete'; updateSidebarSearchStatus(); + session.activeQueryContextPageName = + document.querySelector('title').dataset.withoutWikiName ?? + document.title; + + session.activeQueryContextPagePathname = + location.pathname; + + session.activeQueryContextPageColor = + document.querySelector('.color-style')?.dataset.color ?? + null; + session.activeQuery = query; session.activeQueryResults = results; session.resultsScrollOffset = 0; @@ -804,6 +846,8 @@ function showSidebarSearchResults(results) { } if (shownAnyResults) { + showContextControls(); + cssProp(info.endSearchRule, 'display', 'block'); cssProp(info.endSearchLine, 'display', 'block'); @@ -901,6 +945,35 @@ function showFilterElements(results) { } } +function showContextControls() { + const {session} = info; + + const shouldShow = + session.activeQueryContextPagePathname && + location.pathname !== session.activeQueryContextPagePathname; + + if (shouldShow) { + info.contextBackLink.href = + session.activeQueryContextPagePathname; + + cssProp(info.contextBackLink, + '--primary-color', + session.activeQueryContextPageColor); + + while (info.contextBackLink.firstChild) { + info.contextBackLink.firstChild.remove(); + } + + info.contextBackLink.appendChild( + document.createTextNode( + session.activeQueryContextPageName)); + + cssProp(info.contextContainer, 'display', 'block'); + } else { + cssProp(info.contextContainer, 'display', 'none'); + } +} + function generateSidebarSearchResult(result, results) { const preparedSlots = { color: @@ -1126,6 +1199,7 @@ function generateSidebarSearchResultTemplate(slots) { } function hideSidebarSearchResults() { + cssProp(info.contextContainer, 'display', 'none'); cssProp(info.filterContainer, 'display', 'none'); cssProp(info.resultsRule, 'display', 'none'); diff --git a/src/strings-default.yaml b/src/strings-default.yaml index 233a7ed3..dca17bbd 100644 --- a/src/strings-default.yaml +++ b/src/strings-default.yaml @@ -834,6 +834,8 @@ misc: No results for this query, sorry! Check spelling and use complete words. + back: "Return to: {PAGE}" + currentResult: "(you are here)" endSearch: "(OK, I'm done searching now.)" |