Skip to content

Commit e0ad588

Browse files
committed
fixes #42
1 parent e0668cc commit e0ad588

File tree

3 files changed

+139
-4
lines changed

3 files changed

+139
-4
lines changed

fasthtml/_modidx.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,15 @@
9494
'fasthtml.xtend.Hidden': ('xtend.html#hidden', 'fasthtml/xtend.py'),
9595
'fasthtml.xtend.Html': ('xtend.html#html', 'fasthtml/xtend.py'),
9696
'fasthtml.xtend.Script': ('xtend.html#script', 'fasthtml/xtend.py'),
97+
'fasthtml.xtend.ScriptX': ('xtend.html#scriptx', 'fasthtml/xtend.py'),
9798
'fasthtml.xtend.Search': ('xtend.html#search', 'fasthtml/xtend.py'),
9899
'fasthtml.xtend.Style': ('xtend.html#style', 'fasthtml/xtend.py'),
100+
'fasthtml.xtend.StyleX': ('xtend.html#stylex', 'fasthtml/xtend.py'),
99101
'fasthtml.xtend.Titled': ('xtend.html#titled', 'fasthtml/xtend.py'),
102+
'fasthtml.xtend.double_braces': ('xtend.html#double_braces', 'fasthtml/xtend.py'),
100103
'fasthtml.xtend.jsd': ('xtend.html#jsd', 'fasthtml/xtend.py'),
104+
'fasthtml.xtend.loose_format': ('xtend.html#loose_format', 'fasthtml/xtend.py'),
105+
'fasthtml.xtend.replace_css_vars': ('xtend.html#replace_css_vars', 'fasthtml/xtend.py'),
101106
'fasthtml.xtend.run_js': ('xtend.html#run_js', 'fasthtml/xtend.py'),
102-
'fasthtml.xtend.set_pico_cls': ('xtend.html#set_pico_cls', 'fasthtml/xtend.py')}}}
107+
'fasthtml.xtend.set_pico_cls': ('xtend.html#set_pico_cls', 'fasthtml/xtend.py'),
108+
'fasthtml.xtend.undouble_braces': ('xtend.html#undouble_braces', 'fasthtml/xtend.py')}}}

fasthtml/xtend.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
# %% auto 0
44
__all__ = ['picocss', 'picolink', 'picocondcss', 'picocondlink', 'set_pico_cls', 'Html', 'A', 'AX', 'Checkbox', 'Card', 'Group',
5-
'Search', 'Grid', 'DialogX', 'Hidden', 'Container', 'Script', 'Style', 'run_js', 'Titled', 'jsd']
5+
'Search', 'Grid', 'DialogX', 'Hidden', 'Container', 'Script', 'Style', 'double_braces', 'undouble_braces',
6+
'loose_format', 'ScriptX', 'replace_css_vars', 'StyleX', 'run_js', 'Titled', 'jsd']
67

78
# %% ../nbs/02_xtend.ipynb 2
89
from dataclasses import dataclass, asdict
910

1011
from fastcore.utils import *
12+
from fastcore.xtras import partial_format
1113
from fastcore.xml import *
1214
from fastcore.meta import use_kwargs, delegates
1315
from .components import *
@@ -128,19 +130,58 @@ def Style(*c, **kwargs)->XT:
128130
return xt_html('style', map(NotStr,c), **kwargs)
129131

130132
# %% ../nbs/02_xtend.ipynb 31
133+
def double_braces(s):
134+
"Convert single braces to double braces if next to special chars or newline"
135+
s = re.sub(r'{(?=[\s:;\'"]|$)', '{{', s)
136+
return re.sub(r'(^|[\s:;\'"])}', r'\1}}', s)
137+
138+
# %% ../nbs/02_xtend.ipynb 32
139+
def undouble_braces(s):
140+
"Convert double braces to single braces if next to special chars or newline"
141+
s = re.sub(r'\{\{(?=[\s:;\'"]|$)', '{', s)
142+
return re.sub(r'(^|[\s:;\'"])\}\}', r'\1}', s)
143+
144+
# %% ../nbs/02_xtend.ipynb 33
145+
def loose_format(s, **kw):
146+
"String format `s` using `kw`, without being strict about braces outside of template params"
147+
return undouble_braces(partial_format(double_braces(s), **kw)[0])
148+
149+
# %% ../nbs/02_xtend.ipynb 34
150+
def ScriptX(fname, type=None, _async=None, defer=None, charset=None, crossorigin=None, integrity=None, **kw):
151+
"Create a Script from the text of a file"
152+
attrs = ['src', 'type', 'async', 'defer', 'charset', 'crossorigin', 'integrity', 'nomodule']
153+
scr_kw = {k:kw.pop(k) for k in attrs if k in kw}
154+
s = loose_format(Path(fname).read_text(), **kw)
155+
return Script(s, **scr_kw)
156+
157+
# %% ../nbs/02_xtend.ipynb 35
158+
def replace_css_vars(css, pre='tpl', **kwargs):
159+
def replace_var(m):
160+
var_name = m.group(1).replace('-', '_')
161+
return kwargs.get(var_name, m.group(0))
162+
return re.sub(fr'var\(--{pre}-([\w-]+)\)', replace_var, css)
163+
164+
# %% ../nbs/02_xtend.ipynb 36
165+
def StyleX(fname, **kw):
166+
s = Path(fname).read_text()
167+
attrs = ['type', 'media', 'scoped', 'title', 'nonce', 'integrity', 'crossorigin']
168+
sty_kw = {k:kw.pop(k) for k in attrs if k in kw}
169+
return Style(replace_css_vars(s, **kw), **sty_kw)
170+
171+
# %% ../nbs/02_xtend.ipynb 37
131172
def run_js(js, id=None, **kw):
132173
"Run `js` script, auto-generating `id` based on name of caller if needed, and js-escaping any `kw` params"
133174
if not id: id = sys._getframe(1).f_code.co_name
134175
kw = {k:dumps(v) for k,v in kw.items()}
135176
return Script(js.format(**kw), id=id, hx_swap_oob='true')
136177

