fh-matui 0.9.12__py3-none-any.whl → 0.9.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
fh_matui/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.9.11"
1
+ __version__ = "0.9.12"
fh_matui/components.py CHANGED
@@ -29,9 +29,13 @@ from nbdev.showdoc import show_doc
29
29
  # %% ../nbs/02_components.ipynb 6
30
30
  #| code-fold: true
31
31
  def NavToggleButton(target, icon='menu', **kwargs):
32
- """Create a navigation toggle button that toggles the 'max' class on the target element"""
32
+ """Create a navigation toggle button that toggles the 'max' class on the target element.
33
+
34
+ Also toggles icon between 'menu' and 'menu_open' based on nav state.
35
+ """
33
36
  cls = kwargs.get('cls', 'circle transparent')
34
- onclick = f"toggleNav('{target}'); return false;"
37
+ # Inline JS: toggle nav's max class AND change this button's icon
38
+ onclick = f"var nav=document.querySelector('{target}');if(nav){{nav.classList.toggle('max');var i=this.querySelector('i');if(i)i.textContent=nav.classList.contains('max')?'menu_open':'menu'}};return false;"
35
39
  kwargs.update({'onclick': onclick, 'cls': cls})
36
40
  return Button(I(icon), **kwargs)
37
41
 
@@ -323,12 +327,31 @@ def Icon(icon: str, size: str = None, fill: bool = False, cls = (), **kwargs):
323
327
 
324
328
  # %% ../nbs/02_components.ipynb 32
325
329
  #| code-fold: true
326
- def NavBar(*children, brand=None, sticky=False, cls='', **kwargs):
327
- """Horizontal navigation bar with optional brand and sticky positioning"""
328
- nav_cls = f"{'sticky top' if sticky else ''} surface-container {cls}".strip()
330
+ def NavBar(*children, brand=None, sticky=False, cls='', size='small',
331
+ hx_boost=True, hx_target='#main-content', **kwargs):
332
+ """Horizontal navigation bar with HTMX SPA navigation defaults.
333
+
334
+ Args:
335
+ brand: Brand element (logo/title) positioned on the left
336
+ sticky: Whether navbar sticks to top on scroll
337
+ size: Navbar size - 'small' (default), 'medium', 'large', or None
338
+ hx_boost: Auto-enhance all <a> links for HTMX navigation (default True)
339
+ hx_target: Target element for boosted links (default '#main-content')
340
+ """
341
+ size_cls = size if size else ''
342
+ nav_cls = f"{'sticky top' if sticky else ''} surface-container {size_cls} {cls}".strip()
343
+
344
+ # HTMX SPA optimizations
345
+ if hx_boost: kwargs['hx_boost'] = 'true'
346
+ if hx_target: kwargs['hx_target'] = hx_target
347
+ kwargs.setdefault('hx_push_url', 'true')
348
+
349
+ # Use small-padding for compact navbar
350
+ padding_cls = 'small-padding' if size == 'small' else 'padding'
351
+
329
352
  if brand:
330
- return Nav(brand, Div(cls='max'), *children, cls=f"row middle-align padding {nav_cls}", **kwargs)
331
- return Nav(*children, cls=f"padding {nav_cls}", **kwargs)
353
+ return Nav(brand, Div(cls='max'), *children, cls=f"row middle-align {padding_cls} {nav_cls}", **kwargs)
354
+ return Nav(*children, cls=f"{padding_cls} {nav_cls}", **kwargs)
332
355
 
333
356
  # %% ../nbs/02_components.ipynb 35
334
357
  def Modal(*c, id=None, footer=None, active=False, overlay='default', position=None, cls=(), **kwargs):
@@ -1072,33 +1095,76 @@ def NavSideBarLinks(*children, as_list=False, cls='', **kwargs):
1072
1095
  return Ul(*children, cls=list_cls, **kwargs)
1073
1096
  return Group(*children) if len(children) > 1 else (children[0] if children else Group())
1074
1097
 
