From 611d713c30fe7eefe59ae51c4dfc1ebde33f6fe8 Mon Sep 17 00:00:00 2001 From: Leo Noordergraaf Date: Tue, 5 Nov 2024 22:22:41 +0100 Subject: [PATCH] fix for issue #565 --- fasthtml/components.py | 14 +- nbs/api/01_components.ipynb | 347 ++++++++++++++++++++++++++++++++---- 2 files changed, 323 insertions(+), 38 deletions(-) diff --git a/fasthtml/components.py b/fasthtml/components.py index 68ae3978..f82ed9a0 100644 --- a/fasthtml/components.py +++ b/fasthtml/components.py @@ -121,7 +121,10 @@ def _fill_item(item, obj): if val is not None and not 'skip' in attr: if tag=='input': if attr.get('type', '') == 'checkbox': - if val: attr['checked'] = '1' + if isinstance(val, list): + if attr['value'] in val: attr['checked'] = '1' + else: attr.pop('checked', '') + elif val: attr['checked'] = '1' else: attr.pop('checked', '') elif attr.get('type', '') == 'radio': if val and val == attr['value']: attr['checked'] = '1' @@ -129,8 +132,13 @@ def _fill_item(item, obj): else: attr['value'] = val if tag=='textarea': cs=(val,) if tag == 'select': - option = next((o for o in cs if o.tag=='option' and o.get('value')==val), None) - if option: option.selected = '1' + if isinstance(val, list): + for opt in cs: + if opt.tag == 'option' and opt.get('value') in val: + opt.selected = '1' + else: + option = next((o for o in cs if o.tag=='option' and o.get('value')==val), None) + if option: option.selected = '1' return FT(tag,cs,attr,void_=item.void_) # %% ../nbs/api/01_components.ipynb diff --git a/nbs/api/01_components.ipynb b/nbs/api/01_components.ipynb index 180827b3..0361f3fd 100644 --- a/nbs/api/01_components.ipynb +++ b/nbs/api/01_components.ipynb @@ -73,7 +73,21 @@ "execution_count": null, "id": "4d1dcd4a", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "

\n", + "FastHTML is Fast

