webwidgets 0.2.1__py3-none-any.whl → 1.1.0__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.
webwidgets/__init__.py CHANGED
@@ -10,6 +10,9 @@
10
10
  #
11
11
  # =======================================================================
12
12
 
13
- __version__ = "0.2.1" # Dynamically set by build backend
13
+ __version__ = "1.1.0" # Dynamically set by build backend
14
14
 
15
15
  from . import compilation
16
+ from . import utility
17
+ from .website import *
18
+ from .widgets import *
@@ -10,5 +10,6 @@
10
10
  #
11
11
  # =======================================================================
12
12
 
13
- from .css import compile_css, CSSRule, CompiledCSS, apply_css, \
14
- default_rule_namer
13
+ from .css import apply_css, compile_css, CompiledCSS, default_class_namer
14
+ from .css_rule import ClassRule, CSSRule
15
+ from . import sections
@@ -10,87 +10,102 @@
10
10
  #
11
11
  # =======================================================================
12
12
 
13
+ from .css_rule import ClassRule
13
14
  import itertools
15
+ from .sections.preamble import Preamble
16
+ from .sections.rule_section import RuleSection
14
17
  from typing import Callable, Dict, List, Union
15
18
  from webwidgets.compilation.html.html_node import HTMLNode
16
19
  from webwidgets.utility.representation import ReprMixin
17
- from webwidgets.utility.validation import validate_css_identifier
18
-
19
-
20
- class CSSRule(ReprMixin):
21
- """A rule in a style sheet.
22
- """
23
-
24
- def __init__(self, name: str, declarations: Dict[str, str]):
25
- """Stores the name and declarations of the rule.
26
-
27
- :param name: The name of the rule.
28
- :type name: str
29
- :param declarations: The CSS declarations for the rule, specified as a
30
- dictionary where keys are property names and values are their
31
- corresponding values. For example: `{'color': 'red'}`
32
- :type declarations: Dict[str, str]
33
- """
34
- super().__init__()
35
- self.name = name
36
- self.declarations = declarations
37
20
 
38
21
 
39
22
  class CompiledCSS(ReprMixin):
40
23
  """A utility class to hold compiled CSS rules.
41
24
  """
42
25
 
43
- def __init__(self, trees: List[HTMLNode], rules: List[CSSRule],
44
- mapping: Dict[int, List[CSSRule]]):
45
- """Stores compiled CSS rules.
26
+ def __init__(self, trees: List[HTMLNode], core: RuleSection,
27
+ mapping: Dict[int, List[ClassRule]]):
28
+ """Stores compiled CSS rules and their mapping to the nodes in the
29
+ given trees.
46
30
 
47
31
  :param trees: The HTML trees at the origin of the compilation. These
48
32
  are the elements that have been styled with CSS properties.
49
33
  :type trees: List[HTMLNode]
50
- :param rules: The compiled CSS rules.
51
- :type rules: List[CSSRule]
34
+ :param rules: The CSS section containing the compiled CSS rules.
35
+ :type rules: RuleSection
52
36
  :param mapping: A dictionary mapping each node ID to a list of rules
53
37
  that achieve the same style.
54
- :type mapping: Dict[int, List[CSSRule]]
38
+ :type mapping: Dict[int, List[ClassRule]]
55
39
  """
56
40
  super().__init__()
57
41
  self.trees = trees
58
- self.rules = rules
42
+ self.preamble = Preamble()
43
+ self.core = core
59
44
  self.mapping = mapping
60
45
 
61
46
  def to_css(self, indent_size: int = 4) -> str:
62
- """Converts the `rules` dictionary of the :py:class:`CompiledCSS`
63
- object into CSS code.
47
+ """Converts the `preamble` and `core` sections of the
48
+ :py:class:`CompiledCSS` object into CSS code.
64
49
 
65
- Rule names are converted to class selectors. Note that each rule and
66
- property name is validated with :py:func:`validate_css_identifier`
67
- before being converted.
50
+ Sections are converted with their :py:meth:`RuleSection.to_css`
51
+ methods.
68
52
 
69
- :param indent_size: The number of spaces to use for indentation in the
70
- CSS code. Defaults to 4.
53
+ :param indent_size: See :py:meth:`RuleSection.to_css`.
71
54
  :type indent_size: int
72
55
  :return: The CSS code as a string.
73
56
  :rtype: str
74
57
  """