1075
- def NavSideBarContainer(*children, position='left', size='m', cls='', active=False, **kwargs):
1076
- """BeerCSS navigation sidebar/drawer component."""
1098
+ def NavSideBarContainer(*children, position='left', size='m', cls='', active=False,
1099
+ hx_boost=True, hx_target='#main-content', **kwargs):
1100
+ """BeerCSS navigation sidebar with HTMX SPA navigation defaults.
1101
+
1102
+ Args:
1103
+ position: Sidebar position ('left' or 'right')
1104
+ size: Sidebar size ('s', 'm', 'l')
1105
+ active: Whether sidebar starts visible
1106
+ hx_boost: Auto-enhance all <a> links for HTMX navigation (default True)
1107
+ hx_target: Target element for boosted links (default '#main-content')
1108
+
1109
+ Usage:
1110
+ Routes should check `req.headers` for HX-Request and return content only for HTMX requests:
1111
+
1112
+ @rt("/dashboard")
1113
+ def dashboard(req):
1114
+ content = dashboard_content()
1115
+ if 'HX-Request' in req.headers: return content # HTMX swap
1116
+ return Layout(content) # Full page load
1117
+ """
1077
1118
  base_cls = f"{size} {position} surface-container"
1078
1119
  if active: base_cls += " active"
1079
1120
  nav_cls = f"{base_cls} {cls}".strip()
1121
+
1122
+ # HTMX SPA optimizations
1123
+ if hx_boost: kwargs['hx_boost'] = 'true'
1124
+ if hx_target: kwargs['hx_target'] = hx_target
1125
+ kwargs.setdefault('hx_push_url', 'true')
1126
+
1080
1127
  return Nav(*children, cls=nav_cls, **kwargs)
1081
1128
 
1082
1129
  # %% ../nbs/02_components.ipynb 107
1083
1130
  #| code-fold: true
1084
1131
  def Layout(*content, sidebar=None, sidebar_links=None, nav_bar=None, container_size=ContainerT.expand,
1085
- main_bg='surface', sidebar_id='app-sidebar', cls='', **kwargs):
1086
- """App layout wrapper with auto-toggle sidebar and sensible defaults."""
1087
- main_content = []
1088
-
1089
- if nav_bar:
1090
- if hasattr(nav_bar, 'attrs') and 'cls' in nav_bar.attrs:
1091
- if 'sticky' not in nav_bar.attrs['cls']: nav_bar.attrs['cls'] += ' sticky top'
1092
- main_content.append(nav_bar)
1132
+ main_bg='surface', sidebar_id='app-sidebar', main_id='main-content', cls='', **kwargs):
1133
+ """App layout with HTMX SPA navigation.
1093
1134
 
1135
+ Args:
1136
+ main_id: ID for main content area (default 'main-content') - use as hx-target
1137
+
1138
+ HTMX SPA features:
1139
+ - hx-boost on sidebar automatically enhances all <a> links
1140
+ - hx-history-elt for back/forward button caching
1141
+ - Routes should check `req.headers` for HX-Request and return content only for HTMX requests
1142
+
1143
+ Usage:
1144
+ @rt("/dashboard")
1145
+ def dashboard(req):
1146
+ content = dashboard_content()
1147
+ if 'HX-Request' in req.headers: return content # HTMX swap
1148
+ return Layout(content) # Full page load
1149
+ """
1150
+ # Build content wrapper with history caching
1151
+ content_wrapper = None
1094
1152
  if content:
1095
- container_cls = stringify((container_size, 'padding', main_bg))
1096
- main_content.append(Main(*content, cls=container_cls))
1153
+ content_wrapper = Div(*content, id=main_id, hx_history_elt='true')
1097
1154
 
1155
+ # No sidebar - simple layout
1098
1156
  if not sidebar and not sidebar_links:
