Skip to content

Commit

Permalink
Clean up CSS, Javascript, and Markdown.
Browse files Browse the repository at this point in the history
  • Loading branch information
davepeck committed May 7, 2024
1 parent 1977550 commit a97459a
Show file tree
Hide file tree
Showing 19 changed files with 1,140 additions and 566 deletions.
9 changes: 3 additions & 6 deletions jsconfig.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
{
"compilerOptions": {
"target": "ES6",
"module": "CommonJS",
"module": "None",
"checkJs": true,
"allowJs": true,
"noEmit": true
"lib": ["dom", "es6"]
},
"include": [
"server/static/js/surreal.js",
"server/static/js/css-scope-inline.js"
]
"include": ["server/**/*.js", "types/htmx.d.ts"]
}
82 changes: 82 additions & 0 deletions server/utils/components.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""Utilities for working with HTML-in-python components."""

import json
import pathlib
import typing as t

import htpy as h
import markdown
from markupsafe import Markup
from pydantic.alias_generators import to_camel


def load_file(file_name: str | pathlib.Path, markup: bool) -> str:
"""Load a text file and return its contents."""
with open(file_name, "r") as f:
text = f.read()
return Markup(text) if markup else text


def load_sibling_file(
base_file_name: str | pathlib.Path, file_name: str, markup: bool
) -> str:
"""Load a file in the same directory as the base file."""
return load_file(pathlib.Path(base_file_name).resolve().parent / file_name, markup)


def _css_vars(selector: str, /, **vars: str) -> str:
"""Generate CSS variables to inject into a stylesheet."""
as_css = "\n".join(" --{k.replace('_', '-')}: {v};" for k, v in vars.items())
return f"{selector} {{\n{as_css}\n}}\n"


def style(base_file_name: str | pathlib.Path, file_name: str, **vars: str) -> h.Element:
"""
Load a CSS file in the same directory as the base file.
In addition to the file, you can pass in CSS variables to inject into the
stylesheet.
"""
text = load_sibling_file(base_file_name, file_name, markup=True)
if vars:
text = _css_vars("me", **vars) + text
return h.style[text]


def js(
base_file_name: str | pathlib.Path,
file_name: str,
*,
surreal: bool = True,
**props: t.Any,
) -> h.Element:
"""
Load a JS file in the same directory as the base file.
Return a script element.
If `props` are provided, they are added to the script element
as data-props JSON.
The `surreal` flag, if True, causes us to wrap the provided javascript
in an invocation wrapper. The javascript is expected to take
two arguments: `self` and `props`.
CONSIDER: this still feels awkward to me, and I bet there's a cleaner
pattern -- our CSS pattern feels very clean to me, for instance.
"""
text = load_sibling_file(base_file_name, file_name, markup=True)
element = h.script
if props:
as_camel = {to_camel(k): v for k, v in props.items()}
as_json = json.dumps(as_camel)
element = element(data_props=as_json)
if surreal:
text = f"({text})(me(), me('script').dataset.props && JSON.parse(me('script').dataset.props))" # noqa: E501
return element[text]


def markdown_html(base_file_name: str | pathlib.Path, file_name: str) -> Markup:
"""Load a markdown file in the same directory as the base file."""
text = load_sibling_file(base_file_name, file_name, markup=False)
return Markup(markdown.markdown(text))
115 changes: 115 additions & 0 deletions server/vb/components/check_page.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
me {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}

me main {
width: 100%;
text-align: center;
padding: 0.5rem 0;
}

me main img {
height: 150px;
margin-bottom: -1.75rem;
}

me main p {
font-weight: 378;
font-size: 20px;
line-height: 130%;
}

me main h2 {
font-weight: 500;
font-size: 36px;
line-height: 120%;
text-transform: uppercase;
}

me .faq {
width: 100%;
color: white;
padding: 2rem 0;
}

me .button-holder {
display: flex;
justify-content: center;
margin: 1.5rem 0;
}

me .form {
width: 100%;
background-color: white;
padding: 2rem 0;
}

me .urgency {
flex-direction: column;
gap: 1rem;
}

@media screen and (min-width: 768px) {
me main {
padding: 2rem 0;
}

me main img {
height: 150px;
margin: 1.5rem 0;
}

me .urgency {
flex-direction: row;
gap: 2rem;
}
}

me main {
position: relative;
color: var(--main-color);
background-color: var(--main-bg-color);
}

me main a {
color: var(--main-color);
transition: opacity 0.2s;
}

me main a:hover {
opacity: 0.7;
transition: opacity 0.2s;
}

me main .urgency {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}

me main .fireworks {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
overflow: hidden;
}

me main .separate {
padding-left: 1rem;
}

me main img {
display: block;
}

@media screen and (min-width: 768px) {
me main .urgency {
flex-direction: row;
}
}
40 changes: 40 additions & 0 deletions server/vb/components/check_page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Implement the check page component.
*
* @param {HTMLElement} self
*/

function checkPage(self) {
/**
* Finalize a verify and, possibly, mint a new gift card if all is well.
*
* @param {string} firstName
* @param {string} lastName
* @param {string} email
*/
const finishVerify = (firstName, lastName, email) => {
/** @type {HTMLElement} */
const urgency = self.querySelector(".urgency");
htmx.ajax("POST", "./finish/", {
target: urgency,
values: {
first_name: firstName,
last_name: lastName,
email: email
}
});
};

window.addEventListener('VoteAmericaEvent', (event) => {
/** @type {object}
* @property {object} detail
*/
const objEvent = event;
const { data } = objEvent;
if (data?.tool === "verify" && data?.event === "action-finish") {
setTimeout(() => {
finishVerify(data.first_name, data.last_name, data.email);
}, 500);
}
});
}
Loading

0 comments on commit a97459a

Please sign in to comment.