75
- # Initializing code and defining indentation
76
- css_code = ""
77
- indentation = ' ' * indent_size
58
+ return '\n\n'.join(
59
+ section.to_css(indent_size=indent_size) for section in (
60
+ self.preamble, self.core
61
+ ))
78
62
 
79
- # Writing down each rule
80
- for i, rule in enumerate(self.rules):
81
- validate_css_identifier(rule.name)
82
- css_code += f".{rule.name}" + " {\n"
83
- for property_name, value in rule.declarations.items():
84
- validate_css_identifier(property_name)
85
- css_code += f"{indentation}{property_name}: {value};\n"
86
- css_code += "}" + ('\n\n' if i < len(self.rules) - 1 else '')
87
63
 
88
- return css_code
64
+ def apply_css(css: CompiledCSS, tree: HTMLNode) -> None:
65
+ """Applies the CSS rules to the given tree.
66
+
67
+ Rules are added as HTML classes to each node with a style in the tree. If a
68
+ node does not have a `class` attribute yet, it will be created for that
69
+ node. Nodes that do not have any style are left untouched.
70
+
71
+ Note that this function is recursive and calls itself on each child node of
72
+ the tree.
73
+
74
+ :param css: The compiled CSS object containing the rules to apply and the
75
+ mapping to each node. It should have been created by invoking
76
+ :py:func:`compile_css` on the given tree, but it can be modified before
77
+ passing it to this function, as long as its content remains consistent.
78
+ :type css: CompiledCSS
79
+ :param tree: The tree to which the CSS rules should be applied. It will be
80
+ modified in place by this function. If you want to keep the original
81
+ tree unchanged, make a deep copy of it using its
82
+ :py:meth:`HTMLNode.copy` method and pass this copy instead.
83
+ :type tree: HTMLNode
84
+ """
85
+ # Only modifying nodes if they have a style (and therefore if the list of
86
+ # rules mapped to them in `css.mapping` is not empty)
87
+ if tree.style:
88
+
89
+ # Listing rules to add as classes. We do not add rules that are already
90
+ # there.
91
+ rules_to_add = [r.name for r in css.mapping[id(tree)] if r.name not in
92
+ tree.attributes.get('class', '').split(' ')]
93
+
94
+ # Updating the class attribute. If it already exists and is not empty,
95
+ # we need to insert a space before adding the CSS classes.
96
+ maybe_space = ' ' if tree.attributes.get(
97
+ 'class', None) and rules_to_add else ''
98
+ tree.attributes['class'] = tree.attributes.get(
99
+ 'class', '') + maybe_space + ' '.join(rules_to_add)
100
+
101
+ # Recursively applying the CSS rules to all child nodes of the tree
102
+ for child in tree.children:
103
+ apply_css(css, child)
89
104
 
90
105
 