1099
- if main_content: return Div(*main_content, cls=cls, **kwargs)
1100
- else: return Div(cls=cls, **kwargs)
1101
-
1157
+ result = []
1158
+ if nav_bar:
1159
+ if hasattr(nav_bar, 'attrs') and 'cls' in nav_bar.attrs:
1160
+ if 'sticky' not in nav_bar.attrs['cls']: nav_bar.attrs['cls'] += ' sticky top'
1161
+ result.append(nav_bar)
1162
+ if content_wrapper:
1163
+ container_cls = stringify((container_size, 'padding', main_bg))
1164
+ result.append(Main(content_wrapper, cls=container_cls))
1165
+ return Div(*result, cls=cls, **kwargs) if result else Div(cls=cls, **kwargs)
1166
+
1167
+ # Sidebar layout with hx-boost
1102
1168
  sidebar_children = [NavSideBarHeader(NavToggleButton(f"#{sidebar_id}"))]
1103
1169
 
1104
1170
  if sidebar_links: sidebar_children.extend(sidebar_links)
@@ -1108,23 +1174,23 @@ def Layout(*content, sidebar=None, sidebar_links=None, nav_bar=None, container_s
1108
1174
 
1109
1175
  nav_rail = NavSideBarContainer(*sidebar_children, position='left', size='l', id=sidebar_id)
1110
1176
 
1111
- navbar_elem = None
1112
- content_items = []
1113
- for item in main_content:
1114
- if hasattr(item, 'tag') and item.tag == 'nav': navbar_elem = item
1115
- else: content_items.append(item)
1116
-
1117
1177
  layout_children = []
1118
- if navbar_elem: layout_children.append(navbar_elem)
1178
+
1179
+ if nav_bar:
1180
+ if hasattr(nav_bar, 'attrs') and 'cls' in nav_bar.attrs:
1181
+ if 'sticky' not in nav_bar.attrs['cls']: nav_bar.attrs['cls'] += ' sticky top'
1182
+ layout_children.append(nav_bar)
1183
+
1119
1184
  layout_children.append(nav_rail)
1120
- if content_items:
1185
+
1186
+ if content_wrapper:
1121
1187
  container_cls = stringify((container_size, 'round', 'elevate', 'margin'))
1122
- layout_children.append(Main(*content_items, cls=container_cls))
1188
+ layout_children.append(Main(content_wrapper, cls=container_cls))
1123
1189
 
1124
1190
  final_cls = f"surface-container {cls}".strip() if cls else "surface-container"
1125
1191
  return Div(*layout_children, cls=final_cls, **kwargs)
1126
1192
 
1127
- # %% ../nbs/02_components.ipynb 111
1193
+ # %% ../nbs/02_components.ipynb 113
1128
1194
  #| code-fold: true
1129
1195
  class TextT(VEnum):
1130
1196
  """Text styles using BeerCSS typography classes."""
@@ -1156,7 +1222,7 @@ class TextPresets(VEnum):
1156
1222
  primary_link = 'link primary-text'
1157
1223
  muted_link = 'link secondary-text'
1158
1224
 
1159
- # %% ../nbs/02_components.ipynb 112
1225
+ # %% ../nbs/02_components.ipynb 114
1160
1226
  #| code-fold: true
1161
1227
  def CodeSpan(*c, cls=(), **kwargs):
1162
1228
  """Inline code snippet."""
@@ -1212,7 +1278,7 @@ def Sup(*c, cls=(), **kwargs):
1212
1278
  cls_str = stringify(cls) if cls else None
1213
1279
  return fc.Sup(*c, cls=cls_str, **kwargs) if cls_str else fc.Sup(*c, **kwargs)
1214
1280
 
1215
- # %% ../nbs/02_components.ipynb 114
1281
+ # %% ../nbs/02_components.ipynb 116
1216
1282
  #| code-fold: true
1217
1283
  def FAQItem(question: str, answer: str, question_cls: str = '', answer_cls: str = ''):
1218
1284
  """Collapsible FAQ item using details/summary.
@@ -1230,7 +1296,7 @@ def FAQItem(question: str, answer: str, question_cls: str = '', answer_cls: str
1230
1296
  Summary(Article(Nav(Div(question, cls=f"max bold {question_cls}".strip()), I("expand_more")), cls="round surface-variant border no-elevate")),
1231
1297
  Article(P(answer, cls=f"secondary-text {answer_cls}".strip()), cls="round border padding"))
1232
1298
 
1233
- # %% ../nbs/02_components.ipynb 118
1299
+ # %% ../nbs/02_components.ipynb 120
1234
1300
  #| code-fold: true
1235
1301
  def CookiesBanner(message='We use cookies to enhance your experience. By continuing to visit this site you agree to our use of cookies.',
1236
1302
  accept_text='Accept', decline_text='Decline', settings_text=None, policy_link='/cookies', policy_text='Learn more',
fh_matui/core.py CHANGED
@@ -31,7 +31,7 @@ beer_hdrs = (
31
31
  Script(src=HEADER_URLS["mdc_js"], type='module'),
32
32
  )
33
33
 
34
- # %% ../nbs/01_core.ipynb 8
34
+ # %% ../nbs/01_core.ipynb 7
35
35
  #| code-fold: true
36
36
  # All BeerCSS color names
37
37
  COLOR_NAMES = ['amber', 'blue', 'blue_grey', 'brown', 'cyan', 'deep_orange', 'deep_purple',
@@ -88,7 +88,7 @@ ALL_HELPERS = (SIZES + WIDTH_HEIGHT + ELEVATES + DIRECTIONS + FORMS + MARGINS +
88
88
  POSITIONS + RESPONSIVE + ALIGNMENTS + BLURS + OPACITIES + SHADOWS + SPACES +
89
89
  RIPPLES + SCROLLS + WAVES + ZOOMS + THEME_HELPERS + TYPOGRAPHY + TRIGGERS + COLOR_HELPERS)
90
90
 
91
- # %% ../nbs/01_core.ipynb 11
91
+ # %% ../nbs/01_core.ipynb 10
92
92
  #| code-fold: true
93
93
  class _ThemeChain:
94
94
  """Internal class for building themed headers"""
@@ -142,14 +142,19 @@ class _ThemeChain:
142
142
  }};
143
143
  window.toggleNav = function(selector) {{
144
144
  const nav = document.querySelector(selector);
145
- if (nav) {{ nav.classList.toggle('max'); }}
145
+ if (nav) {{
146
+ nav.classList.toggle('max');
147
+ const isOpen = nav.classList.contains('max');
148
+ const btn = nav.querySelector('button i, button .icon');
149
+ if (btn) btn.textContent = isOpen ? 'menu_open' : 'menu';
150
+ }}
146
151
  }};
147
152
  ''')
148
153
  hdrs.append(theme_script)
149
154
  hdrs.append(Title(title))
150
155
  return tuple(hdrs)
151
156
 
152
- # %% ../nbs/01_core.ipynb 12
157
+ # %% ../nbs/01_core.ipynb 11
153
158
  #| code-fold: true
154
159
  class _ThemeNamespace:
155
160
  """Namespace providing color properties that return _ThemeChain instances"""
@@ -208,7 +213,7 @@ class _ThemeNamespace:
208
213
 
209
214
  MatTheme = _ThemeNamespace()
210
215
 
211
- # %% ../nbs/01_core.ipynb 16
216
+ # %% ../nbs/01_core.ipynb 15
212
217
  #| code-fold: true
213
218
  class BeerCssChain:
214
219
  """Base class for chaining Beer CSS helper classes together"""
fh_matui/datatable.py CHANGED
@@ -452,7 +452,7 @@ class CrudContext:
452
452
  record_id: Optional[Any] = None # ID for update/delete (None for create)
453
453
  feedback_id: Optional[str] = None # Target div ID for HTMX swap (for override handlers)
454
454
 
455
- # %% ../nbs/05_datatable.ipynb 13
455
+ # %% ../nbs/05_datatable.ipynb 14
456
456
  from typing import Callable, Optional, Any, Union
457
457
  from dataclasses import asdict, is_dataclass
458
458
  from datetime import datetime
@@ -488,11 +488,40 @@ class DataTableResource:
488
488
  - Async/sync hook support for external API integration
489
489
  - Auto-refresh table via HX-Trigger after mutations
490
490
  - Layout wrapper for full-page (non-HTMX) responses
491
+ - Optional `get_count` for efficient DB-level pagination
491
492
 
492
493
  **Auto-registers 3 routes:**
493
494
  - `GET {base_route}` → DataTable list view
494
495
  - `GET {base_route}/action` → FormModal for create/edit/view/delete
495
496
  - `POST {base_route}/save` → Save handler with hooks
497
+
498
+ **DB-Level Pagination (Recommended for large datasets):**
499
+
500
+ For efficient pagination, provide both `get_all` (returning paginated rows)
501
+ and `get_count` (returning total count):
502
+
503
+ ```python
504
+ def get_products(req):
505
+ page = int(req.query_params.get('page', 1))
506
+ page_size = int(req.query_params.get('page_size', 10))
507
+ offset = (page - 1) * page_size
508
+ search = req.query_params.get('search', '')
509
+ # SQL-level pagination
510
+ return list(tbl(limit=page_size, offset=offset))
511
+
512
+ def get_product_count(req):
513
+ search = req.query_params.get('search', '')
514
+ return db.execute("SELECT COUNT(*) FROM products").scalar()
515
+
516
+ DataTableResource(
517
+ get_all=get_products, # Returns page_size rows
518
+ get_count=get_product_count, # Returns total count
519
+ ...
520
+ )
521
+ ```
522
+
523
+ If `get_count` is not provided, the library filters and paginates in Python
524
+ (requires `get_all` to return ALL rows - inefficient for large datasets).
496
525
  """
497
526
 
498
527
  def __init__(
@@ -501,8 +530,9 @@ class DataTableResource:
501
530
  base_route: str,
502
531
  columns: list[dict],
503
532
  # Data callbacks - ALL receive request as first param
504
- get_all: Callable[[Any], list], # (req) -> list
533
+ get_all: Callable[[Any], list], # (req) -> list (can be paginated)
505
534
  get_by_id: Callable[[Any, Any], Any], # (req, id) -> record
535
+ get_count: Callable[[Any], int] = None, # (req) -> total count for pagination
506
536
  create: Callable[[Any, dict], Any] = None, # (req, data) -> record
507
537
  update: Callable[[Any, Any, dict], Any] = None, # (req, id, data) -> record
508
538
  delete: Callable[[Any, Any], bool] = None, # (req, id) -> bool
@@ -529,6 +559,7 @@ class DataTableResource:
529
559
  self.columns = columns
530
560
  self.get_all = get_all
531
561
  self.get_by_id = get_by_id
562
+ self.get_count = get_count
532
563
  self.create_fn = create
533
564
  self.update_fn = update
534
565
  self.delete_fn = delete
@@ -704,10 +735,22 @@ class DataTableResource:
704
735
  """Handle main table route."""
705
736
  state = table_state_from_request(req, page_sizes=self.page_sizes)
706
737
  search, page, page_size = state["search"], state["page"], state["page_size"]
707
-
738
+ # Get data from user callback
708
739
  data = self._get_filtered_data(req)
709
- filtered = self._filter_by_search(data, search)
710
- page_data, total, page = self._paginate(filtered, page, page_size)
740
+
741
+ # Determine total count:
742
+ # - If get_count provided: use it (efficient DB-level count)
743
+ # - Otherwise: filter and count in Python (assumes get_all returns all rows)
744
+ if self.get_count:
745
+ # User handles pagination in get_all, we just get count separately
746
+ total = self.get_count(req)
747
+ page_data = data # Already paginated by user
748
+ total_pages = max(1, ceil(total / page_size)) if total > 0 else 1
749
+ page = min(max(1, page), total_pages)
750
+ else:
751
+ # Legacy behavior: filter and paginate in Python
752
+ filtered = self._filter_by_search(data, search)
753
+ page_data, total, page = self._paginate(filtered, page, page_size)
711
754
 
712
755
  table = DataTable(
713
756
  data=page_data,
@@ -828,7 +871,7 @@ class DataTableResource:
828
871
  # Record required for remaining actions
829
872
  if not record:
830
873
  return self._error_toast("Record not found.")
831
-
874
+ return self._wrap_modal(modal)
832
875
  # Handle VIEW (default behavior)
833
876
  if action == "view":
834
877
  modal = FormModal(
fh_matui/web_pages.py CHANGED
@@ -809,20 +809,20 @@ def LandingPageSimple(
809
809
  # %% ../nbs/04_web_pages.ipynb 30
810
810
  def MarkdownSection(
811
811
  content: str, # Markdown text to render
812
- title: str = None, # Optional section title (rendered as H5)
812
+ title: str = None, # Optional section title (rendered as H3 for appropriate size)
813
813
  cls: str = "", # Additional classes
814
814
  ):
815
815
  """Renders markdown content server-side for SEO compatibility.
816
816
 
817
817
  Uses python-markdown to convert markdown to HTML on the server.
818
818
  Search engines see fully rendered HTML (no JavaScript required).
819
- Parent ContentPage uses 'min' class to center the page layout.
819
+ Content is centered using BeerCSS large-width + row center-align pattern.
820
820
 
821
821
  Great for text-heavy pages like Privacy Policy, Terms, About, Blog posts, etc.
822
822
 
823
823
  Args:
824
824
  content: Markdown text string (can include headers, lists, links, code blocks, tables)
825
- title: Optional page title (rendered before markdown content)
825
+ title: Optional page title (rendered as H3 for blog-appropriate sizing)
826
826
  cls: Additional CSS classes
827
827
 
828
828
  Example:
@@ -848,12 +848,15 @@ We value your privacy...
848
848
 
849
849
  elements = []
850
850
  if title:
851
- elements.append(H5(title, cls="center-align"))
851
+ # Use H3 for page title - appropriately sized for content pages
852
+ elements.append(H3(title, cls="bold"))
852
853
 
853
854
  # NotStr tells FastHTML to render raw HTML without escaping
854
855
  elements.append(NotStr(html_content))
855
856
 
856
- return Article(*elements, cls=f"large-padding {cls}".strip())
857
+ # large-width constrains content, wrapped in row center-align for centering
858
+ article = Article(*elements, cls=f"large-width large-padding {cls}".strip())
859
+ return Div(article, cls="row center-align")
857
860
 
858
861
  # %% ../nbs/04_web_pages.ipynb 31
859
862
  def ContentPage(
@@ -871,7 +874,7 @@ def ContentPage(
871
874
  A shell template for text-heavy pages like Privacy Policy, Terms of Service,
872
875
  Security, About, Blog posts, etc. Developer passes any number of content sections.
873
876
 
874
- Layout: Navbar (sticky) -> Content sections (centered with 'min') -> Footer
877
+ Layout: Navbar (sticky) -> Content sections (centered with 'responsive') -> Footer
875
878
 
876
879
  Uses STANDARD_FOOTER_COLUMNS by default for consistent footer across all pages.
877
880
 
@@ -913,7 +916,7 @@ def ContentPage(
913
916
  cls="large-padding",
914
917
  )
915
918
 
916
- # Main content area - use "min" to center content (unlike landing page which uses "max")
917
- main_content = Main(*sections, cls="min large-padding")
919
+ # Main content area - sections handle their own centering
920
+ main_content = Main(*sections, cls="large-padding")
918
921
 
919
922
  return Div(navbar, main_content, footer_el, cls=f"column {cls}".strip())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fh-matui
3
- Version: 0.9.12
3
+ Version: 0.9.13
4
4
  Summary: material-ui for fasthtml
5
5
  Home-page: https://github.com/abhisheksreesaila/fh-matui
6
6
  Author: abhishek sreesaila
@@ -0,0 +1,14 @@
1
+ fh_matui/__init__.py,sha256=4CbYG_FT4tURr5oUs-LmBkXp278FJwRBFCpWMPcLQog,24
2
+ fh_matui/_modidx.py,sha256=naHwPQ4kCo-5saE_uozmdPA881kp1gnqvKhmaG-Ya-4,23914
3
+ fh_matui/app_pages.py,sha256=Sn9-tgBpaPNbR-0nZtPLoSCmAWLOGB4UQ88IkFvzBRY,10361
4
+ fh_matui/components.py,sha256=Cqgsg2i0dNzMgNpETSu7zf1HYyEwZ1_00bBjeue34Nc,53312
5
+ fh_matui/core.py,sha256=t9Y5e3g6F4ZjlRuNt7SDabbCEdsolT6QHJXMOpEPa3A,10962
6
+ fh_matui/datatable.py,sha256=o7BizE4FMKrfsYT4G6rjMvr2xHo_tvTwL0dI6anDcE4,41228
7
+ fh_matui/foundations.py,sha256=b7PnObJpKN8ZAU9NzCm9xpfnHzFjjAROU7E2YvA_tj4,1820
8
+ fh_matui/web_pages.py,sha256=at_M34Vxc1i9O0ukS41PaRJntkXXDzMqyzlcrgESUcw,35103
9
+ fh_matui-0.9.13.dist-info/licenses/LICENSE,sha256=xV8xoN4VOL0uw9X8RSs2IMuD_Ss_a9yAbtGNeBWZwnw,11337
10
+ fh_matui-0.9.13.dist-info/METADATA,sha256=5dnU_4G5vE1zqb28xwlI2pdXnBB0imjDbUxUW9imcLc,10491
11
+ fh_matui-0.9.13.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
12
+ fh_matui-0.9.13.dist-info/entry_points.txt,sha256=zn4CR4gNTiAAxbFsCxHAf2tQhtW29_YOffjbUTgeoWI,38
13
+ fh_matui-0.9.13.dist-info/top_level.txt,sha256=l80d5eoA2ZjqtPYwAorLMS5PiHxUxz3zKzxMJ41Xoso,9
14
+ fh_matui-0.9.13.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,14 +0,0 @@
1
- fh_matui/__init__.py,sha256=4-YPRncYVIx_LEaOTQQk2IcNM-ASQoM7kDmtXA8kaRQ,24
2
- fh_matui/_modidx.py,sha256=naHwPQ4kCo-5saE_uozmdPA881kp1gnqvKhmaG-Ya-4,23914
3
- fh_matui/app_pages.py,sha256=Sn9-tgBpaPNbR-0nZtPLoSCmAWLOGB4UQ88IkFvzBRY,10361
4
- fh_matui/components.py,sha256=5CBhmcStL88vyrbkThDBTRnj5rwCfvtvgZ0vbnljvVE,50418
5
- fh_matui/core.py,sha256=xtVBN8CtC50ZJ4Iu7o-mUhaA87tWdnz8gBfKRk63Zhs,10680
6
- fh_matui/datatable.py,sha256=LsVwuTuoFOh3rIvETXefsqZOqxos4xi9ct2F7YPIIv8,39266
7
- fh_matui/foundations.py,sha256=b7PnObJpKN8ZAU9NzCm9xpfnHzFjjAROU7E2YvA_tj4,1820
8
- fh_matui/web_pages.py,sha256=4mF-jpfVcZTVepfQ-aMGgIUp-nBp0YCkvcdsWhUYeaA,34879
9
- fh_matui-0.9.12.dist-info/licenses/LICENSE,sha256=xV8xoN4VOL0uw9X8RSs2IMuD_Ss_a9yAbtGNeBWZwnw,11337
10
- fh_matui-0.9.12.dist-info/METADATA,sha256=KbTK3cooiJMUGCYaVaFg1JbgEB7UvKHhpKvlo0AxE-s,10491
11
- fh_matui-0.9.12.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
12
- fh_matui-0.9.12.dist-info/entry_points.txt,sha256=zn4CR4gNTiAAxbFsCxHAf2tQhtW29_YOffjbUTgeoWI,38
13
- fh_matui-0.9.12.dist-info/top_level.txt,sha256=l80d5eoA2ZjqtPYwAorLMS5PiHxUxz3zKzxMJ41Xoso,9
14
- fh_matui-0.9.12.dist-info/RECORD,,