Merge pull request #11313 from MoonE/examples-add-tag-count
Add direct link to other examples to tags
This commit is contained in:
@@ -43,15 +43,15 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
words.forEach(function (word) {
|
words.forEach(function (word) {
|
||||||
const dict = info.index[word];
|
const dict = info.wordIndex[word];
|
||||||
if (dict) {
|
if (dict) {
|
||||||
updateScores(dict, word);
|
updateScores(dict, word);
|
||||||
} else {
|
} else {
|
||||||
const r = new RegExp(word);
|
const r = new RegExp(word);
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
for (let idx in info.index) {
|
for (let idx in info.wordIndex) {
|
||||||
if (r.test(idx)) {
|
if (r.test(idx)) {
|
||||||
updateScores(info.index[idx], word);
|
updateScores(info.wordIndex[idx], word);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,35 @@ a:hover, a:focus, footer a:hover, footer a:focus {
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.badge-group {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.badge-group > .badge:not(:last-child) {
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
.badge-group > .badge:not(:first-child) {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
.tag-modal-toggle {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.modal-tag-example .modal-body {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.modal-tag-example .list-group-item:focus,
|
||||||
|
.modal-tag-example .list-group-item:hover,
|
||||||
|
.modal-tag-example .list-group-item:active {
|
||||||
|
background-color: rgba(31, 107, 117, .6875);
|
||||||
|
border-color: #1F6B75;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.modal-tag-example .list-group-item.active {
|
||||||
|
background-color: #1F6B75;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
#docs {
|
#docs {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,9 +113,36 @@
|
|||||||
</h4>
|
</h4>
|
||||||
<p class="tags">
|
<p class="tags">
|
||||||
{{#each tags}}
|
{{#each tags}}
|
||||||
<a href="./index.html?q={{.}}" class="badge badge-info">{{.}}</a>
|
<span class="badge-group">
|
||||||
|
<a
|
||||||
|
href="./index.html?q={{ ./tag }}" class="badge badge-info">{{ ./tag }}</a
|
||||||
|
><a
|
||||||
|
class="badge badge-info tag-modal-toggle text-white"
|
||||||
|
data-toggle="modal"
|
||||||
|
data-target="#tag-example-list"
|
||||||
|
data-title="{{ ./tag }}"
|
||||||
|
data-content="{{#each ./examples}}
|
||||||
|
<a class="list-group-item list-group-item-action{{#if ./isCurrent}} active{{/if}}" href="./{{ ./link }}">{{escape ./title}}</a>{{/each}}"
|
||||||
|
tabindex="0"
|
||||||
|
>{{ ./examples.length }}</a>
|
||||||
|
</span>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</p>
|
</p>
|
||||||
|
<div class="modal modal-tag-example" id="tag-example-list" tabindex="-1" role="dialog" aria-labelledby="tag-example-title" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="tag-example-title"></h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="list-group"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{{{ contents }}}
|
{{{ contents }}}
|
||||||
</div>
|
</div>
|
||||||
<form method="POST" id="codepen-form" target="_blank" action="https://codesandbox.io/api/v1/sandboxes/define">
|
<form method="POST" id="codepen-form" target="_blank" action="https://codesandbox.io/api/v1/sandboxes/define">
|
||||||
@@ -186,6 +213,16 @@
|
|||||||
<script src="./resources/common.js"></script>
|
<script src="./resources/common.js"></script>
|
||||||
<script src="./resources/prism/prism.min.js"></script>
|
<script src="./resources/prism/prism.min.js"></script>
|
||||||
{{{ js.tag }}}
|
{{{ js.tag }}}
|
||||||
|
<script>
|
||||||
|
$('#tag-example-list').on('show.bs.modal', function (event) {
|
||||||
|
const button = $(event.relatedTarget); // Button that triggered the modal
|
||||||
|
const title = button.data('title');
|
||||||
|
const content = button.data('content');
|
||||||
|
const modal = $(this)
|
||||||
|
modal.find('.modal-title').text(title);
|
||||||
|
modal.find('.modal-body').html(content);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
<script>
|
<script>
|
||||||
var packageUrl = 'https://raw.githubusercontent.com/openlayers/openlayers.github.io/build/package.json';
|
var packageUrl = 'https://raw.githubusercontent.com/openlayers/openlayers.github.io/build/package.json';
|
||||||
|
|||||||
@@ -18,6 +18,13 @@ handlebars.registerHelper(
|
|||||||
(str) => new handlebars.SafeString(marked(str))
|
(str) => new handlebars.SafeString(marked(str))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to doube-escape the title when stored as data-* attribute.
|
||||||
|
*/
|
||||||
|
handlebars.registerHelper('escape', (text) => {
|
||||||
|
return handlebars.Utils.escapeExpression(text);
|
||||||
|
});
|
||||||
|
|
||||||
handlebars.registerHelper('indent', (text, options) => {
|
handlebars.registerHelper('indent', (text, options) => {
|
||||||
if (!text) {
|
if (!text) {
|
||||||
return text;
|
return text;
|
||||||
@@ -30,6 +37,43 @@ handlebars.registerHelper('indent', (text, options) => {
|
|||||||
.join('\n');
|
.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
|
* Create an inverted index of keywords from examples. Property names are
|
||||||
* lowercased words. Property values are objects mapping example index to word
|
* lowercased words. Property values are objects mapping example index to word
|
||||||
@@ -140,59 +184,76 @@ class ExampleBuilder {
|
|||||||
.chunks.filter((chunk) => chunk.names[0] !== this.common);
|
.chunks.filter((chunk) => chunk.names[0] !== this.common);
|
||||||
|
|
||||||
const exampleData = [];
|
const exampleData = [];
|
||||||
const uniqueTags = new Set();
|
await Promise.all(
|
||||||
const promises = chunks.map(async (chunk) => {
|
chunks.map(async (chunk) => {
|
||||||
const [assets, data] = await this.render(compiler.context, chunk);
|
const data = await this.readHtml(compiler.context, chunk);
|
||||||
|
exampleData.push(data);
|
||||||
// collect tags for main page... TODO: implement index tag links
|
})
|
||||||
data.tags.forEach((tag) => uniqueTags.add(tag));
|
);
|
||||||
|
const examples = exampleData.map((data) => {
|
||||||
exampleData.push({
|
return {
|
||||||
link: data.filename,
|
link: data.filename,
|
||||||
example: data.filename,
|
example: data.filename,
|
||||||
title: data.title,
|
title: data.title,
|
||||||
shortdesc: data.shortdesc,
|
shortdesc: data.shortdesc,
|
||||||
tags: data.tags,
|
tags: data.tags,
|
||||||
});
|
};
|
||||||
|
|
||||||
for (const file in assets) {
|
|
||||||
compilation.assets[file] = new RawSource(assets[file]);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(promises);
|
examples.sort((a, b) =>
|
||||||
|
|
||||||
exampleData.sort((a, b) =>
|
|
||||||
a.title.localeCompare(b.title, 'en', {sensitivity: 'base'})
|
a.title.localeCompare(b.title, 'en', {sensitivity: 'base'})
|
||||||
);
|
);
|
||||||
|
const tagIndex = createTagIndex(examples);
|
||||||
const info = {
|
const info = {
|
||||||
examples: exampleData,
|
examples: examples,
|
||||||
index: createWordIndex(exampleData),
|
// Tags for main page... TODO: implement index tag links
|
||||||
tags: Array.from(uniqueTags)
|
// tagIndex: sortObjectByKey(tagIndex),
|
||||||
.sort() // sort twice to get predictable, case insensitve order
|
wordIndex: sortObjectByKey(createWordIndex(examples)),
|
||||||
.sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'})),
|
|
||||||
};
|
};
|
||||||
|
exampleData.forEach((data) => {
|
||||||
|
data.tags = data.tags.map((tag) => {
|
||||||
|
const tagExamples = tagIndex[tag.toLowerCase()];
|
||||||
|
return {
|
||||||
|
tag: tag,
|
||||||
|
examples: tagExamples.map((exampleIdx) => {
|
||||||
|
const example = examples[exampleIdx];
|
||||||
|
return {
|
||||||
|
link: example.link,
|
||||||
|
title: example.title,
|
||||||
|
isCurrent: data.filename === example.link,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
const indexSource = `const info = ${JSON.stringify(info)};`;
|
const indexSource = `const info = ${JSON.stringify(info)};`;
|
||||||
compilation.assets['examples-info.js'] = new RawSource(indexSource);
|
compilation.assets['examples-info.js'] = new RawSource(indexSource);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async render(dir, chunk) {
|
async readHtml(dir, chunk) {
|
||||||
const name = chunk.names[0];
|
const name = chunk.names[0];
|
||||||
|
|
||||||
const assets = {};
|
|
||||||
const readOptions = {encoding: 'utf8'};
|
|
||||||
|
|
||||||
const htmlName = `${name}.html`;
|
const htmlName = `${name}.html`;
|
||||||
const htmlPath = path.join(dir, htmlName);
|
const htmlPath = path.join(dir, htmlName);
|
||||||
const htmlSource = await readFile(htmlPath, readOptions);
|
const htmlSource = await readFile(htmlPath, {encoding: 'utf8'});
|
||||||
|
|
||||||
const {attributes, body} = frontMatter(htmlSource);
|
const {attributes, body} = frontMatter(htmlSource);
|
||||||
|
assert(!!attributes.layout, `missing layout in ${htmlPath}`);
|
||||||
const data = Object.assign(attributes, {contents: body});
|
const data = Object.assign(attributes, {contents: body});
|
||||||
|
|
||||||
data.olVersion = pkg.version;
|
data.olVersion = pkg.version;
|
||||||
data.filename = htmlName;
|
data.filename = htmlName;
|
||||||
|
data.dir = dir;
|
||||||
|
data.chunk = chunk;
|
||||||
|
|
||||||
// process tags
|
// process tags
|
||||||
if (data.tags) {
|
if (data.tags) {
|
||||||
@@ -200,6 +261,14 @@ class ExampleBuilder {
|
|||||||
} else {
|
} else {
|
||||||
data.tags = [];
|
data.tags = [];
|
||||||
}
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async render(data, chunk) {
|
||||||
|
const name = chunk.names[0];
|
||||||
|
|
||||||
|
const assets = {};
|
||||||
|
const readOptions = {encoding: 'utf8'};
|
||||||
|
|
||||||
// add in script tag
|
// add in script tag
|
||||||
const jsName = `${name}.js`;
|
const jsName = `${name}.js`;
|
||||||
@@ -234,7 +303,7 @@ class ExampleBuilder {
|
|||||||
|
|
||||||
// check for worker js
|
// check for worker js
|
||||||
const workerName = `${name}.worker.js`;
|
const workerName = `${name}.worker.js`;
|
||||||
const workerPath = path.join(dir, workerName);
|
const workerPath = path.join(data.dir, workerName);
|
||||||
let workerSource;
|
let workerSource;
|
||||||
try {
|
try {
|
||||||
workerSource = await readFile(workerPath, readOptions);
|
workerSource = await readFile(workerPath, readOptions);
|
||||||
@@ -280,7 +349,7 @@ class ExampleBuilder {
|
|||||||
|
|
||||||
// check for example css
|
// check for example css
|
||||||
const cssName = `${name}.css`;
|
const cssName = `${name}.css`;
|
||||||
const cssPath = path.join(dir, cssName);
|
const cssPath = path.join(data.dir, cssName);
|
||||||
let cssSource;
|
let cssSource;
|
||||||
try {
|
try {
|
||||||
cssSource = await readFile(cssPath, readOptions);
|
cssSource = await readFile(cssPath, readOptions);
|
||||||
@@ -321,7 +390,7 @@ class ExampleBuilder {
|
|||||||
'Invalid value for resource: ' +
|
'Invalid value for resource: ' +
|
||||||
resource +
|
resource +
|
||||||
' is not .js or .css: ' +
|
' is not .js or .css: ' +
|
||||||
htmlName
|
data.filename
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -334,12 +403,11 @@ class ExampleBuilder {
|
|||||||
: '';
|
: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!!attributes.layout, `missing layout in ${htmlPath}`);
|
const templatePath = path.join(this.templates, data.layout);
|
||||||
const templatePath = path.join(this.templates, attributes.layout);
|
|
||||||
const templateSource = await readFile(templatePath, readOptions);
|
const templateSource = await readFile(templatePath, readOptions);
|
||||||
|
|
||||||
assets[htmlName] = handlebars.compile(templateSource)(data);
|
assets[data.filename] = handlebars.compile(templateSource)(data);
|
||||||
return [assets, data];
|
return assets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user