137-
# %% ../nbs/02_xtend.ipynb 32
178+
# %% ../nbs/02_xtend.ipynb 38
138179
@delegates(xt_hx, keep=True)
139180
def Titled(title:str="FastHTML app", *args, **kwargs)->XT:
140181
"An HTML partial containing a `Title`, and `H1`, and any provided children"
141182
return Title(title), Main(H1(title), *args, cls="container", **kwargs)
142183

143-
# %% ../nbs/02_xtend.ipynb 33
184+
# %% ../nbs/02_xtend.ipynb 39
144185
def jsd(org, repo, root, path, prov='gh', typ='script', ver=None, esm=False, **kwargs)->XT:
145186
"jsdelivr `Script` or CSS `Link` tag, or URL"
146187
ver = '@'+ver if ver else ''

nbs/02_xtend.ipynb

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"from dataclasses import dataclass, asdict\n",
3030
"\n",
3131
"from fastcore.utils import *\n",
32+
"from fastcore.xtras import partial_format\n",
3233
"from fastcore.xml import *\n",
3334
"from fastcore.meta import use_kwargs, delegates\n",
3435
"from fasthtml.components import *\n",
@@ -567,6 +568,93 @@
567568
" return xt_html('style', map(NotStr,c), **kwargs)"
568569
]
569570
},
571+
{
572+
"cell_type": "code",
573+
"execution_count": null,
574+
"id": "c77a0e88",
575+
"metadata": {},
576+
"outputs": [],
577+
"source": [
578+
"#| export\n",
579+
"def double_braces(s):\n",
580+
" \"Convert single braces to double braces if next to special chars or newline\"\n",
581+
" s = re.sub(r'{(?=[\\s:;\\'\"]|$)', '{{', s)\n",
582+
" return re.sub(r'(^|[\\s:;\\'\"])}', r'\\1}}', s)"
583+
]
584+
},
585+
{
586+
"cell_type": "code",
587+
"execution_count": null,
588+
"id": "9ca90ee1",
589+
"metadata": {},
590+
"outputs": [],
591+
"source": [
592+
"#| export\n",
593+
"def undouble_braces(s):\n",
594+
" \"Convert double braces to single braces if next to special chars or newline\"\n",
595+
" s = re.sub(r'\\{\\{(?=[\\s:;\\'\"]|$)', '{', s)\n",
596+
" return re.sub(r'(^|[\\s:;\\'\"])\\}\\}', r'\\1}', s)"
597+
]
598+
},
599+
{
600+
"cell_type": "code",
601+
"execution_count": null,
602+
"id": "231b7004",
603+
"metadata": {},
604+
"outputs": [],
605+
"source": [
606+
"#| export\n",
607+
"def loose_format(s, **kw):\n",
608+
" \"String format `s` using `kw`, without being strict about braces outside of template params\"\n",
609+
" return undouble_braces(partial_format(double_braces(s), **kw)[0])"
610+
]
611+
},
612+
{
613+
"cell_type": "code",
614+
"execution_count": null,
615+
"id": "29ceb26a",
616+
"metadata": {},
617+
"outputs": [],
618+
"source": [
619+
"#| export\n",
620+
"def ScriptX(fname, type=None, _async=None, defer=None, charset=None, crossorigin=None, integrity=None, **kw):\n",
621+
" \"Create a Script from the text of a file\"\n",
622+
" attrs = ['src', 'type', 'async', 'defer', 'charset', 'crossorigin', 'integrity', 'nomodule']\n",
623+
" scr_kw = {k:kw.pop(k) for k in attrs if k in kw}\n",
624+
" s = loose_format(Path(fname).read_text(), **kw)\n",
625+
" return Script(s, **scr_kw)"
626+
]
627+
},
628+
{
629+
"cell_type": "code",
630+
"execution_count": null,
631+
"id": "1e72dac9",
632+
"metadata": {},
633+
"outputs": [],
634+
"source": [
635+
"#| export\n",
636+
"def replace_css_vars(css, pre='tpl', **kwargs):\n",
637+
" def replace_var(m):\n",
638+
" var_name = m.group(1).replace('-', '_')\n",
639+
" return kwargs.get(var_name, m.group(0))\n",
640+
" return re.sub(fr'var\\(--{pre}-([\\w-]+)\\)', replace_var, css)"
641+
]
642+
},
643+
{
644+
"cell_type": "code",
645+
"execution_count": null,
646+
"id": "9bcbff42",
647+
"metadata": {},
648+
"outputs": [],
649+
"source": [
650+
"#| export\n",
651+
"def StyleX(fname, **kw):\n",
652+
" s = Path(fname).read_text()\n",
653+
" attrs = ['type', 'media', 'scoped', 'title', 'nonce', 'integrity', 'crossorigin']\n",
654+
" sty_kw = {k:kw.pop(k) for k in attrs if k in kw}\n",
655+
" return Style(replace_css_vars(s, **kw), **sty_kw)"
656+
]
657+
},
570658
{
571659
"cell_type": "code",
572660
"execution_count": null,

0 commit comments

Comments
 (0)