Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLDR-17803 Navigation for markdown #4019

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ jobs:
bundler-cache: true
- name: Setup Jekyll
run: 'gem install bundler jekyll kramdown-parser-gfm webrick'
- name: Setup assets
run: 'cd docs/site/assets && npm ci && npm run build'
- name: Build cldr.pages.dev
run: 'cd docs/site && jekyll build'
- name: Pre-install Wrangler
Expand Down
2 changes: 2 additions & 0 deletions docs/site/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/node_modules
/assets/json
3 changes: 3 additions & 0 deletions docs/site/_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ defaults:
values:
layout: page

include:
- node_modules

18 changes: 10 additions & 8 deletions docs/site/_layouts/page.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,25 @@
<div class="icon"><a href="http://www.unicode.org/"> <img border="0"
src="http://www.unicode.org/webscripts/logo60s2.gif" align="middle" alt="[Unicode]" width="34"
height="33"></a>&nbsp;&nbsp;
<a class="bar" href="/">
<font size="3">
CLDR Site / {{page.title}}
</font>
</a>

<div id="nav" class="nav">
<!-- Vue mount here -->
</div>
</div>
<div class="bar"><a href="http://www.unicode.org" class="bar">Home</a>
| <a href="http://www.unicode.org/sitemap/" class="bar">Site
Map</a> | <a href="http://www.unicode.org/search/" class="bar">Search</a></div>
<!-- <div class="bar"><a href="http://www.unicode.org" class="bar">Home</a>
| <a href="http://www.unicode.org/search/" class="bar">Search</a></div> -->
</header>



<section class="body">
{{ content }}
</section>
<footer>
© 1991-2024 Unicode, Inc. Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. See <a href="https://www.unicode.org/copyright.html">Terms of Use</a>.
</footer>
<script src="/node_modules/vue/dist/vue.global.prod.js"></script>
<script src="/assets/js/cldrsite.js"></script>
</body>

</html>
13 changes: 13 additions & 0 deletions docs/site/assets/css/page.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ header > div.icon {
flex-grow: 1;
}

header .title {
color: white;
font-size: 1.5em;
}

header .nav, header .nav > div {
display: inline;
}

header .nav a, header .nav .title {
color: white;
}

