Add number of occurence to tags
It can be a bit frustrating to click on a tag only to realise it was the only example with that tag.
This commit is contained in:
@@ -43,15 +43,15 @@
|
||||
}
|
||||
};
|
||||
words.forEach(function (word) {
|
||||
const dict = info.index[word];
|
||||
const dict = info.wordIndex[word];
|
||||
if (dict) {
|
||||
updateScores(dict, word);
|
||||
} else {
|
||||
const r = new RegExp(word);
|
||||
// eslint-disable-next-line prefer-const
|
||||
for (let idx in info.index) {
|
||||
for (let idx in info.wordIndex) {
|
||||
if (r.test(idx)) {
|
||||
updateScores(info.index[idx], word);
|
||||
updateScores(info.wordIndex[idx], word);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
</h4>
|
||||
<p class="tags">
|
||||
{{#each tags}}
|
||||
<a href="./index.html?q={{.}}" class="badge badge-info">{{.}}</a>
|
||||
<a href="./index.html?q={{./tag}}" class="badge badge-info">{{ ./tag }} ({{ ./amount }})</a>
|
||||
{{/each}}
|
||||
</p>
|
||||
{{{ contents }}}
|
||||
|
||||
@@ -30,6 +30,43 @@ handlebars.registerHelper('indent', (text, options) => {
|
||||
.join('\n');
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the object with the keys inserted in alphabetic order.
|
||||
* When exporting with `JSON.stringify(obj)` the keys are sorted.
|
||||
* @param {Object<string, *>} obj Any object
|
||||
* @return {Object<string, *>} New object
|
||||
*/
|
||||
function sortObjectByKey(obj) {
|
||||
return Object.keys(obj)
|
||||
.sort() // sort twice to get predictable, case insensitve order
|
||||
.sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'}))
|
||||
.reduce((idx, tag) => {
|
||||
idx[tag] = obj[tag];
|
||||
return idx;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an index of tags belonging to examples
|
||||
* @param {Array<Object>} exampleData Array of example data objects.
|
||||
* @return {Object} Word index.
|
||||
*/
|
||||
function createTagIndex(exampleData) {
|
||||
const index = {};
|
||||
exampleData.forEach((data, i) => {
|
||||
data.tags.forEach((tag) => {
|
||||
tag = tag.toLowerCase();
|
||||
let tagIndex = index[tag];
|
||||
if (!tagIndex) {
|
||||
tagIndex = [];
|
||||
index[tag] = tagIndex;
|
||||
}
|
||||
tagIndex.push(i);
|
||||
});
|
||||
});
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an inverted index of keywords from examples. Property names are
|
||||
* lowercased words. Property values are objects mapping example index to word
|
||||
@@ -140,59 +177,68 @@ class ExampleBuilder {
|
||||
.chunks.filter((chunk) => chunk.names[0] !== this.common);
|
||||
|
||||
const exampleData = [];
|
||||
const uniqueTags = new Set();
|
||||
const promises = chunks.map(async (chunk) => {
|
||||
const [assets, data] = await this.render(compiler.context, chunk);
|
||||
|
||||
// collect tags for main page... TODO: implement index tag links
|
||||
data.tags.forEach((tag) => uniqueTags.add(tag));
|
||||
|
||||
exampleData.push({
|
||||
await Promise.all(
|
||||
chunks.map(async (chunk) => {
|
||||
const data = await this.readHtml(compiler.context, chunk);
|
||||
exampleData.push(data);
|
||||
})
|
||||
);
|
||||
const examples = exampleData.map((data) => {
|
||||
return {
|
||||
link: data.filename,
|
||||
example: data.filename,
|
||||
title: data.title,
|
||||
shortdesc: data.shortdesc,
|
||||
tags: data.tags,
|
||||
};
|
||||
});
|
||||
|
||||
examples.sort((a, b) =>
|
||||
a.title.localeCompare(b.title, 'en', {sensitivity: 'base'})
|
||||
);
|
||||
const tagIndex = createTagIndex(examples);
|
||||
const info = {
|
||||
examples: examples,
|
||||
// Tags for main page... TODO: implement index tag links
|
||||
// tagIndex: sortObjectByKey(tagIndex),
|
||||
wordIndex: sortObjectByKey(createWordIndex(examples)),
|
||||
};
|
||||
exampleData.forEach((data) => {
|
||||
data.tags = data.tags.map((tag) => {
|
||||
return {
|
||||
tag: tag,
|
||||
amount: tagIndex[tag.toLowerCase()].length,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
exampleData.map(async (data) => {
|
||||
const assets = await this.render(data, data.chunk);
|
||||
for (const file in assets) {
|
||||
compilation.assets[file] = new RawSource(assets[file]);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
exampleData.sort((a, b) =>
|
||||
a.title.localeCompare(b.title, 'en', {sensitivity: 'base'})
|
||||
})
|
||||
);
|
||||
const info = {
|
||||
examples: exampleData,
|
||||
index: createWordIndex(exampleData),
|
||||
tags: Array.from(uniqueTags)
|
||||
.sort() // sort twice to get predictable, case insensitve order
|
||||
.sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'})),
|
||||
};
|
||||
|
||||
const indexSource = `const info = ${JSON.stringify(info)};`;
|
||||
compilation.assets['examples-info.js'] = new RawSource(indexSource);
|
||||
});
|
||||
}
|
||||
|
||||
async render(dir, chunk) {
|
||||
async readHtml(dir, chunk) {
|
||||
const name = chunk.names[0];
|
||||
|
||||
const assets = {};
|
||||
const readOptions = {encoding: 'utf8'};
|
||||
|
||||
const htmlName = `${name}.html`;
|
||||
const htmlPath = path.join(dir, htmlName);
|
||||
const htmlSource = await readFile(htmlPath, readOptions);
|
||||
const htmlSource = await readFile(htmlPath, {encoding: 'utf8'});
|
||||
|
||||
const {attributes, body} = frontMatter(htmlSource);
|
||||
assert(!!attributes.layout, `missing layout in ${htmlPath}`);
|
||||
const data = Object.assign(attributes, {contents: body});
|
||||
|
||||
data.olVersion = pkg.version;
|
||||
data.filename = htmlName;
|
||||
data.dir = dir;
|
||||
data.chunk = chunk;
|
||||
|
||||
// process tags
|
||||
if (data.tags) {
|
||||
@@ -200,6 +246,14 @@ class ExampleBuilder {
|
||||
} else {
|
||||
data.tags = [];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
async render(data, chunk) {
|
||||
const name = chunk.names[0];
|
||||
|
||||
const assets = {};
|
||||
const readOptions = {encoding: 'utf8'};
|
||||
|
||||
// add in script tag
|
||||
const jsName = `${name}.js`;
|
||||
@@ -234,7 +288,7 @@ class ExampleBuilder {
|
||||
|
||||
// check for worker js
|
||||
const workerName = `${name}.worker.js`;
|
||||
const workerPath = path.join(dir, workerName);
|
||||
const workerPath = path.join(data.dir, workerName);
|
||||
let workerSource;
|
||||
try {
|
||||
workerSource = await readFile(workerPath, readOptions);
|
||||
@@ -280,7 +334,7 @@ class ExampleBuilder {
|
||||
|
||||
// check for example css
|
||||
const cssName = `${name}.css`;
|
||||
const cssPath = path.join(dir, cssName);
|
||||
const cssPath = path.join(data.dir, cssName);
|
||||
let cssSource;
|
||||
try {
|
||||
cssSource = await readFile(cssPath, readOptions);
|
||||
@@ -321,7 +375,7 @@ class ExampleBuilder {
|
||||
'Invalid value for resource: ' +
|
||||
resource +
|
||||
' is not .js or .css: ' +
|
||||
htmlName
|
||||
data.filename
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -334,12 +388,11 @@ class ExampleBuilder {
|
||||
: '';
|
||||
}
|
||||
|
||||
assert(!!attributes.layout, `missing layout in ${htmlPath}`);
|
||||
const templatePath = path.join(this.templates, attributes.layout);
|
||||
const templatePath = path.join(this.templates, data.layout);
|
||||
const templateSource = await readFile(templatePath, readOptions);
|
||||
|
||||
assets[htmlName] = handlebars.compile(templateSource)(data);
|
||||
return [assets, data];
|
||||
assets[data.filename] = handlebars.compile(templateSource)(data);
|
||||
return assets;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user