-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
draft: first draft of cdn path block
- Loading branch information
Showing
2 changed files
with
212 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
.cdn-path form { | ||
display: grid; | ||
gap: 1rem; | ||
} | ||
|
||
.cdn-path label { | ||
position: relative; | ||
display: flex; | ||
align-items: center; | ||
gap: 1rem; | ||
border-radius: var(--image-border-radius-m); | ||
padding: 1rem; | ||
padding-left: calc((2 * 1rem) + var(--circular-icon-tag-size)); | ||
cursor: pointer; | ||
user-select: none; | ||
transition: border-radius 2s, background-color .2s; | ||
} | ||
|
||
@media (min-width: 900px) { | ||
.cdn-path label { | ||
min-height: calc((2 * 1rem) + var(--circular-icon-tag-size)); | ||
} | ||
} | ||
|
||
.cdn-path label:hover { | ||
background-color: var(--bg-color-lightgrey); | ||
} | ||
|
||
.cdn-path input, | ||
.cdn-path span { | ||
display: block; | ||
position: absolute; | ||
top: 50%; | ||
left: 1rem; | ||
transform: translateY(-50%); | ||
margin: 0; | ||
width: var(--circular-icon-tag-size); | ||
height: var(--circular-icon-tag-size); | ||
border-radius: 50%; | ||
} | ||
|
||
.cdn-path span, | ||
.cdn-path span::after { | ||
transition: background-color .2s, opacity .2s; | ||
} | ||
|
||
.cdn-path span::after { | ||
content: ""; | ||
position: absolute; | ||
top: 7px; | ||
left: 11px; | ||
display: block; | ||
width: 5px; | ||
height: 10px; | ||
transform: rotate(45deg); | ||
border: solid var(--color-accent-lightgreen-content); | ||
border-width: 0 3px 3px 0; | ||
opacity: 0; | ||
transition: background-color .4s, opacity .4s; | ||
} | ||
|
||
@media (min-width: 900px) { | ||
.cdn-path span::after { | ||
top: 10px; | ||
left: 19px; | ||
width: 10px; | ||
height: 20px; | ||
border-width: 0 4px 4px 0; | ||
} | ||
} | ||
|
||
.cdn-path input { | ||
appearance: none; | ||
opacity: 0; | ||
} | ||
|
||
.cdn-path span { | ||
box-sizing: border-box; | ||
background-color: var(--bg-color-grey); | ||
} | ||
|
||
.cdn-path input:checked ~ span { | ||
background-color: var(--color-accent-lightgreen-bg); | ||
} | ||
|
||
.cdn-path input:checked ~ span::after { | ||
opacity: 1; | ||
} | ||
|
||
.cdn-path label:hover input ~ span::after { | ||
opacity: .5; | ||
} | ||
|
||
.cdn-path button { | ||
display: inline-block; | ||
width: max-content; | ||
height: fit-content; | ||
margin: 0 auto; | ||
padding: 5px 1.2em 6px; | ||
border: 2px solid; | ||
border-color: var(--spectrum-blue); | ||
border-radius: var(--image-border-radius-xxl); | ||
background-color: var(--spectrum-blue); | ||
color: var(--color-white); | ||
font-family: inherit; | ||
font-size: var(--type-body-s-size); | ||
font-weight: 600; | ||
line-height: var(--body-line-height); | ||
text-align: center; | ||
text-overflow: ellipsis; | ||
overflow: hidden; | ||
white-space: nowrap; | ||
cursor: pointer; | ||
transition: background-color 0.3s, color 0.3s | ||
} | ||
|
||
.cdn-path button:hover { | ||
background-color: var(--dark-spectrum-blue); | ||
border-color: var(--dark-spectrum-blue); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { createTag } from '../../scripts/scripts.js'; | ||
import { toClassName } from '../../scripts/lib-franklin.js'; | ||
|
||
function toCamelCase(string) { | ||
return toClassName(string).replace(/-([a-z])/g, (s) => s[1].toUpperCase()); | ||
} | ||
|
||
async function fetchQuestionnaire(source) { | ||
const { pathname } = new URL(source); | ||
if (pathname) { | ||
const req = await fetch(pathname); | ||
const res = await req.json(); | ||
const questionnaire = { questions: res.data }; | ||
if (questionnaire.questions.length) { | ||
questionnaire.choices = Object.keys(res.data[0]).slice(1); | ||
} | ||
return questionnaire; | ||
} | ||
return {}; | ||
} | ||
|
||
function buildQuestion(q) { | ||
const name = toClassName(q.Question); | ||
const label = createTag('label', { for: name }, q.Question); | ||
const input = createTag('input', { type: 'checkbox', name, id: name }); | ||
Object.keys(q).forEach((choice) => { | ||
// eslint-disable-next-line eqeqeq | ||
if (q[choice] == parseInt(q[choice], 10)) { | ||
input.dataset[toCamelCase(choice)] = q[choice]; | ||
} | ||
}); | ||
const span = createTag('span', { class: 'checkbox' }); | ||
label.append(input, span); | ||
return label; | ||
} | ||
|
||
function findPaths(e) { | ||
const checked = e.target.querySelectorAll('input:checked'); | ||
const results = {}; | ||
checked.forEach((check) => { | ||
Object.keys(check.dataset).forEach((choice) => { | ||
const value = parseInt(check.dataset[choice], 10); | ||
if (results[choice]) { | ||
results[choice] += value; | ||
} else { | ||
results[choice] = value; | ||
} | ||
}); | ||
}); | ||
const sorted = Object.entries(results).sort((a, b) => b[1] - a[1]); | ||
const topMatches = []; | ||
let count = 0; | ||
while (count < 3 && sorted[0] && sorted[0][1] && sorted[0][1] > 0) { | ||
const [choice] = sorted; | ||
topMatches.push(choice); | ||
sorted.shift(); | ||
count += 1; | ||
} | ||
return topMatches; | ||
} | ||
|
||
function displayPaths(e, el, choices) { | ||
e.preventDefault(); | ||
el.innerHTML = ''; | ||
const paths = findPaths(e); | ||
paths.forEach((path) => { | ||
const choice = choices.find((c) => toCamelCase(c) === path[0]); | ||
const li = createTag('li', {}, choice); | ||
// TODO: actually present results in a useful way | ||
el.append(li); | ||
}); | ||
} | ||
|
||
export default async function decorate(block) { | ||
const source = block.querySelector('a[href]'); | ||
block.innerHTML = ''; | ||
if (source) { | ||
const data = await fetchQuestionnaire(source); | ||
// build questionnaire | ||
const form = createTag('form'); | ||
const results = createTag('ol', { class: 'cdn-path-results' }); | ||
form.addEventListener('submit', (e) => displayPaths(e, results, data.choices)); | ||
data.questions.forEach((d) => { | ||
const q = buildQuestion(d); | ||
form.append(q); | ||
}); | ||
// TODO: move string out of code | ||
const button = createTag('button', { type: 'submit' }, 'Find Your Path'); | ||
form.append(button); | ||
block.append(form, results); | ||
} | ||
} |