footer {
width: 100%;
margin-left: auto;
Expand Down
60 changes: 60 additions & 0 deletions docs/site/assets/js/build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// extract site frontmatter, save to json

import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { default as process } from 'node:process';
import { default as matter } from 'gray-matter';


const SKIP_THESE = /(node_modules|\.jekyll-cache)/;


async function processFile(d, fullPath, out) {
const f = await fs.readFile(fullPath, 'utf-8');
const m = matter(f);
if (m && m.data) {
const { data } = m;
out.all.push({ ...data, fullPath });
} else {
out.app.push({ fullPath }); // synthesize data?
}
}

/** process one dirent */
async function processEntry(d, out, e) {
const fullpath = path.join(d, e.name);
if (SKIP_THESE.test(e.name)) return;
if (e.isDirectory()) {
return await traverse(fullpath, out);
} else if(!e.isFile() || !/\.md$/.test(e.name)) {
return;
}
await processFile(d, fullpath, out);
}

/**
* @param {string} d path to directory
* @param {object} out output struct
*/
async function traverse(d, out) {
const dirents = await fs.readdir(d, { withFileTypes: true });
const promises = dirents.map(e => processEntry(d, out, e));
return Promise.all(promises);
}

async function main() {
const out = {
all: [],
dirs: {},
};
await traverse('.', out);
await fs.writeFile('assets/json/tree.json', JSON.stringify(out, null, ' '));
}

main().then(() => console.log('Done.'), (e) => {
console.error(e);
process.exitCode = 1;
});



207 changes: 207 additions & 0 deletions docs/site/assets/js/cldrsite.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
const { ref } = Vue;

// site management

/** replace a/b/c.md with a/b */
function path2dir(p) {
const dir = p.split('/').slice(0,-1).join('/');
return dir;
}

/** replace a/b/c.md with a/b/c.html */
function md2html(p) {
return (p.replace(/\.md$/,'.html'));
}

/** replace a/b/c.html with a/b/c.md */
function html2md(p) {
return (p.replace(/\.html$/,'.md'));
}

/** load and cook the site data */
async function siteData() {
// load the json
const d = await fetch('/assets/json/tree.json');
const j = await d.json();
const { all } = j;

// 'all' is an array of { title, fullPath } entries.
// Flat list of paths
const allPaths = all.map(({fullPath})=>(fullPath));
// Find all 'directories' (ending with /)
const allDirs = new Set();
allPaths.forEach(p => {
const segs = p.split('/').slice(0,-1); // ['', 'dir1']
for(let n=0; n<=segs.length; n++) {
// add all parent paths, so: '', dir1, dir1/dir2 etc.
const subpath = segs.slice(0,n).join('/');
allDirs.add(subpath);
}
});
j.allDirs = {};
j.allIndexes = [];
// allDirs: '', index, downloads, etc…
allDirs.forEach(dir => {
// presumed index page: /downloads -> /downloads.md
// also / -> /index.md
const dirIndex = `${dir || 'index'}.md`;
// console.dir({dir, dirIndex});
if (allPaths.indexOf(dirIndex) !== -1) {
j.allDirs[dir] = {index: dirIndex};
j.allIndexes.push(dirIndex);
} else {
console.error(`No index page: ${dirIndex}`);
j.allDirs[dir] = {};
}
j.allDirs[dir].pages = [];
});
allPaths.forEach(p => {
const dir = path2dir(p);
j.allDirs[dir].pages.push(p);
});
// map md -> title
j.title = {};
all.forEach(({title, fullPath}) => j.title[fullPath] = title);
return j;
}


const app = Vue.createApp({
setup(props) {
const tree = ref({});
const status = ref(null);

return {
tree,
status,
}
},
mounted() {
const t = this;
siteData().then(d => t.tree.value = d, e => t.status = e);
},
props: {
path: String,
},
computed: {
mdPath() {
if(this.path) {
return html2md(this.path)
}
return null;
},
ourDir() {
if(this.path) {
return path2dir(this.path);
}
return '';
},
ourIndex() {
if(this.tree?.value) {
// first ARE we an index page?
if (this.tree.value.allIndexes.indexOf(this.mdPath) != -1) {
return this.mdPath; // we are an index
}
return this.tree.value.allDirs[this.ourDir].index;
}
return null;
},
ourIndexHtml() {
if (this.ourIndex) {
return md2html(this.ourIndex);
} else {
return null;
}
},
ourIndexTitle() {
if (this.ourIndex && this.tree?.value) {
return this.tree.value.title[this.ourIndex] || this.ourIndex;
} else {
return null;
}
},
ourTitle() {
if (this.tree?.value) {
if (this.path === '') return this.rootTitle;
return this.tree.value.title[html2md(this.path)];
}
},
// title of root
rootTitle() {
return this.tree?.value?.title['index.md'];
},
// list of pages for siblings of this dir
siblingPages() {
if (!this.tree?.value) return [];
const dirForPage = this.ourDir;
let thePages = this.tree?.value?.allDirs[dirForPage].pages ?? [];
if (dirForPage === '') {
thePages = [...thePages, ...this.tree?.value?.allDirs['index'].pages];
}
const c = new Intl.Collator([]);
return ( thePages )
.map((path) => ({
path,
html: md2html(path),
title: this.tree.value.title[path] ?? path,
}))
.sort((a, b) => c.compare(a.title, b.title));
},
},
template: `<div>
<div class='status' v-if="status">{{ status }}</div>
<div class='status' v-if="!tree">Loading…</div>
<span v-if="path !== 'index.html'">
<a class='uplink' href="/">{{ rootTitle }}</a> |
</span>

<span v-if="path !== '' && ourIndexHtml && (ourIndexHtml != path) && (ourIndexHtml != 'index.html')">
<a class='uplink'
v-bind:href="'/'+ourIndexHtml"
>
{{ ourIndexTitle }}
</a>
|
</span>
<span class="title"> {{ ourTitle }} </span>
<ul class="subpages">
<li v-for="subpage of siblingPages" :key="subpage.path">
<span v-if="path == subpage.html">
<b>{{ subpage.title }}</b>
</span>
<a v-else v-bind:href="'/'+subpage.html">
{{ subpage.title }}
</a>
</li>
</ul>
</div>`
}, {
// path of / goes to /index.html
path: (window.location.pathname.slice(1) || 'index.html'),
});

app.component(
'CldrPage',
{
setup() {

},
template: `<p>Hello</p>
`
}
);

app.component(
'CldrList',
{
setup() {

},
template: `
<p>Hullo</p>
`
}
);


app.mount('#nav');
2 changes: 1 addition & 1 deletion docs/site/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ title: Internal Development

# Internal Development

![Unicode copyright](https://www.unicode.org/img/hb_notice.gif)
![Unicode copyright](https://www.unicode.org/img/hb_notice.gif)
12 changes: 6 additions & 6 deletions docs/site/development/development-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ title: Handling Tickets (bugs/enhancements)

### Pre\-Assessment for Weekly Triage

*There is a pre\-assessment of tickets to make the triage flow faster. This pre\-assessment should be done off\-line prior to every Wednesday in time for the TC triage. DO NOT fix a bug until it's been approved by the TC in triage.*
*There is a pre\-assessment of tickets to make the triage flow faster. This pre\-assessment should be done off\-line prior to every Wednesday in time for the TC triage. DO NOT fix a bug until it's been approved by the TC in triage.*

*The triage is a part of Monday and Wednesday meetings.*
*The triage is a part of Monday and Wednesday meetings.*

1. Ticket comes in, and the default is **Priority**\=assess, **Milestone**\=to\-assess
1. If user sets Component (we always should!) automatically assigns the Owner based on the component owner for the pre\-assessment. \[Add link to Components and Owner]
Expand All @@ -22,7 +22,7 @@ title: Handling Tickets (bugs/enhancements)
2. All TC members, look at [To\-assess by Owner / Component](https://unicode.org/cldr/trac/report/100) before each triage day.
1. Assess the **to\-assess** tickets assigned to you.
2. You can close bugs as duplicate or if user misunderstanding if it is very clear without committee discussion. Always include a comment if you close the ticket.
3. If the component is wrong, change component and reassign to the right component owner.
3. If the component is wrong, change component and reassign to the right component owner.
1. (TODO: copy components to that page, and add notes to clarify; if possible, link to list of components \+ descriptions at top of new ticket). http://cldr.unicode.org/index/bug\-reports
4. Add your assessment information:
1. Change **Milestone** to “assessed”
Expand All @@ -36,7 +36,7 @@ title: Handling Tickets (bugs/enhancements)
1. Follow the triage practice by making changes to all fields, Owner, Component, Priority, … or closing the ticket.
2. Update Milestone to one of the following (VV \= the current milestone, eg 35\)
1. **VV** if the (new) Owner agrees that it is a priority — and has some level of commitment to fix during release
2. **VV\-optional**, eg 35\-optional
2. **VV\-optional**, eg 35\-optional
1. A **future** milestone
3. Triage decides whether it's for Design or Accepted. (**design** indicates for more discussion needed. See design section below.).
5. Other ticket handling practices:
Expand All @@ -48,7 +48,7 @@ title: Handling Tickets (bugs/enhancements)

### Design

When a ticket is in design, the owner is responsible for bringing back to the committee to approve the design before any lasting (aside from tests, instrumentation, etc.) work is checked in.
When a ticket is in design, the owner is responsible for bringing back to the committee to approve the design before any lasting (aside from tests, instrumentation, etc.) work is checked in.

1. The Owner is responsible for documenting the results of the discussion in the TC in comments in the ticket.
2. The Reviewer is responsible for verifying that the design was accepted by the TC before accepting.
Expand Down Expand Up @@ -166,4 +166,4 @@ If there is a test failure that is due to a bug that cannot be fixed right now (
1. The future folder tickets are moved to the discuss folder
2. Unscheduled tickets (with no release number) are re\-evaluated.

![Unicode copyright](https://www.unicode.org/img/hb_notice.gif)
![Unicode copyright](https://www.unicode.org/img/hb_notice.gif)
4 changes: 4 additions & 0 deletions docs/site/downloads.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
title: CLDR Downloads
---

Loading
Loading