Skip to content

Commit

Permalink
Proj filter styling (hackforla#619)
Browse files Browse the repository at this point in the history
* initial, needs and/or distinction

* initial pass complete

* cleaning and commenting, minimal

* point filter to github data, hard-coded filter categories

* move temp styling to scss

* populate from, and propagate to, URL query string

* support for yaml categories from dom parsing, not robust

* Build a list of project data using Jekyll

* Build the project filter data in Jekyll at build time

* Pull categories out of looking structure; fixed some projects that did not have the correct structure for their looking data

* added filterableCategories parameter to control which fields will create filter drop downs

* added filter styling for desktop view

* edited project filter styles for tablet and mobile

* Remove duplicate script tag

* added counter to fix checkedbox bug

Co-authored-by: Tyler Thome <tyler.thome@outlook.com>
Co-authored-by: Cynthia Kiser <cynthiakiser@gmail.com>
  • Loading branch information
3 people authored Jul 20, 2020
1 parent eacc6d1 commit dee78f8
Show file tree
Hide file tree
Showing 11 changed files with 596 additions and 18 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ dist
.DS_store
.vscode
_site
Gemfile
Gemfile.lock
215 changes: 203 additions & 12 deletions _includes/current-projects.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,210 @@
<section class="content-section projects">
<div class="page-contain projects-inner">
<h2 class="project-header">Current Projects</h2>
<ul class="project-list unstyled-list">
{% assign status_list = "Active, Rebooting, Completed, On Hold" | split: ", " %}
{% for status in status_list %}
{% assign projects = site.projects | where: "status", status | sort: "title" %}
{%- for item in projects -%}
{%- if item.hide != true -%}
{%- include project-card.html project=item -%}
{%- endif -%}
{%- endfor -%}
{%- endfor -%}
</ul>
<p class="project-pitch">
Have an idea? <a href="mailto:team@hackforla.org" class="btn btn-primary">Submit your pitch</a>
Have an idea?
<a href="mailto:team@hackforla.org" class="btn btn-primary"
>Submit your pitch</a
>
</p>
</div>

<div class="page-contain filter-toolbar" style="text-align: center;">
<ul class="filter-list" id="filter-list"></ul>
</div>
<div class="page-contain projects-inner" style="clear: left;">
<ul class="project-list unstyled-list">
{% assign status_list = "Active, Rebooting, Completed, On Hold" | split:
", " %} {% for status in status_list %} {% assign projects = site.projects
| where: "status", status | sort: "title" %} {%- for item in projects -%}
{%- if item.hide != true -%} {%- include project-card.html project=item
-%} {%- endif -%} {%- endfor -%} {%- endfor -%}
</ul>
</div>
</section>

<script>
var projectDataFromJekyll = [
{%- for project in site.projects -%}
{"id": {{ project.identification | default: 0 }},
"status": ["{{ project.status }}"]
{%- if project.location -%}
,"location": {{project.location | jsonify }}
{%- endif -%}
{%- if project.looking -%}
, "looking": {{project.looking | jsonify }}
{%- endif -%}
{%- if project.technologies -%}
, "technologies": {{project.technologies | jsonify }}
{%- endif -%}
}{%- unless forloop.last -%},{% endunless %}
{% endfor %}
];

window.addEventListener('load', function () {

// utilities
const cleanFieldName = (category, term) => `${category}_${term.replace(/\s+/g, '_').replace(/[^a-zA-Z0-9_]/g, '')}`;
const onlyUnique = (value, index, self) => self.indexOf(value) === index;

const projects = projectDataFromJekyll.map(record => {
let lookingCategories = [];
if (record.looking) {
lookingCategories = record.looking.map(x => x.category);
}
record.looking = lookingCategories.filter(onlyUnique);
return new Project(record);
});

const filterableCategories= [ 'status', 'looking', 'technologies', 'location' ];

// Collect all possible filter values for count computation
let valuesByCategory = new Map();
filterableCategories.forEach(cat => {valuesByCategory.set(cat, new Set())});
projects.forEach(p => {
for (let key in p.data) {
if (!filterableCategories.includes(key)) {
continue;
}
for (let v in p.data[key]) {
valuesByCategory.get(key).add(p.data[key][v]);
}
}
});


// Hide cards not satisfying filter, update counts in dropdowns
function applyFilter(filterReport) {

// hide projects not satisfying filter constraints
document.querySelectorAll(".project-card").forEach(element => {
if (filterReport.ids.indexOf(parseInt(element.id)) < 0) {
element.setAttribute('class', 'project-card hidden-project');
} else {
element.setAttribute('class', 'project-card');
}
});


// Assign updated counts to dropdown text
for(let category in filterReport.counts) {
const termCounts = filterReport.counts[category];
for(let term in termCounts) {
// const cleanName = `${category}_${term.replace(/\s+/g, '_').replace(/[^a-zA-Z0-9_]/g, '')}`;
const cleanName = cleanFieldName(category, term);
const text = `${term} (${termCounts[term]})`;
document.getElementById(cleanName).innerHTML = text;
}
}

}

// initialize project filter and get initial report (no filters applied yet)
const projectFilter = new ProjectFilter(valuesByCategory);
projectFilter.populateFromUrlParams();

let report = projectFilter.getFilterReport(projects);

// Render the filter toolbar - depending on screensize
for (let category in report.counts) {

const termSet = report.counts[category];

// dropdown container - menu item
const categoryDropdown = document.createElement('li');
categoryDropdown.setAttribute('class', 'filter-item');

// dropdown label/trigger element
const categoryTitle = document.createElement('a');
categoryTitle.appendChild(document.createTextNode(category));
categoryDropdown.appendChild(categoryTitle);

// dropdown menu
const termList = document.createElement('ul')
termList.setAttribute('class', 'dropdown');

// create label arrow
const arrowSpan = document.createElement('span');
const labelArrow = arrowSpan.appendChild(document.createTextNode(" \u{0221f} "));
arrowSpan.setAttribute('class', 'labelArrow');
categoryTitle.appendChild(arrowSpan);

//counter for checkedboxes
let checkedboxCounter = 0;

for (let term in termSet) {

const count = termSet[term];
const termItem = document.createElement('li');
const content = document.createElement('div');
const checkbox = document.createElement('input');

checkbox.setAttribute('type', 'checkbox');
checkbox.setAttribute('class', 'filter-checkbox');

const text = `${term} (${count})`;

const textSpan = document.createElement('span');

// TODO: Find a better mechanism for indexing dropdown items
textSpan.setAttribute('id', cleanFieldName(category, term));
textSpan.innerHTML = text;

// trigger a new filtering when a checkbox is clicked
checkbox.addEventListener('input', function (event) {

if (event.target.checked) {
projectFilter.addCondition(category, term);
checkedboxCounter++;

//change label background color
categoryDropdown.style.backgroundColor="#fa114f";
categoryTitle.style.color="white";

} else {
projectFilter.removeCondition(category, term);
checkedboxCounter--;

if (checkedboxCounter == 0){
//remove label background color
categoryDropdown.style.backgroundColor="white";
categoryTitle.style.color="black";
}
}

// recompute counts and IDs to hide
report = projectFilter.getFilterReport(projects);

applyFilter(report);

projectFilter.propagateUrlParams();

});

// finalize build UI component
content.appendChild(checkbox);
content.appendChild(textSpan);
termItem.appendChild(content);
termList.appendChild(termItem);
}

// compile DOM node and attach to document tree
categoryDropdown.appendChild(termList);
document.querySelector("#filter-list").appendChild(categoryDropdown);
}



for (let [categoryName, valueSet] of projectFilter.conditions.entries()) {
if (valueSet.size > 0) {
for(let v of valueSet) {
const cleanName = cleanFieldName(categoryName, v);
document.getElementById(cleanName).previousSibling.checked = true;
}
}
}

applyFilter(report);

});
</script>
2 changes: 1 addition & 1 deletion _includes/project-card.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% assign project = include.project %}
<li class="project-card">
<li class="project-card" id="{{ project.identification }}">
<div class="project-card-inner">
{%- if project.project-homepage == false -%}
<div class="project-tmb">
Expand Down
2 changes: 1 addition & 1 deletion _layouts/default.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
{%- include footer.html -%}
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.js"></script>
<script src="{{ '/assets/js/api-actionnetwork.js' | absolute_url }}"></script>
<script src="{{ '/assets/js/api-calendar.js' | absolute_url }}"></script>
<script src="{{ '/assets/js/project-filter.js' | absolute_url }}"></script>
</div>
</body>
</html>
4 changes: 2 additions & 2 deletions _projects/curbmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: curbmap
description: We are building a platform to engage the community to map and update the city's parking restrictions. Simultaneously we want to create an app that is easy for all people (community members, visitors, etc.) to use to see the landscape of parking around themselves. We want to help you avoid endlessly searching for parking in all the wrong places and reduce your risk of getting tickets.
image: /assets/images/projects/curbmap.jpg
alt: "'parking sign'"
links:
links:
- name: GitHub
url: 'https://github.com/curbmap'
- name: Site
Expand All @@ -20,4 +20,4 @@ location:
- Remote
hide: true
status: On Hold
---
---
2 changes: 1 addition & 1 deletion _projects/jobs-for-hope.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ image: /assets/images/projects/jobs-for-hope.png
alt: "'LA county homelessness initiative logo'"
image-hero: /assets/images/projects/jobs-for-hope-hero.png
alt-hero: "Purple background"
links:
links:
- name: GitHub
url: 'https://github.com/hackforla/jobs-for-hope'
- name: Site
Expand Down
2 changes: 1 addition & 1 deletion _projects/lucky-parking.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ looking:
- category: Development
skill: Python Enthusiast
technologies:
- React-JS
- ReactJS
- PostgreSQL
location:
- Santa Monica
Expand Down
1 change: 1 addition & 0 deletions _sass/_main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
@import 'components/impact-stats';
@import 'components/press';
@import 'components/projects';
@import 'components/project-filter';
@import 'components/social-links';
@import 'components/home-getting-started';
@import 'components/getting-started-page.scss';
Expand Down
Loading

0 comments on commit dee78f8

Please sign in to comment.