Source code for highcharts_core.utility_classes.buttons
from typing import Optional, List
from decimal import Decimal
from collections import UserDict
try:
    import orjson as json
except ImportError:
    try:
        import rapidjson as json
    except ImportError:
        try:
            import simplejson as json
        except ImportError:
            import json
from validator_collection import validators, checkers, errors as validator_errors
from highcharts_core import constants, errors, utility_functions
from highcharts_core.decorators import validate_types, class_sensitive
from highcharts_core.metaclasses import HighchartsMeta, JavaScriptDict
from highcharts_core.utility_classes.gradients import Gradient
from highcharts_core.utility_classes.patterns import Pattern
from highcharts_core.utility_classes.javascript_functions import CallbackFunction
[docs]class ButtonTheme(HighchartsMeta):
    """Settings used to style buttons."""
    def __init__(self, **kwargs):
        self._fill = None
        self._stroke = None
        self.fill = kwargs.get('fill', None)
        self.stroke = kwargs.get('stroke', None)
    @property
    def fill(self) -> Optional[str | Gradient | Pattern]:
        """The color of the button's fill. The default fill exists only to capture hover
        events.
        :rtype: :class:`str <python:str>` (for colors), :class:`Gradient` for gradients,
          :class:`Pattern` for pattern definitions, or :obj:`None <python:None>`
        """
        return self._fill
    @fill.setter
    def fill(self, value):
        self._fill = utility_functions.validate_color(value)
    @property
    def stroke(self) -> Optional[str]:
        """The color of the button's stroke. Defaults to ``'none'``.
        :rtype: :class:`str <python:str>` or :obj:`None <python:None>`
        """
        return self._stroke
    @stroke.setter
    def stroke(self, value):
        self._stroke = validators.string(value, allow_empty = True)
    @classmethod
    def _get_kwargs_from_dict(cls, as_dict):
        kwargs = {
            'fill': as_dict.get('fill', None),
            'stroke': as_dict.get('stroke', None)
        }
        return kwargs
    def _to_untrimmed_dict(self, in_cls = None) -> dict:
        untrimmed = {
            'fill': self.fill,
            'stroke': self.stroke
        }
        return untrimmed
[docs]class ButtonConfiguration(HighchartsMeta):
    """Configuration of options that apply to a given button."""
    def __init__(self, **kwargs):
        self._enabled = None
        self._text = None
        self._theme = None
        self._y = None
        self.enabled = kwargs.get('enabled', None)
        self.text = kwargs.get('text', None)
        self.theme = kwargs.get('theme', None)
        self.y = kwargs.get('y', None)
    @property
    def enabled(self) -> Optional[bool]:
        """If ``True``, displays the button. If ``False``, the button will be hidden but
        JavaScript API methods may still be available.
        Defaults to ``True``.
        :returns: Flag indicating whether the button is displayed on the chart.
        :rtype: :class:`bool <python:bool>` or :obj:`None <python:None>`
        """
        return self._enabled
    @enabled.setter
    def enabled(self, value):
        if value is None:
            self._enabled = None
        else:
            self._enabled = bool(value)
    @property
    def text(self) -> Optional[str]:
        """The text to display on the button.
        :rtype: :class:`str <python:str>` or :obj:`None <python:None>`
        """
        return self._text
    @text.setter
    def text(self, value):
        self._text = validators.string(value, allow_empty = True)
    @property
    def theme(self) -> Optional[ButtonTheme]:
        """Configuration of the theme and styling to apply to the button.
        :rtype: :class:`ButtonTheme` or :obj:`None <python:None>`
        """
        return self._theme
    @theme.setter
    @class_sensitive(ButtonTheme)
    def theme(self, value):
        self._theme = value
    @property
    def y(self) -> Optional[int | float | Decimal]:
        """The vertical offset of the button's position relative to its ``verticalAlign``
        setting. Defaults to ``0``.
        :rtype: numeric or :obj:`None <python:None>`
        """
        return self._y
    @y.setter
    def y(self, value):
        self._y = validators.numeric(value, allow_empty = True)
    @classmethod
    def _get_kwargs_from_dict(cls, as_dict):
        kwargs = {
            'enabled': as_dict.get('enabled', None),
            'text': as_dict.get('text', None),
            'theme': as_dict.get('theme', None),
            'y': as_dict.get('y', None)
        }
        return kwargs
    def _to_untrimmed_dict(self, in_cls = None) -> dict:
        untrimmed = {
            'enabled': self.enabled,
            'text': self.text,
            'theme': self.theme,
            'y': self.y
        }
        return untrimmed