\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "sentence = P(Strong(\"FastHTML is \", I(\"Fast\")))\n", "\n", @@ -87,7 +101,25 @@ "execution_count": null, "id": "ce7c9f92", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/markdown": [ + "```html\n", + "

\n", + "FastHTML is Fast

\n", + "\n", + "```" + ], + "text/plain": [ + "p((strong(('FastHTML is ', i(('Fast',),{})),{}),),{})" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Called without the `show()` function, the raw HTML is displayed\n", "sentence" @@ -177,7 +209,23 @@ "execution_count": null, "id": "6434dea1", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/markdown": [ + "```html\n", + "\n", + "```" + ], + "text/plain": [ + "a((),{'@click.away': 1})" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "ft_html('a', **{'@click.away':1})" ] @@ -187,7 +235,23 @@ "execution_count": null, "id": "ed03b851", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/markdown": [ + "```html\n", + "\n", + "```" + ], + "text/plain": [ + "a((),{'@click.away': 1})" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "ft_html('a', {'@click.away':1})" ] @@ -207,7 +271,23 @@ "execution_count": null, "id": "a979e0a6", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/markdown": [ + "```html\n", + "\n", + "```" + ], + "text/plain": [ + "a((),{'id': 'someid', 'name': 'someid'})" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "ft_html('a', id=c)" ] @@ -233,7 +313,23 @@ "execution_count": null, "id": "9f40e7f4", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/markdown": [ + "```html\n", + "\n", + "```" + ], + "text/plain": [ + "a((),{'hx-vals': '{\"a\": 1}'})" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "ft_hx('a', hx_vals={'a':1})" ] @@ -243,7 +339,23 @@ "execution_count": null, "id": "8ca91601", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/markdown": [ + "```html\n", + "\n", + "```" + ], + "text/plain": [ + "a((),{'hx-target': '#someid'})" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "ft_hx('a', hx_target=c)" ] @@ -296,7 +408,23 @@ "execution_count": null, "id": "ecbcfa18", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/markdown": [ + "```html\n", + "
\n", + "```" + ], + "text/plain": [ + "form((button((),{'hx-target': '#foo', 'id': 'btn', 'name': 'btn'}),),{'hx-post': '/', 'hx-target': '#tgt', 'id': 'frm', 'name': 'frm'})" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "Form(Button(target_id='foo', id='btn'),\n", " hx_post='/', target_id='tgt', id='frm')" @@ -319,7 +447,10 @@ " if val is not None and not 'skip' in attr:\n", " if tag=='input':\n", " if attr.get('type', '') == 'checkbox':\n", - " if val: attr['checked'] = '1'\n", + " if isinstance(val, list):\n", + " if attr['value'] in val: attr['checked'] = '1'\n", + " else: attr.pop('checked', '')\n", + " elif val: attr['checked'] = '1'\n", " else: attr.pop('checked', '')\n", " elif attr.get('type', '') == 'radio':\n", " if val and val == attr['value']: attr['checked'] = '1'\n", @@ -327,8 +458,13 @@ " else: attr['value'] = val\n", " if tag=='textarea': cs=(val,)\n", " if tag == 'select':\n", - " option = next((o for o in cs if o.tag=='option' and o.get('value')==val), None)\n", - " if option: option.selected = '1'\n", + " if isinstance(val, list):\n", + " for opt in cs:\n", + " if opt.tag == 'option' and opt.get('value') in val:\n", + " opt.selected = '1'\n", + " else:\n", + " option = next((o for o in cs if o.tag=='option' and o.get('value')==val), None)\n", + " if option: option.selected = '1'\n", " return FT(tag,cs,attr,void_=item.void_)" ] }, @@ -352,7 +488,26 @@ "execution_count": null, "id": "caef04d9", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/markdown": [ + "```html\n", + "
\n", + " \n", + "
\n", + "```" + ], + "text/plain": [ + "form((fieldset((input((),{'value': 'Profit', 'id': 'title', 'class': 'char', 'name': 'title'}), label((input((),{'type': 'checkbox', 'name': 'done', 'data-foo': 'bar', 'class': 'checkboxer', 'checked': '1'}), 'Done'),{'class': 'px-2'}), input((),{'type': 'hidden', 'id': 'id', 'name': 'id', 'value': 2}), select((option((),{'value': 'a'}), option((),{'value': 'b', 'selected': '1'})),{'name': 'opt'}), textarea(('Details',),{'id': 'details', 'name': 'details'}), button(('Save',),{})),{'name': 'stuff'}),),{})" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "@dataclass\n", "class TodoItem:\n", @@ -374,7 +529,23 @@ "execution_count": null, "id": "1513cf64", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/markdown": [ + "```html\n", + "
\n", + "```" + ], + "text/plain": [ + "form((select((option(('a',),{'value': 'a', 'selected': '1'}), option(('b',),{'value': 'b'}), option(('c',),{'value': 'c', 'selected': '1'})),{'multiple': '1', 'name': 'items'}),),{})" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "@dataclass\n", "class MultiSelect:\n", @@ -394,7 +565,26 @@ "execution_count": null, "id": "dd316f94-0695-44bf-96c2-45128a8b0644", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/markdown": [ + "```html\n", + "
\n", + "```" + ], + "text/plain": [ + "form((fieldset((label((input((),{'type': 'checkbox', 'name': 'items', 'value': 'a', 'checked': '1'}), 'a'),{}), label((input((),{'type': 'checkbox', 'name': 'items', 'value': 'b'}), 'b'),{}), label((input((),{'type': 'checkbox', 'name': 'items', 'value': 'c', 'checked': '1'}), 'c'),{})),{}),),{})" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "@dataclass\n", "class MultiCheck:\n", @@ -430,7 +620,18 @@ "execution_count": null, "id": "77e3f785", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "TodoItem(title='Profit', id=2, done=True, details='Details', opt='b')" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "nt = TodoItem('', 0, False, '')\n", "fill_dataclass(todo, nt)\n", @@ -464,7 +665,18 @@ "execution_count": null, "id": "7e740aac", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[input((),{'value': 'Profit', 'id': 'title', 'class': 'char', 'name': 'title'})]" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "inps = find_inputs(form, id='title')\n", "test_eq(len(inps), 1)\n", @@ -552,7 +764,41 @@ "execution_count": null, "id": "3569a964", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/markdown": [ + "```python\n", + "Form(\n", + " Fieldset(\n", + " Input(value='Profit', id='title', name='title', cls='char'),\n", + " Label(\n", + " Input(type='checkbox', name='done', data_foo='bar', checked='1', cls='checkboxer'),\n", + " 'Done',\n", + " cls='px-2'\n", + " ),\n", + " Input(type='hidden', id='id', name='id', value='2'),\n", + " Select(\n", + " Option(value='a'),\n", + " Option(value='b', selected='1'),\n", + " name='opt'\n", + " ),\n", + " Textarea('Details', id='details', name='details'),\n", + " Button('Save'),\n", + " name='stuff'\n", + " )\n", + ")\n", + "```" + ], + "text/plain": [ + "" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "h = to_xml(form)\n", "hl_md(html2ft(h), 'python')" @@ -563,7 +809,38 @@ "execution_count": null, "id": "af92d7d6", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/markdown": [ + "```python\n", + "Form(\n", + " Fieldset(name='stuff')(\n", + " Input(value='Profit', id='title', name='title', cls='char'),\n", + " Label(cls='px-2')(\n", + " Input(type='checkbox', name='done', data_foo='bar', checked='1', cls='checkboxer'),\n", + " 'Done'\n", + " ),\n", + " Input(type='hidden', id='id', name='id', value='2'),\n", + " Select(name='opt')(\n", + " Option(value='a'),\n", + " Option(value='b', selected='1')\n", + " ),\n", + " Textarea('Details', id='details', name='details'),\n", + " Button('Save')\n", + " )\n", + ")\n", + "```" + ], + "text/plain": [ + "" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "hl_md(html2ft(h, attr1st=True), 'python')" ] @@ -587,7 +864,21 @@ "execution_count": null, "id": "50d18540", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "event: message\n", + "data:
\n", + "data:

hi

\n", + "data:

there

\n", + "data:
\n", + "\n", + "\n" + ] + } + ], "source": [ "print(sse_message(Div(P('hi'), P('there'))))" ] @@ -595,9 +886,7 @@ { "cell_type": "markdown", "id": "474e14b4", - "metadata": { - "jp-MarkdownHeadingCollapsed": true - }, + "metadata": {}, "source": [ "# Export -" ] @@ -624,21 +913,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "python3", "language": "python", "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.3" } }, "nbformat": 4,