Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Add list properties #73

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion colosseum/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from .validators import (ValidationError, is_border_spacing, is_color,
is_cursor, is_integer, is_length, is_number,
is_percentage, is_quote, is_rect)
is_percentage, is_quote, is_rect, is_uri)


class Choices:
Expand Down Expand Up @@ -305,8 +305,60 @@ def value(self, context):
# 12.5 Lists
######################################################################
# list_style_type
DISC = 'disc'
CIRCLE = 'circle'
SQUARE = 'square'
DECIMAL = 'decimal'
DECIMAL_LEADING_ZERO = 'decimal-leading-zero'
LOWER_ROMAN = 'lower-roman'
UPPER_ROMAN = 'upper-roman'
LOWER_GREEK = 'lower-greek'
LOWER_LATIN = 'lower-latin'
UPPER_LATIN = 'upper-latin'
ARMENIAN = 'armenian'
GEORGIAN = 'georgian'
LOWER_ALPHA = 'lower-alpha'
UPPER_ALPHA = 'upper-alpha'


LIST_TYPE_CHOICES = Choices(
DISC,
CIRCLE,
SQUARE,
DECIMAL,
DECIMAL_LEADING_ZERO,
LOWER_ROMAN,
UPPER_ROMAN,
LOWER_GREEK,
LOWER_LATIN,
UPPER_LATIN,
ARMENIAN,
GEORGIAN,
LOWER_ALPHA,
UPPER_ALPHA,
None,
explicit_defaulting_constants=[INHERIT],
)

# list_style_image
LIST_IMAGE_CHOICES = Choices(
None,
validators=[is_uri],
explicit_defaulting_constants=[INHERIT],
)


# list_style_position
INSIDE = 'inside'
OUTSIDE = 'outside'

LIST_POSITION_CHOICES = Choices(
INSIDE,
OUTSIDE,
explicit_defaulting_constants=[INHERIT],
)


# list_style