[docs]class ContextButtonConfiguration(ButtonConfiguration):
    """Configuration options that apply to the Context Menu button."""
    def __init__(self, **kwargs):
        self._class_name = None
        self._menu_class_name = None
        self._menu_items = None
        self._onclick = None
        self._symbol = None
        self._symbol_fill = None
        self._title_key = None
        self._x = None
        self.class_name = kwargs.get('class_name', None)
        self.menu_class_name = kwargs.get('menu_class_name', None)
        self.menu_items = kwargs.get('menu_items', None)
        self.onclick = kwargs.get('onclick', None)
        self.symbol = kwargs.get('symbol', None)
        self.symbol_fill = kwargs.get('symbol_fill', None)
        self.title_key = kwargs.get('title_key', None)
        self.x = kwargs.get('x', None)
        super().__init__(**kwargs)
    @property
    def class_name(self) -> Optional[str]:
        """The class name of the context button. Defaults to
        ``'highcharts-contextbutton'``.
        :rtype: :class:`str <python:str>` or :obj:`None <python:None>`
        """
        return self._class_name
    @class_name.setter
    def class_name(self, value):
        self._class_name = validators.string(value, allow_empty = True)
    @property
    def menu_class_name(self) -> Optional[str]:
        """The class name of the context menu that appears from the button. Defaults to
        ``'highcharts-contextmenu'``.
        :rtype: :class:`str <python:str>` or :obj:`None <python:None>`
        """
        return self._menu_class_name
    @menu_class_name.setter
    def menu_class_name(self, value):
        self._menu_class_name = validators.string(value, allow_empty = True)
    @property
    def menu_items(self) -> Optional[List[str]]:
        """A collection of strings pointing to config options for the menu items.
        The config options are defined in the :class:`Exporting.menu_item_definitions`
        option.
        .. note::
          By default, the context menu contains "View in fullscreen" and "Print" menu
          items, plus one menu item for each of the available export types.
        Defaults to:
        .. code-block:: python
          [
              "viewFullscreen",
              "printChart",
              "separator",
              "downloadPNG",
              "downloadJPEG",
              "downloadPDF",
              "downloadSVG"
          ]
        :rtype: :class:`list <python:list>` of :class:`str <python:str>` or
          :obj:`None <python:None>`
        """
        return self._menu_items
    @menu_items.setter
    def menu_items(self, value):
        if value is None:
            self._menu_items = None
        else:
            if not checkers.is_iterable(value):
                raise errors.HighchartsValueError(f'menu_items expects an iterable, but '
                                                  f'received: {value.__class__.__name__}')
            for item in value:
                if not isinstance(item, str):
                    raise errors.HighchartsValueError(f'specific menu items must be '
                                                      f'strings, but received: '
                                                      f'{item.__class__.__name}')
            self._menu_items = value
    @property
    def onclick(self) -> Optional[CallbackFunction]:
        """JavaScript event callback function which fires when the button is clicked.
        :rtype: :class:`CallbackFunction` or :obj:`None <python:None>`
        """
        return self._onclick
    @onclick.setter
    @class_sensitive(CallbackFunction)
    def onclick(self, value):
        self._onclick = value
    @property
    def symbol(self) -> Optional[str]:
        """The symbol to display on the button. Defaults to ``'menu'``.
        Points to a definition function in the JavaScript ``Highcharts.Renderer.symbols``
        collection.
        The default menu function is part of the exporting module. Possible values are:
          * ``"circle"``
          * ``"square"``
          * ``"diamond"``
          * ``"triangle"``
          * ``"triangle-down"``
          * ``"menu"``
          * ``"menuball"``
          * or a custom shape
        :rtype: :class:`str <python:str>` or :obj:`None <python:None>`
        """
        return self._symbol
    @symbol.setter
    def symbol(self, value):
        self._symbol = validators.string(value, allow_empty = True)
    @property
    def symbol_fill(self) -> Optional[str]:
        """The color to use for the symbol. Defaults to ``'#666666'``.
        :rtype: :class:`str <python:str>` or :obj:`None <python:None>`
        """
        return self._symbol_fill
    @symbol_fill.setter
    def symbol_fill(self, value):
        self._symbol_fill = validators.string(value, allow_empty = True)
    @property
    def title_key(self) -> Optional[str]:
        """The key to a :class:`Options.language` option setting that is used for the
        button's title tooltip.
        When the key is ``'contextButtonTitle'``, it refers to
        ``language.contextButtonTitle``, whose value defaults to ``"Chart context menu"``.
        :rtype: :class:`str <python:str>` or :obj:`None <python:None>`
        """
        return self._title_key
    @title_key.setter
    def title_key(self, value):
        self._title_key = validators.string(value, allow_empty = True)
    @property
    def x(self) -> Optional[int | float | Decimal]:
        """The horizontal offset of the button's position relative to its ``align``
        setting. Defaults to ``-10``.
        :rtype: numeric or :obj:`None <python:None>`
        """
        return self._x
    @x.setter
    def x(self, value):
        self._x = validators.numeric(value, allow_empty = True)
    @classmethod
    def _get_kwargs_from_dict(cls, as_dict):
        kwargs = {
            'class_name': as_dict.get('className', None),
            'enabled': as_dict.get('enabled', None),
            'menu_class_name': as_dict.get('menuClassName', None),
            'menu_items': as_dict.get('menuItems', None),
            'onclick': as_dict.get('onclick', None),
            'symbol': as_dict.get('symbol', None),
            'symbol_fill': as_dict.get('symbolFill', None),
            'text': as_dict.get('text', None),
            'theme': as_dict.get('theme', None),
            'title_key': as_dict.get('titleKey', None),
            'x': as_dict.get('x', None),
            'y': as_dict.get('y', None),
        }
        return kwargs
    def _to_untrimmed_dict(self, in_cls = None) -> dict:
        untrimmed = {
            'className': self.class_name,
            'enabled': self.enabled,
            'menuClassName': self.menu_class_name,
            'menuItems': self.menu_items,
            'onclick': self.onclick,
            'symbol': self.symbol,
            'symbolFill': self.symbol_fill,
            'text': self.text,
            'theme': self.theme,
            'titleKey': self.title_key,
            'x': self.x,
            'y': self.y
        }
        return untrimmed
