Spaces:
Sleeping
Sleeping
File size: 83,144 Bytes
d467ea7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 |
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/02_franken.ipynb.
# %% auto 0
__all__ = ['franken_class_map', 'TextT', 'TextPresets', 'CodeSpan', 'CodeBlock', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Subtitle',
'Q', 'Em', 'Strong', 'I', 'Small', 'Mark', 'Del', 'Ins', 'Sub', 'Sup', 'Blockquote', 'Caption', 'Cite',
'Time', 'Address', 'Abbr', 'Dfn', 'Kbd', 'Samp', 'Var', 'Figure', 'Details', 'Summary', 'Data', 'Meter', 'S',
'U', 'Output', 'PicSumImg', 'AccordionItem', 'Accordion', 'ButtonT', 'Button', 'ContainerT', 'BackgroundT',
'Container', 'Titled', 'DividerT', 'Divider', 'DividerSplit', 'DividerLine', 'Article', 'ArticleTitle',
'ArticleMeta', 'SectionT', 'Section', 'Form', 'Fieldset', 'Legend', 'Input', 'Radio', 'CheckboxX', 'Range',
'TextArea', 'Switch', 'Upload', 'UploadZone', 'FormLabel', 'LabelT', 'Label', 'UkFormSection',
'GenericLabelInput', 'LabelInput', 'LabelTextArea', 'LabelSwitch', 'LabelRadio', 'LabelCheckboxX', 'Options',
'Select', 'LabelSelect', 'LabelRange', 'AT', 'ListT', 'ModalContainer', 'ModalDialog', 'ModalHeader',
'ModalBody', 'ModalFooter', 'ModalTitle', 'ModalCloseButton', 'Modal', 'Placeholder', 'Progress', 'UkIcon',
'UkIconLink', 'DiceBearAvatar', 'Center', 'FlexT', 'Grid', 'DivFullySpaced', 'DivCentered', 'DivLAligned',
'DivRAligned', 'DivVStacked', 'DivHStacked', 'NavT', 'NavContainer', 'NavParentLi', 'NavDividerLi',
'NavHeaderLi', 'NavSubtitle', 'NavCloseLi', 'ScrollspyT', 'NavBar', 'SliderContainer', 'SliderItems',
'SliderNav', 'Slider', 'DropDownNavContainer', 'TabContainer', 'CardT', 'CardTitle', 'CardHeader',
'CardBody', 'CardFooter', 'CardContainer', 'Card', 'TableT', 'Table', 'Td', 'Th', 'Tbody', 'TableFromLists',
'TableFromDicts', 'apply_classes', 'FrankenRenderer', 'render_md', 'ThemePicker', 'LightboxContainer',
'LightboxItem', 'ApexChart']
# %% ../nbs/02_franken.ipynb
import fasthtml.common as fh
from .foundations import *
from fasthtml.common import Div, P, Span, FT
from enum import Enum, auto
from fasthtml.components import Uk_select,Uk_input_tag,Uk_icon,Uk_input_range, Uk_chart
from functools import partial
from itertools import zip_longest
from typing import Union, Tuple, Optional, Sequence
from fastcore.all import *
import copy, re, httpx, os
import pathlib
from mistletoe.html_renderer import HTMLRenderer
from mistletoe.span_token import Image
import mistletoe
from lxml import html, etree
import fasthtml.components as fh_comp
import json
# %% ../nbs/02_franken.ipynb
class TextT(VEnum):
'Text Styles from https://franken-ui.dev/docs/text'
def _generate_next_value_(name, start, count, last_values):
return str2ukcls('text', name)
paragraph = "uk-paragraph"
# Text Style
lead,meta, gray, italic= auto(), auto(), 'text-gray-500 dark:text-gray-200', 'italic'
# Text Size
xs, sm, lg, xl = 'text-xs', 'text-sm', 'text-lg', 'text-xl'
# Text Weight
light, normal, medium, bold, extrabold = 'font-light','font-normal','font-medium','font-bold','font-extrabold'
# Text Color
muted,primary,secondary = 'text-gray-500 dark:text-gray-200', 'text-primary', 'text-secondary'
success,warning, error, info = 'text-success', 'text-warning', 'text-error', 'text-info'
# Text Alignment
left, right,center = "text-left","text-right","text-center"
justify, start, end = "text-justify","text-start","text-end"
# Vertical Alignment
top,middle,bottom = 'align-top','align-middle','align-bottom'
# Text Wrapping
truncate,break_,nowrap = 'uk-text-truncate','uk-text-break', 'uk-text-nowrap'
# other
underline = 'underline'
highlight = 'bg-yellow-200 dark:bg-yellow-800 text-black'
class TextPresets(VEnum):
'Common Typography Presets'
muted_sm = TextT.muted+TextT.sm
muted_lg = TextT.muted+TextT.lg
bold_sm = TextT.bold+TextT.sm
bold_lg = TextT.bold+TextT.lg
md_weight_sm = stringify((TextT.sm, TextT.medium))
md_weight_muted = stringify((TextT.medium, TextT.muted))
# %% ../nbs/02_franken.ipynb
def CodeSpan(*c, # Contents of CodeSpan tag (inline text code snippets)
cls=(), # Classes in addition to CodeSpan styling
**kwargs # Additional args for CodeSpan tag
)->FT: # Code(..., cls='uk-codespan')
"A CodeSpan with Styling"
return fh.Code(*c, cls=('uk-codespan', stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def CodeBlock(*c: str, # Contents of Code tag (often text)
cls: Enum | str | tuple = (), # Classes for the outer container
code_cls: Enum | str | tuple = (), # Classes for the code tag
**kwargs # Additional args for Code tag
) -> FT: # Div(Pre(Code(..., cls='uk-codeblock), cls='multiple tailwind styles'), cls='uk-block')
"CodeBlock with Styling"
return Div(
Pre(Code(*c, cls=('uk-codeblock', stringify(code_cls)), **kwargs),
cls=(f'bg-gray-100 dark:bg-gray-800 {TextT.gray} p-0.4 rounded text-sm font-mono')),
# cls=('bg-gray-100 dark:bg-gray-800 dark:text-gray-200 p-0.4 rounded text-sm font-mono')),
cls=('uk-block', stringify(cls)))
# %% ../nbs/02_franken.ipynb
def H1(*c:FT|str, # Contents of H1 tag (often text)
cls:Enum|str|tuple=(), # Classes in addition to H1 styling
**kwargs # Additional args for H1 tag
)->FT: # H1(..., cls='uk-h1')
"H1 with styling and appropriate size"
return fh.H1(*c, cls=('uk-h1',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def H2(*c:FT|str, # Contents of H2 tag (often text)
cls:Enum|str|tuple=(), # Classes in addition to H2 styling
**kwargs # Additional args for H2 tag
)->FT: # H2(..., cls='uk-h2')
"H2 with styling and appropriate size"
return fh.H2(*c, cls=('uk-h2',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def H3(*c:FT|str, # Contents of H3 tag (often text)
cls:Enum|str|tuple=(), # Classes in addition to H3 styling
**kwargs # Additional args for H3 tag
)->FT: # H3(..., cls='uk-h3')
"H3 with styling and appropriate size"
return fh.H3(*c, cls=('uk-h3',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def H4(*c:FT|str, # Contents of H4 tag (often text)
cls:Enum|str|tuple=(), # Classes in addition to H4 styling
**kwargs # Additional args for H4 tag
)->FT: # H4(..., cls='uk-h4')
"H4 with styling and appropriate size"
return fh.H4(*c, cls=('uk-h4',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def H5(*c:FT|str, # Contents of H5 tag (often text)
cls:Enum|str|tuple=(), # Classes in addition to H5 styling
**kwargs # Additional args for H5 tag
)->FT: # H5(..., cls='text-lg font-semibold')
"H5 with styling and appropriate size"
return fh.H5(*c, cls=('text-lg font-semibold',stringify(cls)), **kwargs)
def H6(*c:FT|str, # Contents of H6 tag (often text)
cls:Enum|str|tuple=(), # Classes in addition to H6 styling
**kwargs # Additional args for H6 tag
)->FT: # H6(..., cls='text-base font-semibold')
"H6 with styling and appropriate size"
return fh.H6(*c, cls=('text-base font-semibold',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Subtitle(*c:FT|str, # Contents of P tag (often text)
cls:Enum|str|tuple='mt-1.5', # Additional classes
**kwargs # Additional args for P tag
)->FT:
"Styled muted_sm text designed to go under Headings and Titles"
return fh.P(*c, cls=(TextPresets.muted_sm, stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Q(*c:FT|str, # Contents of Q tag (quote)
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Q tag
)->FT:
"Styled quotation mark"
return fh.Q(*c, cls=(TextT.italic,TextT.lg, stringify(cls)), **kwargs)
def Em(*c:FT|str, # Contents of Em tag (emphasis)
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Em tag
)->FT:
"Styled emphasis text"
return fh.Em(*c, cls=(TextT.medium, stringify(cls)), **kwargs)
def Strong(*c:FT|str, # Contents of Strong tag
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Strong tag
)->FT:
"Styled strong text"
return fh.Strong(*c, cls=(TextT.bold, stringify(cls)), **kwargs)
def I(*c:FT|str, # Contents of I tag (italics)
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for I tag
)->FT:
"Styled italic text"
return fh.I(*c, cls=(TextT.italic, stringify(cls)), **kwargs)
def Small(*c:FT|str, # Contents of Small tag
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Small tag
)->FT:
"Styled small text"
return fh.Small(*c, cls=(TextT.sm, stringify(cls)), **kwargs)
def Mark(*c:FT|str, # Contents of Mark tag (highlighted text)
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Mark tag
)->FT:
"Styled highlighted text"
return fh.Mark(*c, cls=(TextT.highlight, stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Del(*c:FT|str, # Contents of Del tag (deleted text)
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Del tag
)->FT:
"Styled deleted text"
return fh.Del(*c, cls=('line-through', TextT.gray, stringify(cls)), **kwargs)
def Ins(*c:FT|str, # Contents of Ins tag (inserted text)
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Ins tag
)->FT:
"Styled inserted text"
return fh.Ins(*c, cls=(TextT.underline+' text-green-600', stringify(cls)), **kwargs)
def Sub(*c:FT|str, # Contents of Sub tag (subscript)
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Sub tag
)->FT:
"Styled subscript text"
return fh.Sub(*c, cls=(TextT.sm+' -bottom-1 relative', stringify(cls)), **kwargs)
def Sup(*c:FT|str, # Contents of Sup tag (superscript)
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Sup tag
)->FT:
"Styled superscript text"
return fh.Sup(*c, cls=(TextT.sm+' -top-1 relative', stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Blockquote(*c:FT|str, # Contents of Blockquote tag (often text)
cls:Enum|str|tuple=(), # Classes in addition to Blockquote styling
**kwargs # Additional args for Blockquote tag
)->FT: # Blockquote(..., cls='uk-blockquote')
"Blockquote with Styling"
return fh.Blockquote(*c, cls=('uk-blockquote',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Caption(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT:
"Styled caption text"
return fh.Caption(
Span(*c, cls=(TextT.gray, TextT.sm, stringify(cls))),
**kwargs)
# %% ../nbs/02_franken.ipynb
def Cite(*c:FT|str, # Contents of Cite tag
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Cite tag
)->FT:
"Styled citation text"
return fh.Cite(*c, cls=(TextT.italic, TextT.gray, stringify(cls)), **kwargs)
def Time(*c:FT|str, # Contents of Time tag
cls:Enum|str|tuple=(), # Additional classes
datetime:str=None, # datetime attribute
**kwargs # Additional args for Time tag
)->FT:
"Styled time element"
if datetime: kwargs['datetime'] = datetime
return fh.Time(*c, cls=(TextT.gray, stringify(cls)), **kwargs)
def Address(*c:FT|str, # Contents of Address tag
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Address tag
)->FT:
"Styled address element"
return fh.Address(*c, cls=(TextT.italic, stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Abbr(*c:FT|str, # Contents of Abbr tag
cls:Enum|str|tuple=(), # Additional classes
title:str=None, # Title attribute for abbreviation
**kwargs # Additional args for Abbr tag
)->FT:
"Styled abbreviation with dotted underline"
if title: kwargs['title'] = title
return fh.Abbr(*c, cls=('border-b border-dotted border-secondary hover:cursor-help', stringify(cls)), **kwargs)
def Dfn(*c:FT|str, # Contents of Dfn tag (definition)
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Dfn tag
)->FT:
"Styled definition term with italic and medium weight"
return fh.Dfn(*c, cls=(TextT.medium + TextT.italic + TextT.gray, stringify(cls)), **kwargs)
def Kbd(*c:FT|str, # Contents of Kbd tag (keyboard input)
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Kbd tag
)->FT:
"Styled keyboard input with subtle background"
return fh.Kbd(*c, cls=('font-mono px-1.5 py-0.5 text-sm bg-secondary border border-gray-300 dark:border-gray-600 rounded shadow-sm', stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Samp(*c:FT|str, # Contents of Samp tag (sample output)
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Samp tag
)->FT:
"Styled sample output with subtle background"
return fh.Samp(*c, cls=('font-mono bg-secondary px-1 rounded', TextT.gray, stringify(cls)), **kwargs)
def Var(*c:FT|str, # Contents of Var tag (variable)
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Var tag
)->FT:
"Styled variable with italic monospace"
return fh.Var(*c, cls=('font-mono',TextT.italic + TextT.gray, stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Figure(*c:FT|str, # Contents of Figure tag
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Figure tag
)->FT:
"Styled figure container with card-like appearance"
return fh.Figure(*c, cls=('p-4 my-4 border border-gray-200 dark:border-gray-800 rounded-lg shadow-sm bg-card', stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Details(*c:FT|str, # Contents of Details tag
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Details tag
)->FT:
"Styled details element"
return fh.Details(*c, cls=('border border-secondary rounded-lg', stringify(cls)), **kwargs)
def Summary(*c:FT|str, # Contents of Summary tag
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Summary tag
)->FT:
"Styled summary element"
return fh.Summary(*c, cls=(TextT.medium + ' p-3 hover:bg-secondary cursor-pointer', stringify(cls)), **kwargs)
def Data(*c:FT|str, # Contents of Data tag
value:str=None, # Value attribute
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Data tag
)->FT:
"Styled data element"
if value: kwargs['value'] = value
return fh.Data(*c, cls=('font-mono text-sm bg-secondary px-1 rounded', stringify(cls)), **kwargs)
def Meter(*c:FT|str, # Contents of Meter tag
value:float=None, # Current value
min:float=None, # Minimum value
max:float=None, # Maximum value
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Meter tag
)->FT:
"Styled meter element"
if value is not None: kwargs['value'] = value
if min is not None: kwargs['min'] = min
if max is not None: kwargs['max'] = max
return fh.Meter(*c, cls=('w-full h-2 bg-secondary rounded', stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def S(*c:FT|str, # Contents of S tag (strikethrough)
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for S tag
)->FT:
"Styled strikethrough text (different semantic meaning from Del)"
return fh.S(*c, cls=('line-through', TextT.gray, stringify(cls)), **kwargs)
def U(*c:FT|str, # Contents of U tag (unarticulated annotation)
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for U tag
)->FT:
"Styled underline (for proper names in Chinese, proper spelling etc)"
return fh.U(*c, cls=(TextT.underline, stringify(cls)), **kwargs)
def Output(*c:FT|str, # Contents of Output tag
form:str=None, # ID of form this output belongs to
for_:str=None, # IDs of elements this output is for
cls:Enum|str|tuple=(), # Additional classes
**kwargs # Additional args for Output tag
)->FT:
"Styled output element for form results"
if form: kwargs['form'] = form
if for_: kwargs['for'] = for_ # Note: 'for' is reserved in Python
return fh.Output(*c, cls=('font-mono bg-secondary px-2 py-1 rounded',
stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def PicSumImg(h:int=200, # Height in pixels
w:int=200, # Width in pixels
id:int=None, # Optional specific image ID to use
grayscale:bool=False, # Whether to return grayscale version
blur:int=None, # Optional blur amount (1-10)
**kwargs # Additional args for Img tag
)->FT: # Img tag with picsum image
"Creates a placeholder image using https://picsum.photos/"
url = f"https://picsum.photos"
if id is not None: url = f"{url}/id/{id}"
url = f"{url}/{w}/{h}"
if grayscale: url = f"{url}?grayscale"
if blur is not None:
url = f"{url}{'?' if not grayscale else '&'}blur={max(1,min(10,blur))}"
return fh.Img(src=url, loading="lazy", **kwargs)
# %% ../nbs/02_franken.ipynb
def AccordionItem(title: Union[str, FT], # Content for the accordion item title
*c: FT, # Content to display when the item is open
cls: Union[str, Enum, tuple] = (), # Additional classes for the outer `Li` container
title_cls: Union[str, Enum, tuple] = ('flex justify-between items-center w-full',), # Additional classes for the title `A` tag
content_cls: Union[str, Enum, tuple] = (), # Additional classes for the content `Div`
open: bool = False, # Whether this item should be open by default
li_kwargs: Optional[Dict] = None, # Additional attributes for the outer `Li` tag
a_kwargs: Optional[Dict] = None, # Additional attributes for the title `A` tag
div_kwargs: Optional[Dict] = None # Additional attributes for the content `Div` tag
) -> FT: # Li(A(title, Span(Icon, Icon)), Div(content))
"Creates a single item for use within an Accordion component, handling title, content, and open state."
li_attrs, a_attrs, div_attrs = li_kwargs or {}, a_kwargs or {}, div_kwargs or {}
li_classes = ['group', stringify(cls)]
if open: li_classes.append('uk-open')
final_li_cls = stringify(li_classes)
combined_title_cls = stringify(('uk-accordion-title', stringify(title_cls)))
content_classes = stringify(('uk-accordion-content', stringify(content_cls)))
if 'href' not in a_attrs: a_attrs['href'] = '#'
icon_container = Span(
UkIcon("chevron-down", cls="block group-[.uk-open]:hidden h-5 w-5"),
UkIcon("chevron-up", cls="hidden group-[.uk-open]:block h-5 w-5")
)
return fh.Li(
fh.A(title, icon_container, cls=combined_title_cls, **a_attrs),
fh.Div(*c, cls=content_classes, **div_attrs),
cls=final_li_cls,
**li_attrs
)
# %% ../nbs/02_franken.ipynb
def Accordion(*c: 'AccordionItem', # One or more `AccordionItem` components
cls: Union[str, Enum, tuple] = (), # Additional classes for the container (`Ul` or `Div`)
multiple: Optional[bool] = None, # Allow multiple items to be open simultaneously (UIkit option)
collapsible: Optional[bool] = None, # Allow all items to be closed (UIkit option, default True)
animation: Optional[bool] = None, # Enable/disable animation (UIkit option, default True)
duration: Optional[int] = None, # Animation duration in ms (UIkit option, default 200)
active: Optional[int] = None, # Index (0-based) of the item to be open by default (UIkit option)
transition: Optional[str] = None, # Animation transition timing function (UIkit option, e.g., 'ease-out')
tag: str = 'ul', # HTML tag for the container ('ul' or 'div')
**kwargs # Additional attributes for the container tag (e.g., id)
) -> FT: # Ul(*items...) or Div(*items...)
"""
Creates a styled Accordion container using accordion component.
"""
opts = []
if multiple is not None: opts.append(f"multiple: {str(multiple).lower()}")
if collapsible is not None: opts.append(f"collapsible: {str(collapsible).lower()}")
if animation is not None: opts.append(f"animation: {str(animation).lower()}")
if duration is not None: opts.append(f"duration: {duration}")
if active is not None: opts.append(f"active: {active}")
if transition is not None: opts.append(f"transition: {transition}")
uk_attr_val = "; ".join(opts) if opts else ""
kwargs['uk-accordion'] = uk_attr_val
container_tag = fh.Ul if tag.lower() == 'ul' else fh.Div
final_cls = stringify(cls)
return container_tag(*c, cls=final_cls, **kwargs)
# %% ../nbs/02_franken.ipynb
class ButtonT(VEnum):
"Options for styling Buttons"
def _generate_next_value_(name, start, count, last_values): return str2ukcls('btn', name)
default, ghost, primary = auto(),auto(),auto()
secondary, destructive = auto(), auto()
text, link = auto(), auto()
xs, sm, lg, xl = auto(), auto(), auto(), auto()
icon = auto()
# %% ../nbs/02_franken.ipynb
def Button(*c: Union[str, FT], # Contents of `Button` tag (often text)
cls: Union[str, Enum]=ButtonT.default, # Classes in addition to `Button` styling (use `ButtonT` for built in styles)
submit=True, # Whether the button should submit a form
**kwargs # Additional args for `Button` tag
) -> FT: # Button(..., cls='uk-btn')
"Button with Styling (defaults to `submit` for form submission)"
if 'type' not in kwargs: kwargs['type'] = 'submit' if submit else 'button'
return fh.Button(*c, cls=('uk-btn', stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
class ContainerT(VEnum):
'Max width container sizes from https://franken-ui.dev/docs/container'
def _generate_next_value_(name, start, count, last_values): return str2ukcls('container', name)
xs = auto()
sm = auto()
lg = auto()
xl = auto()
expand = auto()
# %% ../nbs/02_franken.ipynb
class BackgroundT(VEnum):
def _generate_next_value_(name, start, count, last_values): return str2ukcls('background', name)
muted = auto()
primary = auto()
secondary = auto()
default = auto()
# %% ../nbs/02_franken.ipynb
def Container(*c, # Contents of Container tag (often other FT Components)
cls=('mt-5', ContainerT.xl), # Classes in addition to Container styling
**kwargs # Additional args for Container (`Div` tag)
)->FT: # Container(..., cls='uk-container')
"Div to be used as a container that often wraps large sections or a page of content"
return Div(*c, cls=('uk-container',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Titled(title:str="FastHTML app", # Title of the page
*c, # Contents of the page (often other tags)
cls=ContainerT.xl, # Classes in addition to Container styling
**kwargs # Additional args for Container (`Div` tag)
)->FT: # Title, Main(Container(H1(title), content))
"Creates a standard page structure for titled page. Main(Container(title, content))"
return fh.Title(title), fh.Main(Container(H1(title), *c, cls=cls, **kwargs))
# %% ../nbs/02_franken.ipynb
class DividerT(VEnum):
"Divider Styles from https://franken-ui.dev/docs/divider"
def _generate_next_value_(name, start, count, last_values): return str2ukcls('divider', name)
icon=auto()
sm=auto()
vertical=auto()
# %% ../nbs/02_franken.ipynb
def Divider(*c, # contents of Divider tag (often nothing)
cls=('my-4', DividerT.icon), # Classes in addition to Divider styling
**kwargs # Additional args for Divider tag
)->FT: # Hr(..., cls='uk-divider-icon') or Div(..., cls='uk-divider-vertical')
"Divider with default styling and margin"
cls = stringify(cls)
container = Div if 'uk-divider-vertical' in cls else Hr
return container(*c, cls=cls, **kwargs)
# %% ../nbs/02_franken.ipynb
def DividerSplit(*c, cls=(), line_cls=(), text_cls=()):
"Creates a simple horizontal line divider with configurable thickness and vertical spacing"
cls, line_cls, text_cls = map(stringify,(cls, line_cls, text_cls))
return Div(cls='relative ' + cls)(
Div(cls="absolute inset-0 flex items-center " + line_cls)(Span(cls="w-full border-t border-border")),
Div(cls="relative flex justify-center " + text_cls)(Span(cls="bg-background px-2 ")(*c)))
# %% ../nbs/02_franken.ipynb
def DividerLine(lwidth=2, y_space=4): return Hr(cls=f"my-{y_space} h-[{lwidth}px] w-full bg-secondary")
# %% ../nbs/02_franken.ipynb
def Article(*c, # contents of Article tag (often other tags)
cls=(), # Classes in addition to Article styling
**kwargs # Additional args for Article tag
)->FT: # Article(..., cls='uk-article')
"A styled article container for blog posts or similar content"
return fh.Article(*c, cls=('uk-article',stringify(cls)), **kwargs)
def ArticleTitle(*c, # contents of ArticleTitle tag (often other tags)
cls=(), # Classes in addition to ArticleTitle styling
**kwargs # Additional args for ArticleTitle tag
)->FT: # H1(..., cls='uk-article-title')
"A title component for use within an Article"
return H1(*c, cls=('uk-article-title',stringify(cls)), **kwargs)
def ArticleMeta(*c, # contents of ArticleMeta tag (often other tags)
cls=(), # Classes in addition to ArticleMeta styling
**kwargs # Additional args for ArticleMeta tag
)->FT: # P(..., cls='uk-article-meta')
"A metadata component for use within an Article showing things like date, author etc"
return P(*c, cls=('uk-article-meta',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
class SectionT(VEnum):
'Section styles from https://franken-ui.dev/docs/section'
def _generate_next_value_(name, start, count, last_values): return str2ukcls('section', name)
default = auto()
muted = auto()
primary = auto()
secondary = auto()
xs = 'uk-section-xsmall'
sm = 'uk-section-small'
lg = 'uk-section-large'
xl = 'uk-section-xlarge'
remove_vertical = auto()
# %% ../nbs/02_franken.ipynb
def Section(*c, # contents of Section tag (often other tags)
cls=(), # Classes in addition to Section styling
**kwargs # Additional args for Section tag
)->FT: # Div(..., cls='uk-section')
"Section with styling and margins"
return fh.Div(*c, cls=('uk-section',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Form(*c, # contents of Form tag (often Buttons, FormLabels, and LabelInputs)
cls='space-y-3', # Classes in addition to Form styling (default is 'space-y-3' to prevent scrunched up form elements)
**kwargs # Additional args for Form tag
)->FT: # Form(..., cls='space-y-3')
"A Form with default spacing between form elements"
return fh.Form(*c, cls=stringify(cls), **kwargs)
# %% ../nbs/02_franken.ipynb
def Fieldset(*c, # contents of Fieldset tag (often other tags)
cls='flex', # Classes in addition to Fieldset styling
**kwargs # Additional args for Fieldset tag
)->FT: # Fieldset(..., cls='uk-fieldset')
"A Fieldset with default styling"
return fh.Fieldset(*c, cls=('uk-fieldset',stringify(cls)), **kwargs)
def Legend(*c, # contents of Legend tag (often other tags)
cls=(), # Classes in addition to Legend styling
**kwargs # Additional args for Legend tag
)->FT: # Legend(..., cls='uk-legend')
"A Legend with default styling"
return fh.Legend(*c, cls=('uk-legend',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Input(*c, # contents of Input tag (often nothing)
cls=(), # Classes in addition to Input styling
**kwargs # Additional args for Input tag
)->FT: # Input(..., cls='uk-input')
"An Input with default styling"
return fh.Input(*c, cls=('uk-input',stringify(cls)), **kwargs)
def Radio(*c, # contents of Radio tag (often nothing)
cls=(), # Classes in addition to Radio styling
**kwargs # Additional args for Radio tag
)->FT: # Input(..., cls='uk-radio', type='radio')
"A Radio with default styling"
return fh.Input(*c, cls=('uk-radio',stringify(cls)), type='radio', **kwargs)
def CheckboxX(*c, # contents of CheckboxX tag (often nothing)
cls=(), # Classes in addition to CheckboxX styling
**kwargs # Additional args for CheckboxX tag
)->FT: # Input(..., cls='uk-checkbox', type='checkbox')
"A Checkbox with default styling"
return fh.Input(*c, cls=('uk-checkbox',stringify(cls)), type='checkbox', **kwargs)
# %% ../nbs/02_franken.ipynb
def Range(*c, # contents of Range tag (often nothing)
value='',
label=True,
min=None,
max=None,
step=None,
cls=(), # Classes in addition to Range styling
**kwargs # Additional args for Range tag
)->FT: # Input(..., cls='uk-range', type='range')
"A Range with default styling"
return Uk_input_range(*c, min=min, label=label, max=max, value=value, step=step, multiple=len(value.split(','))>1, cls=('uk-range',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def TextArea(*c, # contents of TextArea tag (often text)
cls=(), # Classes in addition to TextArea styling
**kwargs # Additional args for TextArea tag
)->FT: # TextArea(..., cls='uk-textarea')
"A Textarea with default styling"
return fh.Textarea(*c, cls=('uk-textarea',stringify(cls)), **kwargs)
def Switch(*c, # contents of Switch tag (often nothing)
cls=(), # Classes in addition to Switch styling
**kwargs # Additional args for Switch tag
)->FT: # Input(..., cls='uk-toggle-switch uk-toggle-switch-primary min-w-9', type='checkbox')
"A Switch with default styling"
return fh.Input(*c, cls=('uk-toggle-switch uk-toggle-switch-primary min-w-9',stringify(cls)), type='checkbox', **kwargs)
# %% ../nbs/02_franken.ipynb
def Upload(*c, # Contents of Upload tag button (often text)
cls=(), # Classes in addition to Upload styling
multiple=False, # Whether to allow multiple file selection
accept=None, # File types to accept (e.g. 'image/*')
button_cls=ButtonT.default, # Classes for the button
id=None, # ID for the file input
name=None, # Name for the file input
**kwargs # Additional args for the outer div
)->FT: # Div(Input(type='file'), Button(...))
"A file upload component with default styling"
input_kwargs = {'type': 'file', 'multiple': multiple}
if accept: input_kwargs['accept'] = accept
if id: input_kwargs['id'] = id
if name: input_kwargs['name'] = name
return Div(
fh.Input(**input_kwargs),
Button(*c, cls=button_cls, submit=False, tabindex="-1"),
cls=('w-full js-upload', stringify(cls)),
uk_form_custom=True)
def UploadZone(*c, # Contents of UploadZone tag (often text or other tags)
cls=(), # Classes in addition to UploadZone styling
multiple=False, # Whether to allow multiple file selection
accept=None, # File types to accept (e.g. 'image/*')
id=None, # ID for the file input
name=None, # Name for the file input
**kwargs # Additional args for the outer div
)->FT:
"A file drop zone component with default styling"
input_kwargs = {'type': 'file', 'multiple': multiple}
if accept: input_kwargs['accept'] = accept
if id: input_kwargs['id'] = id
if name: input_kwargs['name'] = name
return Div(
Div(fh.Input(**input_kwargs),
Span(*c),
uk_form_custom=True,
cls='w-full'),
cls=('js-upload uk-placeholder uk-text-center', stringify(cls)),
**kwargs)
# %% ../nbs/02_franken.ipynb
def FormLabel(*c, # contents of FormLabel tag (often text)
cls=(), # Classes in addition to FormLabel styling
**kwargs # Additional args for FormLabel tag
)->FT: # Label(..., cls='uk-form-label')
"A Label with default styling"
return fh.Label(*c, cls=('uk-form-label',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
class LabelT(VEnum):
def _generate_next_value_(name, start, count, last_values): return str2ukcls('label', name)
primary = auto()
secondary = auto()
destructive = auto()
# %% ../nbs/02_franken.ipynb
def Label(*c, # contents of Label tag (often text)
cls=(), # Classes in addition to Label styling
**kwargs # Additional args for Label tag
)->FT: # Label(..., cls='uk-label')
"FrankenUI labels, which look like pills"
return fh.Label(*c, cls=('uk-label',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def UkFormSection(title, description, *c, button_txt='Update', outer_margin=6, inner_margin=6):
"A form section with a title, description and optional button"
return Div(cls=f'space-y-{inner_margin} py-{outer_margin}')(
Div(H3(title), P(description, cls=TextPresets.muted_sm)),
DividerSplit(), *c,
Div(Button(button_txt, cls=ButtonT.primary)) if button_txt else None)
# %% ../nbs/02_franken.ipynb
def GenericLabelInput(
label:str|FT, # FormLabel content (often text)
lbl_cls='', # Additional classes for FormLabel
input_cls='', # Additional classes for user input (Input, Select, etc)
container=Div, # Container to wrap label and input in (default is Div)
cls='', # Classes on container (default is '')
id=None, # id for label and input (`id`, `name` and `for` attributes are set to this value). If `label` is str, this defaults to `label.lower()`
input_fn=noop, # User input FT component
**kwargs # Additional args for user input
):
"`Div(Label,Input)` component with Uk styling injected appropriately. Generally you should higher level API, such as `LabelInput` which is created for you in this library"
if not id and isinstance(label, str): id = label.lower()
if isinstance(label, str) or label.tag != 'label':
label = FormLabel(cls=stringify(lbl_cls), fr=id)(label)
inp = input_fn(id=id, cls=stringify(input_cls), **kwargs)
if container: return container(label, inp, cls=stringify(cls))
return label, inp
# %% ../nbs/02_franken.ipynb
def LabelInput(label:str|FT, # FormLabel content (often text)
lbl_cls='', # Additional classes for `FormLabel`
input_cls='', # Additional classes for `Input`
cls='space-y-2', # Classes on container (default is `'space-y-2'` to prevent scrunched up form elements)
id='', # id for `FormLabel` and `Input` (`id`, `name` and `for` attributes are set to this value)
**kwargs # Additional args for `Input`
)->FT: # Div(cls='space-y-2')(`FormLabel`, `Input`)
"A `FormLabel` and `Input` pair that provides default spacing and links/names them based on id"
return GenericLabelInput(label=label, lbl_cls=lbl_cls, input_cls=input_cls,
container=Div, cls=cls, id=id, input_fn=Input, **kwargs)
# %% ../nbs/02_franken.ipynb
def LabelTextArea(label:str|FT, # FormLabel content (often text)
value='', # Value for the textarea
lbl_cls='', # Additional classes for `FormLabel`
input_cls='', # Additional classes for `TextArea`
cls='space-y-2', # Classes on container (default is `'space-y-2'` to prevent scrunched up form elements)
id='', # id for `FormLabel` and `TextArea` (`id`, `name` and `for` attributes are set to this value)
**kwargs # Additional args for `TextArea`
)->FT: # Div(cls='space-y-2')(`FormLabel`, `TextArea`)
def text_area_with_value(**kw): return TextArea(value, **kw)
return GenericLabelInput(label=label, lbl_cls=lbl_cls, input_cls=input_cls,
container=Div, cls=cls, id=id, input_fn=text_area_with_value, **kwargs)
# %% ../nbs/02_franken.ipynb
@delegates(GenericLabelInput, but=['input_fn','cls'])
def LabelSwitch(label:str|FT, # FormLabel content (often text)
lbl_cls='', # Additional classes for `FormLabel`
input_cls='', # Additional classes for `Switch`
cls='space-x-2', # Classes on container (default is `'space-x-2'` to prevent scrunched up form elements)
id='', # id for `FormLabel` and `Switch` (`id`, `name` and `for` attributes are set to this value)
**kwargs # Additional args for `Switch`
)->FT: # Div(cls='space-y-2')(`FormLabel`, `Switch`)
return GenericLabelInput(label=label, lbl_cls=lbl_cls, input_cls=input_cls,
container=Div, cls=cls, id=id, input_fn=Switch, **kwargs)
# %% ../nbs/02_franken.ipynb
def LabelRadio(label:str|FT, # FormLabel content (often text)
lbl_cls='', # Additional classes for `FormLabel`
input_cls='', # Additional classes for `Radio`
container=Div, # Container to wrap label and input in (default is Div)
cls='flex items-center space-x-2', # Classes on container (default is 'flex items-center space-x-2')
id='', # id for `FormLabel` and `Radio` (`id`, `name` and `for` attributes are set to this value)
**kwargs # Additional args for `Radio`
)->FT: # Div(cls='flex items-center space-x-2')(`FormLabel`, `Radio`)
"A FormLabel and Radio pair that provides default spacing and links/names them based on id"
if isinstance(label, str) or label.tag != 'label':
label = FormLabel(cls=stringify(lbl_cls), fr=id)(label)
inp = Radio(id=id, cls=stringify(input_cls), **kwargs)
if container: return container(inp, label, cls=stringify(cls))
return inp, label
# %% ../nbs/02_franken.ipynb
def LabelCheckboxX(label:str|FT, # FormLabel content (often text)
lbl_cls='', # Additional classes for `FormLabel`
input_cls='', # Additional classes for `CheckboxX`
container=Div, # Container to wrap label and input in (default is Div)
cls='flex items-center space-x-2', # Classes on container (default is 'flex items-center space-x-2')
id='', # id for `FormLabel` and `CheckboxX` (`id`, `name` and `for` attributes are set to this value)
**kwargs # Additional args for `CheckboxX`
)->FT: # Div(cls='flex items-center space-x-2')(`FormLabel`, `CheckboxX`)
"A FormLabel and CheckboxX pair that provides default spacing and links/names them based on id"
if not id: id = fh.unqid()
if isinstance(label, str) or label.tag != 'label':
label = FormLabel(cls=stringify(lbl_cls), fr=id)(label)
inp = CheckboxX(id=id, cls=stringify(input_cls), **kwargs)
if container: return container(inp, label, cls=stringify(cls))
return inp, label
# %% ../nbs/02_franken.ipynb
def Options(*c, # Content for an `Option`
selected_idx:int=None, # Index location of selected `Option`
disabled_idxs:set=None # Idex locations of disabled `Options`
):
"Helper function to wrap things into `Option`s for use in `Select`"
return [fh.Option(o,selected=i==selected_idx, disabled=disabled_idxs and i in disabled_idxs) for i,o in enumerate(c)]
# %% ../nbs/02_franken.ipynb
def Select(*option, # Options for the select dropdown (can use `Options` helper function to create)
inp_cls=(), # Additional classes for the select input
cls=('h-10',), # Classes for the outer div (default h-10 for consistent height)
cls_custom='button: uk-input-fake; dropdown: w-full', # Classes for the Uk_Select web component
id="", # ID for the select input
name="", # Name attribute for the select input
placeholder="", # Placeholder text for the select input
searchable=False, # Whether the select should be searchable
insertable=False, # Whether to allow user-defined options to be added
select_kwargs=None, # Additional Arguments passed to Select
**kwargs # Additional arguments passed to Uk_select
):
"Creates a select dropdown with uk styling and option for adding a search box"
inp_cls, cls, cls_custom= map(stringify, (inp_cls, cls, cls_custom))
select_kwargs = ifnone(select_kwargs, {})
if 'hx_trigger' not in kwargs: kwargs['hx_trigger']=''
if 'change' in kwargs['hx_trigger']:
if not id: id = fh.unqid()
kwargs['hx_trigger'] = kwargs['hx_trigger'].replace('changed', f'uk-select:input from:#{id}')
kwargs['hx_trigger'] = kwargs['hx_trigger'].replace('change', f'uk-select:input from:#{id}')
if 'delay' not in kwargs['hx_trigger']:
kwargs['hx_trigger'] += ' delay:100ms'
if 'hx_include' not in kwargs: kwargs['hx_include']=''
kwargs['hx_include'] += ' this'
kwargs['hx_include'] = kwargs['hx_include'].strip()
if id and not name: name = id
uk_select = Uk_select(fh.Select(*option, hidden=True,
**select_kwargs,
),
cls_custom=cls_custom,
searchable=searchable,
placeholder=placeholder,
insertable=insertable,
cls=inp_cls,
id=id,
name=name,
**kwargs
)
return Div(cls=cls)(uk_select)
# %% ../nbs/02_franken.ipynb
def LabelSelect(*option, # Options for the select dropdown (can use `Options` helper function to create)
label=(), # String or FT component for the label
lbl_cls=(), # Additional classes for the label
inp_cls=(), # Additional classes for the select input
cls=('space-y-2',), # Classes for the outer div
id="", # ID for the select input
name="", # Name attribute for the select input
placeholder="", # Placeholder text for the select input
searchable=False, # Whether the select should be searchable
select_kwargs=None, # Additional Arguments passed to Select
**kwargs): # Additional arguments passed to Select
"A FormLabel and Select pair that provides default spacing and links/names them based on id"
lbl_cls, inp_cls, cls = map(stringify, (lbl_cls, inp_cls, cls))
select_kwargs = ifnone(select_kwargs, {})
if label:
lbl = FormLabel(cls=f'{lbl_cls}', fr=id)(label)
select = Select(*option, inp_cls=inp_cls, id=id, name=name if name else id,
placeholder=placeholder, searchable=searchable, select_kwargs=select_kwargs, **kwargs)
return Div(cls=cls)(lbl, select) if label else Div(cls=cls)(select)
# %% ../nbs/02_franken.ipynb
@delegates(GenericLabelInput, but=['input_fn','cls'])
def LabelRange(label:str|FT, # FormLabel content (often text)
lbl_cls='', # Additional classes for `FormLabel`
input_cls='', # Additional classes for `Range`
cls='space-y-6', # Classes on container (default is `'space-y-2'` to prevent scrunched up form elements)
id='', # id for `FormLabel` and `Range` (`id`, `name` and `for` attributes are set to this value)
value='', # Value for the range input
min=None, # Minimum value
max=None, # Maximum value
step=None, # Step size
label_range=True, # Whether to show the range value label (label for the `Range` component)
**kwargs # Additional args for `Range`
)->FT: # Div(cls='space-y-2')(`FormLabel`, `Range`)
"A FormLabel and Range pair that provides default spacing and links/names them based on id"
def range_with_value(**kw):
return Div(Range(value=value, min=min, max=max, step=step, label=label_range, **kw))
return GenericLabelInput(label=label, lbl_cls=lbl_cls, input_cls=input_cls,
container=Div, cls=cls, id=id, input_fn=range_with_value, **kwargs)
# %% ../nbs/02_franken.ipynb
class AT(VEnum):
'Link styles from https://franken-ui.dev/docs/link'
def _generate_next_value_(name, start, count, last_values): return str2ukcls('link', name)
muted = auto()
text = auto()
reset = auto()
primary = 'uk-link text-primary hover:text-primary-focus underline'
classic = 'text-blue-600 hover:text-blue-800 underline'
# %% ../nbs/02_franken.ipynb
class ListT(VEnum):
'List styles using Tailwind CSS'
disc = 'list-disc list-inside'
circle = 'list-[circle] list-inside'
square = 'list-[square] list-inside'
decimal = 'uk-list uk-list-decimal'
hyphen = 'uk-list uk-list-hyphen'
bullet = 'uk-list uk-list-bullet'
divider = 'uk-list uk-list-divider'
striped = 'uk-list uk-list-striped'
# %% ../nbs/02_franken.ipynb
def ModalContainer(*c, # Components to put in the modal (often `ModalDialog`)
cls=(), # Additional classes on the `ModalContainer`
**kwargs # Additional args for `Div` tag
)->FT: # Div(..., cls='uk-modal uk-modal-container')
"Creates a modal container that components go in"
return fh.Div(*c, cls=('uk-modal uk-modal-container',stringify(cls)), data_uk_modal=True, **kwargs)
def ModalDialog(*c, # Components to put in the `ModalDialog` (often `ModalBody`, `ModalHeader`, etc)
cls=(), # Additional classes on the `ModalDialog`
**kwargs # Additional args for `Div` tag
)->FT: # Div(..., cls='uk-modal-dialog')
"Creates a modal dialog"
return fh.Div(*c, cls=('uk-modal-dialog', stringify(cls)), **kwargs)
def ModalHeader(*c, # Components to put in the `ModalHeader`
cls=(), # Additional classes on the `ModalHeader`
**kwargs # Additional args for `Div` tag
)->FT: # Div(..., cls='uk-modal-header')
"Creates a modal header"
return fh.Div(*c, cls=('uk-modal-header', stringify(cls)), **kwargs)
def ModalBody(*c, # Components to put in the `ModalBody` (often forms, sign in buttons, images, etc.)
cls=(), # Additional classes on the `ModalBody`
**kwargs # Additional args for `Div` tag
)->FT: # Div(..., cls='uk-modal-body')
"Creates a modal body"
return fh.Div(*c, cls=('uk-modal-body', stringify(cls)), **kwargs)
def ModalFooter(*c, # Components to put in the `ModalFooter` (often buttons)
cls=(), # Additional classes on the `ModalFooter`
**kwargs # Additional args for `Div` tag
)->FT: # Div(..., cls='uk-modal-footer')
"Creates a modal footer"
return fh.Div(*c, cls=('uk-modal-footer', stringify(cls)), **kwargs)
def ModalTitle(*c, # Components to put in the `ModalTitle` (often text)
cls=(), # Additional classes on the `ModalTitle`
**kwargs # Additional args for `H2` tag
)->FT: # H2(..., cls='uk-modal-title')
"Creates a modal title"
return fh.H2(*c, cls=('uk-modal-title', stringify(cls)), **kwargs)
def ModalCloseButton(*c, # Components to put in the button (often text and/or an icon)
cls="absolute top-3 right-3", # Additional classes on the button
**kwargs # Additional args for `Button` tag
)->FT: # Button(..., cls='uk-modal-close')
"Creates a button that closes a modal with js"
cls = (stringify(cls), 'uk-modal-close')
kwargs['data-uk-close'] = True
return Button(*c, cls=(stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Modal(*c, # Components to put in the `ModalBody` (often forms, sign in buttons, images, etc.)
header=None, # Components that go in the `ModalHeader` (often a `ModalTitle`)
footer=None, # Components that go in the `ModalFooter` (often a `ModalCloseButton`)
cls=(), # Additional classes on the outermost `ModalContainer`
dialog_cls=(), # Additional classes on the `ModalDialog`
header_cls='p-6', # Additional classes on the `ModalHeader`
body_cls='space-y-6', # Additional classes on the `ModalBody`
footer_cls=(), # Additional classes on the `ModalFooter`
id='', # id for the outermost container
hx_init=False, # Initialize modal with UIKit on load (used for modals added to the DOM by HTMX)
hx_open=False, # Open modal on load (used for modals added to the DOM by HTMX)
**kwargs # Additional args for the outermost `Div` tag
)->FT: # Fully styled modal FT Component
"Creates a modal with the appropriate classes to put the boilerplate in the appropriate places for you"
if not id: id = fh.unqid()
if hx_open: kwargs["hx_on__load"] = f"UIkit.modal('#{id}').show()"
if hx_init and not hx_open: kwargs["hx_on__load"] = f"UIkit.modal('#{id}')"
if hx_open or hx_init: kwargs["hx-on:hidden"] = "this.remove()"
cls, dialog_cls, header_cls, body_cls, footer_cls = map(stringify, (cls, dialog_cls, header_cls, body_cls, footer_cls))
res = []
if header: res.append(ModalHeader(cls=header_cls)(header))
res.append(ModalBody(cls=body_cls)(*c))
if footer: res.append(ModalFooter(cls=footer_cls)(footer))
return ModalContainer(ModalDialog(*res, cls=dialog_cls), cls=cls, id=id, **kwargs)
# %% ../nbs/02_franken.ipynb
def Placeholder(*c, # Components to put in the placeholder
cls=(), # Additional classes on the placeholder
**kwargs # Additional args for `Div` tag
)->FT: # Div(..., cls='uk-placeholder')
"Creates a placeholder"
return fh.Div(*c, cls=('uk-placeholder',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Progress(*c, # Components to put in the progress bar (often nothing)
cls=(), # Additional classes on the progress bar
value="", # Value of the progress bar
max="100", # Max value of the progress bar (defaults to 100 for percentage)
**kwargs # Additional args for `Progress` tag
)->FT: # Progress(..., cls='uk-progress')
"Creates a progress bar"
return fh.Progress(*c, value=value, max=max, cls=('uk-progress',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def UkIcon(icon:str, # Icon name from [lucide icons](https://lucide.dev/icons/)
height:int=None,
width:int=None,
stroke_width:int=None, # Thickness of lines
cls=(), # Additional classes on the `Uk_icon` tag
**kwargs # Additional args for `Uk_icon` tag
)->FT: # a lucide icon of the specified size
"Creates an icon using lucide icons"
return Uk_icon(icon=icon, height=height, width=width, stroke_width=stroke_width, cls=cls, **kwargs)
# %% ../nbs/02_franken.ipynb
def UkIconLink(icon:str, # Icon name from [lucide icons](https://lucide.dev/icons/)
height:int=None,
width:int=None,
stroke_width:int=None, # Thickness of lines
cls=(), # Additional classes on the icon
button:bool=False, # Whether to use a button (defaults to a link)
**kwargs # Additional args for `A` or `Button` tag
)->FT: # a lucide icon button or link of the specified size
"Creates an icon link using lucide icons"
fn = fh.Button if button else fh.A
return fn(cls=(f"uk-icon-{'button' if button else 'link'}", stringify(cls)), **kwargs)(
UkIcon(icon=icon, height=height, width=width, stroke_width=stroke_width))
# %% ../nbs/02_franken.ipynb
def DiceBearAvatar(seed_name:str, # Seed name (ie 'Isaac Flath')
h:int=20, # Height
w:int=20, # Width
): # Span with Avatar
"Creates an Avatar using https://dicebear.com/"
url = 'https://api.dicebear.com/8.x/lorelei/svg?seed='
return Span(cls=f"relative flex h-{h} w-{w} shrink-0 overflow-hidden rounded-full bg-secondary")(
fh.Img(cls=f"aspect-square h-{h} w-{w}", alt="Avatar", loading="lazy", src=f"{url}{seed_name}"))
# %% ../nbs/02_franken.ipynb
def Center(*c, # Components to center
vertical:bool=True, # Whether to center vertically
horizontal:bool=True, # Whether to center horizontally
cls=(), # Additional classes
**kwargs # Additional args for container div
)->FT: # Div with centered contents
"Centers contents both vertically and horizontally by default"
classes = ['flex']
if vertical: classes.append('items-center min-h-full')
if horizontal: classes.append('justify-center min-w-full')
return fh_comp.Center(*c, cls=(stringify(classes), stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
class FlexT(VEnum):
'Flexbox modifiers using Tailwind CSS'
def _generate_next_value_(name, start, count, last_values): return name
# Display
block = 'flex'
inline = 'inline-flex'
# Horizontal Alignment
left = 'justify-start'
center = 'justify-center'
right = 'justify-end'
between = 'justify-between'
around = 'justify-around'
# Vertical Alignment
stretch = 'items-stretch'
top = 'items-start'
middle = 'items-center'
bottom = 'items-end'
# Direction
row = 'flex-row'
row_reverse = 'flex-row-reverse'
column = 'flex-col'
column_reverse = 'flex-col-reverse'
# Wrap
nowrap = 'flex-nowrap'
wrap = 'flex-wrap'
wrap_reverse = 'flex-wrap-reverse'
# %% ../nbs/02_franken.ipynb
def Grid(*div, # `Div` components to put in the grid
cols_min:int=1, # Minimum number of columns at any screen size
cols_max:int=4, # Maximum number of columns allowed at any screen size
cols_sm:int=None, # Number of columns on small screens
cols_md:int=None, # Number of columns on medium screens
cols_lg:int=None, # Number of columns on large screens
cols_xl:int=None, # Number of columns on extra large screens
cols:int=None, # Number of columns on all screens
cls='gap-4', # Additional classes on the grid (tip: `gap` provides spacing for grids)
**kwargs # Additional args for `Div` tag
)->FT: # Responsive grid component
"Creates a responsive grid layout with smart defaults based on content"
if cols: cols_min = cols_sm = cols_md = cols_lg = cols_xl = cols
else:
n = len(div)
cols_max = min(n, cols_max)
cols_sm = cols_sm or min(n, cols_min, cols_max)
cols_md = cols_md or min(n, cols_min+1, cols_max)
cols_lg = cols_lg or min(n, cols_min+2, cols_max)
cols_xl = cols_xl or cols_max
return Div(cls=(f'grid grid-cols-{cols_min} sm:grid-cols-{cols_sm} md:grid-cols-{cols_md} lg:grid-cols-{cols_lg} xl:grid-cols-{cols_xl}', stringify(cls)), **kwargs)(*div)
# %% ../nbs/02_franken.ipynb
def DivFullySpaced(*c, # Components
cls='w-full',# Classes for outer div (`w-full` makes it use all available width)
**kwargs # Additional args for outer div
): # Div with spaced components via flex classes
"Creates a flex div with it's components having as much space between them as possible"
cls = stringify(cls)
return Div(cls=(FlexT.block,FlexT.between,FlexT.middle,cls), **kwargs)(*c)
# %% ../nbs/02_franken.ipynb
def DivCentered(*c, # Components
cls='space-y-4', # Classes for outer div (`space-y-4` provides spacing between components)
vstack=True, # Whether to stack the components vertically
**kwargs # Additional args for outer div
)->FT: # Div with components centered in it
"Creates a flex div with it's components centered in it"
cls=stringify(cls)
return Div(cls=(FlexT.block,(FlexT.column if vstack else FlexT.row),FlexT.middle,FlexT.center,cls),**kwargs)(*c)
# %% ../nbs/02_franken.ipynb
def DivLAligned(*c, # Components
cls='space-x-4', # Classes for outer div
**kwargs # Additional args for outer div
)->FT: # Div with components aligned to the left
"Creates a flex div with it's components aligned to the left"
cls=stringify(cls)
return Div(cls=(FlexT.block,FlexT.left,FlexT.middle,cls), **kwargs)(*c)
# %% ../nbs/02_franken.ipynb
def DivRAligned(*c, # Components
cls='space-x-4', # Classes for outer div
**kwargs # Additional args for outer div
)->FT: # Div with components aligned to the right
"Creates a flex div with it's components aligned to the right"
cls=stringify(cls)
return Div(cls=(FlexT.block,FlexT.right,FlexT.middle,cls), **kwargs)(*c)
# %% ../nbs/02_franken.ipynb
def DivVStacked(*c, # Components
cls='space-y-4', # Additional classes on the div (tip: `space-y-4` provides spacing between components)
**kwargs # Additional args for the div
)->FT: # Div with components stacked vertically
"Creates a flex div with it's components stacked vertically"
cls=stringify(cls)
return Div(cls=(FlexT.block,FlexT.column,FlexT.middle,cls), **kwargs)(*c)
# %% ../nbs/02_franken.ipynb
def DivHStacked(*c, # Components
cls='space-x-4', # Additional classes on the div (`space-x-4` provides spacing between components)
**kwargs # Additional args for the div
)->FT: # Div with components stacked horizontally
"Creates a flex div with it's components stacked horizontally"
cls=stringify(cls)
return Div(cls=(FlexT.block,FlexT.row,FlexT.middle,cls), **kwargs)(*c)
# %% ../nbs/02_franken.ipynb
class NavT(VEnum):
def _generate_next_value_(name, start, count, last_values): return str2ukcls('nav', name)
default = auto()
primary = auto()
secondary = auto()
# %% ../nbs/02_franken.ipynb
def NavContainer(*li, # List items are navigation elements (Special `Li` such as `NavParentLi`, `NavDividerLi`, `NavHeaderLi`, `NavSubtitle`, `NavCloseLi` can also be used)
cls=NavT.primary, # Additional classes on the nav
parent=True, # Whether this nav is a *parent* or *sub* nav
uk_nav=False, #True for default collapsible behavior, see [frankenui docs](https://franken-ui.dev/docs/nav#component-options) for more advanced options
uk_scrollspy_nav=False, # Activates scrollspy linking each item `A` tags `href` to content's `id` attribute
sticky=False, # Whether to stick to the top of the page while scrolling
**kwargs # Additional args
)->FT: # FT Component that is a list of `Li` styled for a sidebar navigation menu
"Creates a navigation container (useful for creating a sidebar navigation). A Nav is a list (NavBar is something different)"
_uk_scrollspy_nav = False
if uk_scrollspy_nav:
if isinstance(uk_scrollspy_nav, bool): _uk_scrollspy_nav = 'closest: li; scroll: true' if uk_scrollspy_nav else False
else: _uk_scrollspy_nav = uk_scrollspy_nav
_sticky = 'float-left sticky top-4 hidden md:block' if sticky else ''
return fh.Ul(*li, uk_nav=uk_nav, cls=(f"uk-nav{'' if parent else '-sub'}", stringify(cls), _sticky), uk_scrollspy_nav=_uk_scrollspy_nav, **kwargs)
# %% ../nbs/02_franken.ipynb
def NavParentLi(*nav_container, # `NavContainer` container for a nested nav with `parent=False`)
cls=(), # Additional classes on the li
**kwargs # Additional args for the li
)->FT: # Navigation list item
"Creates a navigation list item with a parent nav for nesting"
return fh.Li(*nav_container, cls=('uk-parent', stringify(cls)),**kwargs)
def NavDividerLi(*c, # Components
cls=(), # Additional classes on the li
**kwargs # Additional args for the li
)->FT: # Navigation list item with a divider
"Creates a navigation list item with a divider"
return fh.Li(*c, cls=('uk-nav-divider', stringify(cls)),**kwargs)
def NavHeaderLi(*c, # Components
cls=(), # Additional classes on the li
**kwargs # Additional args for the li
)->FT: # Navigation list item with a header
"Creates a navigation list item with a header"
return fh.Li(*c, cls=('uk-nav-header', stringify(cls)),**kwargs)
def NavSubtitle(*c, # Components
cls=TextPresets.muted_sm, # Additional classes on the div
**kwargs # Additional args for the div
)->FT: # Navigation subtitle
"Creates a navigation subtitle"
return fh.Div(*c, cls=('uk-nav-subtitle', stringify(cls)),**kwargs)
def NavCloseLi(*c, # Components
cls=(), # Additional classes on the li
**kwargs # Additional args for the li
)->FT: # Navigation list item with a close button
"Creates a navigation list item with a close button"
return fh.Li(*c, cls=('uk-drop-close', stringify(cls)),**kwargs)
# %% ../nbs/02_franken.ipynb
class ScrollspyT(VEnum):
underline = 'navbar-underline'
bold = 'navbar-bold'
# %% ../nbs/02_franken.ipynb
def NavBar(*c, # Component for right side of navbar (Often A tag links)
brand=H3("Title"), # Brand/logo component for left side
right_cls='items-center space-x-4', # Spacing for desktop links
mobile_cls='space-y-4', # Spacing for mobile links
sticky:bool=False, # Whether to stick to the top of the page while scrolling
uk_scrollspy_nav:bool|str=False, # Whether to use scrollspy for navigation
cls='p-4', # Classes for navbar
scrollspy_cls=ScrollspyT.underline, # Scrollspy class (usually ScrollspyT.*)
menu_id=None, # ID for menu container (used for mobile toggle)
)->FT: # Responsive NavBar
"Creates a responsive navigation bar with mobile menu support"
if menu_id is None: menu_id = fh.unqid()
sticky_cls = 'sticky top-4 bg-base-100/80 backdrop-blur-sm z-50' if sticky else ''
if uk_scrollspy_nav == True: uk_scrollspy_nav = 'closest: a; scroll: true'
mobile_icon = A(UkIcon("menu", width=30, height=30), cls="md:hidden", data_uk_toggle=f"target: #{menu_id}; cls: hidden")
return Div(
Div(
DivFullySpaced(
brand, # Brand/logo component for left side
mobile_icon, # Hamburger menu icon
Div(*c,cls=(stringify(right_cls),'hidden md:flex'), uk_scrollspy_nav=uk_scrollspy_nav)),# Desktop Navbar (right side)
cls=('monster-navbar', stringify(cls), stringify(scrollspy_cls))
),
DivCentered(*c,
cls=(stringify(mobile_cls),stringify(cls), stringify(scrollspy_cls),
'hidden md:hidden monster-navbar'),
id=menu_id, uk_scrollspy_nav=uk_scrollspy_nav),
cls=sticky_cls)
# %% ../nbs/02_franken.ipynb
def SliderContainer(
*c, # Components
cls='', # Additional classes on the container
uk_slider=True, # See FrankenUI Slider docs for more options
**kwargs # Additional args for the container
) -> FT: # Div(..., cls='relative', uk_slider=True, ...)
"Creates a slider container"
return Div(*c, cls=('relative', stringify(cls)), uk_slider=uk_slider, **kwargs)
# %% ../nbs/02_franken.ipynb
def SliderItems(
*c, # Components
cls='', # Additional classes for the items
**kwargs # Additional args for the items
) -> FT: # Div(..., cls='uk-slider-items uk-grid', ...)
"Creates a slider items container"
return Div(*c, cls=('uk-slider-items uk-grid', stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def SliderNav(
cls='uk-position-small uk-hidden-hover', # Additional classes for the navigation
prev_cls='absolute left-0 top-1/2 -translate-y-1/2', # Additional classes for the previous navigation
next_cls='absolute right-0 top-1/2 -translate-y-1/2', # Additional classes for the next navigation
**kwargs # Additional args for the navigation
) -> FT: # Left and right navigation arrows for Slider component
"Navigation arrows for Slider component"
return (
fh.A(cls=(prev_cls, stringify(cls)), href='',
uk_slidenav_previous=True, uk_slider_item='previous', **kwargs),
fh.A(cls=(next_cls, stringify(cls)), href='',
uk_slidenav_next=True, uk_slider_item='next', **kwargs)
)
# %% ../nbs/02_franken.ipynb
def Slider(*c, # Items to show in slider
cls='', # Classes for slider container
items_cls='gap-4', # Classes for items container
nav=True, # Whether to show navigation arrows
nav_cls='', # Classes for navigation arrows
**kwargs # Additional args for slider container
) -> FT: # SliderContainer(SliderItems(..., cls='gap-4'), SliderNav?)
"Creates a slider with optional navigation arrows"
nav_comp = SliderNav(cls=nav_cls) if nav else ()
return SliderContainer(
SliderItems(*c, cls=items_cls),
*nav_comp,
cls=cls,
**kwargs
)
# %% ../nbs/02_franken.ipynb
def DropDownNavContainer(*li, # Components
cls=NavT.primary, # Additional classes on the nav
parent=True, # Whether to use a parent nav
uk_nav=False, #True for default collapsible behavior, see https://franken-ui.dev/docs/nav#component-options for more advanced options
uk_dropdown=True, # Whether to use a dropdown
**kwargs # Additional args for the nav
)->FT: # DropDown nav container
"A Nav that is part of a DropDown"
return Div(cls = 'uk-drop uk-dropdown',uk_dropdown=uk_dropdown)(NavContainer(*li, cls=('uk-dropdown-nav',stringify(cls)), uk_nav=uk_nav, parent=parent, **kwargs))
# %% ../nbs/02_franken.ipynb
def TabContainer(*li, # Components
cls='', # Additional classes on the `Ul`
alt=False, # Whether to use an alternative tab style
**kwargs # Additional args for the `Ul`
)->FT: # Tab container
"A TabContainer where children will be different tabs"
cls = stringify(cls)
return Ul(cls=(f"uk-tab{'-alt' if alt else ''}",stringify(cls)),**kwargs)(*li)
# %% ../nbs/02_franken.ipynb
class CardT(VEnum):
'Card styles from UIkit'
def _generate_next_value_(name, start, count, last_values): return str2ukcls('card', name)
default = auto()
primary = auto()
secondary = auto()
destructive = auto()
hover = 'uk-card hover:shadow-lg hover:-translate-y-1 transition-all duration-200'
# %% ../nbs/02_franken.ipynb
def CardTitle(*c, # Components (often a string)
cls=(), # Additional classes on the div
**kwargs # Additional args for the div
):
"Creates a card title"
return fh.Div(*c, cls=('uk-card-title',stringify(cls)), **kwargs)
def CardHeader(*c, # Components that goes in the header (often a `ModalTitle` and description)
cls=(), # Additional classes on the div
**kwargs # Additional args for the div
)->FT: # Container for the header of a card
"Creates a card header"
return fh.Div(*c, cls=('uk-card-header',stringify(cls)), **kwargs)
def CardBody(*c, # Components that go in the body (Main content of the card such as a form, and image, a signin form, etc.)
cls=(), # Additional classes on the div
**kwargs # Additional args for the div
)->FT: # Container for the body of a card
"Creates a card body"
return fh.Div(*c, cls=('uk-card-body',stringify(cls)), **kwargs)
def CardFooter(*c, # Components that go in the footer (often a `ModalCloseButton`)
cls=(), # Additional classes on the div
**kwargs # Additional args for the div
)->FT: # Container for the footer of a card
"Creates a card footer"
return fh.Div(*c, cls=('uk-card-footer',stringify(cls)), **kwargs)
def CardContainer(*c, # Components (typically `CardHeader`, `CardBody`, `CardFooter`)
cls=CardT.default, # Additional classes on the div
**kwargs # Additional args for the div
)->FT: # Container for a card
"Creates a card container"
return fh.Div(*c, cls=('uk-card',stringify(cls)), **kwargs)
# %% ../nbs/02_franken.ipynb
def Card(*c, # Components that go in the body (Main content of the card such as a form, and image, a signin form, etc.)
header:FT|Iterable[FT]=None, # Component(s) that goes in the header (often a `ModalTitle` and a subtitle)
footer:FT|Iterable[FT]=None, # Component(s) that goes in the footer (often a `ModalCloseButton`)
body_cls='space-y-6', # classes for the body
header_cls=(), # classes for the header
footer_cls=(), # classes for the footer
cls=(), #class for outermost component
**kwargs # additional arguments for the `CardContainer`
)->FT: # Card component
"Creates a Card with a header, body, and footer"
header_cls, footer_cls, body_cls, cls = map(stringify, (header_cls, footer_cls, body_cls, cls))
res = []
if header: res.append(CardHeader(cls=header_cls)(header))
res.append(CardBody(cls=body_cls)(*c))
if footer: res.append(CardFooter(cls=footer_cls)(footer))
return CardContainer(cls=cls, **kwargs)(*res)
# %% ../nbs/02_franken.ipynb
class TableT(VEnum):
def _generate_next_value_(name, start, count, last_values): return str2ukcls('table', name)
divider = auto()
striped = auto()
hover = auto()
sm = auto()
lg = auto()
justify = auto()
middle = auto()
responsive = auto()
# %% ../nbs/02_franken.ipynb
def Table(*c, # Components (typically `Thead`, `Tbody`, `Tfoot`)
cls=(TableT.middle, TableT.divider, TableT.hover, TableT.sm), # Additional classes on the table
**kwargs # Additional args for the table
)->FT: # Table component
"Creates a table"
return fh.Table(cls=('uk-table', stringify(cls)), *c, **kwargs)
# %% ../nbs/02_franken.ipynb
def _TableCell(Component,
*c, # Components that go in the cell
cls=(), # Additional classes on the cell container
shrink=False, # Whether to shrink the cell
expand=False, # Whether to expand the cell
small=False, # Whether to use a small table
**kwargs # Additional args for the cell
)->FT: # Table cell
"Creates a table cell"
cls = stringify(cls)
if shrink: cls += ' uk-table-shrink'
if expand: cls += ' uk-table-expand'
if small: cls += ' uk-table-small'
return Component(*c,cls=cls, **kwargs)
@delegates(_TableCell, but=['Component'])
def Td(*c,**kwargs): return _TableCell(fh.Td, *c, **kwargs)
@delegates(_TableCell, but=['Component'])
def Th(*c,**kwargs): return _TableCell(fh.Th, *c, **kwargs)
def Tbody(*rows, cls=(), sortable=False, **kwargs): return fh.Tbody(*rows, cls=stringify(cls), uk_sortable=sortable, **kwargs)
# %% ../nbs/02_franken.ipynb
def TableFromLists(header_data:Sequence, # List of header data
body_data:Sequence[Sequence], # List of lists of body data
footer_data=None, # List of footer data
header_cell_render=Th, # Function(content) -> FT that renders header cells
body_cell_render=Td, # Function(key, content) -> FT that renders body cells
footer_cell_render=Td, # Function(key, content) -> FT that renders footer cells
cls=(TableT.middle, TableT.divider, TableT.hover, TableT.sm), # Additional classes on the table
sortable=False, # Whether to use sortable table
**kwargs # Additional args for the table
)->FT: # Table from lists
"Creates a Table from a list of header data and a list of lists of body data"
return Table(
Thead(Tr(*map(header_cell_render, header_data))),
Tbody(*[Tr(*map(body_cell_render, r)) for r in body_data], sortable=sortable),
Tfoot(Tr(*map(footer_cell_render, footer_data))) if footer_data else '',
cls=stringify(cls),
**kwargs)
# %% ../nbs/02_franken.ipynb
def TableFromDicts(header_data:Sequence, # List of header data
body_data:Sequence[dict], # List of dicts of body data
footer_data=None, # List of footer data
header_cell_render=Th, # Function(content) -> FT that renders header cells
body_cell_render=lambda k,v : Td(v), # Function(key, content) -> FT that renders body cells
footer_cell_render=lambda k,v : Td(v), # Function(key, content) -> FT that renders footer cells
cls=(TableT.middle, TableT.divider, TableT.hover, TableT.sm), # Additional classes on the table
sortable=False, # Whether to use sortable table
**kwargs # Additional args for the table
)->FT: # Styled Table
"Creates a Table from a list of header data and a list of dicts of body data"
return Table(
Thead(Tr(*[header_cell_render(h) for h in header_data])),
Tbody(*[Tr(*[body_cell_render(k, r.get(k, '')) for k in header_data]) for r in body_data], sortable=sortable),
Tfoot(Tr(*[footer_cell_render(k, footer_data.get(k.lower(), '')) for k in header_data])) if footer_data else '',
cls=stringify(cls),
**kwargs
)
# %% ../nbs/02_franken.ipynb
franken_class_map = {
'h1': 'uk-h1 text-4xl font-bold mt-12 mb-6',
'h2': 'uk-h2 text-3xl font-bold mt-10 mb-5',
'h3': 'uk-h3 text-2xl font-semibold mt-8 mb-4',
'h4': 'uk-h4 text-xl font-semibold mt-6 mb-3',
# Body text and links
'p': 'text-lg leading-relaxed mb-6',
'a': 'uk-link text-primary hover:text-primary-focus underline',
# Lists with proper spacing
'ul': 'uk-list uk-list-bullet space-y-2 mb-6 ml-6 text-lg',
'ol': 'uk-list uk-list-decimal space-y-2 mb-6 ml-6 text-lg',
'li': 'leading-relaxed',
# Code and quotes
'pre': 'bg-base-200 rounded-lg p-4 mb-6',
'code': 'uk-codespan px-1',
'pre code': 'uk-codespan px-1 block overflow-x-auto',
'blockquote': 'uk-blockquote pl-4 border-l-4 border-primary italic mb-6',
# Tables
'table': 'uk-table uk-table-divider uk-table-hover uk-table-small w-full mb-6',
'th': '!text-left p-2 font-semibold',
'td': 'p-2',
# Other elements
'hr': 'uk-divider-icon my-8',
'img': 'max-w-full h-auto rounded-lg mb-6'
}
# %% ../nbs/02_franken.ipynb
def apply_classes(html_str:str, # Html string
class_map=None, # Class map
class_map_mods=None # Class map that will modify the class map map (for small changes to base map)
)->str: # Html string with classes applied
"Apply classes to html string"
if not html_str: return html_str
# Handles "Unicode strings with encoding declaration are not supported":
if html_str[:100].lstrip().startswith('<?xml'): html_str = html_str.split('?>', 1)[1].strip()
class_map = ifnone(class_map, franken_class_map)
if class_map_mods: class_map = {**class_map, **class_map_mods}
try:
html_str = html.fragment_fromstring(html_str, create_parent=True)
for selector, classes in class_map.items():
# Handle descendant selectors (e.g., 'pre code')
xpath = '//' + '/descendant::'.join(selector.split())
for element in html_str.xpath(xpath):
existing_class = element.get('class', '')
new_class = f"{existing_class} {classes}".strip()
element.set('class', new_class)
return ''.join(etree.tostring(c, encoding='unicode', method='html') for c in html_str)
except (etree.ParserError,ValueError): return html_str
# %% ../nbs/02_franken.ipynb
class FrankenRenderer(HTMLRenderer):
"Custom renderer for Franken UI that handles image paths"
def __init__(self, *args, img_dir=None, **kwargs):
super().__init__(*args, **kwargs)
self.img_dir = img_dir
def render_image(self, token):
"Modify image paths if they're relative and self.img_dir is specified"
template = '<img src="{}" alt="{}"{} class="max-w-full h-auto rounded-lg mb-6">'
title = f' title="{token.title}"' if hasattr(token, 'title') else ''
src = token.src
if self.img_dir and not src.startswith(('http://', 'https://', '/', 'attachment:', 'blob:', 'data:')):
src = f'{pathlib.Path(self.img_dir)}/{src}'
return template.format(src, token.children[0].content if token.children else '', title)
# %% ../nbs/02_franken.ipynb
def render_md(md_content:str, # Markdown content
class_map=None, # Class map
class_map_mods=None, # Additional class map
img_dir:str=None, # Directory containing images
renderer=FrankenRenderer # custom renderer
)->FT: # Rendered markdown
"Renders markdown using mistletoe and lxml with custom image handling"
if md_content=='': return md_content
html_content = mistletoe.markdown(md_content, partial(renderer, img_dir=img_dir))
return NotStr(apply_classes(html_content, class_map, class_map_mods))
# %% ../nbs/02_franken.ipynb
def ThemePicker(color=True, radii=True, shadows=True, font=True, mode=True, cls='p-4', custom_themes=[]):
"Theme picker component with configurable sections"
def _opt(val, txt, **kwargs): return Option(txt, value=val, **kwargs)
def _optgrp(key, lbl, opts): return fh.Optgroup(data_key=key, label=lbl)(*opts)
groups = []
if color: groups.append(_optgrp('theme', 'Theme', [
_opt('uk-theme-zinc', 'Zinc', data_hex='#52525b', selected=True),
*[_opt(f'uk-theme-{c.lower()}', c, data_hex=h) for c,h in
[('Slate','#64748b'),('Stone','#78716c'),('Gray','#6b7280'),
('Neutral','#737373'),('Red','#dc2626'),('Rose','#e11d48'),
('Orange','#f97316'),('Green','#16a34a'),('Blue','#2563eb'),
('Yellow','#facc15'),('Violet','#7c3aed'),*custom_themes]]]))
if radii: groups.append(_optgrp('radii', 'Radii', [
_opt('uk-radii-none','None'), _opt('uk-radii-sm','Small'),
_opt('uk-radii-md','Medium',selected=True), _opt('uk-radii-lg','Large')]))
if shadows: groups.append(_optgrp('shadows', 'Shadows', [
_opt('uk-shadows-none','None'), _opt('uk-shadows-sm','Small',selected=True),
_opt('uk-shadows-md','Medium'), _opt('uk-shadows-lg','Large')]))
if font: groups.append(_optgrp('font', 'Font', [
_opt('uk-font-sm','Small',selected=True), _opt('uk-font-base','Default')]))
if mode: groups.append(_optgrp('mode', 'Mode', [
_opt('light','Light',data_icon='sun'), _opt('dark','Dark',data_icon='moon')]))
from fasthtml.components import Uk_theme_switcher
return Div(Uk_theme_switcher(fh.Select(*groups, hidden=True), id="theme-switcher"), cls=stringify(cls))
# %% ../nbs/02_franken.ipynb
def LightboxContainer(*lightboxitem, # `LightBoxItem`s that will be inside lightbox
data_uk_lightbox='counter: true', # See https://franken-ui.dev/docs/2.0/lightbox for advanced options
**kwargs # Additional options for outer container
)->FT: # Lightbox
"Lightbox container that will hold `LightboxItems`"
return fh.Div(*lightboxitem, data_uk_lightbox=data_uk_lightbox, **kwargs)
# %% ../nbs/02_franken.ipynb
def LightboxItem(*c, # Component that when clicked will open the lightbox (often a button)
href, # Href to image, youtube video, vimeo, google maps, etc.
data_alt=None, # Alt text for the lightbox item/image
data_caption=None, # Caption for the item that shows below it
cls='', # Class for the A tag (often nothing or `uk-btn`)
**kwargs # Additional args for the `A` tag
)->FT: # A(... href, data_alt, cls., ...)
"Anchor tag with appropriate structure to go inside a `LightBoxContainer`"
return fh.A(*c, href=href, data_alt=data_alt, cls=stringify(cls), **kwargs)
# %% ../nbs/02_franken.ipynb
def ApexChart(*,
opts:Dict, # ApexChart options used to render your chart (e.g. {"chart":{"type":"line"}, ...})
cls: Enum | str | tuple = (), # Classes for the outer container
**kws, # Additional args for the outer container
)->FT: # Div(Uk_chart(Script(...)))
"Apex chart component"
js=NotStr(f"<script type='application/json'>{json.dumps(opts)}</script>")
return Div(Uk_chart(js), cls=stringify(cls), **kws)
|