# 13. Paged media ####################################################
Expand Down
35 changes: 19 additions & 16 deletions colosseum/declaration.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
from . import engine as css_engine
from . import parser
from . import engine as css_engine, parser
from .constants import ( # noqa
ALIGN_CONTENT_CHOICES, ALIGN_ITEMS_CHOICES, ALIGN_SELF_CHOICES, AUTO,
BACKGROUND_COLOR_CHOICES, BORDER_COLLAPSE_CHOICES, BORDER_COLOR_CHOICES,
BORDER_SPACING_CHOICES, BORDER_STYLE_CHOICES, BORDER_WIDTH_CHOICES,
BOX_OFFSET_CHOICES, CAPTION_SIDE_CHOICES, CLEAR_CHOICES, CLIP_CHOICES,
COLOR_CHOICES, DIRECTION_CHOICES, DISPLAY_CHOICES, EMPTY_CELLS_CHOICES,
FLEX_BASIS_CHOICES, FLEX_DIRECTION_CHOICES, FLEX_GROW_CHOICES,
FLEX_SHRINK_CHOICES, FLEX_START, FLEX_WRAP_CHOICES, FLOAT_CHOICES,
GRID_AUTO_CHOICES, GRID_AUTO_FLOW_CHOICES, GRID_GAP_CHOICES,
COLOR_CHOICES, DIRECTION_CHOICES, DISC, DISPLAY_CHOICES,
EMPTY_CELLS_CHOICES, FLEX_BASIS_CHOICES, FLEX_DIRECTION_CHOICES,
FLEX_GROW_CHOICES, FLEX_SHRINK_CHOICES, FLEX_START, FLEX_WRAP_CHOICES,
FLOAT_CHOICES, GRID_AUTO_CHOICES, GRID_AUTO_FLOW_CHOICES, GRID_GAP_CHOICES,
GRID_PLACEMENT_CHOICES, GRID_TEMPLATE_AREA_CHOICES, GRID_TEMPLATE_CHOICES,
INITIAL, INLINE, INVERT, JUSTIFY_CONTENT_CHOICES, LETTER_SPACING_CHOICES,
LTR, MARGIN_CHOICES, MAX_SIZE_CHOICES, MEDIUM, MIN_SIZE_CHOICES, NORMAL,
NOWRAP, ORDER_CHOICES, ORPHANS_CHOICES, OUTLINE_COLOR_CHOICES,
OUTLINE_STYLE_CHOICES, OUTLINE_WIDTH_CHOICES, OVERFLOW_CHOICES,
LIST_IMAGE_CHOICES, LIST_POSITION_CHOICES, LIST_TYPE_CHOICES, LTR,
MARGIN_CHOICES, MAX_SIZE_CHOICES, MEDIUM, MIN_SIZE_CHOICES, NORMAL, NOWRAP,
ORDER_CHOICES, ORPHANS_CHOICES, OUTLINE_COLOR_CHOICES,
OUTLINE_STYLE_CHOICES, OUTLINE_WIDTH_CHOICES, OUTSIDE, OVERFLOW_CHOICES,
PADDING_CHOICES, PAGE_BREAK_AFTER_CHOICES, PAGE_BREAK_BEFORE_CHOICES,
PAGE_BREAK_INSIDE_CHOICES, POSITION_CHOICES, QUOTES_CHOICES, ROW,
SEPARATE, SHOW, SIZE_CHOICES, STATIC, STRETCH, TABLE_LAYOUT_CHOICES,
PAGE_BREAK_INSIDE_CHOICES, POSITION_CHOICES, QUOTES_CHOICES, ROW, SEPARATE,
SHOW, SIZE_CHOICES, STATIC, STRETCH, TABLE_LAYOUT_CHOICES,
TEXT_ALIGN_CHOICES, TEXT_DECORATION_CHOICES, TEXT_INDENT_CHOICES,
TEXT_TRANSFORM_CHOICES, TOP, TRANSPARENT, UNICODE_BIDI_CHOICES,
VISIBILITY_CHOICES, VISIBLE, WHITE_SPACE_CHOICES, WIDOWS_CHOICES,
WORD_SPACING_CHOICES, Z_INDEX_CHOICES, OtherProperty,
TextAlignInitialValue, default, CURSOR_CHOICES,
)
from .exceptions import ValidationError
from .wrappers import Border, BorderBottom, BorderLeft, BorderRight, BorderTop, Outline
from .wrappers import (
Border, BorderBottom, BorderLeft, BorderRight, BorderTop, ListStyle,
Outline,
)

_CSS_PROPERTIES = set()

Expand Down Expand Up @@ -329,10 +332,10 @@ def __init__(self, **style):
# counter-increment

# 12.5 Lists
# list_style_type
# list_style_image
# list_style_position
# list_style
list_style_type = validated_property('list_style_type', choices=LIST_TYPE_CHOICES, initial=DISC)
list_style_image = validated_property('list_style_type', choices=LIST_IMAGE_CHOICES, initial=None)
list_style_position = validated_property('list_style_position', choices=LIST_POSITION_CHOICES, initial=OUTSIDE)
list_style = validated_shorthand_property('list_style', parser=parser.list_style, wrapper=ListStyle)

# 13. Paged media ####################################################
# 13.3.1 Page break properties
Expand Down
61 changes: 61 additions & 0 deletions colosseum/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,3 +445,64 @@ def cursor(values):
raise ValueError('Value {value} is not a valid cursor value'.format(value=value))

return Cursor(validated_values)


##############################################################################
# List shorthands
##############################################################################
def _parse_list_style_property_part(value, list_style_dict):
"""Parse list style shorthand property part for known properties."""
from .constants import ( # noqa
LIST_TYPE_CHOICES, LIST_IMAGE_CHOICES, LIST_POSITION_CHOICES
)

