|
738 | 738 | " cts = elm.contents\n", |
739 | 739 | " cs = [repr(c.strip()) if isinstance(c, str) else _parse(c, lvl+1)\n", |
740 | 740 | " for c in cts if str(c).strip()]\n", |
741 | | - " attrs = []\n", |
| 741 | + " attrs, exotic_attrs = [], {}\n", |
742 | 742 | " for key, value in sorted(elm.attrs.items(), key=lambda x: x[0]=='class'):\n", |
743 | 743 | " if isinstance(value,(tuple,list)): value = \" \".join(value)\n", |
744 | | - " key = rev_map.get(key, key)\n", |
745 | | - " attrs.append(f'{key.replace(\"-\", \"_\")}={value!r}'\n", |
746 | | - " if _re_h2x_attr_key.match(key) else f'**{{{key!r}:{value!r}}}')\n", |
| 744 | + " key, value = rev_map.get(key, key), value or True\n", |
| 745 | + " if _re_h2x_attr_key.match(key): attrs.append(f'{key.replace(\"-\", \"_\")}={value!r}')\n", |
| 746 | + " else: exotic_attrs[key] = value\n", |
| 747 | + " if exotic_attrs: attrs.append(f'**{exotic_attrs!r}')\n", |
747 | 748 | " spc = \" \"*lvl*indent\n", |
748 | 749 | " onlychild = not cts or (len(cts)==1 and isinstance(cts[0],str))\n", |
749 | 750 | " j = ', ' if onlychild else f',\\n{spc}'\n", |
750 | 751 | " inner = j.join(filter(None, cs+attrs))\n", |
751 | | - " if onlychild: return f'{tag_name}({inner})'\n", |
| 752 | + " if onlychild:\n", |
| 753 | + " if not attr1st: return f'{tag_name}({inner})'\n", |
| 754 | + " else:\n", |
| 755 | + " # respect attr1st setting\n", |
| 756 | + " attrs = ', '.join(filter(None, attrs))\n", |
| 757 | + " return f'{tag_name}({attrs})({cs[0] if cs else \"\"})'\n", |
752 | 758 | " if not attr1st or not attrs: return f'{tag_name}(\\n{spc}{inner}\\n{\" \"*(lvl-1)*indent})' \n", |
753 | 759 | " inner_cs = j.join(filter(None, cs))\n", |
754 | 760 | " inner_attrs = ', '.join(filter(None, attrs))\n", |
|
816 | 822 | "```python\n", |
817 | 823 | "Form(\n", |
818 | 824 | " Fieldset(name='stuff')(\n", |
819 | | - " Input(value='Profit', id='title', name='title', cls='char'),\n", |
| 825 | + " Input(value='Profit', id='title', name='title', cls='char')(),\n", |
820 | 826 | " Label(cls='px-2')(\n", |
821 | | - " Input(type='checkbox', name='done', data_foo='bar', checked='1', cls='checkboxer'),\n", |
| 827 | + " Input(type='checkbox', name='done', data_foo='bar', checked='1', cls='checkboxer')(),\n", |
822 | 828 | " 'Done'\n", |
823 | 829 | " ),\n", |
824 | | - " Input(type='hidden', id='id', name='id', value='2'),\n", |
| 830 | + " Input(type='hidden', id='id', name='id', value='2')(),\n", |
825 | 831 | " Select(name='opt')(\n", |
826 | | - " Option(value='a'),\n", |
827 | | - " Option(value='b', selected='1')\n", |
| 832 | + " Option(value='a')(),\n", |
| 833 | + " Option(value='b', selected='1')()\n", |
828 | 834 | " ),\n", |
829 | | - " Textarea('Details', id='details', name='details'),\n", |
830 | | - " Button('Save')\n", |
| 835 | + " Textarea(id='details', name='details')('Details'),\n", |
| 836 | + " Button()('Save')\n", |
831 | 837 | " )\n", |
832 | 838 | ")\n", |
833 | 839 | "```" |
|
885 | 891 | }, |
886 | 892 | { |
887 | 893 | "cell_type": "markdown", |
888 | | - "id": "474e14b4", |
| 894 | + "id": "defc22f0", |
889 | 895 | "metadata": {}, |
890 | 896 | "source": [ |
891 | | - "# Export -" |
| 897 | + "## Tests" |
892 | 898 | ] |
893 | 899 | }, |
894 | 900 | { |
895 | 901 | "cell_type": "code", |
896 | 902 | "execution_count": null, |
897 | | - "id": "d211e8e2", |
| 903 | + "id": "46083529", |
898 | 904 | "metadata": {}, |
899 | 905 | "outputs": [], |
900 | 906 | "source": [ |
901 | 907 | "#|hide\n", |
902 | | - "import nbdev; nbdev.nbdev_export()" |
| 908 | + "def test_html2ft(html: str, attr1st=False):\n", |
| 909 | + " # html -> ft -> html\n", |
| 910 | + " assert html == to_xml(eval(html2ft(html, attr1st))).strip()" |
903 | 911 | ] |
904 | 912 | }, |
905 | 913 | { |
906 | 914 | "cell_type": "code", |
907 | 915 | "execution_count": null, |
908 | | - "id": "814cf69a", |
| 916 | + "id": "517bcd86", |
909 | 917 | "metadata": {}, |
910 | 918 | "outputs": [], |
911 | | - "source": [] |
| 919 | + "source": [ |
| 920 | + "test_html2ft('<input value=\"Profit\" name=\"title\" id=\"title\" class=\"char\">', attr1st=True)\n", |
| 921 | + "test_html2ft('<input value=\"Profit\" name=\"title\" id=\"title\" class=\"char\">')\n", |
| 922 | + "test_html2ft('<div id=\"foo\"></div>')\n", |
| 923 | + "test_html2ft('<div id=\"foo\">hi</div>')\n", |
| 924 | + "test_html2ft('<div x-show=\"open\" x-transition:enter=\"transition duration-300\" x-transition:enter-start=\"opacity-0 scale-90\">Hello 👋</div>')\n", |
| 925 | + "test_html2ft('<div x-transition:enter.scale.80 x-transition:leave.scale.90>hello</div>')" |
| 926 | + ] |
| 927 | + }, |
| 928 | + { |
| 929 | + "cell_type": "code", |
| 930 | + "execution_count": null, |
| 931 | + "id": "99773c69", |
| 932 | + "metadata": {}, |
| 933 | + "outputs": [], |
| 934 | + "source": [ |
| 935 | + "assert html2ft('<div id=\"foo\">hi</div>', attr1st=True) == \"Div(id='foo')('hi')\"\n", |
| 936 | + "assert html2ft(\"\"\"\n", |
| 937 | + " <div x-show=\"open\" x-transition:enter=\"transition duration-300\" x-transition:enter-start=\"opacity-0 scale-90\">Hello 👋</div>\n", |
| 938 | + "\"\"\") == \"Div('Hello 👋', x_show='open', **{'x-transition:enter': 'transition duration-300', 'x-transition:enter-start': 'opacity-0 scale-90'})\"\n", |
| 939 | + "assert html2ft('<div x-transition:enter.scale.80 x-transition:leave.scale.90>hello</div>') == \"Div('hello', **{'x-transition:enter.scale.80': True, 'x-transition:leave.scale.90': True})\"\n", |
| 940 | + "assert html2ft(\"<img alt=' ' />\") == \"Img(alt=' ')\"" |
| 941 | + ] |
| 942 | + }, |
| 943 | + { |
| 944 | + "cell_type": "markdown", |
| 945 | + "id": "474e14b4", |
| 946 | + "metadata": {}, |
| 947 | + "source": [ |
| 948 | + "# Export -" |
| 949 | + ] |
| 950 | + }, |
| 951 | + { |
| 952 | + "cell_type": "code", |
| 953 | + "execution_count": null, |
| 954 | + "id": "d211e8e2", |
| 955 | + "metadata": {}, |
| 956 | + "outputs": [], |
| 957 | + "source": [ |
| 958 | + "#|hide\n", |
| 959 | + "import nbdev; nbdev.nbdev_export()" |
| 960 | + ] |
912 | 961 | } |
913 | 962 | ], |
914 | 963 | "metadata": { |
|
0 commit comments