91
106
  def compile_css(trees: Union[HTMLNode, List[HTMLNode]],
92
- rule_namer: Callable[[List[CSSRule], int],
93
- str] = None) -> CompiledCSS:
107
+ class_namer: Callable[[List[ClassRule], int],
108
+ str] = None) -> CompiledCSS:
94
109
  """Computes optimized CSS rules from the given HTML trees.
95
110
 
96
111
  The main purpose of this function is to reduce the number of CSS rules
@@ -117,36 +132,44 @@ def compile_css(trees: Union[HTMLNode, List[HTMLNode]],
117
132
  .. code-block:: python
118
133
 
119
134
  >>> compiled_css = compile_css(tree)
120
- >>> print(compiled_css.rules)
135
+ >>> print(compiled_css.core.rules)
121
136
  [
122
- CSSRule(name='r0', declarations={'color': 'blue'}),
123
- CSSRule(name='r1', declarations={'margin': '0'}),
124
- CSSRule(name='r2', declarations={'padding': '0'})
137
+ ClassRule(selector='.c0', declarations={'color': 'blue'}, ...),
138
+ ClassRule(selector='.c1', declarations={'margin': '0'}, ...),
139
+ ClassRule(selector='.c2', declarations={'padding': '0'}, ...)
125
140
  ]
126
141
 
142
+ Internally, each optimized rule gets compiled into a :py:class:`ClassRule`
143
+ object, which represents a CSS rule whose selector targets the HTML `class`
144
+ attribute. Each rule gets assigned a unique HTML class and all classes can
145
+ then be added to the trees with :py:func:`apply_css`. Classes are named
146
+ `"c0"`, `"c1"`, and so on by default, but this naming process can be
147
+ customized using the `class_namer` argument.
148
+
127
149
  :param trees: A single tree or a list of trees to optimize over. All
128
150
  children are recursively included in the compilation.
129
151
  :type trees: Union[HTMLNode, List[HTMLNode]]
130
- :param rule_namer: A callable that takes two arguments, which are the list
152
+ :param class_namer: A callable that takes two arguments, which are the list
131
153
  of all compiled rules and an index within that list, and returns a
132
- unique name for the rule at the given index.
133
-
134
- This argument allows to customize the rule naming process and use names
135
- other than the default `"r0"`, `"r1"`, etc. For example, it can be used
136
- to achieve something similar to Tailwind CSS and name rules according
137
- to what they achieve, e.g. by prefixing their name with `"m"` for
138
- margin rules or `"p"` for padding rules. Note that all rule names will
139
- be validated with the :py:func:`validate_css_identifier` function
140
- before being written into CSS code.
141
-
142
- Defaults to the :py:func:`default_rule_namer` function which implements
143
- a default naming strategy where each rule is named `"r{i}"` where `i`
144
- is the index of the rule in the list.
145
- :type rule_namer: Callable[[List[CSSRule], int], str]
154
+ unique name for the HTML class to associate with the rule at the given
155
+ index.
156
+
157
+ This argument allows to customize the class naming process and use names
158
+ other than the default `"c0"`, `"c1"`, etc. For example, it can be used
159
+ to achieve something similar to Tailwind CSS and name HTML classes
160
+ according to what they achieve, e.g. by prefixing their name with `"m"`
161
+ for margin rules or `"p"` for padding rules. Note that all class
162
+ selectors will be validated with the :py:func:`validate_css_selector`
163
+ function before being written into CSS code.
164
+
165
+ Defaults to the :py:func:`default_class_namer` function which
166
+ implements a default naming strategy where each class is named `"c{i}"`
167
+ where `i` is the index of the rule in the list.
168
+ :type class_namer: Callable[[List[ClassRule], int], str]
146
169
  :return: The :py:class:`CompiledCSS` object containing the optimized rules.
147
170
  Every HTML node present in one or more of the input trees is included
148
171
  in the :py:attr:`CompiledCSS.mapping` attribute, even if the node does
149
- not have a style. Rules are alphabetically ordered by name in the
172
+ not have a style. Rules are alphabetically ordered by class name in the
150
173
  mapping.
151
174
  :rtype: CompiledCSS
152
175
  """
