fh-matui 0.9.14__py3-none-any.whl → 0.9.16__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 +1 -1
- fh_matui/_modidx.py +1 -0
- fh_matui/app_pages.py +3 -3
- fh_matui/components.py +180 -74
- fh_matui/core.py +7 -7
- fh_matui/datatable.py +37 -10
- fh_matui/foundations.py +5 -5
- fh_matui/web_pages.py +12 -12
- {fh_matui-0.9.14.dist-info → fh_matui-0.9.16.dist-info}/METADATA +7 -9
- fh_matui-0.9.16.dist-info/RECORD +14 -0
- fh_matui-0.9.14.dist-info/RECORD +0 -14
- {fh_matui-0.9.14.dist-info → fh_matui-0.9.16.dist-info}/WHEEL +0 -0
- {fh_matui-0.9.14.dist-info → fh_matui-0.9.16.dist-info}/entry_points.txt +0 -0
- {fh_matui-0.9.14.dist-info → fh_matui-0.9.16.dist-info}/licenses/LICENSE +0 -0
- {fh_matui-0.9.14.dist-info → fh_matui-0.9.16.dist-info}/top_level.txt +0 -0
fh_matui/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.9.
|
|
1
|
+
__version__ = "0.9.16"
|
fh_matui/_modidx.py
CHANGED
|
@@ -61,6 +61,7 @@ d = { 'settings': { 'branch': 'master',
|
|
|
61
61
|
'fh_matui.components.Radio': ('components.html#radio', 'fh_matui/components.py'),
|
|
62
62
|
'fh_matui.components.Range': ('components.html#range', 'fh_matui/components.py'),
|
|
63
63
|
'fh_matui.components.Select': ('components.html#select', 'fh_matui/components.py'),
|
|
64
|
+
'fh_matui.components.SelectMenu': ('components.html#selectmenu', 'fh_matui/components.py'),
|
|
64
65
|
'fh_matui.components.Small': ('components.html#small', 'fh_matui/components.py'),
|
|
65
66
|
'fh_matui.components.Snackbar': ('components.html#snackbar', 'fh_matui/components.py'),
|
|
66
67
|
'fh_matui.components.SpaceT': ('components.html#spacet', 'fh_matui/components.py'),
|
fh_matui/app_pages.py
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/03_app_pages.ipynb.
|
|
4
4
|
|
|
5
|
-
# %% auto 0
|
|
5
|
+
# %% auto #0
|
|
6
6
|
__all__ = ['LoginScreen']
|
|
7
7
|
|
|
8
|
-
# %% ../nbs/03_app_pages.ipynb
|
|
8
|
+
# %% ../nbs/03_app_pages.ipynb #99dc363f
|
|
9
9
|
from fastcore.utils import *
|
|
10
10
|
from fasthtml.common import *
|
|
11
11
|
from fasthtml.jupyter import *
|
|
@@ -18,7 +18,7 @@ from .components import *
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
# %% ../nbs/03_app_pages.ipynb
|
|
21
|
+
# %% ../nbs/03_app_pages.ipynb #47b3b49d
|
|
22
22
|
# Default inspirational quotes for login screen (fully customizable via parameter)
|
|
23
23
|
_DEFAULT_LOGIN_QUOTES = [
|
|
24
24
|
"The only way to do great work is to love what you do. — Steve Jobs",
|
fh_matui/components.py
CHANGED
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/02_components.ipynb.
|
|
4
4
|
|
|
5
|
-
# %% auto 0
|
|
5
|
+
# %% auto #0
|
|
6
6
|
__all__ = ['BUTTON_SPECIALS', 'ButtonT', 'ANCHOR_SPECIALS', 'AT', 'NavToggleButton', 'SpaceT', 'GridSpanT', 'GridCell', 'Grid',
|
|
7
7
|
'DivHStacked', 'DivLAligned', 'DivVStacked', 'DivRAligned', 'DivCentered', 'DivFullySpaced', 'Icon',
|
|
8
8
|
'NavBar', 'Modal', 'ModalButton', 'ModalCancel', 'ModalConfirm', 'ModalTitle', 'ModalBody', 'ModalFooter',
|
|
9
|
-
'Field', 'LabelInput', 'FormLabel', 'CheckboxX', 'Radio', 'Switch', 'TextArea', 'Range', '
|
|
10
|
-
'FormGrid', 'Progress', 'LoadingIndicator', 'Table', 'Td', 'Th', 'Thead', 'Tbody', 'Tfoot',
|
|
11
|
-
'TableFromDicts', 'TableControls', 'Pagination', 'Card', 'Toolbar', 'Toast', 'Snackbar',
|
|
12
|
-
'FormField', 'FormModal', 'NavContainer', 'NavHeaderLi', 'NavDividerLi', 'NavCloseLi',
|
|
13
|
-
'BottomNav', 'NavSideBarHeader', 'NavSideBarLinks', 'NavSideBarContainer', 'Page', 'Layout',
|
|
14
|
-
'TextPresets', 'CodeSpan', 'CodeBlock', 'Blockquote', 'Q', 'Em', 'Strong', 'Small', 'Mark', 'Abbr',
|
|
15
|
-
'Sup', 'FAQItem', 'CookiesBanner']
|
|
16
|
-
|
|
17
|
-
# %% ../nbs/02_components.ipynb
|
|
9
|
+
'Field', 'LabelInput', 'FormLabel', 'CheckboxX', 'Radio', 'Switch', 'TextArea', 'Range', 'SelectMenu',
|
|
10
|
+
'Select', 'FormGrid', 'Progress', 'LoadingIndicator', 'Table', 'Td', 'Th', 'Thead', 'Tbody', 'Tfoot',
|
|
11
|
+
'TableFromLists', 'TableFromDicts', 'TableControls', 'Pagination', 'Card', 'Toolbar', 'Toast', 'Snackbar',
|
|
12
|
+
'ContainerT', 'FormField', 'FormModal', 'NavContainer', 'NavHeaderLi', 'NavDividerLi', 'NavCloseLi',
|
|
13
|
+
'NavSubtitle', 'BottomNav', 'NavSideBarHeader', 'NavSideBarLinks', 'NavSideBarContainer', 'Page', 'Layout',
|
|
14
|
+
'TextT', 'TextPresets', 'CodeSpan', 'CodeBlock', 'Blockquote', 'Q', 'Em', 'Strong', 'Small', 'Mark', 'Abbr',
|
|
15
|
+
'Sub', 'Sup', 'FAQItem', 'CookiesBanner']
|
|
16
|
+
|
|
17
|
+
# %% ../nbs/02_components.ipynb #32bd23ad
|
|
18
18
|
from fastcore.utils import *
|
|
19
19
|
from fasthtml.common import *
|
|
20
20
|
from fasthtml.jupyter import *
|
|
@@ -26,7 +26,7 @@ from .core import *
|
|
|
26
26
|
from nbdev.showdoc import show_doc
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
# %% ../nbs/02_components.ipynb
|
|
29
|
+
# %% ../nbs/02_components.ipynb #31957c2d
|
|
30
30
|
#| code-fold: true
|
|
31
31
|
def NavToggleButton(target, icon='menu', **kwargs):
|
|
32
32
|
"""Create a navigation toggle button that toggles the 'max' class on the target element.
|
|
@@ -39,7 +39,7 @@ def NavToggleButton(target, icon='menu', **kwargs):
|
|
|
39
39
|
kwargs.update({'onclick': onclick, 'cls': cls})
|
|
40
40
|
return Button(I(icon), **kwargs)
|
|
41
41
|
|
|
42
|
-
# %% ../nbs/02_components.ipynb
|
|
42
|
+
# %% ../nbs/02_components.ipynb #aea20daf
|
|
43
43
|
#| code-fold: true
|
|
44
44
|
BUTTON_SPECIALS = {
|
|
45
45
|
'primary': 'primary',
|
|
@@ -64,7 +64,7 @@ for name, css in BUTTON_SPECIALS.items():
|
|
|
64
64
|
|
|
65
65
|
ButtonT = _ButtonChain()
|
|
66
66
|
|
|
67
|
-
# %% ../nbs/02_components.ipynb
|
|
67
|
+
# %% ../nbs/02_components.ipynb #ba073cd1
|
|
68
68
|
#| code-fold: true
|
|
69
69
|
class _AnchorChain(BeerCssChain):
|
|
70
70
|
"""Chainable anchor style helper"""
|
|
@@ -88,7 +88,7 @@ for name, css in ANCHOR_SPECIALS.items():
|
|
|
88
88
|
|
|
89
89
|
AT = _AnchorChain()
|
|
90
90
|
|
|
91
|
-
# %% ../nbs/02_components.ipynb
|
|
91
|
+
# %% ../nbs/02_components.ipynb #e55766c2
|
|
92
92
|
class SpaceT(VEnum):
|
|
93
93
|
"""Space types using BeerCSS spacing classes"""
|
|
94
94
|
no_space = 'no-space'
|
|
@@ -163,28 +163,20 @@ def _snap_to_valid_cols(n: int) -> int:
|
|
|
163
163
|
return 12
|
|
164
164
|
|
|
165
165
|
|
|
166
|
-
def _wrap_grid_children(cells,
|
|
166
|
+
def _wrap_grid_children(cells, cols_sm=None, cols_md=None, cols_lg=None):
|
|
167
167
|
"""Wrap grid children with span classes based on column counts."""
|
|
168
168
|
# If no column counts specified, return cells as-is
|
|
169
|
-
if not any([
|
|
169
|
+
if not any([cols_sm, cols_md, cols_lg]):
|
|
170
170
|
return cells
|
|
171
171
|
|
|
172
172
|
# Build span string from column counts
|
|
173
173
|
spans = []
|
|
174
174
|
if cols_sm:
|
|
175
175
|
spans.append(f's{12 // cols_sm}')
|
|
176
|
-
elif cols:
|
|
177
|
-
spans.append(f's{12 // cols}')
|
|
178
|
-
|
|
179
176
|
if cols_md:
|
|
180
177
|
spans.append(f'm{12 // cols_md}')
|
|
181
|
-
elif cols:
|
|
182
|
-
spans.append(f'm{12 // cols}')
|
|
183
|
-
|
|
184
178
|
if cols_lg:
|
|
185
179
|
spans.append(f'l{12 // cols_lg}')
|
|
186
|
-
elif cols:
|
|
187
|
-
spans.append(f'l{12 // cols}')
|
|
188
180
|
|
|
189
181
|
span_str = ' '.join(spans) if spans else 's12'
|
|
190
182
|
|
|
@@ -196,11 +188,21 @@ def Grid(*cells, space=SpaceT.medium_space,
|
|
|
196
188
|
cols_min: int = 1, cols_max: int = 4,
|
|
197
189
|
cols: int = None, cols_sm: int = None, cols_md: int = None, cols_lg: int = None,
|
|
198
190
|
responsive: bool = True, padding: bool = True, cls: str = '', **kwargs):
|
|
199
|
-
"""BeerCSS responsive grid with smart column defaults and mobile-first design.
|
|
191
|
+
"""BeerCSS responsive grid with smart column defaults and mobile-first design.
|
|
192
|
+
|
|
193
|
+
MonsterUI-compatible API: `cols` parameter auto-derives responsive breakpoints:
|
|
194
|
+
cols=4 -> cols_sm=1, cols_md=2, cols_lg=4 (mobile stacks, tablet halves, desktop full)
|
|
195
|
+
|
|
196
|
+
For explicit control, use cols_sm, cols_md, cols_lg directly.
|
|
197
|
+
"""
|
|
200
198
|
# Smart defaults based on content count (MonsterUI pattern)
|
|
201
199
|
if cols:
|
|
202
|
-
#
|
|
203
|
-
|
|
200
|
+
# Auto-derive responsive breakpoints from cols (mobile-friendly by default)
|
|
201
|
+
# cols=4 -> stack on mobile (1), half on tablet (2), full on desktop (4)
|
|
202
|
+
snapped = _snap_to_valid_cols(cols)
|
|
203
|
+
cols_lg = cols_lg or snapped
|
|
204
|
+
cols_md = cols_md or _snap_to_valid_cols(max(1, snapped // 2))
|
|
205
|
+
cols_sm = cols_sm or 1 # Always stack on mobile for readability
|
|
204
206
|
elif not any([cols_sm, cols_md, cols_lg]):
|
|
205
207
|
# Auto-calculate responsive columns based on item count
|
|
206
208
|
n = len(cells)
|
|
@@ -214,21 +216,23 @@ def Grid(*cells, space=SpaceT.medium_space,
|
|
|
214
216
|
if cols_md: cols_md = _snap_to_valid_cols(cols_md)
|
|
215
217
|
if cols_lg: cols_lg = _snap_to_valid_cols(cols_lg)
|
|
216
218
|
|
|
217
|
-
wrapped_cells = _wrap_grid_children(cells,
|
|
219
|
+
wrapped_cells = _wrap_grid_children(cells, cols_sm, cols_md, cols_lg)
|
|
218
220
|
|
|
219
221
|
cls_tokens = normalize_tokens(cls)
|
|
220
222
|
grid_cls = ['grid']
|
|
221
|
-
|
|
222
|
-
grid_cls.append('responsive')
|
|
223
|
+
|
|
223
224
|
if padding and 'padding' not in cls_tokens and 'no-padding' not in cls_tokens:
|
|
224
225
|
grid_cls.append('padding')
|
|
226
|
+
|
|
225
227
|
if space and not _has_space_token(cls_tokens):
|
|
226
228
|
grid_cls.extend(normalize_tokens(space))
|
|
229
|
+
|
|
227
230
|
grid_cls.extend(cls_tokens)
|
|
228
231
|
grid_cls = [t for t in grid_cls if t]
|
|
232
|
+
|
|
229
233
|
return Div(*wrapped_cells, cls=stringify(dedupe_preserve_order(grid_cls)), **kwargs)
|
|
230
234
|
|
|
231
|
-
# %% ../nbs/02_components.ipynb
|
|
235
|
+
# %% ../nbs/02_components.ipynb #1264260c
|
|
232
236
|
def DivHStacked(*c, responsive=True, padding=True, cls='', **kwargs):
|
|
233
237
|
"""Responsive horizontal stack with padding and mobile compatibility."""
|
|
234
238
|
cls_tokens = normalize_tokens(cls)
|
|
@@ -246,7 +250,7 @@ def DivHStacked(*c, responsive=True, padding=True, cls='', **kwargs):
|
|
|
246
250
|
return Div(*c, cls=stringify(dedupe_preserve_order(tokens)), **kwargs)
|
|
247
251
|
|
|
248
252
|
|
|
249
|
-
# %% ../nbs/02_components.ipynb
|
|
253
|
+
# %% ../nbs/02_components.ipynb #16cae24c
|
|
250
254
|
def DivLAligned(*c, cls='', **kwargs):
|
|
251
255
|
"""MonsterUI-compatible left-aligned row using BeerCSS tokens."""
|
|
252
256
|
cls_tokens = normalize_tokens(cls)
|
|
@@ -257,7 +261,7 @@ def DivLAligned(*c, cls='', **kwargs):
|
|
|
257
261
|
|
|
258
262
|
|
|
259
263
|
|
|
260
|
-
# %% ../nbs/02_components.ipynb
|
|
264
|
+
# %% ../nbs/02_components.ipynb #2435d500
|
|
261
265
|
def DivVStacked(*c, responsive=True, padding=True, cls='', **kwargs):
|
|
262
266
|
"""Responsive vertical stack with padding and mobile compatibility."""
|
|
263
267
|
cls_tokens = normalize_tokens(cls)
|
|
@@ -275,7 +279,7 @@ def DivVStacked(*c, responsive=True, padding=True, cls='', **kwargs):
|
|
|
275
279
|
return Div(*c, cls=stringify(dedupe_preserve_order(tokens)), **kwargs)
|
|
276
280
|
|
|
277
281
|
|
|
278
|
-
# %% ../nbs/02_components.ipynb
|
|
282
|
+
# %% ../nbs/02_components.ipynb #1c19a699
|
|
279
283
|
def DivRAligned(*c, cls='', **kwargs):
|
|
280
284
|
"""MonsterUI-compatible right-aligned row using BeerCSS tokens."""
|
|
281
285
|
cls_tokens = normalize_tokens(cls)
|
|
@@ -284,7 +288,7 @@ def DivRAligned(*c, cls='', **kwargs):
|
|
|
284
288
|
tokens = [t for t in tokens if t]
|
|
285
289
|
return DivHStacked(*c, cls=stringify(dedupe_preserve_order(tokens)), **kwargs)
|
|
286
290
|
|
|
287
|
-
# %% ../nbs/02_components.ipynb
|
|
291
|
+
# %% ../nbs/02_components.ipynb #0a028d84
|
|
288
292
|
def DivCentered(*c, cls='', **kwargs):
|
|
289
293
|
"""Center-aligned container using BeerCSS tokens."""
|
|
290
294
|
cls_tokens = normalize_tokens(cls)
|
|
@@ -293,7 +297,7 @@ def DivCentered(*c, cls='', **kwargs):
|
|
|
293
297
|
tokens = [t for t in tokens if t]
|
|
294
298
|
return Div(*c, cls=stringify(dedupe_preserve_order(tokens)), **kwargs)
|
|
295
299
|
|
|
296
|
-
# %% ../nbs/02_components.ipynb
|
|
300
|
+
# %% ../nbs/02_components.ipynb #7b232371
|
|
297
301
|
def DivFullySpaced(*c, cls='', **kwargs):
|
|
298
302
|
"""Row with children stretched to far ends using BeerCSS `max` spacers."""
|
|
299
303
|
cls_tokens = normalize_tokens(cls)
|
|
@@ -314,7 +318,7 @@ def DivFullySpaced(*c, cls='', **kwargs):
|
|
|
314
318
|
base = spaced_children
|
|
315
319
|
return Div(*base, cls=stringify(dedupe_preserve_order(tokens)), **kwargs)
|
|
316
320
|
|
|
317
|
-
# %% ../nbs/02_components.ipynb
|
|
321
|
+
# %% ../nbs/02_components.ipynb #0e0bbaca
|
|
318
322
|
#| code-fold: true
|
|
319
323
|
def Icon(icon: str, size: str = None, fill: bool = False, cls = (), **kwargs):
|
|
320
324
|
"""Material Design icon with optional size and fill"""
|
|
@@ -325,7 +329,7 @@ def Icon(icon: str, size: str = None, fill: bool = False, cls = (), **kwargs):
|
|
|
325
329
|
cls_str = ' '.join(icon_cls) if icon_cls else None
|
|
326
330
|
return I(icon, cls=cls_str, **kwargs) if cls_str else I(icon, **kwargs)
|
|
327
331
|
|
|
328
|
-
# %% ../nbs/02_components.ipynb
|
|
332
|
+
# %% ../nbs/02_components.ipynb #f1e25f09
|
|
329
333
|
#| code-fold: true
|
|
330
334
|
def NavBar(*children, brand=None, sticky=False, cls='', size='small',
|
|
331
335
|
hx_boost=True, hx_target='#main-content', **kwargs):
|
|
@@ -353,7 +357,7 @@ def NavBar(*children, brand=None, sticky=False, cls='', size='small',
|
|
|
353
357
|
return Nav(brand, Div(cls='max'), *children, cls=f"row middle-align {padding_cls} {nav_cls}", **kwargs)
|
|
354
358
|
return Nav(*children, cls=f"{padding_cls} {nav_cls}", **kwargs)
|
|
355
359
|
|
|
356
|
-
# %% ../nbs/02_components.ipynb
|
|
360
|
+
# %% ../nbs/02_components.ipynb #6d92c66b
|
|
357
361
|
def Modal(*c, id=None, footer=None, active=False, overlay='default', position=None, cls=(), **kwargs):
|
|
358
362
|
"""BeerCSS modal dialog with position and overlay options.
|
|
359
363
|
|
|
@@ -456,7 +460,7 @@ def ModalFooter(*c, cls=(), **kwargs):
|
|
|
456
460
|
footer_cls.extend(['right-align', 'no-space'])
|
|
457
461
|
return Nav(*c, cls=' '.join(footer_cls), **kwargs)
|
|
458
462
|
|
|
459
|
-
# %% ../nbs/02_components.ipynb
|
|
463
|
+
# %% ../nbs/02_components.ipynb #3a16954d
|
|
460
464
|
#| code-fold: true
|
|
461
465
|
def Field(*c, label: bool = False, prefix: bool = False, suffix: bool = False, cls = '', **kwargs):
|
|
462
466
|
"""BeerCSS field wrapper for inputs with border/label/prefix/suffix styling."""
|
|
@@ -481,7 +485,7 @@ def LabelInput(label: str, id: str = None, placeholder: str = None, input_type:
|
|
|
481
485
|
if suffix_icon: children.append(I(suffix_icon))
|
|
482
486
|
return Field(*children, label=True, prefix=bool(prefix_icon), suffix=bool(suffix_icon), cls=cls)
|
|
483
487
|
|
|
484
|
-
# %% ../nbs/02_components.ipynb
|
|
488
|
+
# %% ../nbs/02_components.ipynb #d6668f1b
|
|
485
489
|
#| code-fold: true
|
|
486
490
|
def LabelInput(label: str, id: str = None, placeholder: str = None, input_type: str = 'text',
|
|
487
491
|
prefix_icon: str = None, suffix_icon: str = None, value: str = None, cls = '', **kwargs):
|
|
@@ -503,7 +507,7 @@ def FormLabel(*c, cls=(), **kwargs):
|
|
|
503
507
|
if cls_str: return Label(*c, cls=cls_str, **kwargs)
|
|
504
508
|
return Label(*c, **kwargs)
|
|
505
509
|
|
|
506
|
-
# %% ../nbs/02_components.ipynb
|
|
510
|
+
# %% ../nbs/02_components.ipynb #b9cf5341
|
|
507
511
|
#| code-fold: true
|
|
508
512
|
def CheckboxX(*c, cls=(), **kwargs):
|
|
509
513
|
"""BeerCSS checkbox with label support."""
|
|
@@ -513,7 +517,7 @@ def CheckboxX(*c, cls=(), **kwargs):
|
|
|
513
517
|
cls_str = stringify(checkbox_cls)
|
|
514
518
|
return Label(Input(type='checkbox', **kwargs), Span(label_text) if label_text else Span(), cls=cls_str)
|
|
515
519
|
|
|
516
|
-
# %% ../nbs/02_components.ipynb
|
|
520
|
+
# %% ../nbs/02_components.ipynb #784de047
|
|
517
521
|
#| code-fold: true
|
|
518
522
|
def Radio(*c, cls=(), **kwargs):
|
|
519
523
|
"""BeerCSS radio button with label."""
|
|
@@ -523,7 +527,7 @@ def Radio(*c, cls=(), **kwargs):
|
|
|
523
527
|
cls_str = stringify(radio_cls)
|
|
524
528
|
return Label(Input(type='radio', **kwargs), Span(label_text) if label_text else Span(), cls=cls_str)
|
|
525
529
|
|
|
526
|
-
# %% ../nbs/02_components.ipynb
|
|
530
|
+
# %% ../nbs/02_components.ipynb #128c2b3c
|
|
527
531
|
#| code-fold: true
|
|
528
532
|
def Switch(*c, cls=(), **kwargs):
|
|
529
533
|
"""BeerCSS toggle switch for on/off states.
|
|
@@ -550,7 +554,7 @@ def Switch(*c, cls=(), **kwargs):
|
|
|
550
554
|
cls='middle-align'
|
|
551
555
|
)
|
|
552
556
|
|
|
553
|
-
# %% ../nbs/02_components.ipynb
|
|
557
|
+
# %% ../nbs/02_components.ipynb #0c1c2718
|
|
554
558
|
#| code-fold: true
|
|
555
559
|
def TextArea(*c, cls=(), **kwargs):
|
|
556
560
|
"""BeerCSS textarea with field wrapper for consistent styling."""
|
|
@@ -558,7 +562,7 @@ def TextArea(*c, cls=(), **kwargs):
|
|
|
558
562
|
textarea = Textarea(content, **kwargs) if content else Textarea(**kwargs)
|
|
559
563
|
return Field(textarea, cls=cls)
|
|
560
564
|
|
|
561
|
-
# %% ../nbs/02_components.ipynb
|
|
565
|
+
# %% ../nbs/02_components.ipynb #ddbbd284
|
|
562
566
|
#| code-fold: true
|
|
563
567
|
def Range(*c, min=None, max=None, step=None, cls=(), **kwargs):
|
|
564
568
|
"""BeerCSS range slider with two-tone fill effect."""
|
|
@@ -587,10 +591,22 @@ def Range(*c, min=None, max=None, step=None, cls=(), **kwargs):
|
|
|
587
591
|
style = f"--_start: 0%; --_end: {percentage:.1f}%;"
|
|
588
592
|
return Label(Input(**input_attrs), Span(), cls=cls_str, style=style)
|
|
589
593
|
|
|
590
|
-
# %% ../nbs/02_components.ipynb
|
|
594
|
+
# %% ../nbs/02_components.ipynb #6262a7d8
|
|
591
595
|
#| code-fold: true
|
|
592
|
-
def
|
|
593
|
-
"""BeerCSS menu-based
|
|
596
|
+
def SelectMenu(*items, value='', placeholder='Select...', prefix_icon=None, id=None, cls=(), **kwargs):
|
|
597
|
+
"""BeerCSS menu-based dropdown for display menus (profile, actions, navigation).
|
|
598
|
+
|
|
599
|
+
Not intended for form input - use Select for forms with native change events.
|
|
600
|
+
This component uses BeerCSS's menu element which opens/closes on click.
|
|
601
|
+
|
|
602
|
+
Args:
|
|
603
|
+
*items: Menu items - strings are auto-wrapped in Li, or pass Li elements directly
|
|
604
|
+
value: Currently displayed value text
|
|
605
|
+
placeholder: Placeholder text when no value selected
|
|
606
|
+
prefix_icon: Optional icon to show before the input
|
|
607
|
+
id: Optional wrapper ID
|
|
608
|
+
cls: Additional CSS classes
|
|
609
|
+
"""
|
|
594
610
|
menu_items = []
|
|
595
611
|
for item in items:
|
|
596
612
|
if isinstance(item, str): menu_items.append(Li(item))
|
|
@@ -598,10 +614,11 @@ def Select(*items, value='', placeholder='Select...', prefix_icon=None, name='',
|
|
|
598
614
|
|
|
599
615
|
children = []
|
|
600
616
|
if prefix_icon: children.append(I(prefix_icon))
|
|
617
|
+
|
|
618
|
+
# Display input (readonly, shows selected value)
|
|
601
619
|
input_attrs = {'value': value, 'readonly': True, 'placeholder': placeholder if placeholder else ' '}
|
|
602
|
-
if name: input_attrs['name'] = name
|
|
603
|
-
input_attrs.update(kwargs)
|
|
604
620
|
children.append(Input(**input_attrs))
|
|
621
|
+
|
|
605
622
|
children.append(I('arrow_drop_down'))
|
|
606
623
|
children.append(Menu(*menu_items))
|
|
607
624
|
|
|
@@ -610,9 +627,77 @@ def Select(*items, value='', placeholder='Select...', prefix_icon=None, name='',
|
|
|
610
627
|
field_cls.append('suffix')
|
|
611
628
|
if cls: field_cls.extend(normalize_tokens(cls))
|
|
612
629
|
cls_str = stringify(field_cls)
|
|
630
|
+
|
|
631
|
+
wrapper_attrs = {'cls': cls_str}
|
|
632
|
+
if id:
|
|
633
|
+
wrapper_attrs['id'] = id
|
|
634
|
+
wrapper_attrs.update(kwargs)
|
|
635
|
+
|
|
636
|
+
return Div(*children, **wrapper_attrs)
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
def Select(*options, label=None, value='', name='', id=None, placeholder=None, cls=(), **kwargs):
|
|
640
|
+
"""BeerCSS native HTML select with floating label support.
|
|
641
|
+
|
|
642
|
+
Uses the native <select> element for full browser support including:
|
|
643
|
+
- Standard 'change' event for HTMX (hx-trigger="change")
|
|
644
|
+
- Keyboard navigation
|
|
645
|
+
- Mobile-friendly native picker
|
|
646
|
+
- Works seamlessly with cascading dropdowns
|
|
647
|
+
|
|
648
|
+
Args:
|
|
649
|
+
*options: Option items - strings, Option elements, or dicts with 'value'/'label' keys
|
|
650
|
+
label: Floating label text (optional)
|
|
651
|
+
value: Currently selected value
|
|
652
|
+
name: Form field name
|
|
653
|
+
id: Element ID (auto-generated from name if not provided)
|
|
654
|
+
placeholder: Placeholder option text (shown as disabled first option)
|
|
655
|
+
cls: Additional CSS classes for the field wrapper
|
|
656
|
+
"""
|
|
657
|
+
# Auto-generate ID from name if not provided
|
|
658
|
+
select_id = id or (f"select-{name}" if name else None)
|
|
659
|
+
|
|
660
|
+
# Build options list
|
|
661
|
+
opt_elements = []
|
|
662
|
+
|
|
663
|
+
# Add placeholder as disabled first option if provided
|
|
664
|
+
if placeholder:
|
|
665
|
+
opt_elements.append(Option(placeholder, value='', disabled=True, selected=(not value)))
|
|
666
|
+
|
|
667
|
+
for opt in options:
|
|
668
|
+
if isinstance(opt, str):
|
|
669
|
+
# String option: value and label are the same
|
|
670
|
+
opt_elements.append(Option(opt, value=opt, selected=(opt == value)))
|
|
671
|
+
elif hasattr(opt, 'tag') and opt.tag == 'option':
|
|
672
|
+
# Already an Option element, pass through
|
|
673
|
+
opt_elements.append(opt)
|
|
674
|
+
elif isinstance(opt, dict):
|
|
675
|
+
# Dict with value/label keys
|
|
676
|
+
opt_val = opt.get('value', opt.get('label', ''))
|
|
677
|
+
opt_label = opt.get('label', opt.get('value', ''))
|
|
678
|
+
opt_elements.append(Option(opt_label, value=opt_val, selected=(opt_val == value)))
|
|
679
|
+
|
|
680
|
+
# Build select element
|
|
681
|
+
select_attrs = {}
|
|
682
|
+
if name: select_attrs['name'] = name
|
|
683
|
+
if select_id: select_attrs['id'] = select_id
|
|
684
|
+
select_attrs.update(kwargs)
|
|
685
|
+
|
|
686
|
+
select_el = fc.Select(*opt_elements, **select_attrs)
|
|
687
|
+
|
|
688
|
+
# Build field wrapper
|
|
689
|
+
field_cls = ['field', 'border', 'round']
|
|
690
|
+
if label: field_cls.append('label')
|
|
691
|
+
if cls: field_cls.extend(normalize_tokens(cls))
|
|
692
|
+
cls_str = stringify(field_cls)
|
|
693
|
+
|
|
694
|
+
children = [select_el]
|
|
695
|
+
if label:
|
|
696
|
+
children.append(Label(label, fr=select_id))
|
|
697
|
+
|
|
613
698
|
return Div(*children, cls=cls_str)
|
|
614
699
|
|
|
615
|
-
# %% ../nbs/02_components.ipynb
|
|
700
|
+
# %% ../nbs/02_components.ipynb #e6576c75
|
|
616
701
|
#| code-fold: true
|
|
617
702
|
def FormGrid(*c, cols: int = 1):
|
|
618
703
|
"""Responsive grid layout for form fields that stacks on mobile."""
|
|
@@ -621,7 +706,7 @@ def FormGrid(*c, cols: int = 1):
|
|
|
621
706
|
wrapped = [Div(child, cls=col_cls) for child in c]
|
|
622
707
|
return Div(*wrapped, cls="grid")
|
|
623
708
|
|
|
624
|
-
# %% ../nbs/02_components.ipynb
|
|
709
|
+
# %% ../nbs/02_components.ipynb #5a159345
|
|
625
710
|
#| code-fold: true
|
|
626
711
|
def Progress(*c, value='', max='100', cls=(), **kwargs):
|
|
627
712
|
"""Linear progress bar with value/max support."""
|
|
@@ -633,7 +718,7 @@ def Progress(*c, value='', max='100', cls=(), **kwargs):
|
|
|
633
718
|
if cls_str: return fc.Progress(*c, cls=cls_str, **progress_attrs)
|
|
634
719
|
return fc.Progress(*c, **progress_attrs)
|
|
635
720
|
|
|
636
|
-
# %% ../nbs/02_components.ipynb
|
|
721
|
+
# %% ../nbs/02_components.ipynb #11d87dd6
|
|
637
722
|
#| code-fold: true
|
|
638
723
|
def LoadingIndicator(size='medium', cls='', **kwargs):
|
|
639
724
|
"""BeerCSS circular spinner for async operations."""
|
|
@@ -641,7 +726,7 @@ def LoadingIndicator(size='medium', cls='', **kwargs):
|
|
|
641
726
|
progress_cls = f"circle {size_cls} {cls}".strip()
|
|
642
727
|
return fc.Progress(cls=progress_cls, **kwargs)
|
|
643
728
|
|
|
644
|
-
# %% ../nbs/02_components.ipynb
|
|
729
|
+
# %% ../nbs/02_components.ipynb #396ebe54
|
|
645
730
|
#| code-fold: true
|
|
646
731
|
def Table(*c, cls = 'border', **kwargs):
|
|
647
732
|
"""BeerCSS table with optional border/stripes classes."""
|
|
@@ -709,14 +794,14 @@ def TableFromDicts(header_data, body_data, footer_data = None, header_cell_rende
|
|
|
709
794
|
Tfoot(Tr(*[footer_cell_render(k, footer_data.get(k, '')) for k in header_data])) if footer_data else None,
|
|
710
795
|
cls=cls, **kwargs)
|
|
711
796
|
|
|
712
|
-
# %% ../nbs/02_components.ipynb
|
|
797
|
+
# %% ../nbs/02_components.ipynb #0cf9871f
|
|
713
798
|
#| code-fold: true
|
|
714
799
|
def TableControls(*controls, cls='', **kwargs):
|
|
715
800
|
"""Toolbar container for table filters, search, and actions."""
|
|
716
801
|
controls_cls = f"padding middle-align space {cls}".strip()
|
|
717
802
|
return Div(*controls, cls=controls_cls, **kwargs)
|
|
718
803
|
|
|
719
|
-
# %% ../nbs/02_components.ipynb
|
|
804
|
+
# %% ../nbs/02_components.ipynb #93a6e771
|
|
720
805
|
#| code-fold: true
|
|
721
806
|
def Pagination(current_page: int, total_pages: int, hx_get: str, hx_target: str = '#table-container',
|
|
722
807
|
show_first_last: bool = True, cls='', **kwargs):
|
|
@@ -751,7 +836,7 @@ def Pagination(current_page: int, total_pages: int, hx_get: str, hx_target: str
|
|
|
751
836
|
nav_cls = f"center-align middle-align {cls}".strip()
|
|
752
837
|
return Nav(*buttons, cls=nav_cls, **kwargs)
|
|
753
838
|
|
|
754
|
-
# %% ../nbs/02_components.ipynb
|
|
839
|
+
# %% ../nbs/02_components.ipynb #c573ee87
|
|
755
840
|
#| code-fold: true
|
|
756
841
|
def Card(*c, header = None, footer = None, body_cls = 'padding', header_cls = (), footer_cls = (), cls = (), **kwargs):
|
|
757
842
|
"""BeerCSS card with optional header/footer sections."""
|
|
@@ -765,7 +850,7 @@ def Card(*c, header = None, footer = None, body_cls = 'padding', header_cls = ()
|
|
|
765
850
|
if footer is not None: sections.append(Nav(footer, cls=footer_cls) if footer_cls else Nav(footer))
|
|
766
851
|
return Article(*sections, cls=cls, **kwargs)
|
|
767
852
|
|
|
768
|
-
# %% ../nbs/02_components.ipynb
|
|
853
|
+
# %% ../nbs/02_components.ipynb #f0c71209
|
|
769
854
|
#| code-fold: true
|
|
770
855
|
def Toolbar(*items, cls='', elevate='large', fill=True, **kwargs):
|
|
771
856
|
"""BeerCSS toolbar for action bars with elevation options."""
|
|
@@ -775,7 +860,7 @@ def Toolbar(*items, cls='', elevate='large', fill=True, **kwargs):
|
|
|
775
860
|
if cls: classes.append(cls)
|
|
776
861
|
return Nav(*items, cls=' '.join(classes), **kwargs)
|
|
777
862
|
|
|
778
|
-
# %% ../nbs/02_components.ipynb
|
|
863
|
+
# %% ../nbs/02_components.ipynb #7019d70e
|
|
779
864
|
#| code-fold: true
|
|
780
865
|
def Toast(*c, cls='', position='top', variant='', action=None, active=False, dur=None, **kwargs):
|
|
781
866
|
"""BeerCSS snackbar/toast notification with position ('top' or 'bottom') and variant options."""
|
|
@@ -819,7 +904,7 @@ def Snackbar(*c, **kwargs):
|
|
|
819
904
|
"""Alias for Toast component."""
|
|
820
905
|
return Toast(*c, **kwargs)
|
|
821
906
|
|
|
822
|
-
# %% ../nbs/02_components.ipynb
|
|
907
|
+
# %% ../nbs/02_components.ipynb #fe1b0be7
|
|
823
908
|
#| code-fold: true
|
|
824
909
|
class ContainerT(VEnum):
|
|
825
910
|
"""Container size options (BeerCSS). Most alias to 'responsive'; use 'expand' for full-width."""
|
|
@@ -830,7 +915,7 @@ class ContainerT(VEnum):
|
|
|
830
915
|
xl = 'responsive'
|
|
831
916
|
expand = 'responsive max'
|
|
832
917
|
|
|
833
|
-
# %% ../nbs/02_components.ipynb
|
|
918
|
+
# %% ../nbs/02_components.ipynb #f53893ec
|
|
834
919
|
#| code-fold: true
|
|
835
920
|
def _get_form_config(col: dict) -> dict:
|
|
836
921
|
"""Extract form config from column, with sensible defaults."""
|
|
@@ -906,7 +991,7 @@ def FormField(
|
|
|
906
991
|
**attrs
|
|
907
992
|
)
|
|
908
993
|
|
|
909
|
-
# %% ../nbs/02_components.ipynb
|
|
994
|
+
# %% ../nbs/02_components.ipynb #aff2ee40
|
|
910
995
|
#| code-fold: true
|
|
911
996
|
from typing import Callable, Any
|
|
912
997
|
|
|
@@ -1025,7 +1110,7 @@ def FormModal(
|
|
|
1025
1110
|
cls="large-width"
|
|
1026
1111
|
)
|
|
1027
1112
|
|
|
1028
|
-
# %% ../nbs/02_components.ipynb
|
|
1113
|
+
# %% ../nbs/02_components.ipynb #945684a7
|
|
1029
1114
|
#| code-fold: true
|
|
1030
1115
|
def NavContainer(*li, title=None, brand=None, position='left', close_button=True, cls='active', id=None, **kwargs):
|
|
1031
1116
|
"""Slide-out navigation drawer with header and close button."""
|
|
@@ -1082,7 +1167,7 @@ def BottomNav(*c, cls='bottom', size='s', **kwargs):
|
|
|
1082
1167
|
final_cls = f"{cls} {size_cls}".strip()
|
|
1083
1168
|
return Nav(*c, cls=final_cls, **kwargs)
|
|
1084
1169
|
|
|
1085
|
-
# %% ../nbs/02_components.ipynb
|
|
1170
|
+
# %% ../nbs/02_components.ipynb #359f9555
|
|
1086
1171
|
#| code-fold: true
|
|
1087
1172
|
def NavSideBarHeader(*c, cls='', **kwargs):
|
|
1088
1173
|
"""Sidebar header section for menu buttons and branding."""
|
|
@@ -1126,7 +1211,7 @@ def NavSideBarContainer(*children, position='left', size='m', cls='', active=Fal
|
|
|
1126
1211
|
|
|
1127
1212
|
return Nav(*children, cls=nav_cls, **kwargs)
|
|
1128
1213
|
|
|
1129
|
-
# %% ../nbs/02_components.ipynb
|
|
1214
|
+
# %% ../nbs/02_components.ipynb #541b2d99
|
|
1130
1215
|
#| code-fold: true
|
|
1131
1216
|
def Page(*c, active=True, position=None, cls='', **kwargs):
|
|
1132
1217
|
"""BeerCSS animated page container.
|
|
@@ -1150,7 +1235,7 @@ def Page(*c, active=True, position=None, cls='', **kwargs):
|
|
|
1150
1235
|
|
|
1151
1236
|
def Layout(*content, sidebar=None, sidebar_links=None, nav_bar=None, container_size=ContainerT.expand,
|
|
1152
1237
|
main_bg='surface', sidebar_id='app-sidebar', main_id='main-content', cls='', **kwargs):
|
|
1153
|
-
"""App layout with HTMX SPA navigation.
|
|
1238
|
+
"""App layout with HTMX SPA navigation and responsive mobile support.
|
|
1154
1239
|
|
|
1155
1240
|
Args:
|
|
1156
1241
|
main_id: ID for main content area (default 'main-content') - use as hx-target
|
|
@@ -1160,6 +1245,11 @@ def Layout(*content, sidebar=None, sidebar_links=None, nav_bar=None, container_s
|
|
|
1160
1245
|
- hx-history-elt for back/forward button caching
|
|
1161
1246
|
- Routes should check `req.headers` for HX-Request and return content only for HTMX requests
|
|
1162
1247
|
|
|
1248
|
+
Mobile Responsive:
|
|
1249
|
+
- Desktop (>992px): Left sidebar visible, bottom nav hidden
|
|
1250
|
+
- Mobile (≤992px): Bottom nav visible, sidebar hidden
|
|
1251
|
+
- Both navigations share the same sidebar_links for consistent behavior
|
|
1252
|
+
|
|
1163
1253
|
Usage:
|
|
1164
1254
|
@rt("/dashboard")
|
|
1165
1255
|
def dashboard(req):
|
|
@@ -1193,9 +1283,24 @@ def Layout(*content, sidebar=None, sidebar_links=None, nav_bar=None, container_s
|
|
|
1193
1283
|
if is_listy(sidebar): sidebar_children.extend(sidebar)
|
|
1194
1284
|
else: sidebar_children.append(sidebar)
|
|
1195
1285
|
|
|
1196
|
-
nav_rail = NavSideBarContainer(*sidebar_children, position='left', size='l', id=sidebar_id)
|
|
1286
|
+
nav_rail = NavSideBarContainer(*sidebar_children, position='left', size='l', id=sidebar_id, cls='desktop-nav')
|
|
1287
|
+
|
|
1288
|
+
# Mobile bottom navigation with same links
|
|
1289
|
+
bottom_nav_children = sidebar_links if sidebar_links else (list(sidebar) if is_listy(sidebar) else [sidebar] if sidebar else [])
|
|
1290
|
+
nav_bottom = BottomNav(*bottom_nav_children, cls='bottom mobile-nav',
|
|
1291
|
+
hx_boost='true', hx_target=f'#{main_id}', hx_push_url='true')
|
|
1292
|
+
|
|
1293
|
+
# Responsive CSS for desktop/mobile navigation switching
|
|
1294
|
+
# Only hide elements when needed - don't override Beer CSS flex layout on desktop
|
|
1295
|
+
responsive_nav_css = Style("""
|
|
1296
|
+
.mobile-nav { display: none !important; }
|
|
1297
|
+
@media (max-width: 992px) {
|
|
1298
|
+
.mobile-nav { display: flex !important; }
|
|
1299
|
+
.desktop-nav { display: none !important; }
|
|
1300
|
+
}
|
|
1301
|
+
""")
|
|
1197
1302
|
|
|
1198
|
-
layout_children = []
|
|
1303
|
+
layout_children = [responsive_nav_css]
|
|
1199
1304
|
|
|
1200
1305
|
if nav_bar:
|
|
1201
1306
|
if hasattr(nav_bar, 'attrs') and 'cls' in nav_bar.attrs:
|
|
@@ -1203,9 +1308,10 @@ def Layout(*content, sidebar=None, sidebar_links=None, nav_bar=None, container_s
|
|
|
1203
1308
|
layout_children.append(nav_bar)
|
|
1204
1309
|
|
|
1205
1310
|
layout_children.append(nav_rail)
|
|
1311
|
+
layout_children.append(nav_bottom)
|
|
1206
1312
|
|
|
1207
1313
|
if content_wrapper:
|
|
1208
|
-
container_cls = stringify((container_size, 'surface-container', 'round', 'padding', 'bottom-margin'))
|
|
1314
|
+
container_cls = stringify((container_size, 'surface-container', 'round', 'padding', 'bottom-margin', 'horizontal-margin'))
|
|
1209
1315
|
page_content = Page(content_wrapper, id=main_id)
|
|
1210
1316
|
layout_children.append(Main(page_content, cls=container_cls))
|
|
1211
1317
|
|
|
@@ -1213,7 +1319,7 @@ def Layout(*content, sidebar=None, sidebar_links=None, nav_bar=None, container_s
|
|
|
1213
1319
|
return Div(*layout_children, cls=final_cls, **kwargs)
|
|
1214
1320
|
|
|
1215
1321
|
|
|
1216
|
-
# %% ../nbs/02_components.ipynb
|
|
1322
|
+
# %% ../nbs/02_components.ipynb #4329c780
|
|
1217
1323
|
#| code-fold: true
|
|
1218
1324
|
class TextT(VEnum):
|
|
1219
1325
|
"""Text styles using BeerCSS typography classes."""
|
|
@@ -1245,7 +1351,7 @@ class TextPresets(VEnum):
|
|
|
1245
1351
|
primary_link = 'link primary-text'
|
|
1246
1352
|
muted_link = 'link secondary-text'
|
|
1247
1353
|
|
|
1248
|
-
# %% ../nbs/02_components.ipynb
|
|
1354
|
+
# %% ../nbs/02_components.ipynb #c62d0db5
|
|
1249
1355
|
#| code-fold: true
|
|
1250
1356
|
def CodeSpan(*c, cls=(), **kwargs):
|
|
1251
1357
|
"""Inline code snippet."""
|
|
@@ -1301,7 +1407,7 @@ def Sup(*c, cls=(), **kwargs):
|
|
|
1301
1407
|
cls_str = stringify(cls) if cls else None
|
|
1302
1408
|
return fc.Sup(*c, cls=cls_str, **kwargs) if cls_str else fc.Sup(*c, **kwargs)
|
|
1303
1409
|
|
|
1304
|
-
# %% ../nbs/02_components.ipynb
|
|
1410
|
+
# %% ../nbs/02_components.ipynb #15d01ef3
|
|
1305
1411
|
#| code-fold: true
|
|
1306
1412
|
def FAQItem(question: str, answer: str, question_cls: str = '', answer_cls: str = ''):
|
|
1307
1413
|
"""Collapsible FAQ item using details/summary.
|
|
@@ -1319,7 +1425,7 @@ def FAQItem(question: str, answer: str, question_cls: str = '', answer_cls: str
|
|
|
1319
1425
|
Summary(Article(Nav(Div(question, cls=f"max bold {question_cls}".strip()), I("expand_more")), cls="round surface-variant border no-elevate")),
|
|
1320
1426
|
Article(P(answer, cls=f"secondary-text {answer_cls}".strip()), cls="round border padding"))
|
|
1321
1427
|
|
|
1322
|
-
# %% ../nbs/02_components.ipynb
|
|
1428
|
+
# %% ../nbs/02_components.ipynb #287c86c3
|
|
1323
1429
|
#| code-fold: true
|
|
1324
1430
|
def CookiesBanner(message='We use cookies to enhance your experience. By continuing to visit this site you agree to our use of cookies.',
|
|
1325
1431
|
accept_text='Accept', decline_text='Decline', settings_text=None, policy_link='/cookies', policy_text='Learn more',
|
fh_matui/core.py
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_core.ipynb.
|
|
4
4
|
|
|
5
|
-
# %% auto 0
|
|
5
|
+
# %% auto #0
|
|
6
6
|
__all__ = ['HEADER_URLS', 'beer_hdrs', 'COLOR_NAMES', 'SIZES', 'WIDTH_HEIGHT', 'ELEVATES', 'DIRECTIONS', 'FORMS', 'MARGINS',
|
|
7
7
|
'PADDINGS', 'POSITIONS', 'RESPONSIVE', 'ALIGNMENTS', 'BLURS', 'OPACITIES', 'SHADOWS', 'SPACES', 'RIPPLES',
|
|
8
8
|
'SCROLLS', 'WAVES', 'ZOOMS', 'THEME_HELPERS', 'TYPOGRAPHY', 'TRIGGERS', 'COLOR_HELPERS', 'ALL_HELPERS',
|
|
9
9
|
'MatTheme', 'BeerCssChain']
|
|
10
10
|
|
|
11
|
-
# %% ../nbs/01_core.ipynb
|
|
11
|
+
# %% ../nbs/01_core.ipynb #4894c2b5
|
|
12
12
|
from fastcore.utils import *
|
|
13
13
|
from fasthtml.common import *
|
|
14
14
|
from fasthtml.jupyter import *
|
|
@@ -17,7 +17,7 @@ from fasthtml.common import A, Button as FhButton, I, Span
|
|
|
17
17
|
from .foundations import normalize_tokens, dedupe_preserve_order, stringify, listify, VEnum
|
|
18
18
|
from nbdev.showdoc import show_doc
|
|
19
19
|
|
|
20
|
-
# %% ../nbs/01_core.ipynb
|
|
20
|
+
# %% ../nbs/01_core.ipynb #8e02ad09
|
|
21
21
|
#| code-fold: true
|
|
22
22
|
#| code-fold: true
|
|
23
23
|
HEADER_URLS = {
|
|
@@ -32,7 +32,7 @@ beer_hdrs = (
|
|
|
32
32
|
Script(src=HEADER_URLS["mdc_js"], type='module'),
|
|
33
33
|
)
|
|
34
34
|
|
|
35
|
-
# %% ../nbs/01_core.ipynb
|
|
35
|
+
# %% ../nbs/01_core.ipynb #2e3ec12e
|
|
36
36
|
#| code-fold: true
|
|
37
37
|
#| code-fold: true
|
|
38
38
|
# All BeerCSS color names
|
|
@@ -90,7 +90,7 @@ ALL_HELPERS = (SIZES + WIDTH_HEIGHT + ELEVATES + DIRECTIONS + FORMS + MARGINS +
|
|
|
90
90
|
POSITIONS + RESPONSIVE + ALIGNMENTS + BLURS + OPACITIES + SHADOWS + SPACES +
|
|
91
91
|
RIPPLES + SCROLLS + WAVES + ZOOMS + THEME_HELPERS + TYPOGRAPHY + TRIGGERS + COLOR_HELPERS)
|
|
92
92
|
|
|
93
|
-
# %% ../nbs/01_core.ipynb
|
|
93
|
+
# %% ../nbs/01_core.ipynb #74645b97
|
|
94
94
|
#| code-fold: true
|
|
95
95
|
#| code-fold: true
|
|
96
96
|
class _ThemeChain:
|
|
@@ -157,7 +157,7 @@ class _ThemeChain:
|
|
|
157
157
|
hdrs.append(Title(title))
|
|
158
158
|
return tuple(hdrs)
|
|
159
159
|
|
|
160
|
-
# %% ../nbs/01_core.ipynb
|
|
160
|
+
# %% ../nbs/01_core.ipynb #18fbe01e
|
|
161
161
|
#| code-fold: true
|
|
162
162
|
#| code-fold: true
|
|
163
163
|
class _ThemeNamespace:
|
|
@@ -217,7 +217,7 @@ class _ThemeNamespace:
|
|
|
217
217
|
|
|
218
218
|
MatTheme = _ThemeNamespace()
|
|
219
219
|
|
|
220
|
-
# %% ../nbs/01_core.ipynb
|
|
220
|
+
# %% ../nbs/01_core.ipynb #4672930e
|
|
221
221
|
#| code-fold: true
|
|
222
222
|
#| code-fold: true
|
|
223
223
|
class BeerCssChain:
|
fh_matui/datatable.py
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/05_datatable.ipynb.
|
|
4
4
|
|
|
5
|
-
# %% auto 0
|
|
6
|
-
__all__ = ['PAGE_SIZES', '
|
|
5
|
+
# %% auto #0
|
|
6
|
+
__all__ = ['PAGE_SIZES', 'TABLE_STYLES', 'TABLE_SPACES', 'TABLE_ALIGNS', 'logger', 'table_state_from_request', 'DataTable',
|
|
7
|
+
'CrudContext', 'DataTableResource']
|
|
7
8
|
|
|
8
|
-
# %% ../nbs/05_datatable.ipynb
|
|
9
|
+
# %% ../nbs/05_datatable.ipynb #d7f65962
|
|
9
10
|
from fastcore.utils import *
|
|
10
11
|
from fasthtml.common import *
|
|
11
12
|
from fasthtml.jupyter import *
|
|
@@ -17,7 +18,7 @@ from .core import *
|
|
|
17
18
|
from nbdev.showdoc import show_doc
|
|
18
19
|
from .components import *
|
|
19
20
|
|
|
20
|
-
# %% ../nbs/05_datatable.ipynb
|
|
21
|
+
# %% ../nbs/05_datatable.ipynb #3a9c1b0c
|
|
21
22
|
#| code-fold: true
|
|
22
23
|
#| code-fold: true
|
|
23
24
|
from math import ceil
|
|
@@ -28,6 +29,11 @@ from dataclasses import asdict, is_dataclass
|
|
|
28
29
|
# Default page size options
|
|
29
30
|
PAGE_SIZES = [5, 10, 20, 50]
|
|
30
31
|
|
|
32
|
+
# BeerCSS table styles
|
|
33
|
+
TABLE_STYLES = ['border', 'stripes', 'min', 'fixed'] # Can combine: 'border stripes'
|
|
34
|
+
TABLE_SPACES = ['no-space', 'small-space', 'medium-space', 'large-space']
|
|
35
|
+
TABLE_ALIGNS = ['left-align', 'right-align', 'center-align']
|
|
36
|
+
|
|
31
37
|
|
|
32
38
|
def _to_dict(obj: Any) -> dict:
|
|
33
39
|
"""
|
|
@@ -207,9 +213,20 @@ def DataTable(
|
|
|
207
213
|
page_sizes: list = None,
|
|
208
214
|
search_placeholder: str = 'Search...',
|
|
209
215
|
create_label: str = 'New Record',
|
|
210
|
-
empty_message: str = 'No records match the current filters.'
|
|
216
|
+
empty_message: str = 'No records match the current filters.',
|
|
217
|
+
# BeerCSS table styling options
|
|
218
|
+
table_style: str = 'border', # border, stripes, min, fixed (can combine: 'border stripes')
|
|
219
|
+
space: str = 'small-space', # no-space, small-space, medium-space, large-space
|
|
220
|
+
align: str = None # left-align, right-align, center-align (None = default left)
|
|
211
221
|
):
|
|
212
|
-
"
|
|
222
|
+
"""
|
|
223
|
+
Generic data table with server-side pagination, search, and row actions.
|
|
224
|
+
|
|
225
|
+
Table Styling (BeerCSS):
|
|
226
|
+
table_style: Table visual style - 'border', 'stripes', 'min', 'fixed' or combine like 'border stripes'
|
|
227
|
+
space: Row spacing - 'no-space', 'small-space', 'medium-space', 'large-space'
|
|
228
|
+
align: Text alignment - 'left-align', 'right-align', 'center-align' (None for default)
|
|
229
|
+
"""
|
|
213
230
|
# Defaults
|
|
214
231
|
crud_ops = crud_ops or {"create": False, "update": False, "delete": False}
|
|
215
232
|
crud_enabled = crud_enabled or {k: bool(v) for k, v in crud_ops.items() if k != 'custom_actions'}
|
|
@@ -313,6 +330,16 @@ def DataTable(
|
|
|
313
330
|
cls="max"
|
|
314
331
|
)
|
|
315
332
|
|
|
333
|
+
# Compose table classes from BeerCSS styling options
|
|
334
|
+
table_classes = []
|
|
335
|
+
if table_style:
|
|
336
|
+
table_classes.append(table_style)
|
|
337
|
+
if space:
|
|
338
|
+
table_classes.append(space)
|
|
339
|
+
if align:
|
|
340
|
+
table_classes.append(align)
|
|
341
|
+
table_cls = ' '.join(table_classes) if table_classes else None
|
|
342
|
+
|
|
316
343
|
# Build table with proper Thead/Tbody structure
|
|
317
344
|
data_table = Table(
|
|
318
345
|
Thead(Tr(*[Th(label) for label in header_labels])),
|
|
@@ -320,7 +347,7 @@ def DataTable(
|
|
|
320
347
|
Tr(*[Td(row_dict.get(key, '')) for key in header_keys])
|
|
321
348
|
for row_dict in table_rows
|
|
322
349
|
]),
|
|
323
|
-
cls=
|
|
350
|
+
cls=table_cls
|
|
324
351
|
) if data else Div(empty_message, cls="center-align padding")
|
|
325
352
|
|
|
326
353
|
# Footer section with page size selector and pagination
|
|
@@ -341,7 +368,7 @@ def DataTable(
|
|
|
341
368
|
# Single row when no pagination needed
|
|
342
369
|
footer = page_info_row
|
|
343
370
|
|
|
344
|
-
return
|
|
371
|
+
return Div(
|
|
345
372
|
Div(id=feedback_id), # Feedback container for modals/toasts
|
|
346
373
|
Div(
|
|
347
374
|
search_form,
|
|
@@ -357,7 +384,7 @@ def DataTable(
|
|
|
357
384
|
hx_swap="outerHTML"
|
|
358
385
|
)
|
|
359
386
|
|
|
360
|
-
# %% ../nbs/05_datatable.ipynb
|
|
387
|
+
# %% ../nbs/05_datatable.ipynb #b84c744d
|
|
361
388
|
import asyncio
|
|
362
389
|
import logging
|
|
363
390
|
from dataclasses import dataclass
|
|
@@ -453,7 +480,7 @@ class CrudContext:
|
|
|
453
480
|
record_id: Optional[Any] = None # ID for update/delete (None for create)
|
|
454
481
|
feedback_id: Optional[str] = None # Target div ID for HTMX swap (for override handlers)
|
|
455
482
|
|
|
456
|
-
# %% ../nbs/05_datatable.ipynb
|
|
483
|
+
# %% ../nbs/05_datatable.ipynb #63468220
|
|
457
484
|
from typing import Callable, Optional, Any, Union
|
|
458
485
|
from dataclasses import asdict, is_dataclass
|
|
459
486
|
from datetime import datetime
|
fh_matui/foundations.py
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_foundations.ipynb.
|
|
4
4
|
|
|
5
|
-
# %% auto 0
|
|
5
|
+
# %% auto #0
|
|
6
6
|
__all__ = ['VEnum', 'stringify', 'normalize_tokens', 'dedupe_preserve_order']
|
|
7
7
|
|
|
8
|
-
# %% ../nbs/00_foundations.ipynb
|
|
8
|
+
# %% ../nbs/00_foundations.ipynb #f1c2465a
|
|
9
9
|
from typing import Any, Iterable, Optional, Union
|
|
10
10
|
from enum import Enum
|
|
11
11
|
from fastcore.utils import *
|
|
12
12
|
from nbdev.showdoc import show_doc
|
|
13
13
|
|
|
14
|
-
# %% ../nbs/00_foundations.ipynb
|
|
14
|
+
# %% ../nbs/00_foundations.ipynb #4411c74b
|
|
15
15
|
#| code-fold: true
|
|
16
16
|
#| code-fold: true
|
|
17
17
|
class VEnum(Enum):
|
|
@@ -20,7 +20,7 @@ class VEnum(Enum):
|
|
|
20
20
|
def __add__(self, other): return stringify((self, other))
|
|
21
21
|
def __radd__(self, other): return stringify((other, self))
|
|
22
22
|
|
|
23
|
-
# %% ../nbs/00_foundations.ipynb
|
|
23
|
+
# %% ../nbs/00_foundations.ipynb #f4cac5a4
|
|
24
24
|
#| code-fold: true
|
|
25
25
|
#| code-fold: true
|
|
26
26
|
def stringify(o):
|
|
@@ -29,7 +29,7 @@ def stringify(o):
|
|
|
29
29
|
return ' '.join(map(str, o)) if o else ""
|
|
30
30
|
return str(o)
|
|
31
31
|
|
|
32
|
-
# %% ../nbs/00_foundations.ipynb
|
|
32
|
+
# %% ../nbs/00_foundations.ipynb #865963c6
|
|
33
33
|
#| code-fold: true
|
|
34
34
|
#| code-fold: true
|
|
35
35
|
def normalize_tokens(cls):
|
fh_matui/web_pages.py
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/04_web_pages.ipynb.
|
|
4
4
|
|
|
5
|
-
# %% auto 0
|
|
5
|
+
# %% auto #0
|
|
6
6
|
__all__ = ['SECTION_STYLE', 'SECTION_MARGIN', 'STANDARD_FOOTER_COLUMNS', 'HeroSection', 'FeatureShowcase', 'FeaturesGrid',
|
|
7
7
|
'PricingSection', 'FAQSection', 'PageFooter', 'LandingNavBar', 'LandingPageSimple', 'MarkdownSection',
|
|
8
8
|
'ContentPage']
|
|
9
9
|
|
|
10
|
-
# %% ../nbs/04_web_pages.ipynb
|
|
10
|
+
# %% ../nbs/04_web_pages.ipynb #7f3cabf7
|
|
11
11
|
import importlib
|
|
12
12
|
import markdown
|
|
13
13
|
from .components import *
|
|
@@ -23,7 +23,7 @@ import fasthtml.components as fc
|
|
|
23
23
|
from fasthtml.common import A, Button as FhButton, I, Span
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
# %% ../nbs/04_web_pages.ipynb
|
|
26
|
+
# %% ../nbs/04_web_pages.ipynb #ec869572
|
|
27
27
|
def HeroSection(
|
|
28
28
|
title: str, # Main hero title
|
|
29
29
|
subtitle: str, # Hero subtitle/description
|
|
@@ -95,7 +95,7 @@ def HeroSection(
|
|
|
95
95
|
cls=f"hero-section {cls}".strip(),
|
|
96
96
|
)
|
|
97
97
|
|
|
98
|
-
# %% ../nbs/04_web_pages.ipynb
|
|
98
|
+
# %% ../nbs/04_web_pages.ipynb #9aac83c5
|
|
99
99
|
def FeatureShowcase(
|
|
100
100
|
features: list, # List of dicts: {title, description, image_src?, icon?, image_alt?}
|
|
101
101
|
title: str = None, # Optional section title
|
|
@@ -178,7 +178,7 @@ def FeatureShowcase(
|
|
|
178
178
|
|
|
179
179
|
return Section(*content, cls=f"responsive {cls}".strip())
|
|
180
180
|
|
|
181
|
-
# %% ../nbs/04_web_pages.ipynb
|
|
181
|
+
# %% ../nbs/04_web_pages.ipynb #297f69dc
|
|
182
182
|
def FeaturesGrid(
|
|
183
183
|
features: list, # List of dicts with 'icon', 'title', 'description'
|
|
184
184
|
title: str = None, # Optional section title
|
|
@@ -241,7 +241,7 @@ def FeaturesGrid(
|
|
|
241
241
|
# Use responsive for centered max-width layout
|
|
242
242
|
return Section(*content, cls=f"responsive padding {cls}".strip())
|
|
243
243
|
|
|
244
|
-
# %% ../nbs/04_web_pages.ipynb
|
|
244
|
+
# %% ../nbs/04_web_pages.ipynb #ea5213e3
|
|
245
245
|
def _pricing_toggle_js():
|
|
246
246
|
"""Returns Script + Style for pricing toggle functionality."""
|
|
247
247
|
css = """
|
|
@@ -452,7 +452,7 @@ def PricingSection(
|
|
|
452
452
|
cls=f"responsive padding {cls}".strip(),
|
|
453
453
|
)
|
|
454
454
|
|
|
455
|
-
# %% ../nbs/04_web_pages.ipynb
|
|
455
|
+
# %% ../nbs/04_web_pages.ipynb #35bdc231
|
|
456
456
|
def FAQSection(title: str, faqs: list, cls: str = ""):
|
|
457
457
|
"""FAQ section using FAQItem from components.
|
|
458
458
|
|
|
@@ -477,7 +477,7 @@ def FAQSection(title: str, faqs: list, cls: str = ""):
|
|
|
477
477
|
cls=f"responsive column {cls}".strip(),
|
|
478
478
|
)
|
|
479
479
|
|
|
480
|
-
# %% ../nbs/04_web_pages.ipynb
|
|
480
|
+
# %% ../nbs/04_web_pages.ipynb #a6a029c6
|
|
481
481
|
# CSS for decorative sine wave border on footer
|
|
482
482
|
_FOOTER_WAVE_CSS = """
|
|
483
483
|
.footer-wave {
|
|
@@ -563,7 +563,7 @@ def PageFooter(
|
|
|
563
563
|
style="padding-top: 1.5rem;" if wave_border else "", # Space for wave
|
|
564
564
|
)
|
|
565
565
|
|
|
566
|
-
# %% ../nbs/04_web_pages.ipynb
|
|
566
|
+
# %% ../nbs/04_web_pages.ipynb #be3a76d2
|
|
567
567
|
def LandingNavBar(
|
|
568
568
|
brand_name: str, # Brand name for the navbar
|
|
569
569
|
links: list = None, # List of dicts with 'text' and 'href'
|
|
@@ -612,7 +612,7 @@ def LandingNavBar(
|
|
|
612
612
|
|
|
613
613
|
return Header(inner, cls=toolbar_cls)
|
|
614
614
|
|
|
615
|
-
# %% ../nbs/04_web_pages.ipynb
|
|
615
|
+
# %% ../nbs/04_web_pages.ipynb #a45e13b8
|
|
616
616
|
# Section spacing: padding for internal space, margin for breathing room between sections
|
|
617
617
|
SECTION_STYLE = "large-padding" # Internal padding
|
|
618
618
|
SECTION_MARGIN = "extra-margin bottom-margin" # Vertical spacing between sections
|
|
@@ -806,7 +806,7 @@ def LandingPageSimple(
|
|
|
806
806
|
cls=f"column {cls}".strip()
|
|
807
807
|
)
|
|
808
808
|
|
|
809
|
-
# %% ../nbs/04_web_pages.ipynb
|
|
809
|
+
# %% ../nbs/04_web_pages.ipynb #5aa5632d
|
|
810
810
|
def MarkdownSection(
|
|
811
811
|
content: str, # Markdown text to render
|
|
812
812
|
title: str = None, # Optional section title (rendered as H3 for appropriate size)
|
|
@@ -858,7 +858,7 @@ We value your privacy...
|
|
|
858
858
|
article = Article(*elements, cls=f"large-width large-padding {cls}".strip())
|
|
859
859
|
return Div(article, cls="row center-align")
|
|
860
860
|
|
|
861
|
-
# %% ../nbs/04_web_pages.ipynb
|
|
861
|
+
# %% ../nbs/04_web_pages.ipynb #250d86cb
|
|
862
862
|
def ContentPage(
|
|
863
863
|
brand_name: str, # Brand name for navbar
|
|
864
864
|
footer_copyright: str, # Copyright text for footer
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fh-matui
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.16
|
|
4
4
|
Summary: material-ui for fasthtml
|
|
5
5
|
Home-page: https://github.com/abhisheksreesaila/fh-matui
|
|
6
6
|
Author: abhishek sreesaila
|
|
@@ -145,10 +145,8 @@ styling
|
|
|
145
145
|
|
|
146
146
|
1. **`MatTheme.indigo.headers()`** - Loads BeerCSS with the indigo
|
|
147
147
|
color scheme
|
|
148
|
-
2.
|
|
149
|
-
|
|
150
|
-
3. **[`FormField`](https://abhisheksreesaila.github.io/fh-matui/components.html#formfield)** -
|
|
151
|
-
Generates a styled input with floating label
|
|
148
|
+
2. **`Card`** - Creates a Material Design card component with elevation
|
|
149
|
+
3. **`FormField`** - Generates a styled input with floating label
|
|
152
150
|
4. **`Button`** - Renders a Material Design button with ripple effects
|
|
153
151
|
|
|
154
152
|
## 📚 Module Reference
|
|
@@ -157,9 +155,9 @@ styling
|
|
|
157
155
|
|----|----|----|
|
|
158
156
|
| [Foundations](foundations.html) | Base utilities and helper functions | `BeerHeaders`, `display`, styling helpers |
|
|
159
157
|
| [Core](core.html) | Theme system and styling | `MatTheme`, color presets, theme configuration |
|
|
160
|
-
| [Components](components.html) | UI component library | `Button`,
|
|
158
|
+
| [Components](components.html) | UI component library | `Button`, `Card`, `FormField`, `FormModal`, `Grid` |
|
|
161
159
|
| [App Pages](app_pages.html) | Application layouts | Navigation, sidebars, full-page layouts |
|
|
162
|
-
| [Data Tables](05_table.html) | Data management components |
|
|
160
|
+
| [Data Tables](05_table.html) | Data management components | `DataTable`, `DataTableResource`, CRUD operations |
|
|
163
161
|
| [Web Pages](web_pages.html) | Marketing/landing pages | Hero sections, feature grids, testimonials |
|
|
164
162
|
|
|
165
163
|
## 🛠️ Development
|
|
@@ -187,8 +185,8 @@ nbdev_prepare
|
|
|
187
185
|
| **Component consistency** | Unified API across all components |
|
|
188
186
|
| **Dark mode support** | Built-in with automatic system preference detection |
|
|
189
187
|
| **Responsive design** | Mobile-first grid system and responsive utilities |
|
|
190
|
-
| **Form handling** |
|
|
191
|
-
| **Data management** |
|
|
188
|
+
| **Form handling** | `FormField`, `FormGrid`, `FormModal` for rapid form building |
|
|
189
|
+
| **Data management** | `DataTable` and `DataTableResource` for CRUD operations |
|
|
192
190
|
|
|
193
191
|
## 🤖 For LLM Users
|
|
194
192
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
fh_matui/__init__.py,sha256=1Trt9wiX-dulRPRdt82fqX0JH-xfZMdFAJe6dA4gaQM,24
|
|
2
|
+
fh_matui/_modidx.py,sha256=x9AV-LcrfUpON34QN4WnbO-E_GKQ2PhdlY5Zh0NEAT0,24162
|
|
3
|
+
fh_matui/app_pages.py,sha256=DtaStRuYJ_oiEB2nBzMApCv0NfBkE2wUZMUfKBMhMFs,10378
|
|
4
|
+
fh_matui/components.py,sha256=nbqVBqehyjUYnNlkBwbenKzmlsZ7Ah6i19G4eHoksf0,59053
|
|
5
|
+
fh_matui/core.py,sha256=3j-4ot0MgmyBpsIg7qRh3-KTlRYmgVeetQhQNQPQ19E,11108
|
|
6
|
+
fh_matui/datatable.py,sha256=DRgA6nmm4h_QCMmd6CJK8QluRJ0S7c8j9H-yHjpUjBo,42590
|
|
7
|
+
fh_matui/foundations.py,sha256=jSJuELof3B-YeZ4jlsIV-ABpOn9Hv2Qd67o0LuBTqVY,1912
|
|
8
|
+
fh_matui/web_pages.py,sha256=6l0axbrCYmqSmSGrq0qt3nPUNblArCCAy_LlN-iCsNc,35183
|
|
9
|
+
fh_matui-0.9.16.dist-info/licenses/LICENSE,sha256=xV8xoN4VOL0uw9X8RSs2IMuD_Ss_a9yAbtGNeBWZwnw,11337
|
|
10
|
+
fh_matui-0.9.16.dist-info/METADATA,sha256=RR9vOYzH4hsAqBvlzh2s4kTBvvDXQcfMt1W0DIkWT64,9523
|
|
11
|
+
fh_matui-0.9.16.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
+
fh_matui-0.9.16.dist-info/entry_points.txt,sha256=zn4CR4gNTiAAxbFsCxHAf2tQhtW29_YOffjbUTgeoWI,38
|
|
13
|
+
fh_matui-0.9.16.dist-info/top_level.txt,sha256=l80d5eoA2ZjqtPYwAorLMS5PiHxUxz3zKzxMJ41Xoso,9
|
|
14
|
+
fh_matui-0.9.16.dist-info/RECORD,,
|
fh_matui-0.9.14.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
fh_matui/__init__.py,sha256=s9smkhMiLTJ6_VCSfo7J6aeWBGIlYjSsTDGrVprZjrI,24
|
|
2
|
-
fh_matui/_modidx.py,sha256=r9X_k4ptjzUPy6u6AS6411Qn0Fq0NuxMyV5PrpMH2oA,24032
|
|
3
|
-
fh_matui/app_pages.py,sha256=Sn9-tgBpaPNbR-0nZtPLoSCmAWLOGB4UQ88IkFvzBRY,10361
|
|
4
|
-
fh_matui/components.py,sha256=Lgk8Qazjx3TrrkEcGxiUbgPE-4676wL2fcs9nmhv1Cg,54179
|
|
5
|
-
fh_matui/core.py,sha256=fMSw0fHvUTYt1Qd1DzaWDS4xWIDbkyUCcr-LIz2IJ5k,11062
|
|
6
|
-
fh_matui/datatable.py,sha256=cDk_R9A1WTWa7fxDBBAZsnFU36ko_ml94HxmmbL_ENc,41248
|
|
7
|
-
fh_matui/foundations.py,sha256=xiQOeyV4FhNzPpw6mbtIrPWMyzlMi3BENo41IlGnAz8,1880
|
|
8
|
-
fh_matui/web_pages.py,sha256=at_M34Vxc1i9O0ukS41PaRJntkXXDzMqyzlcrgESUcw,35103
|
|
9
|
-
fh_matui-0.9.14.dist-info/licenses/LICENSE,sha256=xV8xoN4VOL0uw9X8RSs2IMuD_Ss_a9yAbtGNeBWZwnw,11337
|
|
10
|
-
fh_matui-0.9.14.dist-info/METADATA,sha256=JGk9Wq2RxIQKXxyatsmyVpGKwbxsmU6n1W-hx3rJrjs,10491
|
|
11
|
-
fh_matui-0.9.14.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
-
fh_matui-0.9.14.dist-info/entry_points.txt,sha256=zn4CR4gNTiAAxbFsCxHAf2tQhtW29_YOffjbUTgeoWI,38
|
|
13
|
-
fh_matui-0.9.14.dist-info/top_level.txt,sha256=l80d5eoA2ZjqtPYwAorLMS5PiHxUxz3zKzxMJ41Xoso,9
|
|
14
|
-
fh_matui-0.9.14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|