-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstyle.py
More file actions
135 lines (109 loc) · 3.79 KB
/
style.py
File metadata and controls
135 lines (109 loc) · 3.79 KB
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
"""StyleSheet, style resolution, and theming support.
Provides a :class:`StyleSheet` helper for creating and composing
reusable style dictionaries, a :func:`resolve_style` utility for
flattening the ``style`` prop, and built-in theme contexts.
Usage::
import pythonnative as pn
styles = pn.StyleSheet.create(
title={"font_size": 24, "bold": True, "color": "#333"},
container={"padding": 16, "spacing": 12},
)
pn.Text("Hello", style=styles["title"])
pn.Column(..., style=styles["container"])
"""
from typing import Any, Dict, List, Optional, Union
from .hooks import Context, create_context
_StyleDict = Dict[str, Any]
StyleValue = Union[None, _StyleDict, List[Optional[_StyleDict]]]
def resolve_style(style: StyleValue) -> _StyleDict:
"""Flatten a ``style`` prop into a single dict.
Accepts ``None``, a single dict, or a list of dicts (later entries
override earlier ones, mirroring React Native's array style pattern).
"""
if style is None:
return {}
if isinstance(style, dict):
return dict(style)
result: _StyleDict = {}
for entry in style:
if entry:
result.update(entry)
return result
# ======================================================================
# StyleSheet
# ======================================================================
class StyleSheet:
"""Utility for creating and composing style dictionaries."""
@staticmethod
def create(**named_styles: _StyleDict) -> Dict[str, _StyleDict]:
"""Create a set of named styles.
Each keyword argument is a style name mapping to a dict of
property values::
styles = StyleSheet.create(
heading={"font_size": 28, "bold": True},
body={"font_size": 16},
)
"""
return {name: dict(props) for name, props in named_styles.items()}
@staticmethod
def compose(*styles: _StyleDict) -> _StyleDict:
"""Merge multiple style dicts, later values overriding earlier ones."""
merged: _StyleDict = {}
for style in styles:
if style:
merged.update(style)
return merged
@staticmethod
def flatten(styles: Any) -> _StyleDict:
"""Flatten a style or list of styles into a single dict.
Accepts a single dict, a list of dicts, or ``None``.
"""
if styles is None:
return {}
if isinstance(styles, dict):
return dict(styles)
result: _StyleDict = {}
for s in styles:
if s:
result.update(s)
return result
# ======================================================================
# Theming
# ======================================================================
DEFAULT_LIGHT_THEME: _StyleDict = {
"primary_color": "#007AFF",
"secondary_color": "#5856D6",
"background_color": "#FFFFFF",
"surface_color": "#F2F2F7",
"text_color": "#000000",
"text_secondary_color": "#8E8E93",
"error_color": "#FF3B30",
"success_color": "#34C759",
"warning_color": "#FF9500",
"font_size": 16,
"font_size_small": 13,
"font_size_large": 20,
"font_size_title": 28,
"spacing": 8,
"spacing_large": 16,
"border_radius": 8,
}
DEFAULT_DARK_THEME: _StyleDict = {
"primary_color": "#0A84FF",
"secondary_color": "#5E5CE6",
"background_color": "#000000",
"surface_color": "#1C1C1E",
"text_color": "#FFFFFF",
"text_secondary_color": "#8E8E93",
"error_color": "#FF453A",
"success_color": "#30D158",
"warning_color": "#FF9F0A",
"font_size": 16,
"font_size_small": 13,
"font_size_large": 20,
"font_size_title": 28,
"spacing": 8,
"spacing_large": 16,
"border_radius": 8,
}
ThemeContext: Context = create_context(DEFAULT_LIGHT_THEME)