@@ -154,76 +177,37 @@ def compile_css(trees: Union[HTMLNode, List[HTMLNode]],
154
177
  if isinstance(trees, HTMLNode):
155
178
  trees = [trees]
156
179
 
157
- # Handling default rule_namer
158
- rule_namer = default_rule_namer if rule_namer is None else rule_namer
180
+ # Handling default class_namer
181
+ class_namer = default_class_namer if class_namer is None else class_namer
159
182
 
160
- # For now, we just return a simple mapping where each CSS property defines
161
- # its own ruleset
183
+ # We compute a simple mapping where each CSS property defines its own
184
+ # ruleset
162
185
  styles = {k: v for tree in trees for k, v in tree.get_styles().items()}
163
186
  properties = set(itertools.chain.from_iterable(s.items()
164
187
  for s in styles.values()))
165
- rules = [CSSRule(None, dict([p])) # Initializing with no name
188
+ rules = [ClassRule("", dict([p])) # Initializing with empty name
166
189
  for p in sorted(properties)]
167
190
  for i, rule in enumerate(rules): # Assigning name from callback
168
- rule.name = rule_namer(rules, i)
191
+ rule.name = class_namer(rules, i)
169
192
  rules = sorted(rules, key=lambda r: r.name) # Sorting by name
170
193
  mapping = {node_id: [r for r in rules if
171
194
  set(r.declarations.items()).issubset(style.items())]
172
195
  for node_id, style in styles.items()}
173
- return CompiledCSS(trees, rules, mapping)
174
-
175
-
176
- def apply_css(css: CompiledCSS, tree: HTMLNode) -> None:
177
- """Applies the CSS rules to the given tree.
178
-
179
- Rules are added as HTML classes to each node with a style in the tree. If a
180
- node does not have a `class` attribute yet, it will be created for that
181
- node. Nodes that do not have any style are left untouched.
182
-
183
- Note that this function is recursive and calls itself on each child node of
184
- the tree.
185
-
186
- :param css: The compiled CSS object containing the rules to apply and the
187
- mapping to each node. It should have been created by invoking
188
- :py:func:`compile_css` on the given tree, but it can be modified before
189
- passing it to this function, as long as its content remains consistent.
190
- :type css: CompiledCSS
191
- :param tree: The tree to which the CSS rules should be applied. It will be
192
- modified in place by this function. If you want to keep the original
193
- tree unchanged, make a deep copy of it using its
194
- :py:meth:`HTMLNode.copy` method and pass this copy instead.
195
- :type tree: HTMLNode
196
- """
197
- # Only modifying nodes if they have a style (and therefore if the list of
198
- # rules mapped to them in `css.mapping` is not empty)
199
- if tree.style:
200
-
201
- # Listing rules to add as classes. We do not add rules that are already
202
- # there.
203
- rules_to_add = [r.name for r in css.mapping[id(tree)] if r.name not in
204
- tree.attributes.get('class', '').split(' ')]
205
196
 
206
- # Updating the class attribute. If it already exists and is not empty,
207
- # we need to insert a space before adding the CSS classes.
208
- maybe_space = ' ' if tree.attributes.get(
209
- 'class', None) and rules_to_add else ''
210
- tree.attributes['class'] = tree.attributes.get(
211
- 'class', '') + maybe_space + ' '.join(rules_to_add)
212
-
213
- # Recursively applying the CSS rules to all child nodes of the tree
214
- for child in tree.children:
215
- apply_css(css, child)
197
+ # Packaging the results into a CompiledCSS object
198
+ core = RuleSection(rules=rules, title="Core")
199
+ return CompiledCSS(trees, core, mapping)
216
200
 
217
201
 
218
- def default_rule_namer(rules: List[CSSRule], index: int) -> str:
219
- """Default rule naming function. Returns a string like "r{i}" where {i} is
202
+ def default_class_namer(rules: List[ClassRule], index: int) -> str:
203
+ """Default class naming function. Returns a string like "c{i}" where {i} is
220
204
  the index of the rule.
221
205
 
222
- :param rules: List of all compiled CSSRule objects. This argument is not
206
+ :param rules: List of all compiled ClassRule objects. This argument is not
223
207
  used in this function, but it can be used in other naming strategies.
224
- :type rules: List[CSSRule]
225
- :param index: Index of the rule being named.
208
+ :type rules: List[ClassRule]
209
+ :param index: Index of the rule whose class is being named.
226
210
  :type index: int
227
- :return: A string like `"r{i}"` where `i` is the index of the rule.
211
+ :return: A string like `"c{i}"` where `i` is the index of the rule.
228
212
  """
229
- return f'r{index}'
213
+ return f'c{index}'
@@ -0,0 +1,104 @@
1
+ # =======================================================================
2
+ #
3
+ # This file is part of WebWidgets, a Python package for designing web
4
+ # UIs.
5
+ #
6
+ # You should have received a copy of the MIT License along with
7
+ # WebWidgets. If not, see <https://opensource.org/license/mit>.
8
+ #
9
+ # Copyright(C) 2025, mlaasri
10
+ #
11
+ # =======================================================================
12
+
13
+ from typing import Dict
14
+ from webwidgets.utility.indentation import get_indentation
15
+ from webwidgets.utility.representation import ReprMixin
16
+ from webwidgets.utility.validation import validate_css_identifier, validate_css_selector
17
+
18
+
19
+ class CSSRule(ReprMixin):
20
+ """A rule in a style sheet.
21
+ """
22
+
23
+ def __init__(self, selector: str, declarations: Dict[str, str]):
24
+ """Stores the selector and declarations of the rule.
25
+
26
+ :param selector: The selector of the rule.
27
+ :type selector: str
28
+ :param declarations: The CSS declarations for the rule, specified as a
29
+ dictionary where keys are property names and values are their
30
+ corresponding values. For example: `{'color': 'red'}`
31
+ :type declarations: Dict[str, str]
32
+ """
33
+ super().__init__()
34
+ self.selector = selector
35
+ self.declarations = declarations
36
+
37
+ def to_css(self, indent_size: int = 4) -> str:
38
+ """Converts the rule into CSS code.
39
+
40
+ The rule's name is converted to a class selector.
41
+
42
+ Note that the rule's name and all property names are validated before
43
+ being converted. The rule's name is validated with
44
+ :py:func:`validate_css_selector` while the property names are validated
45
+ with :py:func:`validate_css_identifier`.
46
+
47
+ :param indent_size: The number of spaces to use for indentation in the
48
+ CSS code. Defaults to 4.
49
+ :type indent_size: int
50
+ :return: The CSS code as a string.
51
+ :rtype: str
52
+ """
53
+ # Defining indentation
54
+ indentation = get_indentation(level=1, size=indent_size)
55
+
56
+ # Validating the selector
57
+ validate_css_selector(self.selector)
58
+
59
+ # Writing down each property
60
+ css_code = self.selector + " {\n"
61
+ for property_name, value in self.declarations.items():
62
+ validate_css_identifier(property_name)
63
+ css_code += f"{indentation}{property_name}: {value};\n"
64
+ css_code += "}"
65
+
66
+ return css_code
67
+
68
+
69
+ class ClassRule(CSSRule):
70
+ """A CSS rule that targets a CSS class.
71
+
72
+ The class dynamically sets its selector based on its class name.
73
+ """
74
+
75
+ def __init__(self, name: str, declarations: Dict[str, str]):
76
+ """Creates a new CSS class rule.
77
+
78
+ :param name: The name of the CSS class.
79
+ :type name: str
80
+ :param declarations: See :py:meth:`CSSRule.__init__`.
81
+ :type declarations: Dict[str, str]
82
+ """
83
+ super().__init__(None, declarations) # Starting without a selector
84
+ self._name = None # Starting without a name
85
+ self.name = name # Setting both the selector and the name here
86
+
87
+ @property
88
+ def name(self) -> str:
89
+ """Returns the name of the CSS class.
90
+
91
+ :return: The name of the CSS class.
92
+ :rtype: str
93
+ """
94
+ return self._name
95
+
96
+ @name.setter
97
+ def name(self, value: str) -> None:
98
+ """Sets the name of the CSS class.
99
+
100
+ :param value: The new name of the CSS class.
101
+ :type value: str
102
+ """
103
+ self._name = value
104
+ self.selector = f".{value}" # Updating the selector
@@ -0,0 +1,14 @@
1
+ # =======================================================================
2
+ #
3
+ # This file is part of WebWidgets, a Python package for designing web
4
+ # UIs.
5
+ #
6
+ # You should have received a copy of the MIT License along with
7
+ # WebWidgets. If not, see <https://opensource.org/license/mit>.
8
+ #
9
+ # Copyright(C) 2025, mlaasri
10
+ #
11
+ # =======================================================================
12
+
13
+ from .preamble import Preamble
14
+ from .rule_section import RuleSection
@@ -0,0 +1,106 @@
1
+ # =======================================================================
2
+ #
3
+ # This file is part of WebWidgets, a Python package for designing web
4
+ # UIs.
5
+ #
6
+ # You should have received a copy of the MIT License along with
7
+ # WebWidgets. If not, see <https://opensource.org/license/mit>.
8
+ #
9
+ # Copyright(C) 2025, mlaasri
10
+ #
11
+ # =======================================================================
12
+
13
+ from abc import ABC, abstractmethod
14
+ from typing import Any
15
+ from webwidgets.utility.representation import ReprMixin
16
+ from webwidgets.utility.validation import validate_css_comment
17
+
18
+
19
+ class CSSSection(ABC, ReprMixin):
20
+ """Abstract base class representing a section of a CSS file.
21
+
22
+ All subclasses of :py:class:`CSSSection` must implement a
23
+ :py:meth:`compile_content` method that returns a string.
24
+ """
25
+
26
+ @staticmethod
27
+ def prettify_title(title: str, min_length: int) -> str:
28
+ """Returns a prettified version of the given title with decorative
29
+ characters `=` around it.
30
+
31
+ This function will add the minimum number of decorative characters to
32
+ the title while keeping symmetry and remaining over the given minimum
33
+ length. In particular, if the given title is already above the minimum
34
+ length, this function will return it as is.
35
+
36
+ :param title: The title to prettify.
37
+ :type title: str
38
+ :param max_length: The minimum length of the prettified title.
39
+ :type max_length: int
40
+ :return: The prettified title.
41
+ :rtype: str
42
+ """
43
+ # If the title is already above min_length, we don't add decorative
44
+ # characters
45
+ if len(title) >= min_length:
46
+ return title
47
+
48
+ # Otherwise, we add decorative characters around the title
49
+ remaining = min_length - len(title)
50
+ characters = "=" * max(((remaining - 1) // 2), 1)
51
+ return characters + ' ' + title + ' ' + characters
52
+
53
+ def __init__(self, title: str = None):
54
+ """Creates a new section with an optional title.
55
+
56
+ :param title: The title of the section. If provided, the section will
57
+ be preceded by a comment containing the title in the output CSS
58
+ code. If None, no title will be used to separate the section from
59
+ the rest of the code.
60
+ :type title: str
61
+ """
62
+ super().__init__()
63
+ self.title = title
64
+
65
+ @abstractmethod
66
+ def compile_content(self) -> str:
67
+ """Converts the content of the CSSSection object (excluding the title)
68
+ into CSS code.
69
+
70
+ This method must be overridden by subclasses to compile specific CSS
71
+ code.
72
+ """
73
+ pass
74
+
75
+ def to_css(self, *args: Any, **kwargs: Any) -> str:
76
+ """Converts the CSSSection object into CSS code.
77
+
78
+ If the section has a title, it will be prettified with
79
+ :py:meth:`CSSSection.prettify_title` and turned into a comment. That
80
+ comment will be validated with :py:func:`validate_css_comment` and
81
+ inserted before the result of :py:meth:`CSSSection.compile_content` in
82
+ the CSS code.
83
+
84
+ If the section has no title, this function will produce the same result
85
+ as :py:meth:`CSSSection.compile_content`.
86
+
87
+ :param args: Arguments to pass to
88
+ :py:meth:`CSSSection.compile_content`.
89
+ :type args: Any
90
+ :param kwargs: Keyword arguments to pass to
91
+ :py:meth:`CSSSection.compile_content`.
92
+ :type kwargs: Any
93
+ :return: The CSS code for the section.
94
+ :rtype: str
95
+ """
96
+ # If no title, we just return the compiled content
97
+ if self.title is None:
98
+ return self.compile_content(*args, **kwargs)
99
+
100
+ # Otherwise, we turn the title into a comment and validate it
101
+ comment = ' ' + CSSSection.prettify_title(self.title, 40) + ' '
102
+ validate_css_comment(comment)
103
+
104
+ # Adding the comment before the compiled content
105
+ return "/*" + comment + "*/\n\n" + \
106
+ self.compile_content(*args, **kwargs)
@@ -0,0 +1,40 @@
1
+ # =======================================================================
2
+ #
3
+ # This file is part of WebWidgets, a Python package for designing web
4
+ # UIs.
5
+ #
6
+ # You should have received a copy of the MIT License along with
7
+ # WebWidgets. If not, see <https://opensource.org/license/mit>.
8
+ #
9
+ # Copyright(C) 2025, mlaasri
10
+ #
11
+ # =======================================================================
12
+
13
+ from .rule_section import RuleSection
14
+ from webwidgets.compilation.css.css_rule import CSSRule
15
+
16
+
17
+ class Preamble(RuleSection):
18
+ """A set of CSS rules that apply globally to all HTML elements.
19
+
20
+ The CSS preamble serves as a global default for multiple properties. It is
21
+ used to define the document's box model and set all margin and padding
22
+ values to 0.
23
+ """
24
+
25
+ def __init__(self):
26
+ """Creates a new CSS preamble."""
27
+ super().__init__(
28
+ rules=[
29
+ CSSRule("*, *::before, *::after", {
30
+
31
+ # Defining the box model to border-box
32
+ "box-sizing": "border-box",
33
+
34
+ # Setting all margin and padding values to 0
35
+ "margin": "0",
36
+ "padding": "0"
37
+ })
38
+ ],
39
+ title="Preamble"
40
+ )
@@ -0,0 +1,43 @@
1
+ # =======================================================================
2
+ #
3
+ # This file is part of WebWidgets, a Python package for designing web
4
+ # UIs.
5
+ #
6
+ # You should have received a copy of the MIT License along with
7
+ # WebWidgets. If not, see <https://opensource.org/license/mit>.
8
+ #
9
+ # Copyright(C) 2025, mlaasri
10
+ #
11
+ # =======================================================================
12
+
13
+ from .css_section import CSSSection
14
+ from typing import List
15
+ from webwidgets.compilation.css.css_rule import CSSRule
16
+
17
+
18
+ class RuleSection(CSSSection):
19
+ """A section containing a set of CSS rules.
20
+ """
21
+
22
+ def __init__(self, rules: List[CSSRule] = None, title: str = None):
23
+ """Creates a new section with the given rules and title.
24
+
25
+ :param rules: A list of CSSRule objects to include in the section.
26
+ :type rules: List[CSSRule]
27
+ :param title: The title of the section.
28
+ :type title: str
29
+ """
30
+ super().__init__(title=title)
31
+ self.rules = [] if rules is None else rules
32
+
33
+ def compile_content(self, indent_size: int = 4) -> str:
34
+ """Compiles the CSS representation of the rules contained in the
35
+ section.
36
+
37
+ :param indent_size: See :py:meth:`CSSRule.to_css`.
38
+ :type indent_size: int
39
+ :return: The CSS representation of the rules.
40
+ :rtype: str
41
+ """
42
+ return "\n\n".join([
43
+ rule.to_css(indent_size=indent_size) for rule in self.rules])
@@ -10,5 +10,6 @@
10
10
  #
11
11
  # =======================================================================
12
12
 
13
- from .html_node import HTMLNode, no_start_tag, no_end_tag, one_line, RawText
14
- from .html_tags import TextNode
13
+ from .html_node import HTMLNode, no_start_tag, no_end_tag, one_line, RawText, \
14
+ RootNode
15
+ from .html_tags import *