for property_name, choices in {'list_style_type': LIST_TYPE_CHOICES,
'list_style_image': LIST_IMAGE_CHOICES,
'list_style_position': LIST_POSITION_CHOICES}.items():
try:
value = choices.validate(value)
except (ValueError, ValidationError):
continue

if property_name in list_style_dict:
raise ValueError('Invalid duplicated property!')

list_style_dict[property_name] = value
return list_style_dict

raise ValueError('List style value "{value}" not valid!'.format(value=value))


def list_style(value):
"""
Parse list style string into a dictionary of list style properties.

The font CSS property is a shorthand for list-style-type, list-style-image, and list-style-position.

Reference:
- https://www.w3.org/TR/2011/REC-CSS2-20110607/generate.html#lists
"""
if value:
if isinstance(value, str):
values = [val.strip() for val in value.split()]
elif isinstance(value, Sequence):
values = value
else:
raise ValueError('Unknown list style %s ' % value)
else:
raise ValueError('Unknown list style %s ' % value)

# We iteratively split by the first left hand space found and try to validate if that part
# is a valid <list-style-type> or <list-style-image> or <list-style-position> (which can come in any order)

# We use this dictionary to store parsed values and check that values properties are not
# duplicated
list_style_dict = {}
for idx, part in enumerate(values):
if idx > 2:
# Outline can have a maximum of 3 parts
raise ValueError('List style property shorthand contains too many parts!')

list_style_dict = _parse_list_style_property_part(part, list_style_dict)

return list_style_dict
86 changes: 45 additions & 41 deletions colosseum/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,47 +129,6 @@ def to_dict(self):
return properties


class Outline(Shorthand):
VALID_KEYS = ['outline_color', 'outline_style', 'outline_width']


class BorderTop(Shorthand):
VALID_KEYS = ['border_top_width', 'border_top_style', 'border_top_color']


class BorderRight(Shorthand):
VALID_KEYS = ['border_right_width', 'border_right_style', 'border_right_color']


class BorderBottom(Shorthand):
VALID_KEYS = ['border_bottom_width', 'border_bottom_style', 'border_bottom_color']


class BorderLeft(Shorthand):
VALID_KEYS = ['border_left_width', 'border_left_style', 'border_left_color']


class Border(Shorthand):
VALID_KEYS = ['border_width', 'border_style', 'border_color']


class Uri:
"""Wrapper for a url."""

def __init__(self, url):
self._url = url

def __repr__(self):
return 'url("%s")' % self._url

def __str__(self):
return repr(self)

@property
def url(self):
return self._url


class ImmutableList(list):
"""Immutable list to store list properties."""

Expand Down Expand Up @@ -233,5 +192,50 @@ def sort(self, cmp=None, key=None, reverse=False):
raise TypeError("{} values cannot be changed!".format(self.__class__.__name__))


class Outline(Shorthand):
VALID_KEYS = ['outline_color', 'outline_style', 'outline_width']


class BorderTop(Shorthand):
VALID_KEYS = ['border_top_width', 'border_top_style', 'border_top_color']


class BorderRight(Shorthand):
VALID_KEYS = ['border_right_width', 'border_right_style', 'border_right_color']


class BorderBottom(Shorthand):
VALID_KEYS = ['border_bottom_width', 'border_bottom_style', 'border_bottom_color']


class BorderLeft(Shorthand):
VALID_KEYS = ['border_left_width', 'border_left_style', 'border_left_color']


class Border(Shorthand):
VALID_KEYS = ['border_width', 'border_style', 'border_color']


class Uri:
"""Wrapper for a url."""

def __init__(self, url):
self._url = url

def __repr__(self):
return 'url("%s")' % self._url

def __str__(self):
return repr(self)

@property
def url(self):
return self._url


class Cursor(ImmutableList):
"""Immutable list to store cursor property."""


class ListStyle(Shorthand):
VALID_KEYS = ['list_style_type', 'list_style_image', 'list_style_position']