[docs]class ExportingButtons(JavaScriptDict):
    """Special :class:`dict <python:dict>` class used to construct a collection of
    Highcharts button configurations. Each key represents the identifier of a button,
    while the object is a configuration of that button's settings.
    Keys are validated to be valid variable names, while each value is validated to be a
    :class:`ButtonConfiguration`.
    """
    _valid_value_types = ButtonConfiguration
    _allow_empty_value = True
    
    def __init__(self, **kwargs):
        context_button = kwargs.pop('context_button', 
                                    None) or kwargs.pop('contextButton', 
                                                        None)
        if isinstance(context_button, constants.EnforcedNullType):
            context_button = None
        elif not context_button:
            context_button = ContextButtonConfiguration()
        elif isinstance(context_button, ButtonConfiguration):
            pass
        elif isinstance(context_button, dict):
            context_button = ContextButtonConfiguration.from_dict(context_button)
        elif isinstance(context_button, str):
            context_button = ContextButtonConfiguration.from_json(context_button)
        super().__init__(**kwargs)
        self['contextButton'] = context_button
    def __setitem__(self, key, item):
        if key == 'context_button':
            key = 'contextButton'
        validate_key = False
        try:
            validate_key = key not in self
        except AttributeError:
            validate_key = True
        if validate_key:
            try:
                key = validators.variable_name(key, allow_empty = False)
            except validator_errors.InvalidVariableNameError as error:
                if '-' in key:
                    try:
                        test_key = key.replace('-', '_')
                        validators.variable_name(test_key, allow_empty = False)
                    except validator_errors.InvalidVariableNameError:
                        raise error
                else:
                    raise error
        if self._valid_value_types:
            try:
                item = validate_types(item,
                                      types = self._valid_value_types,
                                      allow_none = self._allow_empty_value)
            except errors.HighchartsValueError as error:
                if self._allow_empty_value and not item:
                    item = None
                else:
                    try:
                        item = self._valid_value_types(item)
                    except (TypeError, ValueError, AttributeError):
                        raise error
        super().__setitem__(key, item)
    def __getitem__(self, key):
        if key == 'context_button':
            key = 'contextButton'
            
        return super().__getitem__(key)