From 8a9e2a208314996f3802fdb0f4682f14ab1755b3 Mon Sep 17 00:00:00 2001 From: sax Date: Fri, 7 Apr 2023 09:27:52 +0200 Subject: [PATCH 1/9] new field editor --- pyproject.toml | 2 +- src/aurora/config/__init__.py | 1 + src/aurora/config/settings.py | 2 +- src/aurora/core/admin/field_editor.py | 313 +++++++++++++----- src/aurora/core/admin/flex_field.py | 100 +++--- src/aurora/core/compat.py | 10 +- src/aurora/core/fields/__init__.py | 1 + src/aurora/core/fields/boolean.py | 12 + src/aurora/core/fields/configs.py | 11 + src/aurora/core/fields/mixins.py | 131 ++++---- src/aurora/core/fields/widgets/datetime.py | 14 +- src/aurora/core/fields/widgets/editor.py | 80 ++--- src/aurora/core/fields/widgets/mixins.py | 39 ++- .../migrations/0054_auto_20230403_0853.py | 31 ++ src/aurora/core/models.py | 113 +++++-- src/aurora/core/registry.py | 3 +- .../admin/field_editor/field_editor.css | 9 + .../admin/field_editor/field_editor.css.map | 2 +- .../static/admin/field_editor/field_editor.js | 70 ++-- .../admin/field_editor/field_editor.min.js | 2 +- .../admin/field_editor/field_editor.scss | 14 + .../static/admin/form_editor/field_editor.css | 4 +- .../admin/form_editor/field_editor.scss | 4 +- src/aurora/core/static/cm.js | 2 + src/aurora/core/static/cm.min.js | 2 +- src/aurora/core/static/cm.scss | 3 + .../static/datetimepicker/air-datepicker.css | 7 + .../static/datetimepicker/air-datepicker.js | 6 + src/aurora/core/static/datetimepicker/dt.js | 15 +- .../core/static/datetimepicker/dt.min.js | 2 +- src/aurora/core/static/smart.js | 6 +- src/aurora/core/static/smart.min.js | 2 +- src/aurora/core/static/smart_field.js | 91 ++++- src/aurora/core/static/smart_field.min.js | 2 +- src/aurora/core/static/smart_validation.js | 39 ++- .../core/static/smart_validation.min.js | 2 +- .../core/flexformfield/field_editor/main.html | 114 +++---- .../flexformfield/field_editor/preview.html | 41 ++- .../templates/admin/core/widgets/editor.html | 3 +- .../django/forms/widgets/boolean.html | 3 + .../django/forms/widgets/checkbox.html | 1 + .../django/forms/widgets/jsregex.html | 20 ++ .../core/templates/smart/_fieldset.html | 9 +- src/aurora/core/version_media.py | 6 +- .../static/registration/survey.js | 8 +- .../static/registration/survey.min.js | 2 +- .../templates/registration/register.html | 7 +- src/aurora/web/static/base.css | 2 +- src/aurora/web/static/base.css.map | 2 +- src/aurora/web/static/base.scss | 30 +- src/aurora/web/static/page.js | 1 - tests/extras/testutils/factories.py | 1 + tests/fields/test_builder.py | 72 ++++ 53 files changed, 958 insertions(+), 511 deletions(-) create mode 100644 src/aurora/core/fields/boolean.py create mode 100644 src/aurora/core/fields/configs.py create mode 100644 src/aurora/core/migrations/0054_auto_20230403_0853.py create mode 100644 src/aurora/core/static/cm.scss create mode 100644 src/aurora/core/static/datetimepicker/air-datepicker.css create mode 100644 src/aurora/core/static/datetimepicker/air-datepicker.js create mode 100644 src/aurora/core/templates/django/forms/widgets/boolean.html create mode 100644 src/aurora/core/templates/django/forms/widgets/checkbox.html create mode 100644 src/aurora/core/templates/django/forms/widgets/jsregex.html create mode 100644 tests/fields/test_builder.py diff --git a/pyproject.toml b/pyproject.toml index 7760527a..59347e57 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,9 +68,9 @@ django-filter = "^22.1" django-front-door = "^0.10.0" django-debug-toolbar = "^3" pygments = "^2.14.0" +beautifulsoup4 = "^4.12.0" soupsieve = "^2.4" pyquery = "^2.0.0" -beautifulsoup4 = "^4.12.0" [tool.poetry.dev-dependencies] black = "^22.1.0" diff --git a/src/aurora/config/__init__.py b/src/aurora/config/__init__.py index 86e243f9..07bf2406 100644 --- a/src/aurora/config/__init__.py +++ b/src/aurora/config/__init__.py @@ -81,6 +81,7 @@ def parse_emails(value): "ROOT_TOKEN": (str, uuid.uuid4().hex), "SECRET_KEY": (str, ""), "SENTRY_DSN": (str, ""), + "SENTRY_ENVIRONMENT": (str, "production"), "SENTRY_PROJECT": (str, ""), "SENTRY_SECURITY_TOKEN": (str, ""), "SENTRY_SECURITY_TOKEN_HEADER": (str, "X-Sentry-Token"), diff --git a/src/aurora/config/settings.py b/src/aurora/config/settings.py index 5e0fbef4..79d66275 100644 --- a/src/aurora/config/settings.py +++ b/src/aurora/config/settings.py @@ -381,7 +381,7 @@ sentry_sdk.init( dsn=SENTRY_DSN, - environment="production", + environment=env("SENTRY_ENVIRONMENT"), integrations=[ DjangoIntegration(transaction_style="url"), sentry_logging, diff --git a/src/aurora/core/admin/field_editor.py b/src/aurora/core/admin/field_editor.py index 1d9f4fa0..11697734 100644 --- a/src/aurora/core/admin/field_editor.py +++ b/src/aurora/core/admin/field_editor.py @@ -1,6 +1,13 @@ +from django.core.exceptions import ValidationError +from django_regex.forms import RegexFormField +from unittest.mock import Mock + +import logging + import json from django.conf import settings -from typing import Dict +from strategy_field.utils import fqn +from typing import Dict, Literal, List, Tuple from django import forms from django.core.cache import caches @@ -9,49 +16,147 @@ from django.template import Context, Template from django.utils.functional import cached_property +from aurora.core.compat import JsRegexEditor, StrategyFormField +from aurora.core.fields.configs import fields_config from aurora.core.fields.widgets import JavascriptEditor +from aurora.core.fields.widgets.mixins import TailWindMixin from aurora.core.forms import VersionMedia, FlexFormBaseForm from aurora.core.models import FlexFormField, OptionSet +from aurora.core.registry import field_registry from aurora.core.utils import merge_data +logger = logging.getLogger(__name__) cache = caches["default"] +# field: django.Field attrs +# widget: django.Widget attrs +# css: HTML class attributes +# custom: specific for each field type +# smart: Extra Aurora attributes. W +# data: Added to input element as data-. Used internally + +Section = Literal["field", "widget", "smart", "css", "custom", "data"] +Sections = Tuple[Section, ...] + + +class AdvancendAttrsForm(forms.Form): + title = None + SECTIONS_MAP: Dict[Section, List] = dict(field=[], widget=[], smart=[], css=[], custom=[], data=[]) -class AdvancendAttrsMixin: def __init__(self, *args, **kwargs): self.field = kwargs.pop("field", None) self.prefix = kwargs.get("prefix") - if self.field: - kwargs["initial"] = self.field.advanced.get(self.prefix, {}) + self.cleaned_data = {} + self.title = self.title or self.__class__.__name__ + initial = self.extract_initials() + # initial.update(**self.extract_custom()) + kwargs["initial"] = initial super().__init__(*args, **kwargs) + def get_section(self, section: Section): + targets = self.SECTIONS_MAP.get(section, []) + return {k: v for k, v in self.cleaned_data.items() if k in targets} + + def extract_initials(self): + # if self.SECTION: + # ret = self.field.advanced.get(self.SECTION, {}) + # for name, __ in self.fields.items(): + # ret[name] = getattr(self.field, name) + # else: + ret = {} + for section, field_names in self.SECTIONS_MAP.items(): + if section == "field": + for field_name in field_names: + ret[field_name] = getattr(self.field, field_name, "") + else: + for field_name in field_names: + ret[field_name] = self.field.advanced.get(section, {}).get(field_name, "") + # for name, __ in self.base_fields.items(): + # if name not in ret: + # ret[name] = getattr(self.field, name, "") + return ret + + # + # def extract_custom(self): + # ret = {} + # for section, fields in self.EXTRA_MAP.items(): + # for fld in fields: + # ret[fld] = self.field.advanced.get(section, {}).get(fld, None) + # return ret + + +class AdvancedForm(AdvancendAttrsForm): + SECTIONS_MAP = { + "widget": ["value", "placeholder"], + "smart": ["description", "hint"], + } + value = forms.CharField(label="default", required=False, help_text="default value for the field") + placeholder = forms.CharField(required=False, help_text="placeholder for the input") + description = forms.CharField(required=False, widget=forms.Textarea, help_text="Text to display below the input") + hint = forms.CharField(required=False, widget=forms.Textarea, help_text="Text to display above the input") + + class Meta: + title = "Advanced" + help = "" -class FlexFieldAttributesForm(AdvancendAttrsMixin, forms.ModelForm): - required = forms.BooleanField(widget=forms.CheckboxInput, required=False) - enabled = forms.BooleanField(widget=forms.CheckboxInput, required=False) - # onchange = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) +class CustomForm(AdvancendAttrsForm): def __init__(self, *args, **kwargs): - kwargs["instance"] = kwargs["field"] super().__init__(*args, **kwargs) + if extra := fields_config.get(self.field.field_type, None): + estra_form = extra() + for name, field in estra_form.fields.items(): + self.fields[name] = field + self.SECTIONS_MAP["custom"].append(name) class Meta: - model = FlexFormField - fields = ("field_type", "label", "required", "enabled", "validator", "regex", "validation") + title = "Custom" + help = "" -class FormFieldAttributesForm(AdvancendAttrsMixin, forms.Form): - default_value = forms.CharField(required=False, help_text="default value for the field") +class FlexFieldAttributesForm(AdvancendAttrsForm): + SECTIONS_MAP = { + "field": ["field_type", "label", "required", "enabled"], + "smart": ["visible"], + } + field_type = StrategyFormField(registry=field_registry) + label = forms.CharField() + required = forms.BooleanField(widget=forms.CheckboxInput, required=False) + enabled = forms.BooleanField(widget=forms.CheckboxInput, required=False) + visible = forms.BooleanField(required=False, help_text="Hide/Show field", initial=True) + # choices = forms.JSONField(required=False, initial=[]) + # description = forms.CharField(required=False, help_text="Text to display below the input") + # hint = forms.CharField(required=False, help_text="Text to display above the input") + + # def __init__(self, *args, **kwargs): + # kwargs["instance"] = kwargs["field"] + # super().__init__(*args, **kwargs) + + class Meta: + # model = FlexFormField + # fields = ("field_type", "label", "required", "enabled") + title = "General" + help = "" -class WidgetAttributesForm(AdvancendAttrsMixin, forms.Form): - placeholder = forms.CharField(required=False, help_text="placeholder for the input") - css_class = forms.CharField(label="Field class", required=False, help_text="Input CSS class to apply (will") - extra_classes = forms.CharField(required=False, help_text="Input CSS classes to add input") - fieldset = forms.CharField(label="Fieldset class", required=False, help_text="Fieldset CSS class to apply") - # onchange = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) - # onblur = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) - # onkeyup = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) + +class ValidationForm(AdvancendAttrsForm): + SECTIONS_MAP = { + "widget": ["title", "pattern"], + } + title = forms.CharField(label="Tooltip", required=False, help_text="") + pattern = RegexFormField(label="Regex", required=False, help_text="", widget=JsRegexEditor) + + # validation = RegexFormField(label="Regex", required=False, help_text="", widget=JsRegexEditor) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + class Meta: + # model = FlexFormField + # fields = ("regex", "title", "validation") + title = "Validation" + help = "" def get_datasources(): @@ -59,31 +164,26 @@ def get_datasources(): return [("", "")] + list(zip(v, v)) -class SmartAttributesForm(AdvancendAttrsMixin, forms.Form): - question = forms.CharField(required=False, help_text="If set, user must check related box to display the field") - question_onchange = forms.CharField( - widget=forms.Textarea, required=False, help_text="Js to tigger on 'question' check/uncheck " - ) - hint = forms.CharField(required=False, help_text="Text to display above the input") - description = forms.CharField(required=False, help_text="Text to display below the input") - datasource = forms.ChoiceField(choices=get_datasources, required=False, help_text="Datasource name for ajax field") - parent_datasource = forms.ChoiceField( - choices=get_datasources, required=False, help_text="Parent Datasource name for ajax field" - ) - choices = forms.JSONField(required=False) - # onchange = forms.CharField(widget=forms.Textarea, required=False, help_text="Javascript onchange event") - # onblur = forms.CharField(widget=forms.Textarea, required=False, help_text="Javascript onblur event") - visible = forms.BooleanField(required=False, help_text="Hide/Show field") - - -class CssForm(AdvancendAttrsMixin, forms.Form): - input = forms.CharField(required=False, help_text="") - label = forms.CharField(required=False, help_text="") +class CssForm(AdvancendAttrsForm): + SECTIONS_MAP = {"css": ["input", "label", "fieldset", "description", "hint"]} + input = forms.CharField(required=False, initial=TailWindMixin.default_class) + label = forms.CharField(required=False, initial="block uppercase tracking-wide text-gray-700 font-bold mb-2") fieldset = forms.CharField(required=False, help_text="") - question = forms.CharField(required=False, help_text="") + description = forms.CharField(required=False, help_text="") + hint = forms.CharField(required=False, help_text="") + + class Meta: + title = "Css" + help = "" -class EventForm(AdvancendAttrsMixin, forms.Form): +class EventForm(AdvancendAttrsForm): + SECTIONS_MAP = { + "field": [], + "widget": ["onchange", "onblur", "onkeyup", "onfocus"], + "data": ["onload", "validation"], + } + onchange = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) onblur = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) onkeyup = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) @@ -91,28 +191,46 @@ class EventForm(AdvancendAttrsMixin, forms.Form): onfocus = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) validation = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) - init = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) + # init = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) + + class Meta: + title = "Events" + help = "" + + +# DEFAULTS = { +# "fieldset": {"css": "bg-red-200 p-2"}, +# # "css": {"question": "cursor-pointer", +# # "input": TailWindMixin.default_class, +# # "label": "block uppercase tracking-wide text-gray-700 font-bold mb-2"}, +# } -DEFAULTS = { - "css": {"question": "cursor-pointer", "label": "block uppercase tracking-wide text-gray-700 font-bold mb-2"}, -} +# def get_initial(field, prefix): +# base = DEFAULTS.get(prefix, {}) +# for k, v in field.advanced.get(prefix, {}).items(): +# if v: +# base[k] = v +# return base -def get_initial(field, prefix): - base = DEFAULTS.get(prefix, {}) - for k, v in field.advanced.get(prefix, {}).items(): - if v: - base[k] = v - return base + +# +# class FieldsetAttributesForm(AdvancendAttrsMixin, forms.Form): +# title = "Fieldset" +# css = forms.CharField(required=False, initial=TailWindMixin.default_class) class FieldEditor: FORMS = { "field": FlexFieldAttributesForm, - "kwargs": FormFieldAttributesForm, - "widget": WidgetAttributesForm, - "smart": SmartAttributesForm, + "smart": AdvancedForm, + "custom": CustomForm, + "config": ValidationForm, + # "fieldset": FieldsetAttributesForm, + # "kwargs": FormFieldAttributesForm, + # "widget": WidgetAttributesForm, + # "smart": SmartAttributesForm, "css": CssForm, "events": EventForm, } @@ -130,27 +248,59 @@ def field(self): @cached_property def patched_field(self): fld = self.field + fld.advanced = {} + if config := cache.get(self.cache_key, None): - forms = self.get_forms(config) - fieldForm = forms.pop("field", None) + merged = {} + _forms: Dict[str, AdvancendAttrsForm] = self.get_forms(config) + fieldForm = _forms.get("field", None) if fieldForm.is_valid(): + fld.field_type = field_registry.get_class(fieldForm.cleaned_data.pop("field_type")) for k, v in fieldForm.cleaned_data.items(): setattr(fld, k, v) - for prefix, frm in forms.items(): - frm.is_valid() - merged = merge_data(fld.advanced, {**{prefix: frm.cleaned_data}}) - fld.advanced = merged + else: + raise ValidationError(fieldForm.errors) + + for __, frm in _forms.items(): + if frm.is_valid(): + processed = [] + for sect in AdvancendAttrsForm.SECTIONS_MAP.keys(): + values = {k: v for k, v in frm.get_section(sect).items() if v and str(v).strip()} + processed.extend(values.keys()) + if sect == "field": + for k, v in values.items(): + setattr(fld, k, v) + else: + merged = merge_data(merged, {**{sect: values}}) + else: + raise ValidationError(frm.errors) + fld.advanced = merged return fld def patch(self, request, pk): pass - def get_configuration(self): + def get_kwargs(self): + try: + i = self.patched_field.get_instance() + data = self.field.get_field_kwargs() + data["field"]["widget"] = fqn(i.widget) + + # data["__field__"] = fqn(i) + # data["__widget__"] = {"class": fqn(i.widget), + # "attrs": i.widget.attrs} + rendered = json.dumps(data, indent=4) + except Exception as e: + logger.exception(e) + rendered = f"{e.__class__.__name__}: {e}" + return HttpResponse(rendered, content_type="text/plain") + + def get_advanced(self): self.patched_field.get_instance() rendered = json.dumps(self.field.advanced, indent=4) return HttpResponse(rendered, content_type="text/plain") - def get_code(self): + def get_html(self): from bs4 import BeautifulSoup as bs from bs4 import formatter from pygments import highlight @@ -181,6 +331,7 @@ def render(self): instance = self.patched_field.get_instance() form_class_attrs = { self.field.name: instance, + "flex_form": Mock(), } form_class = type(FlexFormBaseForm)("TestForm", (FlexFormBaseForm,), form_class_attrs) ctx = self.get_context(self.request) @@ -190,8 +341,8 @@ def render(self): else: form = form_class(initial={self.field.name: self.patched_field.get_default_value()}) ctx["valid"] = None - ctx["form"] = form + ctx["media"] = VersionMedia() + form.media + instance.widget.media ctx["instance"] = instance return render(self.request, "admin/core/flexformfield/field_editor/preview.html", ctx) @@ -201,15 +352,9 @@ def get_forms(self, data=None) -> Dict: return {prefix: Form(data, prefix=prefix, field=self.field) for prefix, Form in self.FORMS.items()} if self.request.method == "POST": return { - prefix: Form( - self.request.POST, prefix=prefix, field=self.field, initial=get_initial(self.field, prefix) - ) - for prefix, Form in self.FORMS.items() + prefix: Form(self.request.POST, prefix=prefix, field=self.field) for prefix, Form in self.FORMS.items() } - return { - prefix: Form(prefix=prefix, field=self.field, initial=get_initial(self.field, prefix)) - for prefix, Form in self.FORMS.items() - } + return {prefix: Form(prefix=prefix, field=self.field) for prefix, Form in self.FORMS.items()} def refresh(self): forms = self.get_forms() @@ -217,9 +362,9 @@ def refresh(self): data = self.request.POST.dict() data.pop("csrfmiddlewaretoken") cache.set(self.cache_key, data) + return JsonResponse(data) else: return JsonResponse({prefix: frm.errors for prefix, frm in forms.items()}, status=400) - return JsonResponse(data) def get_context(self, request, pk=None, **kwargs): return { @@ -229,20 +374,28 @@ def get_context(self, request, pk=None, **kwargs): def get(self, request, pk): ctx = self.get_context(request, pk) + ctx["forms"] = {} extra = "" if settings.DEBUG else ".min" - ctx["media"] = VersionMedia( + media = VersionMedia() + for prefix, frm in self.get_forms().items(): + # ctx[f"form_{prefix}"] = frm + ctx["forms"][prefix] = frm + media += frm.media + + media += VersionMedia( js=[ "admin/js/vendor/jquery/jquery%s.js" % extra, "admin/js/jquery.init.js", "jquery.compat%s.js" % extra, + "admin/resizer%s.js" % extra, "smart_validation%s.js" % extra, "smart%s.js" % extra, "smart_field%s.js" % extra, - ] + ], + css={"all": ["admin/field_editor/field_editor.css"]}, ) - for prefix, frm in self.get_forms().items(): - ctx[f"form_{prefix}"] = frm - ctx["media"] += frm.media + ctx["media"] = media + # + VersionMedia(js=["admin/field_editor/field_editor%s.js" % extra]) return render(request, "admin/core/flexformfield/field_editor/main.html", ctx) def post(self, request, pk): @@ -250,3 +403,5 @@ def post(self, request, pk): if all(map(lambda f: f.is_valid(), forms.values())): self.patched_field.save() return HttpResponseRedirect(".") + else: + raise Exception("""---""") diff --git a/src/aurora/core/admin/flex_field.py b/src/aurora/core/admin/flex_field.py index 66ed4ef6..ddd477d7 100644 --- a/src/aurora/core/admin/flex_field.py +++ b/src/aurora/core/admin/flex_field.py @@ -15,8 +15,8 @@ from ...administration.mixin import LoadDumpMixin from ..admin_sync import SyncMixin from ..forms import Select2Widget -from ..models import FIELD_KWARGS, FlexFormField -from ..utils import dict_setdefault, is_root, render +from ..models import FlexFormField +from ..utils import is_root from .base import ConcurrencyVersionAdmin from .field_editor import FieldEditor from .filters import Select2FieldComboFilter @@ -31,13 +31,6 @@ class Meta: model = FlexFormField exclude = () - def clean(self): - ret = super().clean() - ret.setdefault("advanced", {}) - dict_setdefault(ret["advanced"], FlexFormField.FLEX_FIELD_DEFAULT_ATTRS) - dict_setdefault(ret["advanced"], {"kwargs": FIELD_KWARGS.get(ret["field_type"], {})}) - return ret - @register(FlexFormField) class FlexFormFieldAdmin(LoadDumpMixin, SyncMixin, ConcurrencyVersionAdmin, OrderableAdmin, SmartModelAdmin): @@ -101,11 +94,6 @@ def field_editor(self, request, pk): else: return self.editor.get(request, pk) - @view() - def widget_attrs(self, request, pk): - editor = FieldEditor(self, request, pk) - return editor.get_configuration() - @view() def widget_refresh(self, request, pk): editor = FieldEditor(self, request, pk) @@ -114,46 +102,56 @@ def widget_refresh(self, request, pk): @view() def widget_code(self, request, pk): editor = FieldEditor(self, request, pk) - return editor.get_code() + return editor.get_html() + + @view() + def widget_advanced(self, request, pk): + editor = FieldEditor(self, request, pk) + return editor.get_advanced() + + @view() + def widget_kwargs(self, request, pk): + editor = FieldEditor(self, request, pk) + return editor.get_kwargs() @view() def widget_display(self, request, pk): editor = FieldEditor(self, request, pk) return editor.render() - @button() - def test(self, request, pk): - ctx = self.get_common_context(request, pk) - try: - fld = ctx["original"] - instance = fld.get_instance() - ctx["debug_info"] = { - # "widget": getattr(instance, "widget", None), - "field_kwargs": fld.get_field_kwargs(), - # "options": getattr(instance, "options", None), - # "choices": getattr(instance, "choices", None), - # "widget_attrs": instance.widget_attrs(instance.widget), - } - form_class_attrs = { - "sample": instance, - } - form_class = type(forms.Form)("TestForm", (forms.Form,), form_class_attrs) - - if request.method == "POST": - form = form_class(request.POST) - - if form.is_valid(): - ctx["debug_info"]["cleaned_data"] = form.cleaned_data - self.message_user( - request, f"Form validation success. You have selected: {form.cleaned_data['sample']}" - ) - else: - form = form_class() - ctx["form"] = form - ctx["instance"] = instance - except Exception as e: - logger.exception(e) - ctx["error"] = e - raise - - return render(request, "admin/core/flexformfield/test.html", ctx) + # @button() + # def test(self, request, pk): + # ctx = self.get_common_context(request, pk) + # try: + # fld = ctx["original"] + # instance = fld.get_instance() + # ctx["debug_info"] = { + # # "widget": getattr(instance, "widget", None), + # "field_kwargs": fld.get_field_kwargs(), + # # "options": getattr(instance, "options", None), + # # "choices": getattr(instance, "choices", None), + # # "widget_attrs": instance.widget_attrs(instance.widget), + # } + # form_class_attrs = { + # "sample": instance, + # } + # form_class = type(forms.Form)("TestForm", (forms.Form,), form_class_attrs) + # + # if request.method == "POST": + # form = form_class(request.POST) + # + # if form.is_valid(): + # ctx["debug_info"]["cleaned_data"] = form.cleaned_data + # self.message_user( + # request, f"Form validation success. You have selected: {form.cleaned_data['sample']}" + # ) + # else: + # form = form_class() + # ctx["form"] = form + # ctx["instance"] = instance + # except Exception as e: + # logger.exception(e) + # ctx["error"] = e + # raise + # + # return render(request, "admin/core/flexformfield/test.html", ctx) diff --git a/src/aurora/core/compat.py b/src/aurora/core/compat.py index 14a8a79d..f00ea71e 100644 --- a/src/aurora/core/compat.py +++ b/src/aurora/core/compat.py @@ -6,12 +6,16 @@ from strategy_field.utils import fqn -class RegexEditor(forms.Textarea): - template_name = "django/forms/widgets/regex.html" +class JsRegexEditor(forms.Textarea): + template_name = "django/forms/widgets/jsregex.html" class RegexField(RegexField_): - widget = RegexEditor + pass + + +class JavascriptRegexField(RegexField_): + widget = JsRegexEditor def value_from_object(self, obj): """Return the value of this field in the given model instance.""" diff --git a/src/aurora/core/fields/__init__.py b/src/aurora/core/fields/__init__.py index d3cba319..2499cccf 100644 --- a/src/aurora/core/fields/__init__.py +++ b/src/aurora/core/fields/__init__.py @@ -14,6 +14,7 @@ from .remote_ip import RemoteIpField from .select import AjaxSelectField, SelectField, SmartSelectWidget from .webcam import WebcamField +from .boolean import BooleanField WIDGET_FOR_FORMFIELD_DEFAULTS = { forms.DateField: {"widget": widgets.SmartDateWidget}, diff --git a/src/aurora/core/fields/boolean.py b/src/aurora/core/fields/boolean.py new file mode 100644 index 00000000..efa90457 --- /dev/null +++ b/src/aurora/core/fields/boolean.py @@ -0,0 +1,12 @@ +from django import forms + +from aurora.core.fields.widgets.mixins import SmartWidgetMixin + + +class CheckboxInput(SmartWidgetMixin, forms.CheckboxInput): + input_type = "checkbox" + template_name = "django/forms/widgets/boolean.html" + + +class BooleanField(forms.BooleanField): + widget = CheckboxInput diff --git a/src/aurora/core/fields/configs.py b/src/aurora/core/fields/configs.py new file mode 100644 index 00000000..a0f4361b --- /dev/null +++ b/src/aurora/core/fields/configs.py @@ -0,0 +1,11 @@ +from django import forms + +from aurora.core import fields + + +class BooleanFieldExtra(forms.Form): + captions = forms.CharField(required=False) + position = forms.ChoiceField(choices=(["l", "Left"], ["r", "right"])) + + +fields_config = {fields.BooleanField: BooleanFieldExtra} diff --git a/src/aurora/core/fields/mixins.py b/src/aurora/core/fields/mixins.py index e2efe200..7202cd7b 100644 --- a/src/aurora/core/fields/mixins.py +++ b/src/aurora/core/fields/mixins.py @@ -1,26 +1,26 @@ -from django.utils.translation import get_language - -from aurora.core.utils import oneline -from aurora.state import state - - -class TailWindMixin: - def __init__(self, attrs=None, **kwargs): - attrs = { - "class": "shadow appearance-none border rounded w-full py-2 px-3 my-1 cursor-pointer" - "text-gray-700 leading-tight focus:outline-none focus:shadow-outline ", - **(attrs or {}), - } - super().__init__(attrs=attrs, **kwargs) - - -class SmartWidgetMixin: - def get_context(self, name, value, attrs): - ret = super().get_context(name, value, attrs) - ret["LANGUAGE_CODE"] = get_language() - ret["request"] = state.request - ret["user"] = state.request.user - return ret +# from django.utils.translation import get_language +# +# from aurora.core.utils import oneline +# from aurora.state import state +# +# +# class TailWindMixin: +# def __init__(self, attrs=None, **kwargs): +# attrs = { +# "class": "shadow appearance-none border rounded w-full py-2 px-3 my-1 cursor-pointer" +# "text-gray-700 leading-tight focus:outline-none focus:shadow-outline ", +# **(attrs or {}), +# } +# super().__init__(attrs=attrs, **kwargs) +# +# +# class SmartWidgetMixin: +# def get_context(self, name, value, attrs): +# ret = super().get_context(name, value, attrs) +# ret["LANGUAGE_CODE"] = get_language() +# ret["request"] = state.request +# ret["user"] = state.request.user +# return ret class SmartFieldMixin: @@ -31,12 +31,12 @@ class SmartFieldMixin: def __init__(self, *args, **kwargs) -> None: self.flex_field = kwargs.pop("flex_field") - self.smart_attrs = kwargs.pop("smart_attrs", kwargs.pop("smart", {})) - self.field_attrs = kwargs.pop("field_attrs", {}) - self.data_attrs = kwargs.pop("data", {}) + # self.smart_attrs = kwargs.pop("smart_attrs", kwargs.pop("smart", {})) + # self.field_attrs = kwargs.pop("field_attrs", {}) + # self.data_attrs = kwargs.pop("data", {}) self.widget_kwargs = kwargs.pop("widget_kwargs", {}) - self.smart_events = kwargs.pop("smart_events", {}) - self.datasource = kwargs.pop("datasource", None) + # self.smart_events = kwargs.pop("smart_events", {}) + # self.datasource = kwargs.pop("datasource", None) super().__init__(*args, **kwargs) def is_stored(self): @@ -45,43 +45,44 @@ def is_stored(self): def widget_attrs(self, widget): attrs = super().widget_attrs(widget) attrs.update({k: v for k, v in self.widget_kwargs.items() if v is not None}) - - for k, v in self.smart_attrs.items(): - if k.startswith("data-") or k.startswith("on") and v: - attrs[k] = v - - for k, v in self.widget_kwargs.items(): - if k.startswith("data-") or k.startswith("on") and v: - attrs[k] = v - - for k, v in self.data_attrs.items(): - attrs[f"data-{k}"] = v - - if self.flex_field.validator: - attrs["data-smart-validator"] = self.flex_field.validator.name - - if not self.flex_field.required: - attrs.pop("required", "") + # + # for k, v in self.smart_attrs.items(): + # if k.startswith("data-") or k.startswith("on") and v: + # attrs[k] = v + # + # for k, v in self.widget_kwargs.items(): + # if k.startswith("data-") or k.startswith("on") and v: + # attrs[k] = v + # + # for k, v in self.data_attrs.items(): + # attrs[f"data-{k}"] = v + # + # if self.flex_field.validator: + # attrs["data-smart-validator"] = self.flex_field.validator.name + # + # if not self.flex_field.required: + # attrs.pop("required", "") # attrs["flex_field"] = self.flex_field - attrs["data-flex-name"] = self.flex_field.name - for attr in [ - "onblur", - "onchange", - "onkeyup", - ]: - if attr in self.smart_events: - if self.smart_events[attr]: - attrs[attr] = oneline(self.smart_events[attr]) - - for attr in [ - "onload", - ]: - if attr in self.smart_events: - if self.smart_events[attr]: - attrs[f"data-{attr}"] = oneline(self.smart_events[attr]) - - if validation := self.smart_events.get("validation", None): - attrs["data-validation"] = oneline(validation) - widget.smart_attrs = self.smart_attrs + # attrs["data-flex-name"] = self.flex_field.name + # for attr in [ + # "onblur", + # "onchange", + # "onkeyup", + # ]: + # if attr in self.smart_events: + # if self.smart_events[attr]: + # attrs[attr] = oneline(self.smart_events[attr]) + # + # for attr in [ + # "onload", + # ]: + # if attr in self.smart_events: + # if self.smart_events[attr]: + # attrs[f"data-{attr}"] = oneline(self.smart_events[attr]) + # + # if validation := self.smart_events.get("validation", None): + # attrs["data-validation"] = oneline(validation) + # widget.smart_attrs = self.smart_attrs + widget.field = self widget.flex_field = self.flex_field return attrs diff --git a/src/aurora/core/fields/widgets/datetime.py b/src/aurora/core/fields/widgets/datetime.py index 4ba10a1e..07e949b3 100644 --- a/src/aurora/core/fields/widgets/datetime.py +++ b/src/aurora/core/fields/widgets/datetime.py @@ -6,20 +6,14 @@ class SmartDateWidget(TailWindMixin, forms.DateInput): - # class Media: - # js = [ - # get_versioned_static_name("datetimepicker/datepicker"s.js"), - # # "https://cdnjs.cloudflare.com/ajax/libs/jquery-datetimepicker/2.5.20/jquery.datetimepicker.full.min.js", - # get_versioned_static_name("datetimepicker/dt.js"), - # ] - # css = {"all": [get_versioned_static_name("datetimepicker/datepicker.css")]} - def __init__(self, attrs=None, format=None): super().__init__(attrs=attrs, format=format) - self.attrs.setdefault("class", {}) - self.attrs["class"] += " vDateField " self.attrs["size"] = 10 + def build_attrs(self, base_attrs, extra_attrs=None): + base_attrs["class"] += " vDateField" + return super().build_attrs(base_attrs, extra_attrs) + @property def media(self): extra = "" if settings.DEBUG else ".min" diff --git a/src/aurora/core/fields/widgets/editor.py b/src/aurora/core/fields/widgets/editor.py index 4af2b61b..9d19d751 100644 --- a/src/aurora/core/fields/widgets/editor.py +++ b/src/aurora/core/fields/widgets/editor.py @@ -1,4 +1,7 @@ from django import forms +from django.conf import settings + +from aurora.core.version_media import VersionMedia class JavascriptEditor(forms.Textarea): @@ -12,43 +15,42 @@ def __init__(self, *args, **kwargs): self.attrs["theme"] = theme self.attrs["toolbar"] = toolbar - class Media: - css = { - "all": ( - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.css", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/theme/midnight.min.css", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/display/fullscreen.min.css", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/fold/foldgutter.min.css", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.css", - # "codemirror/codemirror.css", - # "codemirror/fullscreen.css", - # "codemirror/midnight.css", - # "codemirror/foldgutter.css", - ) - } - js = ( - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/display/placeholder.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/edit/closebrackets.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/edit/trailingspace.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/fold/foldcode.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/fold/foldgutter.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/fold/brace-fold.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/fold/indent-fold.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/fold/indent-fold.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/hint/javascript-hint.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/javascript-lint.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/javascript/javascript.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/selection/active-line.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/display/fullscreen.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/jshint/2.13.6/jshint.min.js", - "cm.js", - # "codemirror/codemirror.js", - # "codemirror/javascript.js", - # "codemirror/fullscreen.js", - # "codemirror/active-line.js", - # "codemirror/foldcode.js", - # "codemirror/foldgutter.js", - # "codemirror/indent-fold.js", + @property + def media(self): + extra = "" if settings.DEBUG else ".min" + base = super().media + CM_VERSION = "6.65.7" + return base + VersionMedia( + css={ + "all": ( + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/codemirror.min.css", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/theme/midnight.min.css", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/display/fullscreen.min.css", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/fold/foldgutter.min.css", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/lint/lint.min.css", + # "codemirror/codemirror.css", + # "codemirror/fullscreen.css", + # "codemirror/midnight.css", + "cm.css", + ) + }, + js=[ + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/codemirror.min.js", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/display/fullscreen.min.js", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/display/placeholder.min.js", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/edit/closebrackets.min.js", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/edit/trailingspace.min.js", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/fold/brace-fold.min.js", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/fold/foldcode.min.js", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/fold/foldgutter.min.js", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/fold/indent-fold.min.js", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/fold/indent-fold.min.js", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/hint/javascript-hint.min.js", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/lint/javascript-lint.min.js", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/lint/lint.min.js", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/addon/selection/active-line.min.js", + f"https://cdnjs.cloudflare.com/ajax/libs/codemirror/{CM_VERSION}/mode/javascript/javascript.min.js", + "https://cdnjs.cloudflare.com/ajax/libs/jshint/2.13.6/jshint.min.js", + "cm%s.js" % extra, + ], ) diff --git a/src/aurora/core/fields/widgets/mixins.py b/src/aurora/core/fields/widgets/mixins.py index 0be59fde..d1c45950 100644 --- a/src/aurora/core/fields/widgets/mixins.py +++ b/src/aurora/core/fields/widgets/mixins.py @@ -18,23 +18,23 @@ def __init__(self, attrs=None, **kwargs): def build_attrs(self, base_attrs, extra_attrs=None): """Build an attribute dictionary.""" - if extra_classes := base_attrs.pop("extra_classes", None): - base_attrs["class"] += f" {extra_classes}" - - if self.flex_field.required: - base_attrs["required"] = True - else: - required = self.smart_attrs.get("required_by_question", "") - if required == "required": - if self.smart_attrs.get("question", None): - base_attrs.pop("required", None) - extra_attrs.pop("required", None) - base_attrs["class"] += " required_by_question" - else: - base_attrs.pop("required", None) - extra_attrs.pop("required", None) - - return {**base_attrs, **(extra_attrs or {})} + # if extra_classes := base_attrs.pop("extra_classes", None): + # base_attrs["class"] += f" {extra_classes}" + # + # if self.flex_field.required: + # base_attrs["required"] = True + # else: + # required = self.smart_attrs.get("required_by_question", "") + # if required == "required": + # if self.smart_attrs.get("question", None): + # base_attrs.pop("required", None) + # extra_attrs.pop("required", None) + # base_attrs["class"] += " required_by_question" + # else: + # base_attrs.pop("required", None) + # extra_attrs.pop("required", None) + final_attrs = {**base_attrs, **(extra_attrs or {})} + return final_attrs class SmartWidgetMixin: @@ -43,4 +43,9 @@ def get_context(self, name, value, attrs): ret["LANGUAGE_CODE"] = get_language() ret["request"] = state.request ret["user"] = state.request.user + ret["name"] = name + ret["value"] = value + ret["attrs"] = attrs + ret["widget"]["flex_field"] = self.flex_field + return ret diff --git a/src/aurora/core/migrations/0054_auto_20230403_0853.py b/src/aurora/core/migrations/0054_auto_20230403_0853.py new file mode 100644 index 00000000..223959aa --- /dev/null +++ b/src/aurora/core/migrations/0054_auto_20230403_0853.py @@ -0,0 +1,31 @@ +# Generated by Django 3.2.18 on 2023-04-03 08:53 + +import aurora.core.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0053_auto_20230321_0604"), + ] + + operations = [ + migrations.AddField( + model_name="flexformfield", + name="mapping", + field=models.CharField(blank=True, default="", max_length=500, null=True), + ), + migrations.AddField( + model_name="flexformfield", + name="note", + field=models.CharField(blank=True, help_text="Add here for the mapping", max_length=500, null=True), + ), + migrations.AlterField( + model_name="flexformfield", + name="choices", + field=models.CharField( + blank=True, max_length=2000, null=True, validators=[aurora.core.models.ChoicesValidator] + ), + ), + ] diff --git a/src/aurora/core/models.py b/src/aurora/core/models.py index a87fe3ef..aa288ad6 100644 --- a/src/aurora/core/models.py +++ b/src/aurora/core/models.py @@ -1,3 +1,5 @@ +import copy + import json import logging import re @@ -35,11 +37,11 @@ from ..i18n.models import I18NModel from ..state import state from . import fields -from .compat import RegexField, StrategyClassField +from .compat import JavascriptRegexField, StrategyClassField from .fields import WIDGET_FOR_FORMFIELD_DEFAULTS, SmartFieldMixin from .forms import CustomFieldMixin, FlexFormBaseForm, SmartBaseFormSet from .registry import field_registry, form_registry, import_custom_field -from .utils import JSONEncoder, dict_setdefault, jsonfy, namify, underscore_to_camelcase +from .utils import JSONEncoder, dict_setdefault, jsonfy, namify, underscore_to_camelcase, oneline logger = logging.getLogger(__name__) @@ -535,33 +537,23 @@ def __call__(self, value): raise ValidationError(e) +@deconstructible +class ChoicesValidator: + def __call__(self, value): + try: + list(dict(value).items()) + except ValueError: + try: + list(zip(map(str.lower, value), value)) + except Exception as e: + raise ValidationError(e) + + class FlexFormField(AdminReverseMixin, NaturalKeyModel, I18NModel, OrderableModel): I18N_FIELDS = [ "label", ] I18N_ADVANCED = ["smart.hint", "smart.question", "smart.description"] - FLEX_FIELD_DEFAULT_ATTRS = { - "widget": { - "pattern": None, - "onchange": "", - "title": None, - "placeholder": None, - "extra_classes": "", - "css_class": "", - "fieldset": "", - }, - "kwargs": { - "default_value": None, - }, - "smart": { - "hint": "", - "visible": True, - "choices": [], - "question": "", - "description": "", - "index": None, - }, - } version = AutoIncVersionField() last_update_date = models.DateTimeField(auto_now=True) @@ -570,15 +562,17 @@ class FlexFormField(AdminReverseMixin, NaturalKeyModel, I18NModel, OrderableMode label = models.CharField(max_length=2000) name = CICharField(max_length=100, blank=True, validators=[RegexValidator("^[a-z_0-9]*$")]) field_type = StrategyClassField(registry=field_registry, import_error=import_custom_field) - choices = models.CharField(max_length=2000, blank=True, null=True) + choices = models.CharField(max_length=2000, blank=True, null=True, validators=[ChoicesValidator]) required = models.BooleanField(default=False) enabled = models.BooleanField(default=True) validator = models.ForeignKey( Validator, blank=True, null=True, limit_choices_to={"target": Validator.FIELD}, on_delete=models.PROTECT ) validation = models.TextField(blank=True, null=True) - regex = RegexField(blank=True, null=True, validators=[RegexPatternValidator()]) + regex = JavascriptRegexField(blank=True, null=True, validators=[RegexPatternValidator()]) advanced = models.JSONField(default=dict, blank=True, null=True) + mapping = models.CharField(max_length=500, default="", blank=True, null=True) + note = models.CharField(max_length=500, blank=True, null=True, help_text="Add here for the mapping") class Meta: unique_together = (("flex_form", "name"),) @@ -600,7 +594,59 @@ def fqn(self): def get_default_value(self): return self.advanced.get("kwargs", {}).get("default_value", None) + def get_choices(self): + try: + value = json.loads(self.choices) + if isinstance(value, (list, tuple)): + return list(zip(value, value)) + elif isinstance(value, (dict)): + return list(dict(value).items()) + except ValueError: + return None + def get_field_kwargs(self): + field_kwargs = copy.deepcopy({**WIDGET_FOR_FORMFIELD_DEFAULTS.get(self.field_type, {})}) + field_kwargs.update(**self.advanced.get("field", {})) + widget_kwargs = copy.deepcopy(self.advanced.get("widget", {})) + data = copy.deepcopy(self.advanced.get("data", {})) + # smart_attrs = self.advanced.get("smart", {}) + # config = self.advanced.get("config", {}) + # events = self.advanced.get("events", {}) + # onload = events.pop("onload", "") + data["flex-name"] = self.name + + if issubclass(self.field_type, CustomFieldMixin): + raise NotImplementedError("") + else: + # field_type = self.field_type + field_kwargs.update( + **{ + "label": self.label, + "disabled": not self.enabled, + "required": self.required, + } + ) + for k, v in data.items(): + if v: + widget_kwargs[f"data-{k}"] = oneline(v) + + # if hasattr(field_type, "choices"): + # field_kwargs["choices"] = self.get_choices() + # + # widget_kwargs["class"] = self.advanced.get("css", {}).get("input", "") or TailWindMixin.default_class + # widget_kwargs["value"] = smart_attrs.get("default", "") + # widget_kwargs["placeholder"] = smart_attrs.get("placeholder", "") + # widget_kwargs.update(**config) + # + # widget_kwargs.update(**{k: oneline(v) for k, v in events.items()}) + # + # widget_kwargs = {k: v for k, v in widget_kwargs.items() if v and str(v).strip()} + + widget_kwargs = {k: oneline(v) for k, v in widget_kwargs.items() if v and str(v).strip()} + kwargs = {"field": field_kwargs, "widget": widget_kwargs} + return kwargs + + def _get_field_kwargs(self): if issubclass(self.field_type, CustomFieldMixin): advanced = self.advanced.copy() smart_attrs = advanced.pop("smart", {}).copy() @@ -703,7 +749,12 @@ def get_instance(self): kwargs = self.get_field_kwargs() kwargs.setdefault("flex_field", self) tt = type(field_type.__name__, (SmartFieldMixin, field_type), dict()) - fld = tt(**kwargs) + fld = tt( + **kwargs["field"], + flex_field=self, + # smart_attrs=kwargs["smart"], + widget_kwargs=kwargs["widget"], + ) except Exception as e: logger.exception(e) raise @@ -712,8 +763,6 @@ def get_instance(self): def clean(self): if self.field_type: try: - # dict_setdefault(self.advanced, self.FLEX_FIELD_DEFAULT_ATTRS) - # dict_setdefault(self.advanced, {"kwargs": FIELD_KWARGS.get(self.field_type, {})}) self.get_instance() except Exception as e: logger.exception(e) @@ -731,8 +780,8 @@ def get_usage(self): { "type": "Form", "obj": self.flex_form, - "editor_url": reverse("admin:registration_registration_change", args=[self.flex_form.pk]), - "change_url": reverse("admin:registration_registration_change", args=[self.flex_form.pk]), + "editor_url": reverse("admin:core_flexform_change", args=[self.flex_form.pk]), + "change_url": reverse("admin:core_flexform_change", args=[self.flex_form.pk]), } ) return ret @@ -848,7 +897,7 @@ class CustomFieldType(AdminReverseMixin, NaturalKeyModel, models.Model): name = CICharField(max_length=100, unique=True, validators=[RegexValidator("[A-Z][a-zA-Z0-9_]*")]) base_type = StrategyClassField(registry=field_registry, default=forms.CharField) attrs = models.JSONField(default=dict) - regex = RegexField(blank=True, null=True) + regex = JavascriptRegexField(blank=True, null=True) # choices = models.CharField(max_length=2000, blank=True, null=True) # required = models.BooleanField(default=False) validator = models.ForeignKey( diff --git a/src/aurora/core/registry.py b/src/aurora/core/registry.py index 8626c714..56f2c49a 100644 --- a/src/aurora/core/registry.py +++ b/src/aurora/core/registry.py @@ -72,7 +72,7 @@ def __contains__(self, y): field_registry = FieldRegistry(forms.Field) -field_registry.register(forms.BooleanField) +# field_registry.register(forms.BooleanField) field_registry.register(forms.CharField) field_registry.register(forms.ChoiceField) field_registry.register(forms.DateField) @@ -90,6 +90,7 @@ def __contains__(self, y): field_registry.register(forms.URLField) field_registry.register(fields.AjaxSelectField) +field_registry.register(fields.BooleanField) field_registry.register(fields.DocumentField) field_registry.register(fields.MultiCheckboxField) field_registry.register(fields.WebcamField) diff --git a/src/aurora/core/static/admin/field_editor/field_editor.css b/src/aurora/core/static/admin/field_editor/field_editor.css index f09173e8..50dcadf4 100644 --- a/src/aurora/core/static/admin/field_editor/field_editor.css +++ b/src/aurora/core/static/admin/field_editor/field_editor.css @@ -48,6 +48,15 @@ body { width: 60%; background: var(--breadcrumbs-bg); } +#fieldEditor #mainPanel .settings #event_selector option, #formEditor #mainPanel .settings #event_selector option { + font-style: oblique; + font-weight: bold; + color: red; +} +#fieldEditor #mainPanel .settings #event_selector option.used::after, #formEditor #mainPanel .settings #event_selector option.used::after { + content: " --- "; + color: red; +} #fieldEditor #mainPanel .settings table:first-child, #formEditor #mainPanel .settings table:first-child { background: var(--breadcrumbs-bg); width: 100%; diff --git a/src/aurora/core/static/admin/field_editor/field_editor.css.map b/src/aurora/core/static/admin/field_editor/field_editor.css.map index 87bd0f54..12e22888 100644 --- a/src/aurora/core/static/admin/field_editor/field_editor.css.map +++ b/src/aurora/core/static/admin/field_editor/field_editor.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["field_editor.scss"],"names":[],"mappings":"AAAA;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGA;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;AAA2B;EAC3B;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAMR;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAKJ;EACE;;;AAsBZ;EACE;EACA;;;AAsBF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAEA;EACE;;AAGF;EACE;EACA;;AAEA;EACE;;;AAOR;EACE;;AAEA;EACE;EACA;;;AAKF;EACE;;AAEF;EACE;;;AAIJ;EACE;EACA;EACA;;AAGE;EACE;EACA;EACA","file":"field_editor.css"} +{"version":3,"sourceRoot":"","sources":["field_editor.scss"],"names":[],"mappings":"AAAA;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGA;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAGE;EACE;EACA;EACA;;AACA;EACE;EACA;;AAMN;EACE;EACA;EACA;EACA;EACA;AAA2B;EAC3B;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAMR;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAKJ;EACE;;;AAsBZ;EACE;EACA;;;AAsBF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAEA;EACE;;AAGF;EACE;EACA;;AAEA;EACE;;;AAOR;EACE;;AAEA;EACE;EACA;;;AAKF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;;AAGE;EACE;EACA;EACA","file":"field_editor.css"} diff --git a/src/aurora/core/static/admin/field_editor/field_editor.js b/src/aurora/core/static/admin/field_editor/field_editor.js index 6366f715..0faad697 100644 --- a/src/aurora/core/static/admin/field_editor/field_editor.js +++ b/src/aurora/core/static/admin/field_editor/field_editor.js @@ -1,14 +1,7 @@ (function ($) { $(document).ready(function () { var $me = $("#field_editor_script"); - var refreshUrl = $me.data("url"); - var $iFrame1 = $('#widget_display'); - var $iFrame2 = $('#widget_code'); - var $iFrame3 = $('#widget_attrs'); - - var $radioRender = $("#radio_display"); - var $radioCode = $('#radio_code'); - var $radioAttributes = $('#radio_attrs'); + var refreshUrl = $("#url").data("url"); $(".toolbutton").on("click", function () { const action = $(this).data('action'); @@ -27,23 +20,25 @@ $(".field-error").remove(); $.post(refreshUrl, formData) .done(function (data) { - $iFrame1[0].contentWindow.location = $iFrame1[0].contentWindow.location.href; - $iFrame2[0].contentWindow.location = $iFrame2[0].contentWindow.location.href; - $iFrame3[0].contentWindow.location = $iFrame3[0].contentWindow.location.href; + $("iframe").each(function () { + $(this)[0].contentWindow.location = $(this).data("src") + }) }) - .fail(function (xhr) { - var errors = xhr.responseJSON; - var fieldErrors = errors.field; - var kwargsErrors = errors.kwargs; - var widget_kwargs = errors.widget_kwargs; - var smart = errors.smart; - for (const property in fieldErrors) { - $(`#id_field-${property}`).before(`
${fieldErrors[property]}
`); + .fail(function (xhr, err, txt) { + if (xhr.responseJSON) { + var errors = xhr.responseJSON; + var fieldErrors = errors.field; + var kwargsErrors = errors.kwargs; + var widget_kwargs = errors.widget_kwargs; + var smart = errors.smart; + for (const property in fieldErrors) { + $(`#id_field-${property}`).before(`
${fieldErrors[property]}
`); + } + } else { + console.log(11111, xhr); + console.log(11111, err); + console.log(11111, txt); } - ; - // for (const property in kwargsErrors) { - // console.log(`${property}: ${kwargsErrors[property]}`); - // } }) } @@ -54,9 +49,11 @@ update(); }); $('input,textarea').on('keyup', function () { - clearTimeout($.data(this, 'timer')); - var wait = setTimeout(update, 500); - $(this).data('timer', wait); + if ($("#autoRefresh").is(":checked")) { + clearTimeout($.data(this, 'timer')); + var wait = setTimeout(update, 500); + $(this).data('timer', wait); + } }); $('textarea.js-editor').each(function (i, e) { var editor = $(e).data("CodeMirror"); @@ -67,6 +64,14 @@ }) } }); + $("#refresh").on("click", function (e) { + e.preventDefault(); + $("iframe").each(function () { + $(this)[0].contentWindow.location = $(this).data("src") + }) + return false; + }); + $("#event_selector").on("change", function () { let sel = $(this).val(); $('#events .code').removeClass('selected'); @@ -75,10 +80,12 @@ if (editor) editor.refresh(); }); }); - $("#radio_display, #radio_code, #radio_attrs").on("click", function () { - $radioRender.is(":checked") ? $iFrame1.show() : $iFrame1.hide(); - $radioCode.is(":checked") ? $iFrame2.show() : $iFrame2.hide(); - $radioAttributes.is(":checked") ? $iFrame3.show() : $iFrame3.hide(); + $(".submit-row input[type=radio]").on("click", function (e) { + var targetClass = $(this).data("target"); + $("iframe").hide(); + var $target = $("iframe." + targetClass); + $(this).is(":checked") ? $target.show() : $target.hide(); + }) $(".tabs th").on("click", function () { const targetName = $(this).data('target'); @@ -91,6 +98,7 @@ editor.refresh(); }); }); - $radioRender.trigger("click"); + $(".submit-row input[data-target=display]").trigger("click"); + $("#tabForms th:first").trigger("click"); }) })(django.jQuery); diff --git a/src/aurora/core/static/admin/field_editor/field_editor.min.js b/src/aurora/core/static/admin/field_editor/field_editor.min.js index c658dfb0..6a117f46 100644 --- a/src/aurora/core/static/admin/field_editor/field_editor.min.js +++ b/src/aurora/core/static/admin/field_editor/field_editor.min.js @@ -1 +1 @@ -(function($){$(document).ready(function(){var $me=$("#field_editor_script");var refreshUrl=$me.data("url");var $iFrame1=$("#widget_display");var $iFrame2=$("#widget_code");var $iFrame3=$("#widget_attrs");var $radioRender=$("#radio_display");var $radioCode=$("#radio_code");var $radioAttributes=$("#radio_attrs");$(".toolbutton").on("click",function(){const action=$(this).data("action");const op=$(this).data("op");const fieldId=$(this).parents("div.cm-container").data("fieldid");const editor=$(`#${fieldId}`).data("CodeMirror");if(action){window.cmToolBar.action(editor,action)}else if(op){window.cmToolBar[op](editor)}});function update(){var formData=$("form").serialize();$(".field-error").remove();$.post(refreshUrl,formData).done(function(data){$iFrame1[0].contentWindow.location=$iFrame1[0].contentWindow.location.href;$iFrame2[0].contentWindow.location=$iFrame2[0].contentWindow.location.href;$iFrame3[0].contentWindow.location=$iFrame3[0].contentWindow.location.href}).fail(function(xhr){var errors=xhr.responseJSON;var fieldErrors=errors.field;var kwargsErrors=errors.kwargs;var widget_kwargs=errors.widget_kwargs;var smart=errors.smart;for(const property in fieldErrors){$(`#id_field-${property}`).before(`
${fieldErrors[property]}
`)}})}$("input[type=checkbox],input[type=radio]").on("click",function(){update()});$("select").on("change",function(){update()});$("input,textarea").on("keyup",function(){clearTimeout($.data(this,"timer"));var wait=setTimeout(update,500);$(this).data("timer",wait)});$("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");if(editor){editor.refresh();editor.on("change",function(){editor.save()})}});$("#event_selector").on("change",function(){let sel=$(this).val();$("#events .code").removeClass("selected");$(`#event_${sel}`).addClass("selected").find("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");if(editor)editor.refresh()})});$("#radio_display, #radio_code, #radio_attrs").on("click",function(){$radioRender.is(":checked")?$iFrame1.show():$iFrame1.hide();$radioCode.is(":checked")?$iFrame2.show():$iFrame2.hide();$radioAttributes.is(":checked")?$iFrame3.show():$iFrame3.hide()});$(".tabs th").on("click",function(){const targetName=$(this).data("target");$(`.tabs th`).removeClass("selected");$(`table.cfg-form`).addClass("collapsed");$(this).addClass("selected");$(`#${targetName}`).toggleClass("collapsed");$("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");editor.refresh()})});$radioRender.trigger("click")})})(django.jQuery); +(function($){$(document).ready(function(){var $me=$("#field_editor_script");var refreshUrl=$("#url").data("url");$(".toolbutton").on("click",function(){const action=$(this).data("action");const op=$(this).data("op");const fieldId=$(this).parents("div.cm-container").data("fieldid");const editor=$(`#${fieldId}`).data("CodeMirror");if(action){window.cmToolBar.action(editor,action)}else if(op){window.cmToolBar[op](editor)}});function update(){var formData=$("form").serialize();$(".field-error").remove();$.post(refreshUrl,formData).done(function(data){$("iframe").each(function(){$(this)[0].contentWindow.location=$(this).data("src")})}).fail(function(xhr,err,txt){if(xhr.responseJSON){var errors=xhr.responseJSON;var fieldErrors=errors.field;var kwargsErrors=errors.kwargs;var widget_kwargs=errors.widget_kwargs;var smart=errors.smart;for(const property in fieldErrors){$(`#id_field-${property}`).before(`
${fieldErrors[property]}
`)}}else{console.log(11111,xhr);console.log(11111,err);console.log(11111,txt)}})}$("input[type=checkbox],input[type=radio]").on("click",function(){update()});$("select").on("change",function(){update()});$("input,textarea").on("keyup",function(){if($("#autoRefresh").is(":checked")){clearTimeout($.data(this,"timer"));var wait=setTimeout(update,500);$(this).data("timer",wait)}});$("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");if(editor){editor.refresh();editor.on("change",function(){editor.save()})}});$("#refresh").on("click",function(e){e.preventDefault();$("iframe").each(function(){$(this)[0].contentWindow.location=$(this).data("src")});return false});$("#event_selector").on("change",function(){let sel=$(this).val();$("#events .code").removeClass("selected");$(`#event_${sel}`).addClass("selected").find("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");if(editor)editor.refresh()})});$(".submit-row input[type=radio]").on("click",function(e){var targetClass=$(this).data("target");$("iframe").hide();var $target=$("iframe."+targetClass);$(this).is(":checked")?$target.show():$target.hide()});$(".tabs th").on("click",function(){const targetName=$(this).data("target");$(`.tabs th`).removeClass("selected");$(`table.cfg-form`).addClass("collapsed");$(this).addClass("selected");$(`#${targetName}`).toggleClass("collapsed");$("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");editor.refresh()})});$(".submit-row input[data-target=display]").trigger("click");$("#tabForms th:first").trigger("click")})})(django.jQuery); diff --git a/src/aurora/core/static/admin/field_editor/field_editor.scss b/src/aurora/core/static/admin/field_editor/field_editor.scss index a9c43475..504af36f 100644 --- a/src/aurora/core/static/admin/field_editor/field_editor.scss +++ b/src/aurora/core/static/admin/field_editor/field_editor.scss @@ -56,6 +56,19 @@ body { width: 60%; background: var(--breadcrumbs-bg); + #event_selector { + option { + font-style: oblique; + font-weight: bold; + color: red; + &.used::after { + content: " --- "; + color: red; + + } + } + } + table:first-child { background: var(--breadcrumbs-bg); width: 100%; @@ -224,6 +237,7 @@ table2 { .code { display: none; } + .code.selected { display: block; } diff --git a/src/aurora/core/static/admin/form_editor/field_editor.css b/src/aurora/core/static/admin/form_editor/field_editor.css index f09173e8..b72c940f 100644 --- a/src/aurora/core/static/admin/form_editor/field_editor.css +++ b/src/aurora/core/static/admin/form_editor/field_editor.css @@ -35,7 +35,7 @@ body { #fieldEditor #mainPanel .output, #formEditor #mainPanel .output { position: sticky; top: 40px; - width: 40%; + width: 50%; } #fieldEditor #mainPanel .output iframe, #formEditor #mainPanel .output iframe { width: 100%; @@ -45,7 +45,7 @@ body { } #fieldEditor #mainPanel .settings, #formEditor #mainPanel .settings { overflow-y: scroll; - width: 60%; + width: 50%; background: var(--breadcrumbs-bg); } #fieldEditor #mainPanel .settings table:first-child, #formEditor #mainPanel .settings table:first-child { diff --git a/src/aurora/core/static/admin/form_editor/field_editor.scss b/src/aurora/core/static/admin/form_editor/field_editor.scss index a9c43475..94c60d08 100644 --- a/src/aurora/core/static/admin/form_editor/field_editor.scss +++ b/src/aurora/core/static/admin/form_editor/field_editor.scss @@ -41,7 +41,7 @@ body { .output { position: sticky; top: 40px; - width: 40%; + width: 50%; iframe { width: 100%; @@ -53,7 +53,7 @@ body { .settings { overflow-y: scroll; - width: 60%; + width: 50%; background: var(--breadcrumbs-bg); table:first-child { diff --git a/src/aurora/core/static/cm.js b/src/aurora/core/static/cm.js index 2760e479..9337a41b 100644 --- a/src/aurora/core/static/cm.js +++ b/src/aurora/core/static/cm.js @@ -14,8 +14,10 @@ cm.setOption("fullScreen", !cm.getOption("fullScreen")); cm.focus(); if (cm.getOption("fullScreen")) { + $("#fieldEditor .submit-row").hide(); $("#toggle-nav-sidebar").hide(); } else { + $("#fieldEditor .submit-row").show(); $("#toggle-nav-sidebar").show(); } }, diff --git a/src/aurora/core/static/cm.min.js b/src/aurora/core/static/cm.min.js index 79ae1e60..32ce2ee4 100644 --- a/src/aurora/core/static/cm.min.js +++ b/src/aurora/core/static/cm.min.js @@ -1 +1 @@ -(function($){window.cmToolBar={save:function(cm){$("form").submit()},action:function(cm,action){const expression="'use strict';const doc=cm.getDoc(); doc."+action+"()";eval(expression)},expand:function(cm){cm.focus();const $c=$(cm.getTextArea()).parents(".cm-container");$c.addClass("fieldEditor-fullscreen");cm.setOption("fullScreen",!cm.getOption("fullScreen"));cm.focus();if(cm.getOption("fullScreen")){$("#toggle-nav-sidebar").hide()}else{$("#toggle-nav-sidebar").show()}},run:function(cm){}}})(django.jQuery); +(function($){window.cmToolBar={save:function(cm){$("form").submit()},action:function(cm,action){const expression="'use strict';const doc=cm.getDoc(); doc."+action+"()";eval(expression)},expand:function(cm){cm.focus();const $c=$(cm.getTextArea()).parents(".cm-container");$c.addClass("fieldEditor-fullscreen");cm.setOption("fullScreen",!cm.getOption("fullScreen"));cm.focus();if(cm.getOption("fullScreen")){$("#fieldEditor .submit-row").hide();$("#toggle-nav-sidebar").hide()}else{$("#fieldEditor .submit-row").show();$("#toggle-nav-sidebar").show()}},run:function(cm){}}})(django.jQuery); diff --git a/src/aurora/core/static/cm.scss b/src/aurora/core/static/cm.scss new file mode 100644 index 00000000..44677c66 --- /dev/null +++ b/src/aurora/core/static/cm.scss @@ -0,0 +1,3 @@ +.cm-trailingspace { + background-color: red; +} diff --git a/src/aurora/core/static/datetimepicker/air-datepicker.css b/src/aurora/core/static/datetimepicker/air-datepicker.css new file mode 100644 index 00000000..c02761d9 --- /dev/null +++ b/src/aurora/core/static/datetimepicker/air-datepicker.css @@ -0,0 +1,7 @@ +.air-datepicker-cell.-year-.-other-decade-,.air-datepicker-cell.-day-.-other-month-{color:var(--adp-color-other-month)}.air-datepicker-cell.-year-.-other-decade-:hover,.air-datepicker-cell.-day-.-other-month-:hover{color:var(--adp-color-other-month-hover)}.-disabled-.-focus-.air-datepicker-cell.-year-.-other-decade-,.-disabled-.-focus-.air-datepicker-cell.-day-.-other-month-{color:var(--adp-color-other-month)}.-selected-.air-datepicker-cell.-year-.-other-decade-,.-selected-.air-datepicker-cell.-day-.-other-month-{color:#fff;background:var(--adp-background-color-selected-other-month)}.-selected-.-focus-.air-datepicker-cell.-year-.-other-decade-,.-selected-.-focus-.air-datepicker-cell.-day-.-other-month-{background:var(--adp-background-color-selected-other-month-focused)}.-in-range-.air-datepicker-cell.-year-.-other-decade-,.-in-range-.air-datepicker-cell.-day-.-other-month-{background-color:var(--adp-background-color-in-range);color:var(--adp-color)}.-in-range-.-focus-.air-datepicker-cell.-year-.-other-decade-,.-in-range-.-focus-.air-datepicker-cell.-day-.-other-month-{background-color:var(--adp-background-color-in-range-focused)}.air-datepicker-cell.-year-.-other-decade-:empty,.air-datepicker-cell.-day-.-other-month-:empty{background:none;border:none}.air-datepicker-cell{border-radius:var(--adp-cell-border-radius);box-sizing:border-box;cursor:pointer;display:flex;position:relative;align-items:center;justify-content:center;z-index:1}.air-datepicker-cell.-focus-{background:var(--adp-cell-background-color-hover)}.air-datepicker-cell.-current-{color:var(--adp-color-current-date)}.air-datepicker-cell.-current-.-focus-{color:var(--adp-color)}.air-datepicker-cell.-current-.-in-range-{color:var(--adp-color-current-date)}.air-datepicker-cell.-disabled-{cursor:default;color:var(--adp-color-disabled)}.air-datepicker-cell.-disabled-.-focus-{color:var(--adp-color-disabled)}.air-datepicker-cell.-disabled-.-in-range-{color:var(--adp-color-disabled-in-range)}.air-datepicker-cell.-disabled-.-current-.-focus-{color:var(--adp-color-disabled)}.air-datepicker-cell.-in-range-{background:var(--adp-cell-background-color-in-range);border-radius:0}.air-datepicker-cell.-in-range-:hover{background:var(--adp-cell-background-color-in-range-hover)}.air-datepicker-cell.-range-from-{border:1px solid var(--adp-cell-border-color-in-range);background-color:var(--adp-cell-background-color-in-range);border-radius:var(--adp-cell-border-radius) 0 0 var(--adp-cell-border-radius)}.air-datepicker-cell.-range-to-{border:1px solid var(--adp-cell-border-color-in-range);background-color:var(--adp-cell-background-color-in-range);border-radius:0 var(--adp-cell-border-radius) var(--adp-cell-border-radius) 0}.air-datepicker-cell.-range-to-.-range-from-{border-radius:var(--adp-cell-border-radius)}.air-datepicker-cell.-selected-{color:#fff;border:none;background:var(--adp-cell-background-color-selected)}.air-datepicker-cell.-selected-.-current-{color:#fff;background:var(--adp-cell-background-color-selected)}.air-datepicker-cell.-selected-.-focus-{background:var(--adp-cell-background-color-selected-hover)} +.air-datepicker-body{transition:all var(--adp-transition-duration) var(--adp-transition-ease)}.air-datepicker-body.-hidden-{display:none}.air-datepicker-body--day-names{display:grid;grid-template-columns:repeat(7, var(--adp-day-cell-width));margin:8px 0 3px}.air-datepicker-body--day-name{color:var(--adp-day-name-color);display:flex;align-items:center;justify-content:center;flex:1;text-align:center;text-transform:uppercase;font-size:.8em}.air-datepicker-body--day-name.-clickable-{cursor:pointer}.air-datepicker-body--day-name.-clickable-:hover{color:var(--adp-day-name-color-hover)}.air-datepicker-body--cells{display:grid}.air-datepicker-body--cells.-days-{grid-template-columns:repeat(7, var(--adp-day-cell-width));grid-auto-rows:var(--adp-day-cell-height)}.air-datepicker-body--cells.-months-{grid-template-columns:repeat(3, 1fr);grid-auto-rows:var(--adp-month-cell-height)}.air-datepicker-body--cells.-years-{grid-template-columns:repeat(4, 1fr);grid-auto-rows:var(--adp-year-cell-height)} +.air-datepicker-nav{display:flex;justify-content:space-between;border-bottom:1px solid var(--adp-border-color-inner);min-height:var(--adp-nav-height);padding:var(--adp-padding);box-sizing:content-box}.-only-timepicker- .air-datepicker-nav{display:none}.air-datepicker-nav--title,.air-datepicker-nav--action{display:flex;cursor:pointer;align-items:center;justify-content:center}.air-datepicker-nav--action{width:var(--adp-nav-action-size);border-radius:var(--adp-border-radius);-webkit-user-select:none;-moz-user-select:none;user-select:none}.air-datepicker-nav--action:hover{background:var(--adp-background-color-hover)}.air-datepicker-nav--action:active{background:var(--adp-background-color-active)}.air-datepicker-nav--action.-disabled-{visibility:hidden}.air-datepicker-nav--action svg{width:32px;height:32px}.air-datepicker-nav--action path{fill:none;stroke:var(--adp-nav-arrow-color);stroke-width:2px}.air-datepicker-nav--title{border-radius:var(--adp-border-radius);padding:0 8px}.air-datepicker-nav--title i{font-style:normal;color:var(--adp-nav-color-secondary);margin-left:.3em}.air-datepicker-nav--title:hover{background:var(--adp-background-color-hover)}.air-datepicker-nav--title:active{background:var(--adp-background-color-active)}.air-datepicker-nav--title.-disabled-{cursor:default;background:none} +.air-datepicker-buttons{display:grid;grid-auto-columns:1fr;grid-auto-flow:column}.air-datepicker-button{display:inline-flex;color:var(--adp-btn-color);border-radius:var(--adp-btn-border-radius);cursor:pointer;height:var(--adp-btn-height);border:none;background:rgba(255,255,255,0)}.air-datepicker-button:hover{color:var(--adp-btn-color-hover);background:var(--adp-btn-background-color-hover)}.air-datepicker-button:focus{color:var(--adp-btn-color-hover);background:var(--adp-btn-background-color-hover);outline:none}.air-datepicker-button:active{background:var(--adp-btn-background-color-active)}.air-datepicker-button span{outline:none;display:flex;align-items:center;justify-content:center;width:100%;height:100%} +.air-datepicker-time{display:grid;grid-template-columns:max-content 1fr;grid-column-gap:12px;align-items:center;position:relative;padding:0 var(--adp-time-padding-inner)}.-only-timepicker- .air-datepicker-time{border-top:none}.air-datepicker-time--current{display:flex;align-items:center;flex:1;font-size:14px;text-align:center}.air-datepicker-time--current-colon{margin:0 2px 3px;line-height:1}.air-datepicker-time--current-hours,.air-datepicker-time--current-minutes{line-height:1;font-size:19px;font-family:"Century Gothic",CenturyGothic,AppleGothic,sans-serif;position:relative;z-index:1}.air-datepicker-time--current-hours:after,.air-datepicker-time--current-minutes:after{content:"";background:var(--adp-background-color-hover);border-radius:var(--adp-border-radius);position:absolute;left:-2px;top:-3px;right:-2px;bottom:-2px;z-index:-1;opacity:0}.air-datepicker-time--current-hours.-focus-:after,.air-datepicker-time--current-minutes.-focus-:after{opacity:1}.air-datepicker-time--current-ampm{text-transform:uppercase;align-self:flex-end;color:var(--adp-time-day-period-color);margin-left:6px;font-size:11px;margin-bottom:1px}.air-datepicker-time--row{display:flex;align-items:center;font-size:11px;height:17px;background:linear-gradient(to right, var(--adp-time-track-color), var(--adp-time-track-color)) left 50%/100% var(--adp-time-track-height) no-repeat}.air-datepicker-time--row:first-child{margin-bottom:4px}.air-datepicker-time--row input[type=range]{background:none;cursor:pointer;flex:1;height:100%;width:100%;padding:0;margin:0;-webkit-appearance:none}.air-datepicker-time--row input[type=range]::-webkit-slider-thumb{-webkit-appearance:none}.air-datepicker-time--row input[type=range]::-ms-tooltip{display:none}.air-datepicker-time--row input[type=range]:hover::-webkit-slider-thumb{border-color:var(--adp-time-track-color-hover)}.air-datepicker-time--row input[type=range]:hover::-moz-range-thumb{border-color:var(--adp-time-track-color-hover)}.air-datepicker-time--row input[type=range]:hover::-ms-thumb{border-color:var(--adp-time-track-color-hover)}.air-datepicker-time--row input[type=range]:focus{outline:none}.air-datepicker-time--row input[type=range]:focus::-webkit-slider-thumb{background:var(--adp-cell-background-color-selected);border-color:var(--adp-cell-background-color-selected)}.air-datepicker-time--row input[type=range]:focus::-moz-range-thumb{background:var(--adp-cell-background-color-selected);border-color:var(--adp-cell-background-color-selected)}.air-datepicker-time--row input[type=range]:focus::-ms-thumb{background:var(--adp-cell-background-color-selected);border-color:var(--adp-cell-background-color-selected)}.air-datepicker-time--row input[type=range]::-webkit-slider-thumb{box-sizing:border-box;height:12px;width:12px;border-radius:3px;border:1px solid var(--adp-time-track-color);background:#fff;cursor:pointer;-webkit-transition:background var(--adp-transition-duration);transition:background var(--adp-transition-duration)}.air-datepicker-time--row input[type=range]::-moz-range-thumb{box-sizing:border-box;height:12px;width:12px;border-radius:3px;border:1px solid var(--adp-time-track-color);background:#fff;cursor:pointer;-moz-transition:background var(--adp-transition-duration);transition:background var(--adp-transition-duration)}.air-datepicker-time--row input[type=range]::-ms-thumb{box-sizing:border-box;height:12px;width:12px;border-radius:3px;border:1px solid var(--adp-time-track-color);background:#fff;cursor:pointer;-ms-transition:background var(--adp-transition-duration);transition:background var(--adp-transition-duration)}.air-datepicker-time--row input[type=range]::-webkit-slider-thumb{margin-top:calc(var(--adp-time-thumb-size)/2*-1)}.air-datepicker-time--row input[type=range]::-webkit-slider-runnable-track{border:none;height:var(--adp-time-track-height);cursor:pointer;color:rgba(0,0,0,0);background:rgba(0,0,0,0)}.air-datepicker-time--row input[type=range]::-moz-range-track{border:none;height:var(--adp-time-track-height);cursor:pointer;color:rgba(0,0,0,0);background:rgba(0,0,0,0)}.air-datepicker-time--row input[type=range]::-ms-track{border:none;height:var(--adp-time-track-height);cursor:pointer;color:rgba(0,0,0,0);background:rgba(0,0,0,0)}.air-datepicker-time--row input[type=range]::-ms-fill-lower{background:rgba(0,0,0,0)}.air-datepicker-time--row input[type=range]::-ms-fill-upper{background:rgba(0,0,0,0)} +.air-datepicker{--adp-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";--adp-font-size: 14px;--adp-width: 246px;--adp-z-index: 100;--adp-padding: 4px;--adp-grid-areas: "nav" "body" "timepicker" "buttons";--adp-transition-duration: .3s;--adp-transition-ease: ease-out;--adp-transition-offset: 8px;--adp-background-color: #fff;--adp-background-color-hover: #f0f0f0;--adp-background-color-active: #eaeaea;--adp-background-color-in-range: rgba(92, 196, 239, .1);--adp-background-color-in-range-focused: rgba(92, 196, 239, .2);--adp-background-color-selected-other-month-focused: #8ad5f4;--adp-background-color-selected-other-month: #a2ddf6;--adp-color: #4a4a4a;--adp-color-secondary: #9c9c9c;--adp-accent-color: #4eb5e6;--adp-color-current-date: var(--adp-accent-color);--adp-color-other-month: #dedede;--adp-color-disabled: #aeaeae;--adp-color-disabled-in-range: #939393;--adp-color-other-month-hover: #c5c5c5;--adp-border-color: #dbdbdb;--adp-border-color-inner: #efefef;--adp-border-radius: 4px;--adp-border-color-inline: #d7d7d7;--adp-nav-height: 32px;--adp-nav-arrow-color: var(--adp-color-secondary);--adp-nav-action-size: 32px;--adp-nav-color-secondary: var(--adp-color-secondary);--adp-day-name-color: #ff9a19;--adp-day-name-color-hover: #8ad5f4;--adp-day-cell-width: 1fr;--adp-day-cell-height: 32px;--adp-month-cell-height: 42px;--adp-year-cell-height: 56px;--adp-pointer-size: 10px;--adp-poiner-border-radius: 2px;--adp-pointer-offset: 14px;--adp-cell-border-radius: 4px;--adp-cell-background-color-hover: var(--adp-background-color-hover);--adp-cell-background-color-selected: #5cc4ef;--adp-cell-background-color-selected-hover: #45bced;--adp-cell-background-color-in-range: rgba(92, 196, 239, 0.1);--adp-cell-background-color-in-range-hover: rgba(92, 196, 239, 0.2);--adp-cell-border-color-in-range: var(--adp-cell-background-color-selected);--adp-btn-height: 32px;--adp-btn-color: var(--adp-accent-color);--adp-btn-color-hover: var(--adp-color);--adp-btn-border-radius: var(--adp-border-radius);--adp-btn-background-color-hover: var(--adp-background-color-hover);--adp-btn-background-color-active: var(--adp-background-color-active);--adp-time-track-height: 1px;--adp-time-track-color: #dedede;--adp-time-track-color-hover: #b1b1b1;--adp-time-thumb-size: 12px;--adp-time-padding-inner: 10px;--adp-time-day-period-color: var(--adp-color-secondary);--adp-mobile-font-size: 16px;--adp-mobile-nav-height: 40px;--adp-mobile-width: 320px;--adp-mobile-day-cell-height: 38px;--adp-mobile-month-cell-height: 48px;--adp-mobile-year-cell-height: 64px}.air-datepicker-overlay{--adp-overlay-background-color: rgba(0, 0, 0, .3);--adp-overlay-transition-duration: .3s;--adp-overlay-transition-ease: ease-out;--adp-overlay-z-index: 99} +.air-datepicker{background:var(--adp-background-color);border:1px solid var(--adp-border-color);box-shadow:0 4px 12px rgba(0,0,0,.15);border-radius:var(--adp-border-radius);box-sizing:content-box;display:grid;grid-template-columns:1fr;grid-template-rows:repeat(4, max-content);grid-template-areas:var(--adp-grid-areas);font-family:var(--adp-font-family),sans-serif;font-size:var(--adp-font-size);color:var(--adp-color);width:var(--adp-width);position:absolute;transition:opacity var(--adp-transition-duration) var(--adp-transition-ease),transform var(--adp-transition-duration) var(--adp-transition-ease);z-index:var(--adp-z-index)}.air-datepicker:not(.-custom-position-){opacity:0}.air-datepicker.-from-top-{transform:translateY(calc(var(--adp-transition-offset) * -1))}.air-datepicker.-from-right-{transform:translateX(var(--adp-transition-offset))}.air-datepicker.-from-bottom-{transform:translateY(var(--adp-transition-offset))}.air-datepicker.-from-left-{transform:translateX(calc(var(--adp-transition-offset) * -1))}.air-datepicker.-active-:not(.-custom-position-){transform:translate(0, 0);opacity:1}.air-datepicker.-active-.-custom-position-{transition:none}.air-datepicker.-inline-{border-color:var(--adp-border-color-inline);box-shadow:none;position:static;left:auto;right:auto;opacity:1;transform:none}.air-datepicker.-inline- .air-datepicker--pointer{display:none}.air-datepicker.-is-mobile-{--adp-font-size: var(--adp-mobile-font-size);--adp-day-cell-height: var(--adp-mobile-day-cell-height);--adp-month-cell-height: var(--adp-mobile-month-cell-height);--adp-year-cell-height: var(--adp-mobile-year-cell-height);--adp-nav-height: var(--adp-mobile-nav-height);--adp-nav-action-size: var(--adp-mobile-nav-height);position:fixed;width:var(--adp-mobile-width);border:none}.air-datepicker.-is-mobile- *{-webkit-tap-highlight-color:rgba(0,0,0,0)}.air-datepicker.-is-mobile- .air-datepicker--pointer{display:none}.air-datepicker.-is-mobile-:not(.-custom-position-){transform:translate(-50%, calc(-50% + var(--adp-transition-offset)))}.air-datepicker.-is-mobile-.-active-:not(.-custom-position-){transform:translate(-50%, -50%)}.air-datepicker.-custom-position-{transition:none}.air-datepicker-global-container{position:absolute;left:0;top:0}.air-datepicker--pointer{--pointer-half-size: calc(var(--adp-pointer-size) / 2);position:absolute;width:var(--adp-pointer-size);height:var(--adp-pointer-size);z-index:-1}.air-datepicker--pointer:after{content:"";position:absolute;background:#fff;border-top:1px solid var(--adp-border-color-inline);border-right:1px solid var(--adp-border-color-inline);border-top-right-radius:var(--adp-poiner-border-radius);width:var(--adp-pointer-size);height:var(--adp-pointer-size);box-sizing:border-box}.-top-left- .air-datepicker--pointer,.-top-center- .air-datepicker--pointer,.-top-right- .air-datepicker--pointer,[data-popper-placement^=top] .air-datepicker--pointer{top:calc(100% - var(--pointer-half-size) + 1px)}.-top-left- .air-datepicker--pointer:after,.-top-center- .air-datepicker--pointer:after,.-top-right- .air-datepicker--pointer:after,[data-popper-placement^=top] .air-datepicker--pointer:after{transform:rotate(135deg)}.-right-top- .air-datepicker--pointer,.-right-center- .air-datepicker--pointer,.-right-bottom- .air-datepicker--pointer,[data-popper-placement^=right] .air-datepicker--pointer{right:calc(100% - var(--pointer-half-size) + 1px)}.-right-top- .air-datepicker--pointer:after,.-right-center- .air-datepicker--pointer:after,.-right-bottom- .air-datepicker--pointer:after,[data-popper-placement^=right] .air-datepicker--pointer:after{transform:rotate(225deg)}.-bottom-left- .air-datepicker--pointer,.-bottom-center- .air-datepicker--pointer,.-bottom-right- .air-datepicker--pointer,[data-popper-placement^=bottom] .air-datepicker--pointer{bottom:calc(100% - var(--pointer-half-size) + 1px)}.-bottom-left- .air-datepicker--pointer:after,.-bottom-center- .air-datepicker--pointer:after,.-bottom-right- .air-datepicker--pointer:after,[data-popper-placement^=bottom] .air-datepicker--pointer:after{transform:rotate(315deg)}.-left-top- .air-datepicker--pointer,.-left-center- .air-datepicker--pointer,.-left-bottom- .air-datepicker--pointer,[data-popper-placement^=left] .air-datepicker--pointer{left:calc(100% - var(--pointer-half-size) + 1px)}.-left-top- .air-datepicker--pointer:after,.-left-center- .air-datepicker--pointer:after,.-left-bottom- .air-datepicker--pointer:after,[data-popper-placement^=left] .air-datepicker--pointer:after{transform:rotate(45deg)}.-top-left- .air-datepicker--pointer,.-bottom-left- .air-datepicker--pointer{left:var(--adp-pointer-offset)}.-top-right- .air-datepicker--pointer,.-bottom-right- .air-datepicker--pointer{right:var(--adp-pointer-offset)}.-top-center- .air-datepicker--pointer,.-bottom-center- .air-datepicker--pointer{left:calc(50% - var(--adp-pointer-size)/2)}.-left-top- .air-datepicker--pointer,.-right-top- .air-datepicker--pointer{top:var(--adp-pointer-offset)}.-left-bottom- .air-datepicker--pointer,.-right-bottom- .air-datepicker--pointer{bottom:var(--adp-pointer-offset)}.-left-center- .air-datepicker--pointer,.-right-center- .air-datepicker--pointer{top:calc(50% - var(--adp-pointer-size)/2)}.air-datepicker--navigation{grid-area:nav}.air-datepicker--content{box-sizing:content-box;padding:var(--adp-padding);grid-area:body}.-only-timepicker- .air-datepicker--content{display:none}.air-datepicker--time{grid-area:timepicker}.air-datepicker--buttons{grid-area:buttons}.air-datepicker--buttons,.air-datepicker--time{padding:var(--adp-padding);border-top:1px solid var(--adp-border-color-inner)}.air-datepicker-overlay{position:fixed;background:var(--adp-overlay-background-color);left:0;top:0;width:0;height:0;opacity:0;transition:opacity var(--adp-overlay-transition-duration) var(--adp-overlay-transition-ease),left 0s,height 0s,width 0s;transition-delay:0s,var(--adp-overlay-transition-duration),var(--adp-overlay-transition-duration),var(--adp-overlay-transition-duration);z-index:var(--adp-overlay-z-index)}.air-datepicker-overlay.-active-{opacity:1;width:100%;height:100%;transition:opacity var(--adp-overlay-transition-duration) var(--adp-overlay-transition-ease),height 0s,width 0s} diff --git a/src/aurora/core/static/datetimepicker/air-datepicker.js b/src/aurora/core/static/datetimepicker/air-datepicker.js new file mode 100644 index 00000000..184a7033 --- /dev/null +++ b/src/aurora/core/static/datetimepicker/air-datepicker.js @@ -0,0 +1,6 @@ +/** + * https://air-datepicker.com/ + * https://github.com/t1m0n/air-datepicker + * + * */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.AirDatepicker=t():e.AirDatepicker=t()}(this,(function(){return function(){"use strict";var e={d:function(t,i){for(var s in i)e.o(i,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:i[s]})},o:function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}},t={};e.d(t,{default:function(){return K}});var i={days:"days",months:"months",years:"years",day:"day",month:"month",year:"year",eventChangeViewDate:"changeViewDate",eventChangeCurrentView:"changeCurrentView",eventChangeFocusDate:"changeFocusDate",eventChangeSelectedDate:"changeSelectedDate",eventChangeTime:"changeTime",eventChangeLastSelectedDate:"changeLastSelectedDate",actionSelectDate:"selectDate",actionUnselectDate:"unselectDate",cssClassWeekend:"-weekend-"},s={classes:"",inline:!1,locale:{days:["Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница","Суббота"],daysShort:["Вос","Пон","Вто","Сре","Чет","Пят","Суб"],daysMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthsShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],today:"Сегодня",clear:"Очистить",dateFormat:"dd.MM.yyyy",timeFormat:"HH:mm",firstDay:1},startDate:new Date,firstDay:"",weekends:[6,0],dateFormat:"",altField:"",altFieldDateFormat:"T",toggleSelected:!0,keyboardNav:!0,selectedDates:!1,container:"",isMobile:!1,visible:!1,position:"bottom left",offset:12,view:i.days,minView:i.days,showOtherMonths:!0,selectOtherMonths:!0,moveToOtherMonthsOnSelect:!0,showOtherYears:!0,selectOtherYears:!0,moveToOtherYearsOnSelect:!0,minDate:"",maxDate:"",disableNavWhenOutOfRange:!0,multipleDates:!1,multipleDatesSeparator:", ",range:!1,dynamicRange:!0,buttons:!1,monthsField:"monthsShort",showEvent:"focus",autoClose:!1,prevHtml:'',nextHtml:'',navTitles:{days:"MMMM, yyyy",months:"yyyy",years:"yyyy1 - yyyy2"},timepicker:!1,onlyTimepicker:!1,dateTimeSeparator:" ",timeFormat:"",minHours:0,maxHours:24,minMinutes:0,maxMinutes:59,hoursStep:1,minutesStep:1,onSelect:!1,onChangeViewDate:!1,onChangeView:!1,onRenderCell:!1,onShow:!1,onHide:!1,onClickDayName:!1};function a(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:document;return"string"==typeof e?t.querySelector(e):e}function n(){let{tagName:e="div",className:t="",innerHtml:i="",id:s="",attrs:a={}}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=document.createElement(e);return t&&n.classList.add(...t.split(" ")),s&&(n.id=s),i&&(n.innerHTML=i),a&&r(n,a),n}function r(e,t){for(let[i,s]of Object.entries(t))void 0!==s&&e.setAttribute(i,s);return e}function h(e){return new Date(e.getFullYear(),e.getMonth()+1,0).getDate()}function o(e){let t=e.getHours(),{hours:i,dayPeriod:s}=l(t);return{year:e.getFullYear(),month:e.getMonth(),fullMonth:e.getMonth()+1<10?"0"+(e.getMonth()+1):e.getMonth()+1,date:e.getDate(),fullDate:e.getDate()<10?"0"+e.getDate():e.getDate(),day:e.getDay(),hours:t,fullHours:d(t),hours12:i,dayPeriod:s,fullHours12:d(i),minutes:e.getMinutes(),fullMinutes:e.getMinutes()<10?"0"+e.getMinutes():e.getMinutes()}}function l(e){return{dayPeriod:e>11?"pm":"am",hours:e%12==0?12:e%12}}function d(e){return e<10?"0"+e:e}function c(e){let t=10*Math.floor(e.getFullYear()/10);return[t,t+9]}function u(){let e=[];for(var t=arguments.length,i=new Array(t),s=0;s{if("object"==typeof t)for(let i in t)t[i]&&e.push(i);else t&&e.push(t)})),e.join(" ")}function p(e,t){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:i.days;if(!e||!t)return!1;let a=o(e),n=o(t),r={[i.days]:a.date===n.date&&a.month===n.month&&a.year===n.year,[i.months]:a.month===n.month&&a.year===n.year,[i.years]:a.year===n.year};return r[s]}function m(e,t,i){let s=g(e,!1).getTime(),a=g(t,!1).getTime();return i?s>=a:s>a}function v(e,t){return!m(e,t,!0)}function g(e){let t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=new Date(e.getTime());return"boolean"!=typeof t||t||D(i),i}function D(e){return e.setHours(0,0,0,0),e}function y(e,t,i){e.length?e.forEach((e=>{e.addEventListener(t,i)})):e.addEventListener(t,i)}function f(e,t){return!(!e||e===document||e instanceof DocumentFragment)&&(e.matches(t)?e:f(e.parentNode,t))}function w(e,t,i){return e>i?i:e1?t-1:0),s=1;se)).forEach((t=>{for(let[i,s]of Object.entries(t))if(void 0!==s&&"[object Object]"===s.toString()){let t=void 0!==e[i]?e[i].toString():void 0,a=s.toString(),n=Array.isArray(s)?[]:{};e[i]=e[i]?t!==a?n:e[i]:n,b(e[i],s)}else e[i]=s})),e}function k(e){let t=e;return e instanceof Date||(t=new Date(e)),isNaN(t.getTime())&&(console.log(`Unable to convert value "${e}" to Date object`),t=!1),t}function C(e){let t="\\s|\\.|-|/|\\\\|,|\\$|\\!|\\?|:|;";return new RegExp("(^|>|"+t+")("+e+")($|<|"+t+")","g")}function $(e,t,i){return(t=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var i=e[Symbol.toPrimitive];if(void 0!==i){var s=i.call(e,t);if("object"!=typeof s)return s;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e,"string");return"symbol"==typeof t?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}class _{constructor(){let{type:e,date:t,dp:i,opts:s,body:a}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};$(this,"focus",(()=>{this.$cell.classList.add("-focus-"),this.focused=!0})),$(this,"removeFocus",(()=>{this.$cell.classList.remove("-focus-"),this.focused=!1})),$(this,"select",(()=>{this.$cell.classList.add("-selected-"),this.selected=!0})),$(this,"removeSelect",(()=>{this.$cell.classList.remove("-selected-","-range-from-","-range-to-"),this.selected=!1})),$(this,"onChangeSelectedDate",(()=>{this.isDisabled||(this._handleSelectedStatus(),this.opts.range&&this._handleRangeStatus())})),$(this,"onChangeFocusDate",(e=>{if(!e)return void(this.focused&&this.removeFocus());let t=p(e,this.date,this.type);t?this.focus():!t&&this.focused&&this.removeFocus(),this.opts.range&&this._handleRangeStatus()})),$(this,"render",(()=>(this.$cell.innerHTML=this._getHtml(),this.$cell.adpCell=this,this.$cell))),this.type=e,this.singleType=this.type.slice(0,-1),this.date=t,this.dp=i,this.opts=s,this.body=a,this.customData=!1,this.init()}init(){let{range:e,onRenderCell:t}=this.opts;t&&(this.customData=t({date:this.date,cellType:this.singleType,datepicker:this.dp})),this._createElement(),this._bindDatepickerEvents(),this._handleInitialFocusStatus(),this.dp.hasSelectedDates&&(this._handleSelectedStatus(),e&&this._handleRangeStatus())}_bindDatepickerEvents(){this.dp.on(i.eventChangeSelectedDate,this.onChangeSelectedDate),this.dp.on(i.eventChangeFocusDate,this.onChangeFocusDate)}unbindDatepickerEvents(){this.dp.off(i.eventChangeSelectedDate,this.onChangeSelectedDate),this.dp.off(i.eventChangeFocusDate,this.onChangeFocusDate)}_createElement(){var e;let{year:t,month:i,date:s}=o(this.date),a=(null===(e=this.customData)||void 0===e?void 0:e.attrs)||{};this.$cell=n({className:this._getClassName(),attrs:{"data-year":t,"data-month":i,"data-date":s,...a}})}_getClassName(){var e,t;let s=new Date,{selectOtherMonths:a,selectOtherYears:n}=this.opts,{minDate:r,maxDate:h}=this.dp,{day:l}=o(this.date),d=this._isOutOfMinMaxRange(),c=null===(e=this.customData)||void 0===e?void 0:e.disabled,m=u("air-datepicker-cell",`-${this.singleType}-`,{"-current-":p(s,this.date,this.type),"-min-date-":r&&p(r,this.date,this.type),"-max-date-":h&&p(h,this.date,this.type)}),v="";switch(this.type){case i.days:v=u({"-weekend-":this.dp.isWeekend(l),"-other-month-":this.isOtherMonth,"-disabled-":this.isOtherMonth&&!a||d||c});break;case i.months:v=u({"-disabled-":d||c});break;case i.years:v=u({"-other-decade-":this.isOtherDecade,"-disabled-":d||this.isOtherDecade&&!n||c})}return u(m,v,null===(t=this.customData)||void 0===t?void 0:t.classes)}_getHtml(){var e;let{year:t,month:s,date:a}=o(this.date),{showOtherMonths:n,showOtherYears:r}=this.opts;if(null!==(e=this.customData)&&void 0!==e&&e.html)return this.customData.html;switch(this.type){case i.days:return!n&&this.isOtherMonth?"":a;case i.months:return this.dp.locale[this.opts.monthsField][s];case i.years:return!r&&this.isOtherDecade?"":t}}_isOutOfMinMaxRange(){let{minDate:e,maxDate:t}=this.dp,{type:s,date:a}=this,{month:n,year:r,date:h}=o(a),l=s===i.days,d=s===i.years,c=!!e&&new Date(r,d?e.getMonth():n,l?h:e.getDate()),u=!!t&&new Date(r,d?t.getMonth():n,l?h:t.getDate());return e&&t?v(c,e)||m(u,t):e?v(c,e):t?m(u,t):void 0}destroy(){this.unbindDatepickerEvents()}_handleRangeStatus(){let{rangeDateFrom:e,rangeDateTo:t}=this.dp,i=u({"-in-range-":e&&t&&(s=this.date,a=e,n=t,m(s,a)&&v(s,n)),"-range-from-":e&&p(this.date,e,this.type),"-range-to-":t&&p(this.date,t,this.type)});var s,a,n;this.$cell.classList.remove("-range-from-","-range-to-","-in-range-"),i&&this.$cell.classList.add(...i.split(" "))}_handleSelectedStatus(){let e=this.dp._checkIfDateIsSelected(this.date,this.type);e?this.select():!e&&this.selected&&this.removeSelect()}_handleInitialFocusStatus(){p(this.dp.focusDate,this.date,this.type)&&this.focus()}get isDisabled(){return this.$cell.matches(".-disabled-")}get isOtherMonth(){return this.dp.isOtherMonth(this.date)}get isOtherDecade(){return this.dp.isOtherDecade(this.date)}}function M(e,t,i){return(t=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var i=e[Symbol.toPrimitive];if(void 0!==i){var s=i.call(e,t);if("object"!=typeof s)return s;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e,"string");return"symbol"==typeof t?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}let S={[i.days]:`
`,[i.months]:`
`,[i.years]:`
`};const T=".air-datepicker-cell";class F{constructor(e){let{dp:t,type:s,opts:a}=e;M(this,"handleClick",(e=>{let t=e.target.closest(T).adpCell;if(t.isDisabled)return;if(!this.dp.isMinViewReached)return void this.dp.down();let i=this.dp._checkIfDateIsSelected(t.date,t.type);i?this.dp._handleAlreadySelectedDates(i,t.date):this.dp.selectDate(t.date)})),M(this,"handleDayNameClick",(e=>{let t=e.target.getAttribute("data-day-index");this.opts.onClickDayName({dayIndex:Number(t),datepicker:this.dp})})),M(this,"onChangeCurrentView",(e=>{e!==this.type?this.hide():(this.show(),this.render())})),M(this,"onMouseOverCell",(e=>{let t=f(e.target,T);this.dp.setFocusDate(!!t&&t.adpCell.date)})),M(this,"onMouseOutCell",(()=>{this.dp.setFocusDate(!1)})),M(this,"onClickBody",(e=>{let{onClickDayName:t}=this.opts,i=e.target;i.closest(T)&&this.handleClick(e),t&&i.closest(".air-datepicker-body--day-name")&&this.handleDayNameClick(e)})),M(this,"onMouseDown",(e=>{this.pressed=!0;let t=f(e.target,T),i=t&&t.adpCell;p(i.date,this.dp.rangeDateFrom)&&(this.rangeFromFocused=!0),p(i.date,this.dp.rangeDateTo)&&(this.rangeToFocused=!0)})),M(this,"onMouseMove",(e=>{if(!this.pressed||!this.dp.isMinViewReached)return;e.preventDefault();let t=f(e.target,T),i=t&&t.adpCell,{selectedDates:s,rangeDateTo:a,rangeDateFrom:n}=this.dp;if(!i||i.isDisabled)return;let{date:r}=i;if(2===s.length){if(this.rangeFromFocused&&!m(r,a)){let{hours:e,minutes:t}=o(n);r.setHours(e),r.setMinutes(t),this.dp.rangeDateFrom=r,this.dp.replaceDate(n,r)}if(this.rangeToFocused&&!v(r,n)){let{hours:e,minutes:t}=o(a);r.setHours(e),r.setMinutes(t),this.dp.rangeDateTo=r,this.dp.replaceDate(a,r)}}})),M(this,"onMouseUp",(()=>{this.pressed=!1,this.rangeFromFocused=!1,this.rangeToFocused=!1})),M(this,"onChangeViewDate",((e,t)=>{if(!this.isVisible)return;let s=c(e),a=c(t);switch(this.dp.currentView){case i.days:if(p(e,t,i.months))return;break;case i.months:if(p(e,t,i.years))return;break;case i.years:if(s[0]===a[0]&&s[1]===a[1])return}this.render()})),M(this,"render",(()=>{this.destroyCells(),this._generateCells(),this.cells.forEach((e=>{this.$cells.appendChild(e.render())}))})),this.dp=t,this.type=s,this.opts=a,this.cells=[],this.$el="",this.pressed=!1,this.isVisible=!0,this.init()}init(){this._buildBaseHtml(),this.type===i.days&&this.renderDayNames(),this.render(),this._bindEvents(),this._bindDatepickerEvents()}_bindEvents(){let{range:e,dynamicRange:t}=this.opts;y(this.$el,"mouseover",this.onMouseOverCell),y(this.$el,"mouseout",this.onMouseOutCell),y(this.$el,"click",this.onClickBody),e&&t&&(y(this.$el,"mousedown",this.onMouseDown),y(this.$el,"mousemove",this.onMouseMove),y(window.document,"mouseup",this.onMouseUp))}_bindDatepickerEvents(){this.dp.on(i.eventChangeViewDate,this.onChangeViewDate),this.dp.on(i.eventChangeCurrentView,this.onChangeCurrentView)}_buildBaseHtml(){this.$el=n({className:`air-datepicker-body -${this.type}-`,innerHtml:S[this.type]}),this.$names=a(".air-datepicker-body--day-names",this.$el),this.$cells=a(".air-datepicker-body--cells",this.$el)}_getDayNamesHtml(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.dp.locale.firstDay,t="",s=this.dp.isWeekend,{onClickDayName:a}=this.opts,n=e,r=0;for(;r<7;){let e=n%7;t+=`
${this.dp.locale.daysMin[e]}
`,r++,n++}return t}_getDaysCells(){let{viewDate:e,locale:{firstDay:t}}=this.dp,i=h(e),{year:s,month:a}=o(e),n=new Date(s,a,1),r=new Date(s,a,i),l=n.getDay()-t,d=6-r.getDay()+t;l=l<0?l+7:l,d=d>6?d-7:d;let c=function(e,t){let{year:i,month:s,date:a}=o(e);return new Date(i,s,a-t)}(n,l),u=i+l+d,p=c.getDate(),{year:m,month:v}=o(c),g=0;for(;ge.destroy())),this.cells=[],this.$cells.innerHTML=""}destroy(){this.destroyCells(),this.dp.off(i.eventChangeViewDate,this.onChangeViewDate),this.dp.off(i.eventChangeCurrentView,this.onChangeCurrentView)}}function V(e,t,i){return(t=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var i=e[Symbol.toPrimitive];if(void 0!==i){var s=i.call(e,t);if("object"!=typeof s)return s;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e,"string");return"symbol"==typeof t?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}class x{constructor(e){let{dp:t,opts:i}=e;V(this,"onClickNav",(e=>{let t=f(e.target,".air-datepicker-nav--action");if(!t)return;let i=t.dataset.action;this.dp[i]()})),V(this,"onChangeViewDate",(()=>{this.render(),this._resetNavStatus(),this.handleNavStatus()})),V(this,"onChangeCurrentView",(()=>{this.render(),this._resetNavStatus(),this.handleNavStatus()})),V(this,"onClickNavTitle",(()=>{this.dp.isFinalView||this.dp.up()})),V(this,"update",(()=>{let{prevHtml:e,nextHtml:t}=this.opts;this.$prev.innerHTML=e,this.$next.innerHTML=t,this._resetNavStatus(),this.render(),this.handleNavStatus()})),V(this,"renderDelay",(()=>{setTimeout(this.render)})),V(this,"render",(()=>{this.$title.innerHTML=this._getTitle(),function(e,t){for(let i in t)t[i]?e.classList.add(i):e.classList.remove(i)}(this.$title,{"-disabled-":this.dp.isFinalView})})),this.dp=t,this.opts=i,this.init()}init(){this._createElement(),this._buildBaseHtml(),this._defineDOM(),this.render(),this.handleNavStatus(),this._bindEvents(),this._bindDatepickerEvents()}_defineDOM(){this.$title=a(".air-datepicker-nav--title",this.$el),this.$prev=a('[data-action="prev"]',this.$el),this.$next=a('[data-action="next"]',this.$el)}_bindEvents(){this.$el.addEventListener("click",this.onClickNav),this.$title.addEventListener("click",this.onClickNavTitle)}_bindDatepickerEvents(){this.dp.on(i.eventChangeViewDate,this.onChangeViewDate),this.dp.on(i.eventChangeCurrentView,this.onChangeCurrentView),this.isNavIsFunction&&(this.dp.on(i.eventChangeSelectedDate,this.renderDelay),this.dp.opts.timepicker&&this.dp.on(i.eventChangeTime,this.render))}destroy(){this.dp.off(i.eventChangeViewDate,this.onChangeViewDate),this.dp.off(i.eventChangeCurrentView,this.onChangeCurrentView),this.isNavIsFunction&&(this.dp.off(i.eventChangeSelectedDate,this.renderDelay),this.dp.opts.timepicker&&this.dp.off(i.eventChangeTime,this.render))}_createElement(){this.$el=n({tagName:"nav",className:"air-datepicker-nav"})}_getTitle(){let{dp:e,opts:t}=this,i=t.navTitles[e.currentView];return"function"==typeof i?i(e):e.formatDate(e.viewDate,i)}handleNavStatus(){let{disableNavWhenOutOfRange:e}=this.opts,{minDate:t,maxDate:s}=this.dp;if(!t&&!s||!e)return;let{year:a,month:n}=this.dp.parsedViewDate,r=!!t&&o(t),h=!!s&&o(s);switch(this.dp.currentView){case i.days:t&&r.month>=n&&r.year>=a&&this._disableNav("prev"),s&&h.month<=n&&h.year<=a&&this._disableNav("next");break;case i.months:t&&r.year>=a&&this._disableNav("prev"),s&&h.year<=a&&this._disableNav("next");break;case i.years:{let e=c(this.dp.viewDate);t&&r.year>=e[0]&&this._disableNav("prev"),s&&h.year<=e[1]&&this._disableNav("next");break}}}_disableNav(e){a('[data-action="'+e+'"]',this.$el).classList.add("-disabled-")}_resetNavStatus(){!function(e){for(var t=arguments.length,i=new Array(t>1?t-1:0),s=1;s{e.classList.remove(...i)})):e.classList.remove(...i)}(this.$el.querySelectorAll(".air-datepicker-nav--action"),"-disabled-")}_buildBaseHtml(){let{prevHtml:e,nextHtml:t}=this.opts;this.$el.innerHTML=`
${e}
${t}
`}get isNavIsFunction(){let{navTitles:e}=this.opts;return Object.keys(e).find((t=>"function"==typeof e[t]))}}var H={today:{content:e=>e.locale.today,onClick:e=>e.setViewDate(new Date)},clear:{content:e=>e.locale.clear,onClick:e=>e.clear()}};class E{constructor(e){let{dp:t,opts:i}=e;this.dp=t,this.opts=i,this.init()}init(){this.createElement(),this.render()}createElement(){this.$el=n({className:"air-datepicker-buttons"})}destroy(){this.$el.parentNode.removeChild(this.$el)}clearHtml(){return this.$el.innerHTML="",this}generateButtons(){let{buttons:e}=this.opts;Array.isArray(e)||(e=[e]),e.forEach((e=>{let t=e;"string"==typeof e&&H[e]&&(t=H[e]);let i=this.createButton(t);t.onClick&&this.attachEventToButton(i,t.onClick),this.$el.appendChild(i)}))}attachEventToButton(e,t){e.addEventListener("click",(()=>{t(this.dp)}))}createButton(e){let{content:t,className:i,tagName:s="button",attrs:a={}}=e;return n({tagName:s,innerHtml:`${"function"==typeof t?t(this.dp):t}`,className:u("air-datepicker-button",i),attrs:a})}render(){this.generateButtons()}}function L(e,t,i){return(t=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var i=e[Symbol.toPrimitive];if(void 0!==i){var s=i.call(e,t);if("object"!=typeof s)return s;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e,"string");return"symbol"==typeof t?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}class O{constructor(){let{opts:e,dp:t}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};L(this,"toggleTimepickerIsActive",(e=>{this.dp.timepickerIsActive=e})),L(this,"onChangeSelectedDate",(e=>{let{date:t,updateTime:i=!1}=e;t&&(this.setMinMaxTime(t),this.setCurrentTime(!!i&&t),this.addTimeToDate(t))})),L(this,"onChangeLastSelectedDate",(e=>{e&&(this.setTime(e),this.render())})),L(this,"onChangeInputRange",(e=>{let t=e.target;this[t.getAttribute("name")]=t.value,this.updateText(),this.dp.trigger(i.eventChangeTime,{hours:this.hours,minutes:this.minutes})})),L(this,"onMouseEnterLeave",(e=>{let t=e.target.getAttribute("name"),i=this.$minutesText;"hours"===t&&(i=this.$hoursText),i.classList.toggle("-focus-")})),L(this,"onFocus",(()=>{this.toggleTimepickerIsActive(!0)})),L(this,"onBlur",(()=>{this.toggleTimepickerIsActive(!1)})),this.opts=e,this.dp=t;let{timeFormat:s}=this.dp.locale;s&&(s.match(C("h"))||s.match(C("hh")))&&(this.ampm=!0),this.init()}init(){this.setTime(this.dp.lastSelectedDate||this.dp.viewDate),this.createElement(),this.buildHtml(),this.defineDOM(),this.render(),this.bindDatepickerEvents(),this.bindDOMEvents()}bindDatepickerEvents(){this.dp.on(i.eventChangeSelectedDate,this.onChangeSelectedDate),this.dp.on(i.eventChangeLastSelectedDate,this.onChangeLastSelectedDate)}bindDOMEvents(){let e="input";navigator.userAgent.match(/trident/gi)&&(e="change"),y(this.$ranges,e,this.onChangeInputRange),y(this.$ranges,"mouseenter",this.onMouseEnterLeave),y(this.$ranges,"mouseleave",this.onMouseEnterLeave),y(this.$ranges,"focus",this.onFocus),y(this.$ranges,"mousedown",this.onFocus),y(this.$ranges,"blur",this.onBlur)}createElement(){this.$el=n({className:u("air-datepicker-time",{"-am-pm-":this.dp.ampm})})}destroy(){this.dp.off(i.eventChangeSelectedDate,this.onChangeSelectedDate),this.dp.off(i.eventChangeLastSelectedDate,this.onChangeLastSelectedDate),this.$el.parentNode.removeChild(this.$el)}buildHtml(){let{ampm:e,hours:t,displayHours:i,minutes:s,minHours:a,minMinutes:n,maxHours:r,maxMinutes:h,dayPeriod:o,opts:{hoursStep:l,minutesStep:c}}=this;this.$el.innerHTML=`
${d(i)} : ${d(s)} `+(e?`${o}`:"")+'
'+`
`+`
`}defineDOM(){let e=e=>a(e,this.$el);this.$ranges=this.$el.querySelectorAll('[type="range"]'),this.$hours=e('[name="hours"]'),this.$minutes=e('[name="minutes"]'),this.$hoursText=e(".air-datepicker-time--current-hours"),this.$minutesText=e(".air-datepicker-time--current-minutes"),this.$ampm=e(".air-datepicker-time--current-ampm")}setTime(e){this.setMinMaxTime(e),this.setCurrentTime(e)}addTimeToDate(e){e&&(e.setHours(this.hours),e.setMinutes(this.minutes))}setMinMaxTime(e){if(this.setMinMaxTimeFromOptions(),e){let{minDate:t,maxDate:i}=this.dp;t&&p(e,t)&&this.setMinTimeFromMinDate(t),i&&p(e,i)&&this.setMaxTimeFromMaxDate(i)}}setCurrentTime(e){let{hours:t,minutes:i}=e?o(e):this;this.hours=w(t,this.minHours,this.maxHours),this.minutes=w(i,this.minMinutes,this.maxMinutes)}setMinMaxTimeFromOptions(){let{minHours:e,minMinutes:t,maxHours:i,maxMinutes:s}=this.opts;this.minHours=w(e,0,23),this.minMinutes=w(t,0,59),this.maxHours=w(i,0,23),this.maxMinutes=w(s,0,59)}setMinTimeFromMinDate(e){let{lastSelectedDate:t}=this.dp;this.minHours=e.getHours(),t&&t.getHours()>e.getHours()?this.minMinutes=this.opts.minMinutes:this.minMinutes=e.getMinutes()}setMaxTimeFromMaxDate(e){let{lastSelectedDate:t}=this.dp;this.maxHours=e.getHours(),t&&t.getHours()e.month++],[[["Control","ArrowLeft"],["Control","ArrowDown"]],e=>e.month--],[[["Shift","ArrowRight"],["Shift","ArrowUp"]],e=>e.year++],[[["Shift","ArrowLeft"],["Shift","ArrowDown"]],e=>e.year--],[[["Alt","ArrowRight"],["Alt","ArrowUp"]],e=>e.year+=10],[[["Alt","ArrowLeft"],["Alt","ArrowDown"]],e=>e.year-=10],[["Control","Shift","ArrowUp"],(e,t)=>t.up()]])),A(this,"handleHotKey",(e=>{let t=this.hotKeys.get(e),i=o(this.getInitialFocusDate());t(i,this.dp);let{year:s,month:a,date:n}=i,r=h(new Date(s,a));r{let e=!1,t=this.pressedKeys.size,i=e=>this.pressedKeys.has(e);for(let[s]of this.hotKeys){if(e)break;if(Array.isArray(s[0]))s.forEach((a=>{e||t!==a.length||(e=a.every(i)&&s)}));else{if(t!==s.length)continue;e=s.every(i)&&s}}return e})),A(this,"isArrow",(e=>e>=37&&e<=40)),A(this,"onKeyDown",(e=>{let{key:t,which:i}=e,{dp:s,dp:{focusDate:a},opts:n}=this;this.registerKey(t);let r=this.isHotKeyPressed();if(r)return e.preventDefault(),void this.handleHotKey(r);if(this.isArrow(i))return e.preventDefault(),void this.focusNextCell(t);if("Enter"===t){if(s.currentView!==n.minView)return void s.down();if(a){let e=s._checkIfDateIsSelected(a);return void(e?s._handleAlreadySelectedDates(e,a):s.selectDate(a))}}"Escape"===t&&this.dp.hide()})),A(this,"onKeyUp",(e=>{this.removeKey(e.key)})),this.dp=t,this.opts=i,this.init()}init(){this.bindKeyboardEvents()}bindKeyboardEvents(){let{$el:e}=this.dp;e.addEventListener("keydown",this.onKeyDown),e.addEventListener("keyup",this.onKeyUp)}destroy(){let{$el:e}=this.dp;e.removeEventListener("keydown",this.onKeyDown),e.removeEventListener("keyup",this.onKeyUp),this.hotKeys=null,this.pressedKeys=null}getInitialFocusDate(){let{focusDate:e,currentView:t,selectedDates:s,parsedViewDate:{year:a,month:n}}=this.dp,r=e||s[s.length-1];if(!r)switch(t){case i.days:r=new Date(a,n,(new Date).getDate());break;case i.months:r=new Date(a,n,1);break;case i.years:r=new Date(a,0,1)}return r}focusNextCell(e){let t=this.getInitialFocusDate(),{currentView:s}=this.dp,{days:a,months:n,years:r}=i,h=o(t),l=h.year,d=h.month,c=h.date;switch(e){case"ArrowLeft":s===a&&(c-=1),s===n&&(d-=1),s===r&&(l-=1);break;case"ArrowUp":s===a&&(c-=7),s===n&&(d-=3),s===r&&(l-=4);break;case"ArrowRight":s===a&&(c+=1),s===n&&(d+=1),s===r&&(l+=1);break;case"ArrowDown":s===a&&(c+=7),s===n&&(d+=3),s===r&&(l+=4)}let u=this.dp.getClampedDate(new Date(l,d,c));this.dp.setFocusDate(u,{viewDateTransition:!0})}registerKey(e){this.pressedKeys.add(e)}removeKey(e){this.pressedKeys.delete(e)}}let I={on(e,t){this.__events||(this.__events={}),this.__events[e]?this.__events[e].push(t):this.__events[e]=[t]},off(e,t){this.__events&&this.__events[e]&&(this.__events[e]=this.__events[e].filter((e=>e!==t)))},removeAllEvents(){this.__events={}},trigger(e){for(var t=arguments.length,i=new Array(t>1?t-1:0),s=1;s{e(...i)}))}};function P(e,t,i){return(t=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var i=e[Symbol.toPrimitive];if(void 0!==i){var s=i.call(e,t);if("object"!=typeof s)return s;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e,"string");return"symbol"==typeof t?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}let j="",R="",B=!1;class K{static buildGlobalContainer(e){B=!0,j=n({className:e,id:e}),a("body").appendChild(j)}constructor(e,t){var r=this;if(P(this,"viewIndexes",[i.days,i.months,i.years]),P(this,"next",(()=>{let{year:e,month:t}=this.parsedViewDate;switch(this.currentView){case i.days:this.setViewDate(new Date(e,t+1,1));break;case i.months:this.setViewDate(new Date(e+1,t,1));break;case i.years:this.setViewDate(new Date(e+10,0,1))}})),P(this,"prev",(()=>{let{year:e,month:t}=this.parsedViewDate;switch(this.currentView){case i.days:this.setViewDate(new Date(e,t-1,1));break;case i.months:this.setViewDate(new Date(e-1,t,1));break;case i.years:this.setViewDate(new Date(e-10,0,1))}})),P(this,"_finishHide",(()=>{this.hideAnimation=!1,this._destroyComponents(),this.$container.removeChild(this.$datepicker)})),P(this,"setPosition",(function(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if("function"==typeof(e=e||r.opts.position))return void(r.customHide=e({$datepicker:r.$datepicker,$target:r.$el,$pointer:r.$pointer,isViewChange:t,done:r._finishHide}));let i,s,{isMobile:a}=r.opts,n=r.$el.getBoundingClientRect(),h=r.$el.getBoundingClientRect(),o=r.$datepicker.offsetParent,l=r.$el.offsetParent,d=r.$datepicker.getBoundingClientRect(),c=e.split(" "),u=window.scrollY,p=window.scrollX,m=r.opts.offset,v=c[0],g=c[1];if(a)r.$datepicker.style.cssText="left: 50%; top: 50%";else{if(o===l&&o!==document.body&&(h={top:r.$el.offsetTop,left:r.$el.offsetLeft,width:n.width,height:r.$el.offsetHeight},u=0,p=0),o!==l&&o!==document.body){let e=o.getBoundingClientRect();h={top:n.top-e.top,left:n.left-e.left,width:n.width,height:n.height},u=0,p=0}switch(v){case"top":i=h.top-d.height-m;break;case"right":s=h.left+h.width+m;break;case"bottom":i=h.top+h.height+m;break;case"left":s=h.left-d.width-m}switch(g){case"top":i=h.top;break;case"right":s=h.left+h.width-d.width;break;case"bottom":i=h.top+h.height-d.height;break;case"left":s=h.left;break;case"center":/left|right/.test(v)?i=h.top+h.height/2-d.height/2:s=h.left+h.width/2-d.width/2}r.$datepicker.style.cssText=`left: ${s+p}px; top: ${i+u}px`}})),P(this,"_setInputValue",(()=>{let{opts:e,$altField:t,locale:{dateFormat:i}}=this,{altFieldDateFormat:s,altField:a}=e;a&&t&&(t.value=this._getInputValue(s)),this.$el.value=this._getInputValue(i)})),P(this,"_getInputValue",(e=>{let{selectedDates:t,opts:i}=this,{multipleDates:s,multipleDatesSeparator:a}=i;if(!t.length)return"";let n="function"==typeof e,r=n?e(s?t:t[0]):t.map((t=>this.formatDate(t,e)));return r=n?r:r.join(a),r})),P(this,"_checkIfDateIsSelected",(function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:i.days,s=!1;return r.selectedDates.some((i=>{let a=p(e,i,t);return s=a&&i,a})),s})),P(this,"_scheduleCallAfterTransition",(e=>{this._cancelScheduledCall(),e&&e(!1),this._onTransitionEnd=()=>{e&&e(!0)},this.$datepicker.addEventListener("transitionend",this._onTransitionEnd,{once:!0})})),P(this,"_cancelScheduledCall",(()=>{this.$datepicker.removeEventListener("transitionend",this._onTransitionEnd)})),P(this,"setViewDate",(e=>{if(!((e=k(e))instanceof Date))return;if(p(e,this.viewDate))return;let t=this.viewDate;this.viewDate=e;let{onChangeViewDate:s}=this.opts;if(s){let{month:e,year:t}=this.parsedViewDate;s({month:e,year:t,decade:this.curDecade})}this.trigger(i.eventChangeViewDate,e,t)})),P(this,"setFocusDate",(function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};(!e||(e=k(e))instanceof Date)&&(r.focusDate=e,r.opts.range&&e&&r._handleRangeOnFocus(),r.trigger(i.eventChangeFocusDate,e,t))})),P(this,"setCurrentView",(e=>{if(this.viewIndexes.includes(e)){if(this.currentView=e,this.elIsInput&&this.visible&&this.setPosition(void 0,!0),this.trigger(i.eventChangeCurrentView,e),!this.views[e]){let t=this.views[e]=new F({dp:this,opts:this.opts,type:e});this.shouldUpdateDOM&&this.$content.appendChild(t.$el)}this.opts.onChangeView&&this.opts.onChangeView(e)}})),P(this,"_updateLastSelectedDate",(e=>{this.lastSelectedDate=e,this.trigger(i.eventChangeLastSelectedDate,e)})),P(this,"destroy",(()=>{let{showEvent:e,isMobile:t}=this.opts,i=this.$datepicker.parentNode;i&&i.removeChild(this.$datepicker),this.$el.removeEventListener(e,this._onFocus),this.$el.removeEventListener("blur",this._onBlur),window.removeEventListener("resize",this._onResize),t&&this._removeMobileAttributes(),this.keyboardNav&&this.keyboardNav.destroy(),this.views=null,this.nav=null,this.$datepicker=null,this.opts=null,this.$customContainer=null,this.viewDate=null,this.focusDate=null,this.selectedDates=null,this.rangeDateFrom=null,this.rangeDateTo=null})),P(this,"update",(e=>{let t=b({},this.opts);b(this.opts,e);let{timepicker:s,buttons:a,range:n,selectedDates:r,isMobile:h}=this.opts,o=this.visible||this.treatAsInline;this._createMinMaxDates(),this._limitViewDateByMaxMinDates(),this._handleLocale(),!t.selectedDates&&r&&this.selectDate(r),e.view&&this.setCurrentView(e.view),this._setInputValue(),t.range&&!n?(this.rangeDateTo=!1,this.rangeDateFrom=!1):!t.range&&n&&this.selectedDates.length&&(this.rangeDateFrom=this.selectedDates[0],this.rangeDateTo=this.selectedDates[1]),t.timepicker&&!s?(o&&this.timepicker.destroy(),this.timepicker=!1,this.$timepicker.parentNode.removeChild(this.$timepicker)):!t.timepicker&&s&&this._addTimepicker(),!t.buttons&&a?this._addButtons():t.buttons&&!a?(this.buttons.destroy(),this.$buttons.parentNode.removeChild(this.$buttons)):o&&t.buttons&&a&&this.buttons.clearHtml().render(),!t.isMobile&&h?(this.treatAsInline||R||this._createMobileOverlay(),this._addMobileAttributes(),this.visible&&this._showMobileOverlay()):t.isMobile&&!h&&(this._removeMobileAttributes(),this.visible&&(R.classList.remove("-active-"),"function"!=typeof this.opts.position&&this.setPosition())),o&&(this.nav.update(),this.views[this.currentView].render(),this.currentView===i.days&&this.views[this.currentView].renderDayNames())})),P(this,"isOtherMonth",(e=>{let{month:t}=o(e);return t!==this.parsedViewDate.month})),P(this,"isOtherYear",(e=>{let{year:t}=o(e);return t!==this.parsedViewDate.year})),P(this,"isOtherDecade",(e=>{let{year:t}=o(e),[i,s]=c(this.viewDate);return ts})),P(this,"_onChangeSelectedDate",(e=>{let{silent:t}=e;setTimeout((()=>{this._setInputValue(),this.opts.onSelect&&!t&&this._triggerOnSelect()}))})),P(this,"_onChangeFocusedDate",(function(e){let{viewDateTransition:t}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!e)return;let i=!1;t&&(i=r.isOtherMonth(e)||r.isOtherYear(e)||r.isOtherDecade(e)),i&&r.setViewDate(e)})),P(this,"_onChangeTime",(e=>{let{hours:t,minutes:i}=e,s=new Date,{lastSelectedDate:a,opts:{onSelect:n}}=this,r=a;a||(r=s);let h=this.getCell(r,this.currentViewSingular),o=h&&h.adpCell;o&&o.isDisabled||(r.setHours(t),r.setMinutes(i),a?(this._setInputValue(),n&&this._triggerOnSelect()):this.selectDate(r))})),P(this,"_onFocus",(e=>{this.visible||this.show()})),P(this,"_onBlur",(e=>{this.inFocus||!this.visible||this.opts.isMobile||this.hide()})),P(this,"_onMouseDown",(e=>{this.inFocus=!0})),P(this,"_onMouseUp",(e=>{this.inFocus=!1,this.$el.focus()})),P(this,"_onResize",(()=>{this.visible&&"function"!=typeof this.opts.position&&this.setPosition()})),P(this,"_onClickOverlay",(()=>{this.visible&&this.hide()})),P(this,"isWeekend",(e=>this.opts.weekends.includes(e))),P(this,"getClampedDate",(e=>{let{minDate:t,maxDate:i}=this,s=e;return i&&m(e,i)?s=i:t&&v(e,t)&&(s=t),s})),this.$el=a(e),!this.$el)return;this.$datepicker=n({className:"air-datepicker"}),this.opts=b({},s,t),this.$customContainer=!!this.opts.container&&a(this.opts.container),this.$altField=a(this.opts.altField||!1);let{view:h,startDate:l}=this.opts;l||(this.opts.startDate=new Date),"INPUT"===this.$el.nodeName&&(this.elIsInput=!0),this.inited=!1,this.visible=!1,this.viewDate=k(this.opts.startDate),this.focusDate=!1,this.initialReadonly=this.$el.getAttribute("readonly"),this.customHide=!1,this.currentView=h,this.selectedDates=[],this.views={},this.keys=[],this.rangeDateFrom="",this.rangeDateTo="",this.timepickerIsActive=!1,this.treatAsInline=this.opts.inline||!this.elIsInput,this.init()}init(){let{opts:e,treatAsInline:t,opts:{inline:i,isMobile:s,selectedDates:n,keyboardNav:r,onlyTimepicker:h}}=this,o=a("body");(!B||B&&j&&!o.contains(j))&&!i&&this.elIsInput&&!this.$customContainer&&K.buildGlobalContainer(K.defaultGlobalContainerId),!s||R||t||this._createMobileOverlay(),this._handleLocale(),this._bindSubEvents(),this._createMinMaxDates(),this._limitViewDateByMaxMinDates(),this.elIsInput&&(i||this._bindEvents(),r&&!h&&(this.keyboardNav=new N({dp:this,opts:e}))),n&&this.selectDate(n,{silent:!0}),this.opts.visible&&!t&&this.show(),s&&!t&&this.$el.setAttribute("readonly",!0),t&&this._createComponents()}_createMobileOverlay(){R=n({className:"air-datepicker-overlay"}),j.appendChild(R)}_createComponents(){let{opts:e,treatAsInline:t,opts:{inline:i,buttons:s,timepicker:a,position:n,classes:r,onlyTimepicker:h,isMobile:o}}=this;this._buildBaseHtml(),this.elIsInput&&(i||this._setPositionClasses(n)),!i&&this.elIsInput||this.$datepicker.classList.add("-inline-"),r&&this.$datepicker.classList.add(...r.split(" ")),h&&this.$datepicker.classList.add("-only-timepicker-"),o&&!t&&this._addMobileAttributes(),this.views[this.currentView]=new F({dp:this,type:this.currentView,opts:e}),this.nav=new x({dp:this,opts:e}),a&&this._addTimepicker(),s&&this._addButtons(),this.$content.appendChild(this.views[this.currentView].$el),this.$nav.appendChild(this.nav.$el)}_destroyComponents(){for(let e in this.views)this.views[e].destroy();this.views={},this.nav.destroy(),this.timepicker&&this.timepicker.destroy()}_addMobileAttributes(){R.addEventListener("click",this._onClickOverlay),this.$datepicker.classList.add("-is-mobile-"),this.$el.setAttribute("readonly",!0)}_removeMobileAttributes(){R.removeEventListener("click",this._onClickOverlay),this.$datepicker.classList.remove("-is-mobile-"),this.initialReadonly||""===this.initialReadonly||this.$el.removeAttribute("readonly")}_createMinMaxDates(){let{minDate:e,maxDate:t}=this.opts;this.minDate=!!e&&k(e),this.maxDate=!!t&&k(t)}_addTimepicker(){this.$timepicker=n({className:"air-datepicker--time"}),this.$datepicker.appendChild(this.$timepicker),this.timepicker=new O({dp:this,opts:this.opts}),this.$timepicker.appendChild(this.timepicker.$el)}_addButtons(){this.$buttons=n({className:"air-datepicker--buttons"}),this.$datepicker.appendChild(this.$buttons),this.buttons=new E({dp:this,opts:this.opts}),this.$buttons.appendChild(this.buttons.$el)}_bindSubEvents(){this.on(i.eventChangeSelectedDate,this._onChangeSelectedDate),this.on(i.eventChangeFocusDate,this._onChangeFocusedDate),this.on(i.eventChangeTime,this._onChangeTime)}_buildBaseHtml(){let{inline:e}=this.opts;var t,i;this.elIsInput?e?(t=this.$datepicker,(i=this.$el).parentNode.insertBefore(t,i.nextSibling)):this.$container.appendChild(this.$datepicker):this.$el.appendChild(this.$datepicker),this.$datepicker.innerHTML='
',this.$content=a(".air-datepicker--content",this.$datepicker),this.$pointer=a(".air-datepicker--pointer",this.$datepicker),this.$nav=a(".air-datepicker--navigation",this.$datepicker)}_handleLocale(){let{locale:e,dateFormat:t,firstDay:i,timepicker:s,onlyTimepicker:a,timeFormat:n,dateTimeSeparator:r}=this.opts;var h;this.locale=(h=e,JSON.parse(JSON.stringify(h))),t&&(this.locale.dateFormat=t),void 0!==n&&""!==n&&(this.locale.timeFormat=n);let{timeFormat:o}=this.locale;if(""!==i&&(this.locale.firstDay=i),s&&"function"!=typeof t){let e=o?r:"";this.locale.dateFormat=[this.locale.dateFormat,o||""].join(e)}a&&"function"!=typeof t&&(this.locale.dateFormat=this.locale.timeFormat)}_setPositionClasses(e){if("function"==typeof e)return void this.$datepicker.classList.add("-custom-position-");let t=(e=e.split(" "))[0],i=`air-datepicker -${t}-${e[1]}- -from-${t}-`;this.$datepicker.classList.add(...i.split(" "))}_bindEvents(){this.$el.addEventListener(this.opts.showEvent,this._onFocus),this.$el.addEventListener("blur",this._onBlur),this.$datepicker.addEventListener("mousedown",this._onMouseDown),this.$datepicker.addEventListener("mouseup",this._onMouseUp),window.addEventListener("resize",this._onResize)}_limitViewDateByMaxMinDates(){let{viewDate:e,minDate:t,maxDate:i}=this;i&&m(e,i)&&this.setViewDate(i),t&&v(e,t)&&this.setViewDate(t)}formatDate(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.viewDate,t=arguments.length>1?arguments[1]:void 0;if(e=k(e),!(e instanceof Date))return;let i=t,s=this.locale,a=o(e),n=a.dayPeriod,r=c(e),h=K.replacer,l={T:e.getTime(),m:a.minutes,mm:a.fullMinutes,h:a.hours12,hh:a.fullHours12,H:a.hours,HH:a.fullHours,aa:n,AA:n.toUpperCase(),E:s.daysShort[a.day],EEEE:s.days[a.day],d:a.date,dd:a.fullDate,M:a.month+1,MM:a.fullMonth,MMM:s.monthsShort[a.month],MMMM:s.months[a.month],yy:a.year.toString().slice(-2),yyyy:a.year,yyyy1:r[0],yyyy2:r[1]};for(let[e,t]of Object.entries(l))i=h(i,C(e),t);return i}down(e){this._handleUpDownActions(e,"down")}up(e){this._handleUpDownActions(e,"up")}selectDate(e){let t,s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},{currentView:a,parsedViewDate:n,selectedDates:r}=this,{updateTime:h}=s,{moveToOtherMonthsOnSelect:o,moveToOtherYearsOnSelect:l,multipleDates:d,range:c,autoClose:u}=this.opts,p=r.length;if(Array.isArray(e))return e.forEach((e=>{this.selectDate(e,s)})),new Promise((e=>{setTimeout(e)}));if((e=k(e))instanceof Date){if(a===i.days&&e.getMonth()!==n.month&&o&&(t=new Date(e.getFullYear(),e.getMonth(),1)),a===i.years&&e.getFullYear()!==n.year&&l&&(t=new Date(e.getFullYear(),0,1)),t&&this.setViewDate(t),d&&!c){if(p===d)return;this._checkIfDateIsSelected(e)||r.push(e)}else if(c)switch(p){case 1:r.push(e),this.rangeDateTo||(this.rangeDateTo=e),m(this.rangeDateFrom,this.rangeDateTo)&&(this.rangeDateTo=this.rangeDateFrom,this.rangeDateFrom=e),this.selectedDates=[this.rangeDateFrom,this.rangeDateTo];break;case 2:this.selectedDates=[e],this.rangeDateFrom=e,this.rangeDateTo="";break;default:this.selectedDates=[e],this.rangeDateFrom=e}else this.selectedDates=[e];return this.trigger(i.eventChangeSelectedDate,{action:i.actionSelectDate,silent:null==s?void 0:s.silent,date:e,updateTime:h}),this._updateLastSelectedDate(e),u&&!this.timepickerIsActive&&this.visible&&(d||c?c&&1===p&&this.hide():this.hide()),new Promise((e=>{setTimeout(e)}))}}unselectDate(e){let t=this.selectedDates,s=this;if((e=k(e))instanceof Date)return t.some(((a,n)=>{if(p(a,e))return t.splice(n,1),s.selectedDates.length?s._updateLastSelectedDate(s.selectedDates[s.selectedDates.length-1]):(s.rangeDateFrom="",s.rangeDateTo="",s._updateLastSelectedDate(!1)),this.trigger(i.eventChangeSelectedDate,{action:i.actionUnselectDate,date:e}),!0}))}replaceDate(e,t){let s=this.selectedDates.find((t=>p(t,e,this.currentView))),a=this.selectedDates.indexOf(s);a<0||p(this.selectedDates[a],t,this.currentView)||(this.selectedDates[a]=t,this.trigger(i.eventChangeSelectedDate,{action:i.actionSelectDate,date:t,updateTime:!0}),this._updateLastSelectedDate(t))}clear(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return this.selectedDates=[],this.rangeDateFrom=!1,this.rangeDateTo=!1,this.trigger(i.eventChangeSelectedDate,{action:i.actionUnselectDate,silent:e.silent}),new Promise((e=>{setTimeout(e)}))}show(){let{onShow:e,isMobile:t}=this.opts;this._cancelScheduledCall(),this.visible||this.hideAnimation||this._createComponents(),this.setPosition(this.opts.position),this.$datepicker.classList.add("-active-"),this.visible=!0,e&&this._scheduleCallAfterTransition(e),t&&this._showMobileOverlay()}hide(){let{onHide:e,isMobile:t}=this.opts,i=this._hasTransition();this.visible=!1,this.hideAnimation=!0,this.$datepicker.classList.remove("-active-"),this.customHide&&this.customHide(),this.elIsInput&&this.$el.blur(),this._scheduleCallAfterTransition((t=>{!this.customHide&&(t&&i||!t&&!i)&&this._finishHide(),e&&e(t)})),t&&R.classList.remove("-active-")}_triggerOnSelect(){let e=[],t=[],{selectedDates:i,locale:s,opts:{onSelect:a,multipleDates:n,range:r}}=this,h=n||r,o="function"==typeof s.dateFormat;i.length&&(e=i.map(g),t=o?n?s.dateFormat(e):e.map((e=>s.dateFormat(e))):e.map((e=>this.formatDate(e,s.dateFormat)))),a({date:h?e:e[0],formattedDate:h?t:t[0],datepicker:this})}_handleAlreadySelectedDates(e,t){let{range:i,toggleSelected:s}=this.opts;i?s?this.unselectDate(t):2!==this.selectedDates.length&&this.selectDate(t):s&&this.unselectDate(t),s||this._updateLastSelectedDate(e)}_handleUpDownActions(e,t){if(!((e=k(e||this.focusDate||this.viewDate))instanceof Date))return;let i="up"===t?this.viewIndex+1:this.viewIndex-1;i>2&&(i=2),i<0&&(i=0),this.setViewDate(new Date(e.getFullYear(),e.getMonth(),1)),this.setCurrentView(this.viewIndexes[i])}_handleRangeOnFocus(){1===this.selectedDates.length&&(m(this.selectedDates[0],this.focusDate)?(this.rangeDateTo=this.selectedDates[0],this.rangeDateFrom=this.focusDate):(this.rangeDateTo=this.focusDate,this.rangeDateFrom=this.selectedDates[0]))}getCell(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:i.day;if(!((e=k(e))instanceof Date))return;let{year:s,month:a,date:n}=o(e),r=`[data-year="${s}"]`,h=`[data-month="${a}"]`,l={[i.day]:`${r}${h}[data-date="${n}"]`,[i.month]:`${r}${h}`,[i.year]:`${r}`};return this.views[this.currentView].$el.querySelector(l[t])}_showMobileOverlay(){R.classList.add("-active-")}_hasTransition(){return window.getComputedStyle(this.$datepicker).getPropertyValue("transition-duration").split(", ").reduce(((e,t)=>parseFloat(t)+e),0)>0}get shouldUpdateDOM(){return this.visible||this.treatAsInline}get parsedViewDate(){return o(this.viewDate)}get currentViewSingular(){return this.currentView.slice(0,-1)}get curDecade(){return c(this.viewDate)}get viewIndex(){return this.viewIndexes.indexOf(this.currentView)}get isFinalView(){return this.currentView===i.years}get hasSelectedDates(){return this.selectedDates.length>0}get isMinViewReached(){return this.currentView===this.opts.minView||this.currentView===i.days}get $container(){return this.$customContainer||j}static replacer(e,t,i){return e.replace(t,(function(e,t,s,a){return t+i+a}))}}var U;return P(K,"defaults",s),P(K,"version","3.3.5"),P(K,"defaultGlobalContainerId","air-datepicker-global-container"),U=K.prototype,Object.assign(U,I),t.default}()})); diff --git a/src/aurora/core/static/datetimepicker/dt.js b/src/aurora/core/static/datetimepicker/dt.js index 5e866512..c4fb6e9d 100644 --- a/src/aurora/core/static/datetimepicker/dt.js +++ b/src/aurora/core/static/datetimepicker/dt.js @@ -10,11 +10,18 @@ // clear: "Clear", // titleFormat: "MM y" // }; - $("body").on("focus", ".vDateField", function () { - new Datepicker(this, { - autohide: true, - format: "yyyy-mm-dd", + $("input.vDateField").on("focus", function () { + var $input = $(this); + var dt = new Datepicker(this, { + autohide: true, format: "yyyy-mm-dd", }); + this.addEventListener( + "changeDate", + function () { + $input.trigger("change"); + }, + false + ); }); }); })(jQuery); diff --git a/src/aurora/core/static/datetimepicker/dt.min.js b/src/aurora/core/static/datetimepicker/dt.min.js index d5dd89c6..e5933415 100644 --- a/src/aurora/core/static/datetimepicker/dt.min.js +++ b/src/aurora/core/static/datetimepicker/dt.min.js @@ -1 +1 @@ -(function($){$(function(){$("body").on("focus",".vDateField",function(){new Datepicker(this,{autohide:true,format:"yyyy-mm-dd"})})})})(jQuery); +(function($){$(function(){$("input.vDateField").on("focus",function(){var $input=$(this);var dt=new Datepicker(this,{autohide:true,format:"yyyy-mm-dd"});this.addEventListener("changeDate",function(){$input.trigger("change")},false)})})})(jQuery); diff --git a/src/aurora/core/static/smart.js b/src/aurora/core/static/smart.js index a4dcf3ba..e25c1ed8 100644 --- a/src/aurora/core/static/smart.js +++ b/src/aurora/core/static/smart.js @@ -45,8 +45,8 @@ if ($ === undefined) { smart.handleQuestion(this); }); // trigger onload events - $('[data-onload]').each(function () { - eval($(this).data('onload')); - }); + // $('[data-onload]').each(function () { + // eval($(this).data('onload')); + // }); }); })($); diff --git a/src/aurora/core/static/smart.min.js b/src/aurora/core/static/smart.min.js index 444a9fb7..92884c86 100644 --- a/src/aurora/core/static/smart.min.js +++ b/src/aurora/core/static/smart.min.js @@ -1 +1 @@ -if($===undefined){$=django.jQuery}(function($){$(function(){$(".field-container.required span.required").each(function(i,e){$(e).removeClass("hidden")});$(".required_by_question").each(function(i,e){var $question=$(e).parents("fieldset").find(".question-visibility");var $container=$(e).parents("fieldset").find(".field-container");var $input=$container.find("input,select");$question.on("change",function(e){$input.attr("required",$(this).is(":checked"))})});$("[data-visibility=hidden]").parents(".field-container").hide();$("[data-trigger=change]").each(function(i,e){try{$(e).trigger("change")}catch(e){}});$(".errorlist").each(function(i,e){var $container=$(e).parents("fieldset").find(".field-container");$container.show()});$(".question-visibility").each(function(i,e){var $container=$(e).parents("fieldset").find(".field-container");var $input=$container.find("input,select, textarea");if(smart.has_any_value($input)){$(e).prop("checked","checked");$container.show()}}).on("click",function(){smart.handleQuestion(this)});$("[data-onload]").each(function(){eval($(this).data("onload"))})})})($); +if($===undefined){$=django.jQuery}(function($){$(function(){$(".field-container.required span.required").each(function(i,e){$(e).removeClass("hidden")});$(".required_by_question").each(function(i,e){var $question=$(e).parents("fieldset").find(".question-visibility");var $container=$(e).parents("fieldset").find(".field-container");var $input=$container.find("input,select");$question.on("change",function(e){$input.attr("required",$(this).is(":checked"))})});$("[data-visibility=hidden]").parents(".field-container").hide();$("[data-trigger=change]").each(function(i,e){try{$(e).trigger("change")}catch(e){}});$(".errorlist").each(function(i,e){var $container=$(e).parents("fieldset").find(".field-container");$container.show()});$(".question-visibility").each(function(i,e){var $container=$(e).parents("fieldset").find(".field-container");var $input=$container.find("input,select, textarea");if(smart.has_any_value($input)){$(e).prop("checked","checked");$container.show()}}).on("click",function(){smart.handleQuestion(this)})})})($); diff --git a/src/aurora/core/static/smart_field.js b/src/aurora/core/static/smart_field.js index 71200433..fdb2049d 100644 --- a/src/aurora/core/static/smart_field.js +++ b/src/aurora/core/static/smart_field.js @@ -1,4 +1,8 @@ ;(function ($) { + const NUMBERS = ['month', 'number', 'week']; + const DATES = ['date', 'time']; + const STRINGS = ['email', 'tel', 'text', 'url']; + var highLight = function (onOff) { if (onOff) { this.__oldBorder = this.$.css("border"); @@ -8,14 +12,14 @@ } return this; }; - window.debug = function (arguments){ + window.debug = function (arguments) { var log; - if (window.parent.document.getElementById('debugLog') ){ + if (window.parent.document.getElementById('debugLog')) { log = window.parent.document.getElementById('debugLog'); - }else{ + } else { log = window.document.getElementById('debugLog'); } - if (log){ + if (log) { $(log).append(arguments); $(log).append("\n"); } @@ -30,25 +34,50 @@ self.$submit = $form.find("input[type=submit]"); self.config = config; self.name = config.name; - self.setError = function (msg){ + self.setError = function (msg) { self.$.find('.alert-message').html(msg); }; self.pushError = function (f) { - self.enableSubmit(false); errorsStack[f] = true; }; self.popError = function (f) { errorsStack[f] = false; - self.enableSubmit(self.isValid()); + // var ret = false; + var ret = _.filter(_.values(errorsStack), function (i) { + return i === true; + }); + } + self.getError = function (f) { + return errorsStack[f]; } + self.getErrors = function () { + return _.filter(_.keys(errorsStack), function (i, e) { + return errorsStack[i] === true; + }); + } + var _fields = null; + self.fields = function () { + if (_fields == null) { + _fields = {} + $form.find(`:input[data-flex-name]`).each(function (i, e) { + var f = new aurora.Field(e); + _fields[f.name] = f; + }); + } + return _fields + }; self.isValid = function () { - for (let i = 0, keys = Object.keys(errorsStack), ii = keys.length; i < ii; i++) { - if (errorsStack[i] === false) { - return false + var ret = true + for (let i = 0, keys = Object.keys(self.fields()), ii = keys.length; i < ii; i++) { + var f = self.fields()[keys[i]]; + var ok = f.isValid(); + if (ok === false) { + ret = false; + break; } } - return true; + return ret; } self.enableSubmit = function (onOff) { @@ -62,7 +91,8 @@ $form.on('submit', function (e) { if (self.isValid()) { - $(this).find("input[type=submit]").prop("disabled", "disabled").val(gettext("Please wait...")); + $form.find("input[type=submit]").prop("disabled", "disabled").val(gettext("Please wait...")); + return true; } else { return false; } @@ -114,11 +144,16 @@ const $formContainer = $me.parents('.form-container'); // const $input = $fieldset.find(`#${id}`); const $input = $(origin); + const inputType = $input.attr("type"); const initial = { required: $input.attr("required"), } self.$ = $fieldset; + self.$input = $input; + self.$fieldset = $fieldset; self.name = $fieldset.data("fname"); + self.$description = $fieldset.find('.description'); + self.$hint = $fieldset.find('.hint'); self.highLight = highLight; self.setRequired = function (onOff) { @@ -161,13 +196,30 @@ return $input.val().trim() !== ''; } }; + self.isValid = function () { + if ($input.data('validation')) { + var code = $input.data('validation'); + var ret = true; + try { + (function () { + return eval(code); + }.call(self.origin)); + ret = !module.getError(self.name); + return ret; + } catch (e) { + return false; + } + } + }; self.setError = function (text) { if (text) { + $fieldset.data("error", text); $fieldset.find(".errors").html(`
  • ${text}
`); - module.pushError(self); + module.pushError(self.name); } else { + $fieldset.data("error", ""); $fieldset.find(".errors").html(""); - module.popError(self); + module.popError(self.name); } return self; } @@ -198,7 +250,11 @@ return self; }; self.getValue = function () { - return $input.val(); + var raw = $input.val(); + if (NUMBERS.indexOf(inputType) >= 0) { + return parseInt(raw); + } + return raw; }; self.setDescription = function (value) { $fieldset.find('.description').text(value); @@ -257,5 +313,8 @@ if (!window.module) { window.module = new aurora.Module({}); } + $('[data-onload]').each(function () { + eval($(this).data('onload')); + }); }); -})(jQuery||django.jQuery); +})(jQuery || django.jQuery); diff --git a/src/aurora/core/static/smart_field.min.js b/src/aurora/core/static/smart_field.min.js index 4b805b5e..5f5f7478 100644 --- a/src/aurora/core/static/smart_field.min.js +++ b/src/aurora/core/static/smart_field.min.js @@ -1 +1 @@ -(function($){var highLight=function(onOff){if(onOff){this.__oldBorder=this.$.css("border");this.$.css("border","1px solid red")}else{this.$.css("border",this.__oldBorder)}return this};window.debug=function(arguments){var log;if(window.parent.document.getElementById("debugLog")){log=window.parent.document.getElementById("debugLog")}else{log=window.document.getElementById("debugLog")}if(log){$(log).append(arguments);$(log).append("\n")}};window.aurora={Module:function(config){var self=this;var errorsStack={};self.$=$("#formContainer");var $form=$("#registrationForm");self.$submit=$form.find("input[type=submit]");self.config=config;self.name=config.name;self.setError=function(msg){self.$.find(".alert-message").html(msg)};self.pushError=function(f){self.enableSubmit(false);errorsStack[f]=true};self.popError=function(f){errorsStack[f]=false;self.enableSubmit(self.isValid())};self.isValid=function(){for(let i=0,keys=Object.keys(errorsStack),ii=keys.length;i
  • ${text}
  • `);module.pushError(self)}else{$fieldset.find(".errors").html("");module.popError(self)}return self};self.assertBetween=function(min,max){};self.assertDateBetween=function(min,max){var limit1=Date.parse(min);var limit2=Date.parse(max);var dt=Date.parse(self.getValue());if(!(dtlimit2)){self.setError("the date cannot be before 1 dec 1930 or after 1 dec 2007")}else{self.setError("")}};self.sameAs=function(target){const $target=self.sibling(target);if($target.getValue()==self.getValue()){$input.css("background-color","#d7f6ca")}else{$input.css("background-color","#e1adad")}};self.setValue=function(value){$input.val(value);return self};self.getValue=function(){return $input.val()};self.setDescription=function(value){$fieldset.find(".description").text(value);return self};self.setHint=function(value){$fieldset.find(".hint").text(value);return self};self.sibling=function(name){var $target=$formContainer.find(`:input[data-flex=${name}]`);if(!$target[0]){alert(`Cannot find "input[name=${name}]"`)}return new aurora.Field($target)};self.formsets=function(name){var $target=$formContainer.find(`input[data-flex=${name}]`);if(!$target[0]){alert(`Cannot find "input[name=${name}]"`)}return new aurora.Field($target)};self.setRequiredOnValue=function(value,targets){try{const cmp=value.toString().toLowerCase();const val=self.getValue();$form.find(targets).each(function(i,e){var $c=$(e).parents(".field-container");if(val==cmp){$(e).attr("required",true);$c.find(".required-label").show()}else{$(e).attr("required",false);$c.find(".required-label").hide()}})}catch(error){console.error(error)}};self.inspect=function(){console.log(`${id}: `,{value:self.getValue(),id:id,me:$me,form:$form,fieldset:$fieldset,input:$input})}}};$(function(){if(!window.module){window.module=new aurora.Module({})}})})(jQuery||django.jQuery); +(function($){const NUMBERS=["month","number","week"];const DATES=["date","time"];const STRINGS=["email","tel","text","url"];var highLight=function(onOff){if(onOff){this.__oldBorder=this.$.css("border");this.$.css("border","1px solid red")}else{this.$.css("border",this.__oldBorder)}return this};window.debug=function(arguments){var log;if(window.parent.document.getElementById("debugLog")){log=window.parent.document.getElementById("debugLog")}else{log=window.document.getElementById("debugLog")}if(log){$(log).append(arguments);$(log).append("\n")}};window.aurora={Module:function(config){var self=this;var errorsStack={};self.$=$("#formContainer");var $form=$("#registrationForm");self.$submit=$form.find("input[type=submit]");self.config=config;self.name=config.name;self.setError=function(msg){self.$.find(".alert-message").html(msg)};self.pushError=function(f){errorsStack[f]=true};self.popError=function(f){errorsStack[f]=false;var ret=_.filter(_.values(errorsStack),function(i){return i===true})};self.getError=function(f){return errorsStack[f]};self.getErrors=function(){return _.filter(_.keys(errorsStack),function(i,e){return errorsStack[i]===true})};var _fields=null;self.fields=function(){if(_fields==null){_fields={};$form.find(`:input[data-flex-name]`).each(function(i,e){var f=new aurora.Field(e);_fields[f.name]=f})}return _fields};self.isValid=function(){var ret=true;for(let i=0,keys=Object.keys(self.fields()),ii=keys.length;i
  • ${text}
  • `);module.pushError(self.name)}else{$fieldset.data("error","");$fieldset.find(".errors").html("");module.popError(self.name)}return self};self.assertBetween=function(min,max){};self.assertDateBetween=function(min,max){var limit1=Date.parse(min);var limit2=Date.parse(max);var dt=Date.parse(self.getValue());if(!(dtlimit2)){self.setError("the date cannot be before 1 dec 1930 or after 1 dec 2007")}else{self.setError("")}};self.sameAs=function(target){const $target=self.sibling(target);if($target.getValue()==self.getValue()){$input.css("background-color","#d7f6ca")}else{$input.css("background-color","#e1adad")}};self.setValue=function(value){$input.val(value);return self};self.getValue=function(){var raw=$input.val();if(NUMBERS.indexOf(inputType)>=0){return parseInt(raw)}return raw};self.setDescription=function(value){$fieldset.find(".description").text(value);return self};self.setHint=function(value){$fieldset.find(".hint").text(value);return self};self.sibling=function(name){var $target=$formContainer.find(`:input[data-flex=${name}]`);if(!$target[0]){alert(`Cannot find "input[name=${name}]"`)}return new aurora.Field($target)};self.formsets=function(name){var $target=$formContainer.find(`input[data-flex=${name}]`);if(!$target[0]){alert(`Cannot find "input[name=${name}]"`)}return new aurora.Field($target)};self.setRequiredOnValue=function(value,targets){try{const cmp=value.toString().toLowerCase();const val=self.getValue();$form.find(targets).each(function(i,e){var $c=$(e).parents(".field-container");if(val==cmp){$(e).attr("required",true);$c.find(".required-label").show()}else{$(e).attr("required",false);$c.find(".required-label").hide()}})}catch(error){console.error(error)}};self.inspect=function(){console.log(`${id}: `,{value:self.getValue(),id:id,me:$me,form:$form,fieldset:$fieldset,input:$input})}}};$(function(){if(!window.module){window.module=new aurora.Module({})}$("[data-onload]").each(function(){eval($(this).data("onload"))})})})(jQuery||django.jQuery); diff --git a/src/aurora/core/static/smart_validation.js b/src/aurora/core/static/smart_validation.js index a1d959f7..9e842f5a 100644 --- a/src/aurora/core/static/smart_validation.js +++ b/src/aurora/core/static/smart_validation.js @@ -3,7 +3,15 @@ dateutil = { today: TODAY, years18: new Date(new Date().setDate(TODAY.getDate() - (365 * 18))), years2: new Date(new Date().setDate(TODAY.getDate() - (365 * 2))), - getAge(date2, date1) { + isDate: function (d) { + try { + new Date(d) + return true + } catch (e) { + return false; + } + }, + getAge: function (date2, date1) { // use today's date if ageAtDate is not provided if (typeof date1 == "undefined") date1 = new Date(); @@ -19,20 +27,20 @@ dateutil = { return years; } }; -_ = { - is_child: function (d) { - return d && dateutil.getAge(d) < 18 - }, - is_baby: function (d) { - return d && dateutil.getAge(d) <= 2 - }, - is_future: function (d) { - return d && Date.parse(d) > dateutil.today - }, -}; -_.is_adult = function (d) { - return !_.is_child(d) -}; +// _ = { +// is_child: function (d) { +// return d && dateutil.getAge(d) < 18 +// }, +// is_baby: function (d) { +// return d && dateutil.getAge(d) <= 2 +// }, +// is_future: function (d) { +// return d && Date.parse(d) > dateutil.today +// }, +// }; +// _.is_adult = function (d) { +// return !_.is_child(d) +// }; smart_fs = { getCollector: function (cd) { @@ -86,7 +94,6 @@ smart = { } }, setRequired: function (targets, onOff) { - console.log(111.2, targets, onOff); $(targets).each(function (i, e) { if (onOff) { $(e).attr("required", true); diff --git a/src/aurora/core/static/smart_validation.min.js b/src/aurora/core/static/smart_validation.min.js index 9307bd6c..fa298f84 100644 --- a/src/aurora/core/static/smart_validation.min.js +++ b/src/aurora/core/static/smart_validation.min.js @@ -1 +1 @@ -TODAY=new Date;dateutil={today:TODAY,years18:new Date((new Date).setDate(TODAY.getDate()-365*18)),years2:new Date((new Date).setDate(TODAY.getDate()-365*2)),getAge(date2,date1){if(typeof date1=="undefined")date1=new Date;let years=new Date(date1).getFullYear()-new Date(date2).getFullYear();let month=new Date(date1).getMonth()-new Date(date2).getMonth();let dateDiff=new Date(date1).getDay()-new Date(date2).getDay();if(dateDiff<0){month-=1}if(month<0){years-=1}return years}};_={is_child:function(d){return d&&dateutil.getAge(d)<18},is_baby:function(d){return d&&dateutil.getAge(d)<=2},is_future:function(d){return d&&Date.parse(d)>dateutil.today}};_.is_adult=function(d){return!_.is_child(d)};smart_fs={getCollector:function(cd){var collectors=cd.filter(e=>e.role_i_c==="y");if(collectors.length===1){return collectors[0]}return null}};smart={sameAs:function(sender,target){var $sender=$(sender);var $form=$sender.parents(".form-container");var $target=$form.find("[data-flex='"+target+"']");if($sender.val()==$target.val()){$sender.css("background-color","#d7f6ca")}else{$sender.css("background-color","#e1adad")}},preventSubmit:function(sender){var $form=$(sender).parents("form");var $target=$form.find("input[type=submit]");var valid=false;if($(sender).is('input[type="checkbox"]')){valid=$(sender).is(":checked")}else if($(sender).is('input[type="radio"]')){valid=$(sender).val()==="y"}else{valid=!!$(sender).val()}if(valid){$target.prop("disabled",false)}else{$target.prop("disabled",true)}},showHideInForm:function(sender,target,showHide){try{var $form=$(sender).parents(".form-container");var $target=$form.find(target);if(showHide){$target.show()}else{$target.hide()}}catch(error){console.error(error)}},setRequired:function(targets,onOff){console.log(111.2,targets,onOff);$(targets).each(function(i,e){if(onOff){$(e).attr("required",true);$c.find(".required-label").show()}else{$(e).attr("required",false);$c.find(".required-label").hide()}})},setRequiredOnValue:function(sender,targets,value){try{var cmp=value.toLowerCase();var $form=$(sender).parents(".form-container");$form.find(targets).each(function(i,e){$c=$(e).parents(".field-container");if($(sender).val()==cmp){$(e).attr("required",true);$c.find(".required-label").show()}else{$(e).attr("required",false);$c.find(".required-label").hide()}})}catch(error){console.error(error)}},getSiblingField:function(sender,target){var $sender=$(sender);var $form=$sender.parents(".form-container");return $form.find("[data-flex='"+target+"']")},getSibling:function(sender,target){var $form=$(sender).parents(".form-container");return $form.find(target).parents(".field-container")},showHideDependant:function(sender,target,value){try{var cmp=null;if(Array.isArray(value)){cmp=function(a,b){b.includes(a)}}else{cmp=function(a,b){return a==b}}var cleared=value.toLowerCase();var $form=$(sender).parents(".form-container");var $target=$form.find(target).parents(".field-container");if(cmp($(sender).val(),cleared)){$target.show()}else{$target.hide()}}catch(error){console.error(error)}},setDependant:function(sender,target,value){try{var $form=$(sender).parents(".form-container");var $target=$form.find(target);$target.prop("disabled",!($(sender).val()==value))}catch(error){console.error(error)}},is_adult:function(d){return d&&Date.parse(d)<=dateutil.years18?true:false},handleQuestion:function(e){var $container=$(e).parents("fieldset").find(".field-container");if($(e).is(":checked")){$container.show()}else{$container.hide()}},updateDeleteLabel:function(sender,label){$(sender).parents(".form-container").find(".delete-button").text(label+$(sender).val())},getControlledField:function(questionCheckbox){var $container=$(questionCheckbox).parents("fieldset").find(".field-container");return $container.find("input,select, textarea")},has_any_value:function($sender){var inputType=$sender.attr("type");if(inputType==="radio"||inputType==="checkbox"){$sender.is(":checked")}else{return $sender.val().trim()!==""}},getField:function($sender){var $fieldset=$($sender).parents("fieldset");var $form=$($sender).parents(".form-container");var $container=$fieldset.find(".field-container");var $question=$fieldset.find(".question");var $input=$container.find("input,select, textarea");var inputType=$sender.attr("type");var value=null;var hasValue=null;if(inputType==="radio"||inputType==="checkbox"){value=$sender.val();hasValue=$sender.is(":checked")}else{value=$sender.val();hasValue=value!==""}return{fieldset:$fieldset,container:$container,question:$question,input:$input,inputType:inputType,form:$form,value:value,hasValue:hasValue,checked:$sender.is(":checked")}}}; +TODAY=new Date;dateutil={today:TODAY,years18:new Date((new Date).setDate(TODAY.getDate()-365*18)),years2:new Date((new Date).setDate(TODAY.getDate()-365*2)),isDate:function(d){try{new Date(d);return true}catch(e){return false}},getAge:function(date2,date1){if(typeof date1=="undefined")date1=new Date;let years=new Date(date1).getFullYear()-new Date(date2).getFullYear();let month=new Date(date1).getMonth()-new Date(date2).getMonth();let dateDiff=new Date(date1).getDay()-new Date(date2).getDay();if(dateDiff<0){month-=1}if(month<0){years-=1}return years}};smart_fs={getCollector:function(cd){var collectors=cd.filter(e=>e.role_i_c==="y");if(collectors.length===1){return collectors[0]}return null}};smart={sameAs:function(sender,target){var $sender=$(sender);var $form=$sender.parents(".form-container");var $target=$form.find("[data-flex='"+target+"']");if($sender.val()==$target.val()){$sender.css("background-color","#d7f6ca")}else{$sender.css("background-color","#e1adad")}},preventSubmit:function(sender){var $form=$(sender).parents("form");var $target=$form.find("input[type=submit]");var valid=false;if($(sender).is('input[type="checkbox"]')){valid=$(sender).is(":checked")}else if($(sender).is('input[type="radio"]')){valid=$(sender).val()==="y"}else{valid=!!$(sender).val()}if(valid){$target.prop("disabled",false)}else{$target.prop("disabled",true)}},showHideInForm:function(sender,target,showHide){try{var $form=$(sender).parents(".form-container");var $target=$form.find(target);if(showHide){$target.show()}else{$target.hide()}}catch(error){console.error(error)}},setRequired:function(targets,onOff){$(targets).each(function(i,e){if(onOff){$(e).attr("required",true);$c.find(".required-label").show()}else{$(e).attr("required",false);$c.find(".required-label").hide()}})},setRequiredOnValue:function(sender,targets,value){try{var cmp=value.toLowerCase();var $form=$(sender).parents(".form-container");$form.find(targets).each(function(i,e){$c=$(e).parents(".field-container");if($(sender).val()==cmp){$(e).attr("required",true);$c.find(".required-label").show()}else{$(e).attr("required",false);$c.find(".required-label").hide()}})}catch(error){console.error(error)}},getSiblingField:function(sender,target){var $sender=$(sender);var $form=$sender.parents(".form-container");return $form.find("[data-flex='"+target+"']")},getSibling:function(sender,target){var $form=$(sender).parents(".form-container");return $form.find(target).parents(".field-container")},showHideDependant:function(sender,target,value){try{var cmp=null;if(Array.isArray(value)){cmp=function(a,b){b.includes(a)}}else{cmp=function(a,b){return a==b}}var cleared=value.toLowerCase();var $form=$(sender).parents(".form-container");var $target=$form.find(target).parents(".field-container");if(cmp($(sender).val(),cleared)){$target.show()}else{$target.hide()}}catch(error){console.error(error)}},setDependant:function(sender,target,value){try{var $form=$(sender).parents(".form-container");var $target=$form.find(target);$target.prop("disabled",!($(sender).val()==value))}catch(error){console.error(error)}},is_adult:function(d){return d&&Date.parse(d)<=dateutil.years18?true:false},handleQuestion:function(e){var $container=$(e).parents("fieldset").find(".field-container");if($(e).is(":checked")){$container.show()}else{$container.hide()}},updateDeleteLabel:function(sender,label){$(sender).parents(".form-container").find(".delete-button").text(label+$(sender).val())},getControlledField:function(questionCheckbox){var $container=$(questionCheckbox).parents("fieldset").find(".field-container");return $container.find("input,select, textarea")},has_any_value:function($sender){var inputType=$sender.attr("type");if(inputType==="radio"||inputType==="checkbox"){$sender.is(":checked")}else{return $sender.val().trim()!==""}},getField:function($sender){var $fieldset=$($sender).parents("fieldset");var $form=$($sender).parents(".form-container");var $container=$fieldset.find(".field-container");var $question=$fieldset.find(".question");var $input=$container.find("input,select, textarea");var inputType=$sender.attr("type");var value=null;var hasValue=null;if(inputType==="radio"||inputType==="checkbox"){value=$sender.val();hasValue=$sender.is(":checked")}else{value=$sender.val();hasValue=value!==""}return{fieldset:$fieldset,container:$container,question:$question,input:$input,inputType:inputType,form:$form,value:value,hasValue:hasValue,checked:$sender.is(":checked")}}}; diff --git a/src/aurora/core/templates/admin/core/flexformfield/field_editor/main.html b/src/aurora/core/templates/admin/core/flexformfield/field_editor/main.html index 96adbb80..61e45d0f 100644 --- a/src/aurora/core/templates/admin/core/flexformfield/field_editor/main.html +++ b/src/aurora/core/templates/admin/core/flexformfield/field_editor/main.html @@ -1,12 +1,8 @@ {% extends "admin_extra_buttons/action_page.html" %}{% load static itrans aurora field_editor %} {% block extrahead %} - -{# #} -{# #} -{# #} -{# #} -{# #} -{{ media }} + {##} + + {{ media }} {% endblock %} {% block object-tools %}{% endblock %} {% block content_title %}

    Field: {{ original.name }}

    {% endblock %} @@ -15,69 +11,70 @@
    -
    -
    - - - +
    + + + +
    -
    +
    - - - - - - - + + {% for prefix, frm in forms.items %} + {% if prefix != "events" %} + + {% endif %} + {% endfor %} + - @@ -124,6 +185,5 @@ - {% endblock action-content %} diff --git a/src/aurora/core/templates/smart/_form.html b/src/aurora/core/templates/smart/_form.html index 0be3c4e2..0d85fd77 100644 --- a/src/aurora/core/templates/smart/_form.html +++ b/src/aurora/core/templates/smart/_form.html @@ -33,7 +33,7 @@ {% endif %} {% csrf_token %} -
    +
    {% for field in form %}{% include "smart/_fieldset.html" %}{% endfor %}
    {% for name, formset in formsets.items %} @@ -90,7 +90,13 @@ {% include "registration/_staff_edit_form.html" %} {% endif %} {% for field in form %} + {% ifchanged field.field.flex_field.advanced.css.group %} +
    + {% endifchanged %} {% include "smart/_fieldset.html" with counter=forloop.counter %} + {% ifchanged field.field.flex_field.advanced.css.group %} +
    + {% endifchanged %} {% endfor %}
    From 31d99b35c6c10e604273ea5aa2ddb073cdd94015 Mon Sep 17 00:00:00 2001 From: sax Date: Thu, 13 Apr 2023 20:10:21 +0200 Subject: [PATCH 6/9] form editor --- src/aurora/core/editors/flex_form.py | 75 ++++--- src/aurora/core/editors/forms.py | 9 +- src/aurora/core/models.py | 1 - src/aurora/core/static/admin/f_editor.css | 185 ++++++++++++++++++ src/aurora/core/static/admin/f_editor.css.map | 1 + .../static/admin/form_editor/form_editor.css | 19 +- .../admin/form_editor/form_editor.css.map | 1 + .../static/admin/form_editor/form_editor.scss | 27 ++- .../forms/widgets/fieldconfigwidget.html | 1 + 9 files changed, 274 insertions(+), 45 deletions(-) create mode 100644 src/aurora/core/static/admin/f_editor.css create mode 100644 src/aurora/core/static/admin/f_editor.css.map create mode 100644 src/aurora/core/static/admin/form_editor/form_editor.css.map create mode 100644 src/aurora/core/templates/django/forms/widgets/fieldconfigwidget.html diff --git a/src/aurora/core/editors/flex_form.py b/src/aurora/core/editors/flex_form.py index cd27a709..82be707d 100644 --- a/src/aurora/core/editors/flex_form.py +++ b/src/aurora/core/editors/flex_form.py @@ -3,6 +3,7 @@ from django.conf import settings from django.core.cache import caches from django.core.exceptions import ValidationError +from django.db.transaction import atomic from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import render from django.template.loader import get_template @@ -22,6 +23,44 @@ cache = caches["default"] +class FlexFormWrapper(FlexForm): + def __init__(self, form: FlexForm, *args, **kwargs): + self.form = form + self.overrides = {} + self._get_form_fields = self.form.get_form_fields + self.form.get_form_fields = self.get_form_fields + self.override = {} + + @property + def advanced(self): + return self.form.advanced + + @advanced.setter + def advanced(self, value): + self.form.advanced = value + + def get_form_class(self): + return self.form.get_form_class() + + def get_form_fields(self): + original = self._get_form_fields() + for field_name, field in original.items(): + original[field_name].required = self.overrides[field_name]["required"] + original[field_name].enabled = self.overrides[field_name]["enabled"] + original[field_name].label = self.overrides[field_name]["label"] + return original + + @atomic() + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): + fields = self._get_form_fields() + for field_name, field in fields.items(): + field.flex_field.required = self.overrides[field_name]["required"] + field.flex_field.enabled = self.overrides[field_name]["enabled"] + field.flex_field.label = self.overrides[field_name]["label"] + field.flex_field.save() + return self.form.save() + + class FormEditor: FORMS = { "form": FlexFormAttributesForm, @@ -44,7 +83,7 @@ def flex_form(self): @cached_property def patched_form(self) -> FlexForm: - form: FlexForm = self.flex_form + form: FlexForm = FlexFormWrapper(self.flex_form) form.advanced = {} if config := cache.get(self.cache_key, None): @@ -53,7 +92,14 @@ def patched_form(self) -> FlexForm: for prefix, frm in _forms.items(): if frm.is_valid(): if prefix == "fields": - merged["field_order"] = [x[0] for x in sorted(frm.cleaned_data.items(), key=lambda x: x[1][-1])] + ordered = [x[0] for x in sorted(frm.cleaned_data.items(), key=lambda x: x[1][-1])] + merged["field_order"] = ordered + for fname in ordered: + form.overrides[fname] = { + "label": frm.cleaned_data[fname][0], + "required": frm.cleaned_data[fname][1], + "enabled": frm.cleaned_data[fname][2], + } else: processed = [] for sect in AdvancedFlexFormAttrsForm.SECTIONS_MAP.keys(): @@ -69,8 +115,10 @@ def patched_form(self) -> FlexForm: form.advanced = merged return form - def patch(self, request, pk): - pass + def get_advanced(self): + frm: FlexForm = self.patched_form + rendered = attr_dumps(frm.advanced, indent=4) + return HttpResponse(rendered, content_type="text/plain") def get_configuration(self): frm: FlexForm = self.patched_form @@ -85,18 +133,8 @@ def get_code(self): from pygments.formatters.html import HtmlFormatter from pygments.lexers import HtmlLexer - # instance = self.patched_form - # form_class_attrs = { - # self.field.name: instance, - # } - # form_class = type(forms.Form)("TestForm", (forms.Form,), form_class_attrs) ctx = self.get_context(self.request) ctx["form"] = self.patched_form.get_form_class() - # ctx["form"] = self.flex_form.get_form_class() - # ctx["instance"] = instance - # code = Template( - # "{{ form }}" - # ).render(Context(ctx)) code = get_template("smart/_form.html").render(ctx) formatter = formatter.HTMLFormatter(indent=2) soup = bs(code) @@ -106,15 +144,8 @@ def get_code(self): ctx["code"] = highlight(prettyHTML, HtmlLexer(), formatter) return render(self.request, "admin/core/flexformfield/field_editor/code.html", ctx, content_type="text/html") - def get_advanced(self): - rendered = attr_dumps(self.flex_form.advanced, indent=4) - return HttpResponse(rendered, content_type="text/plain") - def render(self): instance = self.patched_form - # form_class_attrs = { - # 'fo': instance, - # } form_class = self.flex_form.get_form_class() ctx = self.get_context(self.request) if self.request.method == "POST": @@ -188,5 +219,5 @@ def get(self, request, pk): def post(self, request, pk): forms = self.get_forms() if all(map(lambda f: f.is_valid(), forms.values())): - # self.patched_f.save() + self.patched_form.save() return HttpResponseRedirect(".") diff --git a/src/aurora/core/editors/forms.py b/src/aurora/core/editors/forms.py index f0c9cde7..259a0bcd 100644 --- a/src/aurora/core/editors/forms.py +++ b/src/aurora/core/editors/forms.py @@ -217,11 +217,13 @@ class Meta: class FieldConfigWidget(widgets.MultiWidget): + template_name = "django/forms/widgets/fieldconfigwidget.html" + def __init__(self, attrs=None): _widgets = ( widgets.TextInput(attrs={"required": "required"}), - widgets.CheckboxInput(attrs={"class": "required"}), - widgets.CheckboxInput(attrs={"class": "enabled"}), + widgets.CheckboxInput(attrs={"class": "required", "title": "required"}), + widgets.CheckboxInput(attrs={"class": "enabled", "title": "enabled"}), widgets.HiddenInput(attrs={"style": "width: 20px", "class": "ordering"}), ) super().__init__(_widgets, attrs) @@ -237,9 +239,6 @@ def value_from_datadict(self, data, files, name): for widget_name, widget in zip(self.widgets_names, self.widgets) ] - def format_output(self, rendered_widgets): - return "".join(rendered_widgets) - class FieldConfigField(forms.Field): def clean(self, value: Any) -> Any: diff --git a/src/aurora/core/models.py b/src/aurora/core/models.py index 209eb69d..6e6b0db3 100644 --- a/src/aurora/core/models.py +++ b/src/aurora/core/models.py @@ -388,7 +388,6 @@ def get_form_attrs(self): "flex_form": self, "compilation_time_field": fields.pop("compilation_time_field_name", None), "field_order": self.advanced.get("field_order"), - # "indexes": indexes, **fields, } diff --git a/src/aurora/core/static/admin/f_editor.css b/src/aurora/core/static/admin/f_editor.css new file mode 100644 index 00000000..fc9ef7cb --- /dev/null +++ b/src/aurora/core/static/admin/f_editor.css @@ -0,0 +1,185 @@ +body { + height: 100px; +} + +#fieldEditor, #formEditor { + height: 100%; + position: relative; + top: 0; + background-color: var(--body-bg); + z-index: 10; +} +#fieldEditor .submit-row, #formEditor .submit-row { + width: 100%; + z-index: 999; +} +#fieldEditor .submit-row div:first-child, #formEditor .submit-row div:first-child { + float: left; +} +#fieldEditor .submit-row input, #formEditor .submit-row input { + background-color: cornflowerblue; +} +#fieldEditor #mainPanel, #formEditor #mainPanel { + top: 0; + display: inline-flex; + width: 100%; + height: 100%; +} +#fieldEditor #mainPanel .panel, #formEditor #mainPanel .panel { + display: inline-block; + height: 500px; +} +#fieldEditor #mainPanel .panel:first-child, #formEditor #mainPanel .panel:first-child { + margin-right: 10px; +} +#fieldEditor #mainPanel .output, #formEditor #mainPanel .output { + position: sticky; + top: 40px; + width: 40%; +} +#fieldEditor #mainPanel .output iframe, #formEditor #mainPanel .output iframe { + width: 100%; + height: 100%; + border: 0 transparent; + display: none; +} +#fieldEditor #mainPanel .settings, #formEditor #mainPanel .settings { + overflow-y: scroll; + width: 60%; + background: var(--breadcrumbs-bg); +} +#fieldEditor #mainPanel .settings #event_selector option, #formEditor #mainPanel .settings #event_selector option { + font-style: oblique; + font-weight: bold; + color: red; +} +#fieldEditor #mainPanel .settings #event_selector option.used::after, #formEditor #mainPanel .settings #event_selector option.used::after { + content: " --- "; + color: red; +} +#fieldEditor #mainPanel .settings table:first-child, #formEditor #mainPanel .settings table:first-child { + background: var(--breadcrumbs-bg); + width: 100%; + margin: 0; + padding: 0; + border-collapse: separate; + /* Don't collapse */ + border-spacing: 0; + border: 1px solid grey; +} +#fieldEditor #mainPanel .settings table:first-child thead, #formEditor #mainPanel .settings table:first-child thead { + background-color: var(--body-bg); +} +#fieldEditor #mainPanel .settings table:first-child thead tr.tabs, #formEditor #mainPanel .settings table:first-child thead tr.tabs { + width: 100%; + padding: 0; + margin-top: 5px; + margin-bottom: 0; +} +#fieldEditor #mainPanel .settings table:first-child thead tr.tabs th, #formEditor #mainPanel .settings table:first-child thead tr.tabs th { + text-transform: capitalize; + font-weight: normal; + font-size: 90%; + text-align: center; + border: 1px solid grey; + cursor: pointer; + background-color: var(--body-bg); + position: sticky; + z-index: 6; + top: 0; +} +#fieldEditor #mainPanel .settings table:first-child thead tr.tabs th.selected, #formEditor #mainPanel .settings table:first-child thead tr.tabs th.selected { + background: var(--breadcrumbs-bg); + border-bottom: none; +} +#fieldEditor #mainPanel .settings table:first-child tbody, #formEditor #mainPanel .settings table:first-child tbody { + background: var(--breadcrumbs-bg); +} +#fieldEditor #mainPanel .settings table:first-child tbody td, #formEditor #mainPanel .settings table:first-child tbody td { + background: var(--breadcrumbs-bg); +} +#fieldEditor #mainPanel .settings table:first-child .cfg-form, #formEditor #mainPanel .settings table:first-child .cfg-form { + width: 100%; + background: var(--breadcrumbs-bg); +} +#fieldEditor #mainPanel .settings table:first-child .cfg-form tr, #formEditor #mainPanel .settings table:first-child .cfg-form tr { + background: var(--breadcrumbs-bg); +} +#fieldEditor #mainPanel .settings table:first-child .cfg-form th, #formEditor #mainPanel .settings table:first-child .cfg-form th { + width: 10%; +} +#fieldEditor #mainPanel .settings table:first-child .cfg-form td, #formEditor #mainPanel .settings table:first-child .cfg-form td { + text-align: left; + align-content: flex-start; + padding-right: 5px; +} +#fieldEditor #mainPanel .settings table:first-child .cfg-form td input:not([type=checkbox]), #fieldEditor #mainPanel .settings table:first-child .cfg-form td textarea, #formEditor #mainPanel .settings table:first-child .cfg-form td input:not([type=checkbox]), #formEditor #mainPanel .settings table:first-child .cfg-form td textarea { + width: 97%; +} +#fieldEditor #mainPanel .settings table:first-child .cfg-form td textarea, #formEditor #mainPanel .settings table:first-child .cfg-form td textarea { + height: 50px; +} +#fieldEditor #mainPanel .settings table:first-child .cfg-form td select, #formEditor #mainPanel .settings table:first-child .cfg-form td select { + width: 100%; +} +#fieldEditor #mainPanel .settings table:first-child .cfg-form.collapsed, #formEditor #mainPanel .settings table:first-child .cfg-form.collapsed { + display: none; +} + +.field-error { + color: red; + margin-bottom: 3px; +} + +table2 { + width: 100%; + padding: 0; + margin: 0; +} +table2.cfg-form { + margin-bottom: 5px; + border-left: 1px solid grey; + padding: 0; + margin: 0; +} +table2 tr { + display: table-row; +} +table2 tr th { + min-height: 200px; +} +table2 tr td { + width: 100%; + padding-left: 0; +} +table2 tr td input, table2 tr td select, table2 tr td textarea { + width: 100%; +} + +.toolbar { + margin: 10px; +} +.toolbar label { + margin-right: 10px; + cursor: pointer; +} + +#events .code { + display: none; +} +#events .code.selected { + display: block; +} + +.cm-container { + margin: 0; + background-color: var(--body-bg); + width: 100%; +} +.cm-container .cm-toolbar a.toolbutton { + margin: 0 3px 5px 0; + padding: 5px; + font-variant: none; +} + +/*# sourceMappingURL=f_editor.css.map */ diff --git a/src/aurora/core/static/admin/f_editor.css.map b/src/aurora/core/static/admin/f_editor.css.map new file mode 100644 index 00000000..c4ce61c4 --- /dev/null +++ b/src/aurora/core/static/admin/f_editor.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["f_editor.scss"],"names":[],"mappings":"AAAA;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGA;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAGE;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAMN;EACE;EACA;EACA;EACA;EACA;AAA2B;EAC3B;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAMR;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAKJ;EACE;;;AAsBZ;EACE;EACA;;;AAsBF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAEA;EACE;;AAGF;EACE;EACA;;AAEA;EACE;;;AAOR;EACE;;AAEA;EACE;EACA;;;AAKF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;;AAGE;EACE;EACA;EACA","file":"f_editor.css"} diff --git a/src/aurora/core/static/admin/form_editor/form_editor.css b/src/aurora/core/static/admin/form_editor/form_editor.css index 4e24355d..8d4962f8 100644 --- a/src/aurora/core/static/admin/form_editor/form_editor.css +++ b/src/aurora/core/static/admin/form_editor/form_editor.css @@ -182,19 +182,24 @@ table2 tr td input, table2 tr td select, table2 tr td textarea { font-variant: none; } +#formEditor #mainPanel .settings #fields tr td .subwidget { + display: inline-block; + content: ""; + width: 14px; + height: 14px; + background-size: 14px 14px; + background-image: url("admin_ordering-icon.svg"); + background-repeat: no-repeat; + background-position: center center; + vertical-align: middle; + margin-right: 5px; +} #formEditor #mainPanel .settings #fields tr td input { margin-right: 10px; } #formEditor #mainPanel .settings #fields tr td input[type=text] { width: 60%; -} -#formEditor #mainPanel .settings #fields tr td input[type=text]::before { - content: "=="; display: inline-block; - background-size: 14px 14px; - background-image: url("admin_ordering-icon.svg"); - background-repeat: no-repeat; - background-position: center center; } /*# sourceMappingURL=form_editor.css.map */ diff --git a/src/aurora/core/static/admin/form_editor/form_editor.css.map b/src/aurora/core/static/admin/form_editor/form_editor.css.map new file mode 100644 index 00000000..8452277c --- /dev/null +++ b/src/aurora/core/static/admin/form_editor/form_editor.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["../f_editor.scss","form_editor.scss"],"names":[],"mappings":"AAAA;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGA;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAGE;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAMN;EACE;EACA;EACA;EACA;EACA;AAA2B;EAC3B;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAMR;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAKJ;EACE;;;AAsBZ;EACE;EACA;;;AAsBF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAEA;EACE;;AAGF;EACE;EACA;;AAEA;EACE;;;AAOR;EACE;;AAEA;EACE;EACA;;;AAKF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;;AAGE;EACE;EACA;EACA;;;ACtPM;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA","file":"form_editor.css"} diff --git a/src/aurora/core/static/admin/form_editor/form_editor.scss b/src/aurora/core/static/admin/form_editor/form_editor.scss index 1e8fb18b..cba88fb1 100644 --- a/src/aurora/core/static/admin/form_editor/form_editor.scss +++ b/src/aurora/core/static/admin/form_editor/form_editor.scss @@ -7,24 +7,31 @@ #fields { tr { td { - input{ + .subwidget { + display: inline-block; + content: ""; + width: 14px; + height: 14px; + background-size: 14px 14px; + background-image: url("admin_ordering-icon.svg"); + background-repeat: no-repeat; + background-position: center center; + vertical-align: middle; + margin-right: 5px; + } + + input { margin-right: 10px; } + input[type=text] { width: 60%; - - &::before { - content: "=="; - display: inline-block; - background-size: 14px 14px; - background-image: url("admin_ordering-icon.svg"); - background-repeat: no-repeat; - background-position: center center; - } + display: inline-block; } } } } } } + } diff --git a/src/aurora/core/templates/django/forms/widgets/fieldconfigwidget.html b/src/aurora/core/templates/django/forms/widgets/fieldconfigwidget.html new file mode 100644 index 00000000..1b313305 --- /dev/null +++ b/src/aurora/core/templates/django/forms/widgets/fieldconfigwidget.html @@ -0,0 +1 @@ +{% spaceless %}{% for widget in widget.subwidgets %}{% include widget.template_name %}{% endfor %}{% endspaceless %} From 3f95e91768c1eaa795478805dd583a1cdedc51a8 Mon Sep 17 00:00:00 2001 From: sax Date: Thu, 13 Apr 2023 20:30:48 +0200 Subject: [PATCH 7/9] form editor field editing --- src/aurora/core/editors/flex_form.py | 46 ++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/aurora/core/editors/flex_form.py b/src/aurora/core/editors/flex_form.py index 82be707d..d7114009 100644 --- a/src/aurora/core/editors/flex_form.py +++ b/src/aurora/core/editors/flex_form.py @@ -43,21 +43,43 @@ def get_form_class(self): return self.form.get_form_class() def get_form_fields(self): - original = self._get_form_fields() - for field_name, field in original.items(): - original[field_name].required = self.overrides[field_name]["required"] - original[field_name].enabled = self.overrides[field_name]["enabled"] - original[field_name].label = self.overrides[field_name]["label"] - return original + from aurora.core.fields import CompilationTimeField + + fields = {} + # indexes = FlexFormBaseForm.indexes.copy() + # base_order = self.advanced.get("field_order", []) + + for field in self.form.fields.order_by("ordering"): + try: + field.required = self.overrides[field.name]["required"] + field.label = self.overrides[field.name]["label"] + field.enabled = self.overrides[field.name]["enabled"] + fld = field.get_instance() + if field.enabled: + fields[field.name] = fld + if isinstance(fld, CompilationTimeField): + fields["compilation_time_field_name"] = field.name + self.form._initial[field.name] = field.get_default_value() + except TypeError: + pass + return fields + # + # original = self._get_form_fields() + # fields = {} + # for field_name, field in original.items(): + # if self.overrides[field_name]["enabled"]: + # fields[field_name] = field + # fields[field_name].required = self.overrides[field_name]["required"] + # fields[field_name].label = self.overrides[field_name]["label"] + # return fields @atomic() def save(self, force_insert=False, force_update=False, using=None, update_fields=None): - fields = self._get_form_fields() - for field_name, field in fields.items(): - field.flex_field.required = self.overrides[field_name]["required"] - field.flex_field.enabled = self.overrides[field_name]["enabled"] - field.flex_field.label = self.overrides[field_name]["label"] - field.flex_field.save() + for field in self.form.fields.order_by("ordering"): + field.required = self.overrides[field.name]["required"] + field.label = self.overrides[field.name]["label"] + field.enabled = self.overrides[field.name]["enabled"] + field.save() return self.form.save() From 060fe5c4901c30492904242b2edb491eb9004341 Mon Sep 17 00:00:00 2001 From: sax Date: Fri, 14 Apr 2023 05:58:25 +0200 Subject: [PATCH 8/9] bug fixes --- src/aurora/core/editors/flex_form.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/aurora/core/editors/flex_form.py b/src/aurora/core/editors/flex_form.py index d7114009..8709d31e 100644 --- a/src/aurora/core/editors/flex_form.py +++ b/src/aurora/core/editors/flex_form.py @@ -23,7 +23,7 @@ cache = caches["default"] -class FlexFormWrapper(FlexForm): +class FlexFormWrapper: def __init__(self, form: FlexForm, *args, **kwargs): self.form = form self.overrides = {} @@ -42,6 +42,9 @@ def advanced(self, value): def get_form_class(self): return self.form.get_form_class() + def get_form_attrs(self): + return self.form.get_form_attrs() + def get_form_fields(self): from aurora.core.fields import CompilationTimeField From c1f2e720d375f42b7a203bbef602e1e1cd324ead Mon Sep 17 00:00:00 2001 From: sax Date: Fri, 14 Apr 2023 09:12:30 +0200 Subject: [PATCH 9/9] updates --- src/aurora/core/editors/flex_form.py | 9 ++------- src/aurora/core/forms.py | 11 ++++++++++- src/aurora/core/models.py | 21 +++++++++++++-------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/aurora/core/editors/flex_form.py b/src/aurora/core/editors/flex_form.py index 8709d31e..bfed45b7 100644 --- a/src/aurora/core/editors/flex_form.py +++ b/src/aurora/core/editors/flex_form.py @@ -46,12 +46,7 @@ def get_form_attrs(self): return self.form.get_form_attrs() def get_form_fields(self): - from aurora.core.fields import CompilationTimeField - fields = {} - # indexes = FlexFormBaseForm.indexes.copy() - # base_order = self.advanced.get("field_order", []) - for field in self.form.fields.order_by("ordering"): try: field.required = self.overrides[field.name]["required"] @@ -60,8 +55,8 @@ def get_form_fields(self): fld = field.get_instance() if field.enabled: fields[field.name] = fld - if isinstance(fld, CompilationTimeField): - fields["compilation_time_field_name"] = field.name + # if isinstance(fld, CompilationTimeField): + # fields["compilation_time_field_name"] = field.name self.form._initial[field.name] = field.get_default_value() except TypeError: pass diff --git a/src/aurora/core/forms.py b/src/aurora/core/forms.py index 5770db1f..cc3e1eb0 100644 --- a/src/aurora/core/forms.py +++ b/src/aurora/core/forms.py @@ -6,6 +6,7 @@ from django.core.exceptions import ValidationError from django.forms import BaseFormSet from django.utils import formats +from django.utils.functional import cached_property from django.utils.translation import gettext as _ from .fields.widgets import JavascriptEditor @@ -26,10 +27,18 @@ class CustomFieldMixin: class FlexFormBaseForm(forms.Form): flex_form = None - compilation_time_field = None + # compilation_time_field = None indexes = {"1": None, "2": None, "3": None} field_order = None + @cached_property + def compilation_time_field(self) -> str: + from aurora.core.fields import CompilationTimeField + + for name, fld in self.fields.items(): + if isinstance(fld, CompilationTimeField): + return name + def get_counters(self, data): if self.compilation_time_field: return data.pop(self.compilation_time_field, {}) diff --git a/src/aurora/core/models.py b/src/aurora/core/models.py index 6e6b0db3..43572cc4 100644 --- a/src/aurora/core/models.py +++ b/src/aurora/core/models.py @@ -363,30 +363,35 @@ def add_formset(self, form, **extra): # @cache_form def get_form_fields(self): - from aurora.core.fields import CompilationTimeField - fields = {} - # indexes = FlexFormBaseForm.indexes.copy() # base_order = self.advanced.get("field_order", []) for field in self.fields.filter(enabled=True).select_related("validator").order_by("ordering"): try: fld = field.get_instance() fields[field.name] = fld - if isinstance(fld, CompilationTimeField): - fields["compilation_time_field_name"] = field.name - # if index := field.advanced.get("smart", {}).get("index"): - # indexes[str(index)] = field.name + # if isinstance(fld, CompilationTimeField): + # fields["compilation_time_field_name"] = field.name self._initial[field.name] = field.get_default_value() except TypeError: pass return fields + def get_indexed_fields(self, fields): + indexes = FlexFormBaseForm.indexes.copy() + for name, field in fields.items(): + if index := field.flex_field.advanced.get("smart", {}).get("index"): + indexes[str(index)] = name + return indexes + def get_form_attrs(self): fields = self.get_form_fields() + indexed = self.get_indexed_fields(fields) + return { "flex_form": self, - "compilation_time_field": fields.pop("compilation_time_field_name", None), + # "compilation_time_field": fields.pop("compilation_time_field_name", None), + "indexes": indexed, "field_order": self.advanced.get("field_order"), **fields, }
    Flex Field attributesForm Field attributesWidget Field attributesSmart Field attributesCSS Field attributesEvents
    {{ frm.Meta.title }}{{ forms.events.Meta.title }} Usage
    + {% csrf_token %} - - - - - - {{ form_field }} -
    Form - {{ original.flex_form }} -
    - - {{ form_kwargs }} - - - {{ form_widget }} - - - {{ form_smart }} - - - {{ form_css }} - + {% for prefix, frm in forms.items %} + {% if prefix != "events" %} + + + + + {{ frm }} + + {% endif %} + {% endfor %} @@ -126,8 +117,7 @@ -
    
    +        {#        
    #}
         
    -    
    +    
     {% endblock action-content %}
    diff --git a/src/aurora/core/templates/admin/core/flexformfield/field_editor/preview.html b/src/aurora/core/templates/admin/core/flexformfield/field_editor/preview.html
    index 620377df..252a8fd0 100644
    --- a/src/aurora/core/templates/admin/core/flexformfield/field_editor/preview.html
    +++ b/src/aurora/core/templates/admin/core/flexformfield/field_editor/preview.html
    @@ -1,20 +1,22 @@
     {% load static aurora %}
    -
    -
    -
    -
    -{##}
    -{##}
    -{##}
    -{##}
    -{##}
    -{##}
    -{{ form.media }}
    -
    +
    +
    +    
    +    
    +    
    +    
    +    {#    #}
    +    {{ media }}
    +
    +
     {% if request.method == "POST" %}
         {% if valid %}
             
    Success
    @@ -25,7 +27,7 @@ {% endif %} {% endif %}
    -
    + {% csrf_token %} {% for field in form %} {% include "smart/_fieldset.html" %} @@ -35,3 +37,8 @@
    +
    + +
    + + diff --git a/src/aurora/core/templates/admin/core/widgets/editor.html b/src/aurora/core/templates/admin/core/widgets/editor.html index b01a57a4..ff0f6a75 100644 --- a/src/aurora/core/templates/admin/core/widgets/editor.html +++ b/src/aurora/core/templates/admin/core/widgets/editor.html @@ -5,7 +5,7 @@ undo redo fullscreen - run +{# run #} {% endif %} @@ -22,6 +22,7 @@ lineWrapping: true, indentUnit: 4, styleActiveLine: true, + showTrailingSpace: true, theme: "{{ widget.attrs.theme }}", foldGutter: true, gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"], diff --git a/src/aurora/core/templates/django/forms/widgets/boolean.html b/src/aurora/core/templates/django/forms/widgets/boolean.html new file mode 100644 index 00000000..58932fa1 --- /dev/null +++ b/src/aurora/core/templates/django/forms/widgets/boolean.html @@ -0,0 +1,3 @@ +{% load itrans aurora %}{% if widget.flex_field.advanced.custom.position == "l" %}{% trans widget.flex_field.advanced.custom.captions %}{% endif %} +{% include "django/forms/widgets/input.html" %} +{% if widget.flex_field.advanced.custom.position == "r" %}{% trans widget.flex_field.advanced.custom.captions %}{% endif %} diff --git a/src/aurora/core/templates/django/forms/widgets/checkbox.html b/src/aurora/core/templates/django/forms/widgets/checkbox.html new file mode 100644 index 00000000..08b1e61c --- /dev/null +++ b/src/aurora/core/templates/django/forms/widgets/checkbox.html @@ -0,0 +1 @@ +{% include "django/forms/widgets/input.html" %} diff --git a/src/aurora/core/templates/django/forms/widgets/jsregex.html b/src/aurora/core/templates/django/forms/widgets/jsregex.html new file mode 100644 index 00000000..fbcf08eb --- /dev/null +++ b/src/aurora/core/templates/django/forms/widgets/jsregex.html @@ -0,0 +1,20 @@ + +Regexnbsp;101 + diff --git a/src/aurora/core/templates/smart/_fieldset.html b/src/aurora/core/templates/smart/_fieldset.html index d380fd97..c268b319 100644 --- a/src/aurora/core/templates/smart/_fieldset.html +++ b/src/aurora/core/templates/smart/_fieldset.html @@ -1,6 +1,7 @@ {% load itrans aurora %}{% with field.field.flex_field.advanced as ADV %} {% if can_edit_inpage %}{% include "registration/_staff_edit_field.html" %}{% endif %} -
    +
    {% if field.field.flex_field.validator and registration.client_validation %} {% endif %} @@ -21,20 +22,20 @@ -
    +
    {% if ADV.smart.description %}{% trans ADV.smart.description %}{% endif %}
    {# -- {{ field.field.flex_field.advanced.events.validation }} --#} {{ field.errors }} {{ field }} -
    +
    {# {% if user.is_staff %}{% include "i18n/_staff_edit_i18n.html" with original=field|smart:"hint,0" %}{% endif %}#} {% trans ADV.smart.hint %}
    diff --git a/src/aurora/core/version_media.py b/src/aurora/core/version_media.py index 751ac3e7..d823650c 100644 --- a/src/aurora/core/version_media.py +++ b/src/aurora/core/version_media.py @@ -11,9 +11,9 @@ class VersionMedia(forms.Media): def __str__(self): return self.render() - # def render_js(self): - # version = os.environ.get("VERSION", "") - # return [format_html('', self.absolute_path(path), version) for path in self._js] + def render_js(self): + version = os.environ.get("VERSION", "") + return [format_html('', self.absolute_path(path), version) for path in self._js] def render_css(self): # To keep rendering order consistent, we can't just iterate over items(). diff --git a/src/aurora/registration/static/registration/survey.js b/src/aurora/registration/static/registration/survey.js index 80e87f05..6f12631c 100644 --- a/src/aurora/registration/static/registration/survey.js +++ b/src/aurora/registration/static/registration/survey.js @@ -15,21 +15,15 @@ const lang = $("meta[name=\"Language\"]").attr("content"); const sessionUrl = get_session(); var parts = location.href.split("/"); - console.log(lang); $.get("/api/registration/" + pk + "/" + lang + "/version/?" + Math.random(), function (data) { const version = parseInt(parts[parts.length - 2]); if (version !== data.version) { - console.log("version mismatch: redirect", data.url) location.href = data.url; } else if (data.auth && (data.session_id !== sessionUrl)) { - console.log("session_id mismatch: redirect", data.url) location.href = data.url; } else if (!data.auth && sessionUrl) { - console.log("session_id tampered with: redirect", data.url) location.href = data.url; - } else { - console.log("version matches", data.url) - } + }; }); }); })($); diff --git a/src/aurora/registration/static/registration/survey.min.js b/src/aurora/registration/static/registration/survey.min.js index 3f088e53..7b009282 100644 --- a/src/aurora/registration/static/registration/survey.min.js +++ b/src/aurora/registration/static/registration/survey.min.js @@ -1 +1 @@ -(function($){var queryParams=window.location.search.substring(1).split("&").reduce(function(q,query){var chunks=query.split("=");var key=chunks[0];var value=decodeURIComponent(chunks[1]);value=isNaN(Number(value))?value:Number(value);return q[key]=value,q},{});var get_session=function(){return queryParams["s"]};$(function(){const pk=$('meta[name="RegId"]').attr("content");const lang=$('meta[name="Language"]').attr("content");const sessionUrl=get_session();var parts=location.href.split("/");console.log(lang);$.get("/api/registration/"+pk+"/"+lang+"/version/?"+Math.random(),function(data){const version=parseInt(parts[parts.length-2]);if(version!==data.version){console.log("version mismatch: redirect",data.url);location.href=data.url}else if(data.auth&&data.session_id!==sessionUrl){console.log("session_id mismatch: redirect",data.url);location.href=data.url}else if(!data.auth&&sessionUrl){console.log("session_id tampered with: redirect",data.url);location.href=data.url}else{console.log("version matches",data.url)}})})})($); +(function($){var queryParams=window.location.search.substring(1).split("&").reduce(function(q,query){var chunks=query.split("=");var key=chunks[0];var value=decodeURIComponent(chunks[1]);value=isNaN(Number(value))?value:Number(value);return q[key]=value,q},{});var get_session=function(){return queryParams["s"]};$(function(){const pk=$('meta[name="RegId"]').attr("content");const lang=$('meta[name="Language"]').attr("content");const sessionUrl=get_session();var parts=location.href.split("/");$.get("/api/registration/"+pk+"/"+lang+"/version/?"+Math.random(),function(data){const version=parseInt(parts[parts.length-2]);if(version!==data.version){location.href=data.url}else if(data.auth&&data.session_id!==sessionUrl){location.href=data.url}else if(!data.auth&&sessionUrl){location.href=data.url}})})})($); diff --git a/src/aurora/registration/templates/registration/register.html b/src/aurora/registration/templates/registration/register.html index e5f0678a..fdc55a8b 100644 --- a/src/aurora/registration/templates/registration/register.html +++ b/src/aurora/registration/templates/registration/register.html @@ -12,11 +12,10 @@ {% endblock meta %} {% block cache %}{% endblock %} {% block head %} -{# #} -{# #} -{# #} - + {{ media }} {% endblock %} {% block object-tools %}{% endblock %} diff --git a/src/aurora/core/templates/admin/core/flexformfield/field_editor/preview.html b/src/aurora/core/templates/admin/core/flexformfield/field_editor/preview.html index 252a8fd0..4125a3f7 100644 --- a/src/aurora/core/templates/admin/core/flexformfield/field_editor/preview.html +++ b/src/aurora/core/templates/admin/core/flexformfield/field_editor/preview.html @@ -9,6 +9,9 @@ + {# #} - {% block cache %}{##}{% endblock %} - + {% block cache %}{% endblock %} {% block head %}{% endblock head %} @@ -43,213 +39,5 @@ {% endblock %} {% block body %}{% endblock body %} {% block footer %}{% include "_footer.html" %}{% endblock %} - -{##} - diff --git a/src/aurora/web/templates/homes/org_index.html b/src/aurora/web/templates/homes/org_index.html new file mode 100644 index 00000000..4c13cd95 --- /dev/null +++ b/src/aurora/web/templates/homes/org_index.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} +{% block body %} +
    +
    +

    {{ organization }}

    +
    +
      {% for prj in organization.projects.all %} +
    • + {{ prj }} +
    • + {% endfor %} +
    +
    +
    +
    + +{% endblock %} diff --git a/src/aurora/web/templates/homes/prj_index.html b/src/aurora/web/templates/homes/prj_index.html new file mode 100644 index 00000000..9387ad5b --- /dev/null +++ b/src/aurora/web/templates/homes/prj_index.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} +{% block body %} +
    +
    +

    {{ organization }}

    +

    {{ project }}

    +
    +
      {% for reg in project.registrations.all %} +
    • + {% if reg.show_in_homepage or user.is_staff %} + {{ reg }} + {% endif %} +
    • + {% endfor %} +
    +
    +
    +
    +{% endblock %} diff --git a/src/aurora/web/urls.py b/src/aurora/web/urls.py index 5cac815b..43d10f0d 100644 --- a/src/aurora/web/urls.py +++ b/src/aurora/web/urls.py @@ -10,6 +10,8 @@ QRCodeView, RegistrarLoginView, offline, + org_index, + prj_index, ) urlpatterns = [ @@ -22,4 +24,6 @@ path("maintenance", MaintenanceView.as_view(), name="maintenance"), path("qrcode/", QRCodeView.as_view(), name="qrcode"), path("offline/", offline, name="offline"), + path("org//", org_index, name="org-index"), + path("org///", prj_index, name="prj-index"), ] diff --git a/src/aurora/web/views/__init__.py b/src/aurora/web/views/__init__.py index 517f6ddc..daffc82e 100644 --- a/src/aurora/web/views/__init__.py +++ b/src/aurora/web/views/__init__.py @@ -1,5 +1,3 @@ -# from .api import RegistrationDataApi -# from .core import OptionsListView -# from .registration import QRVerify, RegisterCompleteView, RegisterView from .login import LoginRouter, RegistrarLoginView from .site import HomeView, MaintenanceView, PageView, ProbeView, QRCodeView, offline +from .homes import org_index, prj_index diff --git a/src/aurora/web/views/homes.py b/src/aurora/web/views/homes.py new file mode 100644 index 00000000..72f89441 --- /dev/null +++ b/src/aurora/web/views/homes.py @@ -0,0 +1,27 @@ +import logging +from django.shortcuts import get_object_or_404 +from django.http import Http404 +from aurora.core.models import Organization, Project +from aurora.core.utils import render + +logger = logging.getLogger(__name__) + + +def org_index(request, org): + ctx = {} + organization = get_object_or_404(Organization, slug=org) + ctx["organization"] = organization + + return render(request, "homes/org_index.html", ctx) + + +def prj_index(request, org, prj): + ctx = {} + try: + project = Project.objects.select_related("organization").get(organization__slug=org, slug=prj) + except Project.DoesNotExist: + raise Http404("No Project matches the given query.") + ctx["organization"] = project.organization + ctx["project"] = project + + return render(request, "homes/prj_index.html", ctx) diff --git a/src/aurora/web/views/site.py b/src/aurora/web/views/site.py index 4fee2c80..e2847789 100644 --- a/src/aurora/web/views/site.py +++ b/src/aurora/web/views/site.py @@ -13,6 +13,7 @@ from django.views.generic import TemplateView from aurora.core.utils import get_etag, get_qrcode, render +from aurora.i18n.gettext import gettext as _ from aurora.registration.models import Registration logger = logging.getLogger(__name__) @@ -44,7 +45,6 @@ def get_template_names(self): return [f"{self.kwargs['page']}.html"] def get_context_data(self, **kwargs): - from aurora.i18n.gettext import gettext as _ return super().get_context_data( title="Title", registrations=get_active_registrations(), title2=_("Title2"), **kwargs diff --git a/src/dbtemplates/static/dbtemplates/css/django.css b/src/dbtemplates/static/dbtemplates/css/django.scss similarity index 95% rename from src/dbtemplates/static/dbtemplates/css/django.css rename to src/dbtemplates/static/dbtemplates/css/django.scss index 055b5e6d..4b748350 100644 --- a/src/dbtemplates/static/dbtemplates/css/django.css +++ b/src/dbtemplates/static/dbtemplates/css/django.scss @@ -1,6 +1,9 @@ html { cursor: text; } +body{ + background-color: azure; +} .editbox { margin: .4em; diff --git a/tests/conftest.py b/tests/conftest.py index 68816a31..a36d2b22 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -34,7 +34,9 @@ def configure_settings(settings): def pytest_configure(config): os.environ["DEBUG"] = "0" os.environ["ADMINS"] = "admin@demo.org" - os.environ["CAPTCHA_TEST_MODE"] = "true" + os.environ["SESSION_COOKIE_HTTPONLY"] = "1" + os.environ["SESSION_COOKIE_SECURE"] = "0" + os.environ["CSRF_COOKIE_SECURE"] = "0" if config.option.show_browser: setattr(config.option, "enable_selenium", True) diff --git a/tox.ini b/tox.ini index c6fe0d41..d0f2e247 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,12 @@ [tox] -envlist = py39 -;skip_missing_interpreters = true +;envlist = py39 +envlist = d{32,42}-py39 +skip_missing_interpreters = true isolated_build = True [testenv] whitelist_externals = poetry -;skip_install = true +skip_install = true passenv = DATABASE_URL STATIC_URL @@ -21,6 +22,8 @@ setenv = deps = poetry + d32: django==3.2.* + d42: django==4.2.* commands_pre = poetry install --no-root --sync -v From e9bd2efff5d47ce00500443b0358b03ac47761ce Mon Sep 17 00:00:00 2001 From: sax Date: Tue, 11 Apr 2023 18:33:03 +0200 Subject: [PATCH 4/9] add missing css --- .../static/dbtemplates/css/django.css | 74 +++++++++++++++++++ .../static/dbtemplates/css/django.css.map | 1 + 2 files changed, 75 insertions(+) create mode 100644 src/dbtemplates/static/dbtemplates/css/django.css create mode 100644 src/dbtemplates/static/dbtemplates/css/django.css.map diff --git a/src/dbtemplates/static/dbtemplates/css/django.css b/src/dbtemplates/static/dbtemplates/css/django.css new file mode 100644 index 00000000..e54fe3c7 --- /dev/null +++ b/src/dbtemplates/static/dbtemplates/css/django.css @@ -0,0 +1,74 @@ +html { + cursor: text; +} + +body { + background-color: azure; +} + +.editbox { + margin: 0.4em; + padding: 0; + font-family: monospace; + font-size: 10pt; + color: black; +} + +.editbox p { + margin: 0; +} + +span.django { + color: #999; +} + +span.django-quote { + color: #281; +} + +span.django-tag-name { + color: #000; + font-weight: bold; +} + +span.xml-tagname { + color: #A0B; +} + +span.xml-attribute { + color: #281; +} + +span.xml-punctuation { + color: black; +} + +span.xml-attname { + color: #00F; +} + +span.xml-comment { + color: #A70; +} + +span.xml-cdata { + color: #48A; +} + +span.xml-processing { + color: #999; +} + +span.xml-entity { + color: #A22; +} + +span.xml-error { + color: #F00 !important; +} + +span.xml-text { + color: black; +} + +/*# sourceMappingURL=django.css.map */ diff --git a/src/dbtemplates/static/dbtemplates/css/django.css.map b/src/dbtemplates/static/dbtemplates/css/django.css.map new file mode 100644 index 00000000..a6606c40 --- /dev/null +++ b/src/dbtemplates/static/dbtemplates/css/django.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["django.scss"],"names":[],"mappings":"AAAA;EACE;;;AAEF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACC;;;AAGD;EACE","file":"django.css"} From 1f4345885d682d8995240c4707f5835a999d31b6 Mon Sep 17 00:00:00 2001 From: sax Date: Thu, 13 Apr 2023 14:56:44 +0200 Subject: [PATCH 5/9] form fields ordering --- src/aurora/core/admin/flex_form.py | 40 ++- src/aurora/core/editors/field.py | 12 - .../form_editor.py => editors/flex_form.py} | 148 +++++----- src/aurora/core/editors/forms.py | 126 +++++++-- src/aurora/core/editors/utils.py | 25 ++ src/aurora/core/forms.py | 1 + src/aurora/core/models.py | 28 +- .../field_editor.scss => f_editor.scss} | 19 +- .../admin/field_editor/field_editor.css.map | 2 +- .../static/admin/field_editor/field_editor.js | 19 +- .../admin/field_editor/field_editor.min.js | 2 +- .../admin/field_editor/field_editor.scss | 259 +----------------- .../admin/form_editor/admin_ordering-icon.svg | 8 + .../admin/form_editor/field_editor.css.map | 1 - .../{field_editor.css => form_editor.css} | 30 +- .../static/admin/form_editor/form_editor.js | 97 ++++--- .../admin/form_editor/form_editor.min.js | 2 +- .../static/admin/form_editor/form_editor.scss | 30 ++ src/aurora/core/static/edit.js | 13 +- src/aurora/core/static/edit.min.js | 2 +- src/aurora/core/static/smart_field.js | 20 +- src/aurora/core/static/smart_field.min.js | 2 +- .../admin/core/flexform/form_editor/main.html | 206 +++++++++----- .../core/flexformfield/field_editor/main.html | 2 - src/aurora/core/templates/smart/_form.html | 8 +- 25 files changed, 584 insertions(+), 518 deletions(-) rename src/aurora/core/{admin/form_editor.py => editors/flex_form.py} (53%) create mode 100644 src/aurora/core/editors/utils.py rename src/aurora/core/static/admin/{form_editor/field_editor.scss => f_editor.scss} (93%) create mode 100644 src/aurora/core/static/admin/form_editor/admin_ordering-icon.svg delete mode 100644 src/aurora/core/static/admin/form_editor/field_editor.css.map rename src/aurora/core/static/admin/form_editor/{field_editor.css => form_editor.css} (85%) create mode 100644 src/aurora/core/static/admin/form_editor/form_editor.scss diff --git a/src/aurora/core/admin/flex_form.py b/src/aurora/core/admin/flex_form.py index 1c254101..8d77e354 100644 --- a/src/aurora/core/admin/flex_form.py +++ b/src/aurora/core/admin/flex_form.py @@ -17,7 +17,7 @@ from ..utils import render from .base import ConcurrencyVersionAdmin from .filters import ProjectFilter, UsedByRegistration, UsedInRFormset -from .form_editor import FormEditor +from ..editors.flex_form import FormEditor logger = logging.getLogger(__name__) @@ -139,6 +139,16 @@ def widget_attrs(self, request, pk): editor = FormEditor(self, request, pk) return editor.get_configuration() + @view() + def editor_sort(self, request, pk): + editor = FormEditor(self, request, pk) + return editor.sort() + + @view() + def editor_advanced(self, request, pk): + editor = FormEditor(self, request, pk) + return editor.get_advanced() + @view() def widget_refresh(self, request, pk): editor = FormEditor(self, request, pk) @@ -154,20 +164,20 @@ def widget_display(self, request, pk): editor = FormEditor(self, request, pk) return editor.render() - @button() - def test(self, request, pk): - ctx = self.get_common_context(request, pk) - form_class = self.object.get_form_class() - if request.method == "POST": - form = form_class(request.POST, initial=self.object.get_initial()) - if form.is_valid(): - ctx["cleaned_data"] = form.cleaned_data - self.message_user(request, "Form is valid") - else: - form = form_class(initial=self.object.get_initial()) - ctx["form"] = form - return render(request, "admin/core/flexform/test.html", ctx) - + # @button() + # def test(self, request, pk): + # ctx = self.get_common_context(request, pk) + # form_class = self.object.get_form_class() + # if request.method == "POST": + # form = form_class(request.POST, initial=self.object.get_initial()) + # if form.is_valid(): + # ctx["cleaned_data"] = form.cleaned_data + # self.message_user(request, "Form is valid") + # else: + # form = form_class(initial=self.object.get_initial()) + # ctx["form"] = form + # return render(request, "admin/core/flexform/test.html", ctx) + # # @view(http_basic_auth=True, permission=lambda request, obj: request.user.is_superuser) # def export(self, request): # try: diff --git a/src/aurora/core/editors/field.py b/src/aurora/core/editors/field.py index 1011b020..69293c7f 100644 --- a/src/aurora/core/editors/field.py +++ b/src/aurora/core/editors/field.py @@ -93,14 +93,6 @@ def patched_field(self): if config := cache.get(self.cache_key, None): merged = {} _forms: Dict[str, AdvancendAttrsForm] = self.get_forms(config) - # fieldForm = _forms.get("field", None) - # if fieldForm.is_valid(): - # fld.field_type = field_registry.get_class(fieldForm.cleaned_data.pop("field_type")) - # for k, v in fieldForm.cleaned_data.items(): - # setattr(fld, k, v) - # else: - # raise ValidationError(fieldForm.errors) - for __, frm in _forms.items(): if frm.is_valid(): processed = [] @@ -125,10 +117,6 @@ def get_kwargs(self): i = self.patched_field.get_instance() data = self.field.get_field_kwargs() data["field"]["widget"] = fqn(i.widget) - - # data["__field__"] = fqn(i) - # data["__widget__"] = {"class": fqn(i.widget), - # "attrs": i.widget.attrs} rendered = json.dumps(data, indent=4) except Exception as e: logger.exception(e) diff --git a/src/aurora/core/admin/form_editor.py b/src/aurora/core/editors/flex_form.py similarity index 53% rename from src/aurora/core/admin/form_editor.py rename to src/aurora/core/editors/flex_form.py index 486f86e5..cd27a709 100644 --- a/src/aurora/core/admin/form_editor.py +++ b/src/aurora/core/editors/flex_form.py @@ -1,64 +1,35 @@ -import json from typing import Dict -from django import forms +from django.conf import settings from django.core.cache import caches -from django.forms import Media +from django.core.exceptions import ValidationError from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import render from django.template.loader import get_template from django.utils.functional import cached_property -from aurora.core.fields.widgets import JavascriptEditor +from aurora.core.editors.forms import ( + FlexFormAttributesForm, + FlexFormEventForm, + AdvancedFlexFormAttrsForm, + FlexFormFieldsForm, +) +from aurora.core.editors.utils import attr_dumps from aurora.core.models import FlexForm +from aurora.core.utils import merge_data +from aurora.core.version_media import VersionMedia cache = caches["default"] -class AdvancendAttrsMixin: - def __init__(self, *args, **kwargs): - self.form = kwargs.pop("form", None) - super().__init__(*args, **kwargs) - - -# -class FlexFormAttributesForm(AdvancendAttrsMixin, forms.ModelForm): - class Meta: - model = FlexForm - fields = ( - "name", - "base_type", - ) - - -class EventForm(AdvancendAttrsMixin, forms.Form): - onsubmit = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) - onload = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) - validation = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) - - -DEFAULTS = { - # "css": {"question": "cursor-pointer", "label": "block uppercase tracking-wide text-gray-700 font-bold mb-2"}, - # "css": {"question": "cursor-pointer", "label": "block uppercase tracking-wide text-gray-700 font-bold mb-2"}, -} - - -def get_initial(form, prefix): - base = DEFAULTS.get(prefix, {}) - # for k, v in form.advanced.get(prefix, {}).items(): - # if v: - # base[k] = v - return base - - class FormEditor: FORMS = { - "frm": FlexFormAttributesForm, - # "kwargs": FormFieldAttributesForm, + "form": FlexFormAttributesForm, + "fields": FlexFormFieldsForm, # "widget": WidgetAttributesForm, # "smart": SmartAttributesForm, # "css": CssForm, - "events": EventForm, + "events": FlexFormEventForm, } def __init__(self, modeladmin, request, pk): @@ -72,26 +43,39 @@ def flex_form(self): return FlexForm.objects.get(pk=self.pk) @cached_property - def patched_form(self): - fld = self.flex_form.get_form_class() - # if config := cache.get(self.cache_key, None): - # forms = self.get_forms(config) - # fieldForm = forms.pop("field", None) - # if fieldForm.is_valid(): - # for k, v in fieldForm.cleaned_data.items(): - # setattr(fld, k, v) - # for prefix, frm in forms.items(): - # frm.is_valid() - # merged = merge_data(fld.advanced, {**{prefix: frm.cleaned_data}}) - # fld.advanced = merged - return fld + def patched_form(self) -> FlexForm: + form: FlexForm = self.flex_form + form.advanced = {} + + if config := cache.get(self.cache_key, None): + merged = {} + _forms: Dict[str, AdvancedFlexFormAttrsForm] = self.get_forms(config) + for prefix, frm in _forms.items(): + if frm.is_valid(): + if prefix == "fields": + merged["field_order"] = [x[0] for x in sorted(frm.cleaned_data.items(), key=lambda x: x[1][-1])] + else: + processed = [] + for sect in AdvancedFlexFormAttrsForm.SECTIONS_MAP.keys(): + values = {k: v for k, v in frm.get_section(sect).items() if v and str(v).strip()} + processed.extend(values.keys()) + if sect == "field": + for k, v in values.items(): + setattr(frm, k, v) + else: + merged = merge_data(merged, {**{sect: values}}) + else: + raise ValidationError(frm.errors) + form.advanced = merged + return form def patch(self, request, pk): pass def get_configuration(self): - self.patched_form.get_instance() - rendered = json.dumps(self.flex_form.advanced, indent=4) + frm: FlexForm = self.patched_form + attrs = frm.get_form_attrs() + rendered = attr_dumps(attrs, indent=4) return HttpResponse(rendered, content_type="text/plain") def get_code(self): @@ -101,14 +85,15 @@ def get_code(self): from pygments.formatters.html import HtmlFormatter from pygments.lexers import HtmlLexer - instance = self.patched_form() + # instance = self.patched_form # form_class_attrs = { # self.field.name: instance, # } # form_class = type(forms.Form)("TestForm", (forms.Form,), form_class_attrs) ctx = self.get_context(self.request) - ctx["form"] = self.flex_form.get_form_class() - ctx["instance"] = instance + ctx["form"] = self.patched_form.get_form_class() + # ctx["form"] = self.flex_form.get_form_class() + # ctx["instance"] = instance # code = Template( # "{{ form }}" # ).render(Context(ctx)) @@ -121,6 +106,10 @@ def get_code(self): ctx["code"] = highlight(prettyHTML, HtmlLexer(), formatter) return render(self.request, "admin/core/flexformfield/field_editor/code.html", ctx, content_type="text/html") + def get_advanced(self): + rendered = attr_dumps(self.flex_form.advanced, indent=4) + return HttpResponse(rendered, content_type="text/plain") + def render(self): instance = self.patched_form # form_class_attrs = { @@ -145,15 +134,10 @@ def get_forms(self, data=None) -> Dict: return {prefix: Form(data, prefix=prefix, form=self.flex_form) for prefix, Form in self.FORMS.items()} if self.request.method == "POST": return { - prefix: Form( - self.request.POST, prefix=prefix, form=self.flex_form, initial=get_initial(self.flex_form, prefix) - ) + prefix: Form(self.request.POST, prefix=prefix, form=self.flex_form) for prefix, Form in self.FORMS.items() } - return { - prefix: Form(prefix=prefix, form=self.flex_form, initial=get_initial(self.flex_form, prefix)) - for prefix, Form in self.FORMS.items() - } + return {prefix: Form(prefix=prefix, form=self.flex_form) for prefix, Form in self.FORMS.items()} def refresh(self): forms = self.get_forms() @@ -171,12 +155,34 @@ def get_context(self, request, pk=None, **kwargs): **kwargs, } + def sort(self): + print("aurora/core/editors/flex_form.py: 136", self.request.body) + def get(self, request, pk): ctx = self.get_context(request, pk) - ctx["forms_media"] = Media() + ctx["forms"] = {} + extra = "" if settings.DEBUG else ".min" + media = VersionMedia() for prefix, frm in self.get_forms().items(): - ctx[f"form_{prefix}"] = frm - ctx["forms_media"] += frm.media + # ctx[f"form_{prefix}"] = frm + ctx["forms"][prefix] = frm + media += frm.media + + media += VersionMedia( + js=[ + "admin/js/vendor/jquery/jquery%s.js" % extra, + "admin/js/jquery.init.js", + "jquery.compat%s.js" % extra, + # "admin/resizer%s.js" % extra, + "smart_validation%s.js" % extra, + "smart%s.js" % extra, + "smart_field%s.js" % extra, + "https://cdn.jsdelivr.net/npm/dragsort@1.0.6/dist/js/jquery.dragsort.min.js", + ], + css={"all": ["admin/form_editor/form_editor.css"]}, + ) + ctx["media"] = media + # + VersionMedia(js=["admin/field_editor/field_editor%s.js" % extra]) return render(request, "admin/core/flexform/form_editor/main.html", ctx) def post(self, request, pk): diff --git a/src/aurora/core/editors/forms.py b/src/aurora/core/editors/forms.py index 7aae51b3..f0c9cde7 100644 --- a/src/aurora/core/editors/forms.py +++ b/src/aurora/core/editors/forms.py @@ -1,5 +1,6 @@ +from django.forms import widgets from django_regex.forms import RegexFormField -from typing import Dict, List +from typing import Dict, List, Any from django import forms @@ -7,7 +8,7 @@ from aurora.core.fields.widgets import JavascriptEditor from aurora.core.fields.widgets.mixins import TailWindMixin from aurora.core.models import Section -from aurora.core.registry import field_registry +from aurora.core.registry import field_registry, form_registry class CustomAttrsForm(forms.Form): @@ -34,11 +35,6 @@ def get_section(self, section: Section): return {k: v for k, v in self.cleaned_data.items() if k in targets} def extract_initials(self): - # if self.SECTION: - # ret = self.field.advanced.get(self.SECTION, {}) - # for name, __ in self.fields.items(): - # ret[name] = getattr(self.field, name) - # else: ret = {} for section, field_names in self.SECTIONS_MAP.items(): if section == "field": @@ -47,19 +43,8 @@ def extract_initials(self): else: for field_name in field_names: ret[field_name] = self.field.advanced.get(section, {}).get(field_name, "") - # for name, __ in self.base_fields.items(): - # if name not in ret: - # ret[name] = getattr(self.field, name, "") return ret - # - # def extract_custom(self): - # ret = {} - # for section, fields in self.EXTRA_MAP.items(): - # for fld in fields: - # ret[fld] = self.field.advanced.get(section, {}).get(fld, None) - # return ret - class AdvancedForm(AdvancendAttrsForm): SECTIONS_MAP = { @@ -146,13 +131,16 @@ class Meta: class CssForm(AdvancendAttrsForm): - SECTIONS_MAP = {"css": ["input", "label", "fieldset", "description", "hint"]} + SECTIONS_MAP = {"css": ["input", "label", "fieldset", "description", "hint", "group", "group_title"]} input = forms.CharField(required=False, initial=TailWindMixin.default_class) label = forms.CharField(required=False, initial="block uppercase tracking-wide text-gray-700 font-bold mb-2") fieldset = forms.CharField(required=False, help_text="") description = forms.CharField(required=False, help_text="") hint = forms.CharField(required=False, help_text="") + group = forms.CharField(required=False, help_text="", initial="default") + group_title = forms.CharField(required=False, help_text="", initial="") + class Meta: title = "Css" help = "" @@ -178,3 +166,103 @@ class EventForm(AdvancendAttrsForm): class Meta: title = "Events" help = "" + + +class AdvancedFlexFormAttrsForm(forms.Form): + title = None + SECTIONS_MAP: Dict[Section, List] = dict(form=[], smart=[], css=[], custom=[], data=[]) + + def __init__(self, *args, **kwargs): + self.form = kwargs.pop("form", None) + self.prefix = kwargs.get("prefix") + self.cleaned_data = {} + self.title = self.title or self.__class__.__name__ + initial = self.extract_initials() + # initial.update(**self.extract_custom()) + kwargs["initial"] = initial + super().__init__(*args, **kwargs) + + def get_section(self, section: Section): + targets = self.SECTIONS_MAP.get(section, []) + return {k: v for k, v in self.cleaned_data.items() if k in targets} + + def extract_initials(self): + ret = {} + for section, field_names in self.SECTIONS_MAP.items(): + if section == "form": + for field_name in field_names: + ret[field_name] = getattr(self.form, field_name, "") + else: + for field_name in field_names: + ret[field_name] = self.form.advanced.get(section, {}).get(field_name, "") + return ret + + +class FlexFormAttributesForm(AdvancedFlexFormAttrsForm): + SECTIONS_MAP = {"form": ["name", "base_type"]} + name = forms.CharField(label="name", required=False, help_text="default value for the field") + base_type = StrategyFormField(registry=form_registry) + + class Meta: + title = "Form" + + +class FlexFormEventForm(AdvancedFlexFormAttrsForm): + onsubmit = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) + onload = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) + validation = forms.CharField(widget=JavascriptEditor(toolbar=True), required=False) + + class Meta: + title = "Events" + + +class FieldConfigWidget(widgets.MultiWidget): + def __init__(self, attrs=None): + _widgets = ( + widgets.TextInput(attrs={"required": "required"}), + widgets.CheckboxInput(attrs={"class": "required"}), + widgets.CheckboxInput(attrs={"class": "enabled"}), + widgets.HiddenInput(attrs={"style": "width: 20px", "class": "ordering"}), + ) + super().__init__(_widgets, attrs) + + def decompress(self, value): + if value: + return value + return [None, None, None, None] + + def value_from_datadict(self, data, files, name): + return [ + widget.value_from_datadict(data, files, name + widget_name) + for widget_name, widget in zip(self.widgets_names, self.widgets) + ] + + def format_output(self, rendered_widgets): + return "".join(rendered_widgets) + + +class FieldConfigField(forms.Field): + def clean(self, value: Any) -> Any: + value[-1] = int(value[-1]) + return value + + +class FlexFormFieldsForm(AdvancedFlexFormAttrsForm): + class Meta: + title = "Fields" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + for field in self.form.fields.order_by("ordering"): + self.fields[field.name] = FieldConfigField(widget=FieldConfigWidget, required=False) + + def extract_initials(self): + ret = {} + for field in self.form.fields.order_by("ordering"): + ret[field.name] = [ + field.label, + field.required, + field.enabled, + field.ordering, + ] + return ret diff --git a/src/aurora/core/editors/utils.py b/src/aurora/core/editors/utils.py new file mode 100644 index 00000000..765f352d --- /dev/null +++ b/src/aurora/core/editors/utils.py @@ -0,0 +1,25 @@ +import json + +from django import forms +from strategy_field.utils import fqn + +from aurora.core.models import FlexForm +from aurora.core.utils import JSONEncoder + + +class AttrEncoder(JSONEncoder): + def default(self, o): + if isinstance(o, FlexForm): + return fqn(o) + elif isinstance(o, forms.Field): + return fqn(o) + elif isinstance(o, forms.Form): + return fqn(o) + + return super().default(o) + + +def attr_dumps(data, **kwargs): + kwargs.setdefault("indent", 4) + kwargs.setdefault("sort_keys", True) + return json.dumps(data, cls=AttrEncoder, **kwargs) diff --git a/src/aurora/core/forms.py b/src/aurora/core/forms.py index 818c14e3..5770db1f 100644 --- a/src/aurora/core/forms.py +++ b/src/aurora/core/forms.py @@ -28,6 +28,7 @@ class FlexFormBaseForm(forms.Form): flex_form = None compilation_time_field = None indexes = {"1": None, "2": None, "3": None} + field_order = None def get_counters(self, data): if self.compilation_time_field: diff --git a/src/aurora/core/models.py b/src/aurora/core/models.py index ed039609..209eb69d 100644 --- a/src/aurora/core/models.py +++ b/src/aurora/core/models.py @@ -362,30 +362,38 @@ def add_formset(self, form, **extra): return FormSet.objects.update_or_create(parent=self, flex_form=form, defaults=defaults)[0] # @cache_form - def get_form_class(self): + def get_form_fields(self): from aurora.core.fields import CompilationTimeField fields = {} - compilation_time_field = None - indexes = FlexFormBaseForm.indexes.copy() + # indexes = FlexFormBaseForm.indexes.copy() + # base_order = self.advanced.get("field_order", []) + for field in self.fields.filter(enabled=True).select_related("validator").order_by("ordering"): try: fld = field.get_instance() fields[field.name] = fld if isinstance(fld, CompilationTimeField): - compilation_time_field = field.name - if index := field.advanced.get("smart", {}).get("index"): - indexes[str(index)] = field.name + fields["compilation_time_field_name"] = field.name + # if index := field.advanced.get("smart", {}).get("index"): + # indexes[str(index)] = field.name self._initial[field.name] = field.get_default_value() except TypeError: pass - form_class_attrs = { + return fields + + def get_form_attrs(self): + fields = self.get_form_fields() + return { "flex_form": self, - "compilation_time_field": compilation_time_field, - "indexes": indexes, + "compilation_time_field": fields.pop("compilation_time_field_name", None), + "field_order": self.advanced.get("field_order"), + # "indexes": indexes, **fields, } - flexForm = type(f"{self.name}FlexForm", (self.base_type,), form_class_attrs) + + def get_form_class(self): + flexForm = type(f"{self.name}FlexForm", (self.base_type,), self.get_form_attrs()) return flexForm def get_formsets_classes(self): diff --git a/src/aurora/core/static/admin/form_editor/field_editor.scss b/src/aurora/core/static/admin/f_editor.scss similarity index 93% rename from src/aurora/core/static/admin/form_editor/field_editor.scss rename to src/aurora/core/static/admin/f_editor.scss index 94c60d08..27bc3534 100644 --- a/src/aurora/core/static/admin/form_editor/field_editor.scss +++ b/src/aurora/core/static/admin/f_editor.scss @@ -41,7 +41,7 @@ body { .output { position: sticky; top: 40px; - width: 50%; + width: 40%; iframe { width: 100%; @@ -53,9 +53,23 @@ body { .settings { overflow-y: scroll; - width: 50%; + width: 60%; background: var(--breadcrumbs-bg); + #event_selector { + option { + font-style: oblique; + font-weight: bold; + color: red; + + &.used::after { + content: " --- "; + color: red; + + } + } + } + table:first-child { background: var(--breadcrumbs-bg); width: 100%; @@ -224,6 +238,7 @@ table2 { .code { display: none; } + .code.selected { display: block; } diff --git a/src/aurora/core/static/admin/field_editor/field_editor.css.map b/src/aurora/core/static/admin/field_editor/field_editor.css.map index 12e22888..83b52bdd 100644 --- a/src/aurora/core/static/admin/field_editor/field_editor.css.map +++ b/src/aurora/core/static/admin/field_editor/field_editor.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["field_editor.scss"],"names":[],"mappings":"AAAA;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGA;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAGE;EACE;EACA;EACA;;AACA;EACE;EACA;;AAMN;EACE;EACA;EACA;EACA;EACA;AAA2B;EAC3B;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAMR;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAKJ;EACE;;;AAsBZ;EACE;EACA;;;AAsBF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAEA;EACE;;AAGF;EACE;EACA;;AAEA;EACE;;;AAOR;EACE;;AAEA;EACE;EACA;;;AAKF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;;AAGE;EACE;EACA;EACA","file":"field_editor.css"} +{"version":3,"sourceRoot":"","sources":["../f_editor.scss"],"names":[],"mappings":"AAAA;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGA;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAGE;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAMN;EACE;EACA;EACA;EACA;EACA;AAA2B;EAC3B;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAMR;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAKJ;EACE;;;AAsBZ;EACE;EACA;;;AAsBF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAEA;EACE;;AAGF;EACE;EACA;;AAEA;EACE;;;AAOR;EACE;;AAEA;EACE;EACA;;;AAKF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;;AAGE;EACE;EACA;EACA","file":"field_editor.css"} diff --git a/src/aurora/core/static/admin/field_editor/field_editor.js b/src/aurora/core/static/admin/field_editor/field_editor.js index 0faad697..bec3a88c 100644 --- a/src/aurora/core/static/admin/field_editor/field_editor.js +++ b/src/aurora/core/static/admin/field_editor/field_editor.js @@ -1,6 +1,6 @@ (function ($) { $(document).ready(function () { - var $me = $("#field_editor_script"); + // var $me = $("#field_editor_script"); var refreshUrl = $("#url").data("url"); $(".toolbutton").on("click", function () { @@ -42,6 +42,16 @@ }) } + function checkForUpdate() { + if ($("#autoRefresh").is(":checked")) { + clearTimeout($.data(this, 'timer')); + var wait = setTimeout(update, 500); + $(this).data('timer', wait); + } else { + $("iframe.display").contents().find("*").css("background-color", "#bbbbbb"); + } + } + $('input[type=checkbox],input[type=radio]').on('click', function () { update(); }); @@ -49,11 +59,7 @@ update(); }); $('input,textarea').on('keyup', function () { - if ($("#autoRefresh").is(":checked")) { - clearTimeout($.data(this, 'timer')); - var wait = setTimeout(update, 500); - $(this).data('timer', wait); - } + checkForUpdate(); }); $('textarea.js-editor').each(function (i, e) { var editor = $(e).data("CodeMirror"); @@ -85,7 +91,6 @@ $("iframe").hide(); var $target = $("iframe." + targetClass); $(this).is(":checked") ? $target.show() : $target.hide(); - }) $(".tabs th").on("click", function () { const targetName = $(this).data('target'); diff --git a/src/aurora/core/static/admin/field_editor/field_editor.min.js b/src/aurora/core/static/admin/field_editor/field_editor.min.js index 6a117f46..31f6953d 100644 --- a/src/aurora/core/static/admin/field_editor/field_editor.min.js +++ b/src/aurora/core/static/admin/field_editor/field_editor.min.js @@ -1 +1 @@ -(function($){$(document).ready(function(){var $me=$("#field_editor_script");var refreshUrl=$("#url").data("url");$(".toolbutton").on("click",function(){const action=$(this).data("action");const op=$(this).data("op");const fieldId=$(this).parents("div.cm-container").data("fieldid");const editor=$(`#${fieldId}`).data("CodeMirror");if(action){window.cmToolBar.action(editor,action)}else if(op){window.cmToolBar[op](editor)}});function update(){var formData=$("form").serialize();$(".field-error").remove();$.post(refreshUrl,formData).done(function(data){$("iframe").each(function(){$(this)[0].contentWindow.location=$(this).data("src")})}).fail(function(xhr,err,txt){if(xhr.responseJSON){var errors=xhr.responseJSON;var fieldErrors=errors.field;var kwargsErrors=errors.kwargs;var widget_kwargs=errors.widget_kwargs;var smart=errors.smart;for(const property in fieldErrors){$(`#id_field-${property}`).before(`
    ${fieldErrors[property]}
    `)}}else{console.log(11111,xhr);console.log(11111,err);console.log(11111,txt)}})}$("input[type=checkbox],input[type=radio]").on("click",function(){update()});$("select").on("change",function(){update()});$("input,textarea").on("keyup",function(){if($("#autoRefresh").is(":checked")){clearTimeout($.data(this,"timer"));var wait=setTimeout(update,500);$(this).data("timer",wait)}});$("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");if(editor){editor.refresh();editor.on("change",function(){editor.save()})}});$("#refresh").on("click",function(e){e.preventDefault();$("iframe").each(function(){$(this)[0].contentWindow.location=$(this).data("src")});return false});$("#event_selector").on("change",function(){let sel=$(this).val();$("#events .code").removeClass("selected");$(`#event_${sel}`).addClass("selected").find("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");if(editor)editor.refresh()})});$(".submit-row input[type=radio]").on("click",function(e){var targetClass=$(this).data("target");$("iframe").hide();var $target=$("iframe."+targetClass);$(this).is(":checked")?$target.show():$target.hide()});$(".tabs th").on("click",function(){const targetName=$(this).data("target");$(`.tabs th`).removeClass("selected");$(`table.cfg-form`).addClass("collapsed");$(this).addClass("selected");$(`#${targetName}`).toggleClass("collapsed");$("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");editor.refresh()})});$(".submit-row input[data-target=display]").trigger("click");$("#tabForms th:first").trigger("click")})})(django.jQuery); +(function($){$(document).ready(function(){var refreshUrl=$("#url").data("url");$(".toolbutton").on("click",function(){const action=$(this).data("action");const op=$(this).data("op");const fieldId=$(this).parents("div.cm-container").data("fieldid");const editor=$(`#${fieldId}`).data("CodeMirror");if(action){window.cmToolBar.action(editor,action)}else if(op){window.cmToolBar[op](editor)}});function update(){var formData=$("form").serialize();$(".field-error").remove();$.post(refreshUrl,formData).done(function(data){$("iframe").each(function(){$(this)[0].contentWindow.location=$(this).data("src")})}).fail(function(xhr,err,txt){if(xhr.responseJSON){var errors=xhr.responseJSON;var fieldErrors=errors.field;var kwargsErrors=errors.kwargs;var widget_kwargs=errors.widget_kwargs;var smart=errors.smart;for(const property in fieldErrors){$(`#id_field-${property}`).before(`
    ${fieldErrors[property]}
    `)}}else{console.log(11111,xhr);console.log(11111,err);console.log(11111,txt)}})}function checkForUpdate(){if($("#autoRefresh").is(":checked")){clearTimeout($.data(this,"timer"));var wait=setTimeout(update,500);$(this).data("timer",wait)}else{$("iframe.display").contents().find("*").css("background-color","#bbbbbb")}}$("input[type=checkbox],input[type=radio]").on("click",function(){update()});$("select").on("change",function(){update()});$("input,textarea").on("keyup",function(){checkForUpdate()});$("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");if(editor){editor.refresh();editor.on("change",function(){editor.save()})}});$("#refresh").on("click",function(e){e.preventDefault();$("iframe").each(function(){$(this)[0].contentWindow.location=$(this).data("src")});return false});$("#event_selector").on("change",function(){let sel=$(this).val();$("#events .code").removeClass("selected");$(`#event_${sel}`).addClass("selected").find("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");if(editor)editor.refresh()})});$(".submit-row input[type=radio]").on("click",function(e){var targetClass=$(this).data("target");$("iframe").hide();var $target=$("iframe."+targetClass);$(this).is(":checked")?$target.show():$target.hide()});$(".tabs th").on("click",function(){const targetName=$(this).data("target");$(`.tabs th`).removeClass("selected");$(`table.cfg-form`).addClass("collapsed");$(this).addClass("selected");$(`#${targetName}`).toggleClass("collapsed");$("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");editor.refresh()})});$(".submit-row input[data-target=display]").trigger("click");$("#tabForms th:first").trigger("click")})})(django.jQuery); diff --git a/src/aurora/core/static/admin/field_editor/field_editor.scss b/src/aurora/core/static/admin/field_editor/field_editor.scss index 504af36f..273495fd 100644 --- a/src/aurora/core/static/admin/field_editor/field_editor.scss +++ b/src/aurora/core/static/admin/field_editor/field_editor.scss @@ -1,258 +1 @@ -body { - height: 100px; -} - -#fieldEditor, #formEditor { - height: 100%; - position: relative; - top: 0; - background-color: var(--body-bg); - z-index: 10; - //} - - .submit-row { - width: 100%; - z-index: 999; - - div:first-child { - float: left; - } - - input { - background-color: cornflowerblue; - } - } - - #mainPanel { - top: 0; - display: inline-flex; - width: 100%; - height: 100%; - - .panel { - display: inline-block; - height: 500px; - - &:first-child { - margin-right: 10px; - } - } - - .output { - position: sticky; - top: 40px; - width: 40%; - - iframe { - width: 100%; - height: 100%; - border: 0 transparent; - display: none; - } - } - - .settings { - overflow-y: scroll; - width: 60%; - background: var(--breadcrumbs-bg); - - #event_selector { - option { - font-style: oblique; - font-weight: bold; - color: red; - &.used::after { - content: " --- "; - color: red; - - } - } - } - - table:first-child { - background: var(--breadcrumbs-bg); - width: 100%; - margin: 0; - padding: 0; - border-collapse: separate; /* Don't collapse */ - border-spacing: 0; - border: 1px solid grey; - - thead { - background-color: var(--body-bg); - - tr.tabs { - width: 100%; - padding: 0; - margin-top: 5px; - margin-bottom: 0; - - th { - text-transform: capitalize; - font-weight: normal; - font-size: 90%; - text-align: center; - border: 1px solid grey; - cursor: pointer; - background-color: var(--body-bg); - position: sticky; - z-index: 6; - top: 0; - - &.selected { - background: var(--breadcrumbs-bg); - border-bottom: none; - } - } - } - } - - tbody { - background: var(--breadcrumbs-bg); - - td { - background: var(--breadcrumbs-bg); - } - } - - .cfg-form { - width: 100%; - background: var(--breadcrumbs-bg); - - tr { - background: var(--breadcrumbs-bg); - } - - th { - width: 10%; - } - - td { - text-align: left; - align-content: flex-start; - padding-right: 5px; - - input:not([type=checkbox]), textarea { - width: 97%; - } - - textarea { - height: 50px; - } - - select { - width: 100%; - } - - } - - &.collapsed { - display: none; - } - } - } - - } - } -} - -// -//#iframe-container { -// display: block; -// overflow: auto; -// resize: vertical; -// height: 400px; -// -// iframe { -// height: 100%; -// border: 0 transparent; -// } -//} - -.field-error { - color: red; - margin-bottom: 3px; -} - -// -//table#tabs { -// width: 100%; -// padding: 0; -// margin-top: 5px; -// margin-bottom: 0; -// -// th { -// border-top: 1px solid grey; -// border-right: 1px solid grey; -// border-left: 1px solid grey; -// cursor: pointer; -// -// &.selected { -// background: var(--breadcrumbs-bg); -// } -// } -//} - -table2 { - width: 100%; - padding: 0; - margin: 0; - - &.cfg-form { - margin-bottom: 5px; - border-left: 1px solid grey; - padding: 0; - margin: 0; - } - - tr { - display: table-row; - - th { - min-height: 200px; - } - - td { - width: 100%; - padding-left: 0; - - input, select, textarea { - width: 100%; - } - } - } - -} - -.toolbar { - margin: 10px; - - label { - margin-right: 10px; - cursor: pointer; - } -} - -#events { - .code { - display: none; - } - - .code.selected { - display: block; - } -} - -.cm-container { - margin: 0; - background-color: var(--body-bg); - width: 100%; - - .cm-toolbar { - a.toolbutton { - margin: 0 3px 5px 0; - padding: 5px; - font-variant: none; - } - } -} +@import "../f_editor.scss"; diff --git a/src/aurora/core/static/admin/form_editor/admin_ordering-icon.svg b/src/aurora/core/static/admin/form_editor/admin_ordering-icon.svg new file mode 100644 index 00000000..fc4b6750 --- /dev/null +++ b/src/aurora/core/static/admin/form_editor/admin_ordering-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/aurora/core/static/admin/form_editor/field_editor.css.map b/src/aurora/core/static/admin/form_editor/field_editor.css.map deleted file mode 100644 index 87bd0f54..00000000 --- a/src/aurora/core/static/admin/form_editor/field_editor.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sourceRoot":"","sources":["field_editor.scss"],"names":[],"mappings":"AAAA;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGA;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;AAA2B;EAC3B;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAMR;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAKJ;EACE;;;AAsBZ;EACE;EACA;;;AAsBF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAEA;EACE;;AAGF;EACE;EACA;;AAEA;EACE;;;AAOR;EACE;;AAEA;EACE;EACA;;;AAKF;EACE;;AAEF;EACE;;;AAIJ;EACE;EACA;EACA;;AAGE;EACE;EACA;EACA","file":"field_editor.css"} diff --git a/src/aurora/core/static/admin/form_editor/field_editor.css b/src/aurora/core/static/admin/form_editor/form_editor.css similarity index 85% rename from src/aurora/core/static/admin/form_editor/field_editor.css rename to src/aurora/core/static/admin/form_editor/form_editor.css index b72c940f..4e24355d 100644 --- a/src/aurora/core/static/admin/form_editor/field_editor.css +++ b/src/aurora/core/static/admin/form_editor/form_editor.css @@ -35,7 +35,7 @@ body { #fieldEditor #mainPanel .output, #formEditor #mainPanel .output { position: sticky; top: 40px; - width: 50%; + width: 40%; } #fieldEditor #mainPanel .output iframe, #formEditor #mainPanel .output iframe { width: 100%; @@ -45,9 +45,18 @@ body { } #fieldEditor #mainPanel .settings, #formEditor #mainPanel .settings { overflow-y: scroll; - width: 50%; + width: 60%; background: var(--breadcrumbs-bg); } +#fieldEditor #mainPanel .settings #event_selector option, #formEditor #mainPanel .settings #event_selector option { + font-style: oblique; + font-weight: bold; + color: red; +} +#fieldEditor #mainPanel .settings #event_selector option.used::after, #formEditor #mainPanel .settings #event_selector option.used::after { + content: " --- "; + color: red; +} #fieldEditor #mainPanel .settings table:first-child, #formEditor #mainPanel .settings table:first-child { background: var(--breadcrumbs-bg); width: 100%; @@ -173,4 +182,19 @@ table2 tr td input, table2 tr td select, table2 tr td textarea { font-variant: none; } -/*# sourceMappingURL=field_editor.css.map */ +#formEditor #mainPanel .settings #fields tr td input { + margin-right: 10px; +} +#formEditor #mainPanel .settings #fields tr td input[type=text] { + width: 60%; +} +#formEditor #mainPanel .settings #fields tr td input[type=text]::before { + content: "=="; + display: inline-block; + background-size: 14px 14px; + background-image: url("admin_ordering-icon.svg"); + background-repeat: no-repeat; + background-position: center center; +} + +/*# sourceMappingURL=form_editor.css.map */ diff --git a/src/aurora/core/static/admin/form_editor/form_editor.js b/src/aurora/core/static/admin/form_editor/form_editor.js index a9136d02..0e683e5b 100644 --- a/src/aurora/core/static/admin/form_editor/form_editor.js +++ b/src/aurora/core/static/admin/form_editor/form_editor.js @@ -1,15 +1,8 @@ (function ($) { $(document).ready(function () { - - var $me = $("#form_editor_script"); - var refreshUrl = $me.data("url"); - var $iFrame1 = $('#widget_display'); - var $iFrame2 = $('#widget_code'); - var $iFrame3 = $('#widget_attrs'); - - var $radioRender = $("#radio_display"); - var $radioCode = $('#radio_code'); - var $radioAttributes = $('#radio_attrs'); + // var $me = $("#form_editor_script"); + var refreshUrl = $("#url").data("url"); + var orderUrl = $("#orderUrl").data("url"); $(".toolbutton").on("click", function () { const action = $(this).data('action'); @@ -26,28 +19,42 @@ function update() { var formData = $("form").serialize(); $(".field-error").remove(); + console.log(1111, formData); $.post(refreshUrl, formData) .done(function (data) { - $iFrame1[0].contentWindow.location = $iFrame1[0].contentWindow.location.href; - $iFrame2[0].contentWindow.location = $iFrame2[0].contentWindow.location.href; - $iFrame3[0].contentWindow.location = $iFrame3[0].contentWindow.location.href; + $("iframe").each(function () { + $(this)[0].contentWindow.location = $(this).data("src") + }) }) .fail(function (xhr) { - var errors = xhr.responseJSON; - var fieldErrors = errors.field; - var kwargsErrors = errors.kwargs; - var widget_kwargs = errors.widget_kwargs; - var smart = errors.smart; - for (const property in fieldErrors) { - $(`#id_field-${property}`).before(`
    ${fieldErrors[property]}
    `); + if (xhr.responseJSON) { + var errors = xhr.responseJSON; + var fieldErrors = errors.field; + var kwargsErrors = errors.kwargs; + var widget_kwargs = errors.widget_kwargs; + var smart = errors.smart; + for (const property in fieldErrors) { + $(`#id_field-${property}`).before(`
    ${fieldErrors[property]}
    `); + } + } else { + console.log(11111, xhr); + console.log(11111, err); + console.log(11111, txt); } - ; - // for (const property in kwargsErrors) { - // console.log(`${property}: ${kwargsErrors[property]}`); - // } }) } + + function checkForUpdate() { + if ($("#autoRefresh").is(":checked")) { + clearTimeout($.data(this, 'timer')); + var wait = setTimeout(update, 500); + $(this).data('timer', wait); + } else { + $("iframe.display").contents().find("*").css("background-color", "#bbbbbb"); + } + } + $('input[type=checkbox],input[type=radio]').on('click', function () { update(); }); @@ -55,9 +62,7 @@ update(); }); $('input,textarea').on('keyup', function () { - clearTimeout($.data(this, 'timer')); - var wait = setTimeout(update, 500); - $(this).data('timer', wait); + checkForUpdate() }); $('textarea.js-editor').each(function (i, e) { var editor = $(e).data("CodeMirror"); @@ -68,19 +73,31 @@ }) } }); - $("#event_selector").on("change", function (){ + $("#refresh").on("click", function (e) { + e.preventDefault(); + $("iframe").each(function () { + $(this)[0].contentWindow.location = $(this).data("src") + }) + return false; + }); + + $("#event_selector").on("change", function () { let sel = $(this).val(); $('#events .code').removeClass('selected'); $(`#event_${sel}`).addClass('selected').find('textarea.js-editor').each(function (i, e) { var editor = $(e).data("CodeMirror"); - if (editor) editor.refresh(); + if (editor) { + editor.refresh() + } }); }); - $("#radio_display, #radio_code, #radio_attrs").on("click", function () { - $radioRender.is(":checked") ? $iFrame1.show() : $iFrame1.hide(); - $radioCode.is(":checked") ? $iFrame2.show() : $iFrame2.hide(); - $radioAttributes.is(":checked") ? $iFrame3.show() : $iFrame3.hide(); + $(".submit-row input[type=radio]").on("click", function (e) { + var targetClass = $(this).data("target"); + $("iframe").hide(); + var $target = $("iframe." + targetClass); + $(this).is(":checked") ? $target.show() : $target.hide(); }) + $(".tabs th").on("click", function () { const targetName = $(this).data('target'); $(`.tabs th`).removeClass('selected'); @@ -92,6 +109,18 @@ editor.refresh(); }); }); - $radioRender.trigger("click"); + $(".submit-row input[data-target=display]").trigger("click"); + $("#tabForms th:first").trigger("click"); + + $('#fields').dragsort({ + dragSelector: 'tr', + dragEnd: function () { + $('#fields tr').each(function (i, row) { + $(row).find("input.ordering").val(10 * (1 + i)); + }) + checkForUpdate(); + }, dragBetween: false, + placeHolderTemplate: '
  • ' + }); }) })(django.jQuery); diff --git a/src/aurora/core/static/admin/form_editor/form_editor.min.js b/src/aurora/core/static/admin/form_editor/form_editor.min.js index 4225f544..d2138501 100644 --- a/src/aurora/core/static/admin/form_editor/form_editor.min.js +++ b/src/aurora/core/static/admin/form_editor/form_editor.min.js @@ -1 +1 @@ -(function($){$(document).ready(function(){var $me=$("#form_editor_script");var refreshUrl=$me.data("url");var $iFrame1=$("#widget_display");var $iFrame2=$("#widget_code");var $iFrame3=$("#widget_attrs");var $radioRender=$("#radio_display");var $radioCode=$("#radio_code");var $radioAttributes=$("#radio_attrs");$(".toolbutton").on("click",function(){const action=$(this).data("action");const op=$(this).data("op");const fieldId=$(this).parents("div.cm-container").data("fieldid");const editor=$(`#${fieldId}`).data("CodeMirror");if(action){window.cmToolBar.action(editor,action)}else if(op){window.cmToolBar[op](editor)}});function update(){var formData=$("form").serialize();$(".field-error").remove();$.post(refreshUrl,formData).done(function(data){$iFrame1[0].contentWindow.location=$iFrame1[0].contentWindow.location.href;$iFrame2[0].contentWindow.location=$iFrame2[0].contentWindow.location.href;$iFrame3[0].contentWindow.location=$iFrame3[0].contentWindow.location.href}).fail(function(xhr){var errors=xhr.responseJSON;var fieldErrors=errors.field;var kwargsErrors=errors.kwargs;var widget_kwargs=errors.widget_kwargs;var smart=errors.smart;for(const property in fieldErrors){$(`#id_field-${property}`).before(`
    ${fieldErrors[property]}
    `)}})}$("input[type=checkbox],input[type=radio]").on("click",function(){update()});$("select").on("change",function(){update()});$("input,textarea").on("keyup",function(){clearTimeout($.data(this,"timer"));var wait=setTimeout(update,500);$(this).data("timer",wait)});$("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");if(editor){editor.refresh();editor.on("change",function(){editor.save()})}});$("#event_selector").on("change",function(){let sel=$(this).val();$("#events .code").removeClass("selected");$(`#event_${sel}`).addClass("selected").find("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");if(editor)editor.refresh()})});$("#radio_display, #radio_code, #radio_attrs").on("click",function(){$radioRender.is(":checked")?$iFrame1.show():$iFrame1.hide();$radioCode.is(":checked")?$iFrame2.show():$iFrame2.hide();$radioAttributes.is(":checked")?$iFrame3.show():$iFrame3.hide()});$(".tabs th").on("click",function(){const targetName=$(this).data("target");$(`.tabs th`).removeClass("selected");$(`table.cfg-form`).addClass("collapsed");$(this).addClass("selected");$(`#${targetName}`).toggleClass("collapsed");$("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");editor.refresh()})});$radioRender.trigger("click")})})(django.jQuery); +(function($){$(document).ready(function(){var refreshUrl=$("#url").data("url");var orderUrl=$("#orderUrl").data("url");$(".toolbutton").on("click",function(){const action=$(this).data("action");const op=$(this).data("op");const fieldId=$(this).parents("div.cm-container").data("fieldid");const editor=$(`#${fieldId}`).data("CodeMirror");if(action){window.cmToolBar.action(editor,action)}else if(op){window.cmToolBar[op](editor)}});function update(){var formData=$("form").serialize();$(".field-error").remove();console.log(1111,formData);$.post(refreshUrl,formData).done(function(data){$("iframe").each(function(){$(this)[0].contentWindow.location=$(this).data("src")})}).fail(function(xhr){if(xhr.responseJSON){var errors=xhr.responseJSON;var fieldErrors=errors.field;var kwargsErrors=errors.kwargs;var widget_kwargs=errors.widget_kwargs;var smart=errors.smart;for(const property in fieldErrors){$(`#id_field-${property}`).before(`
    ${fieldErrors[property]}
    `)}}else{console.log(11111,xhr);console.log(11111,err);console.log(11111,txt)}})}function checkForUpdate(){if($("#autoRefresh").is(":checked")){clearTimeout($.data(this,"timer"));var wait=setTimeout(update,500);$(this).data("timer",wait)}else{$("iframe.display").contents().find("*").css("background-color","#bbbbbb")}}$("input[type=checkbox],input[type=radio]").on("click",function(){update()});$("select").on("change",function(){update()});$("input,textarea").on("keyup",function(){checkForUpdate()});$("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");if(editor){editor.refresh();editor.on("change",function(){editor.save()})}});$("#refresh").on("click",function(e){e.preventDefault();$("iframe").each(function(){$(this)[0].contentWindow.location=$(this).data("src")});return false});$("#event_selector").on("change",function(){let sel=$(this).val();$("#events .code").removeClass("selected");$(`#event_${sel}`).addClass("selected").find("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");if(editor){editor.refresh()}})});$(".submit-row input[type=radio]").on("click",function(e){var targetClass=$(this).data("target");$("iframe").hide();var $target=$("iframe."+targetClass);$(this).is(":checked")?$target.show():$target.hide()});$(".tabs th").on("click",function(){const targetName=$(this).data("target");$(`.tabs th`).removeClass("selected");$(`table.cfg-form`).addClass("collapsed");$(this).addClass("selected");$(`#${targetName}`).toggleClass("collapsed");$("textarea.js-editor").each(function(i,e){var editor=$(e).data("CodeMirror");editor.refresh()})});$(".submit-row input[data-target=display]").trigger("click");$("#tabForms th:first").trigger("click");$("#fields").dragsort({dragSelector:"tr",dragEnd:function(){$("#fields tr").each(function(i,row){$(row).find("input.ordering").val(10*(1+i))});checkForUpdate()},dragBetween:false,placeHolderTemplate:"
  • "})})})(django.jQuery); diff --git a/src/aurora/core/static/admin/form_editor/form_editor.scss b/src/aurora/core/static/admin/form_editor/form_editor.scss new file mode 100644 index 00000000..1e8fb18b --- /dev/null +++ b/src/aurora/core/static/admin/form_editor/form_editor.scss @@ -0,0 +1,30 @@ +@import "../f_editor.scss"; + +#formEditor { + #mainPanel { + .settings { + + #fields { + tr { + td { + input{ + margin-right: 10px; + } + input[type=text] { + width: 60%; + + &::before { + content: "=="; + display: inline-block; + background-size: 14px 14px; + background-image: url("admin_ordering-icon.svg"); + background-repeat: no-repeat; + background-position: center center; + } + } + } + } + } + } + } +} diff --git a/src/aurora/core/static/edit.js b/src/aurora/core/static/edit.js index 56fea4b7..da501f61 100644 --- a/src/aurora/core/static/edit.js +++ b/src/aurora/core/static/edit.js @@ -54,10 +54,15 @@ $(`.${t}`).hide(); } }); - // $editCheckBox.on("click", function () { - // setEditorIcons($editCheckBox.is(":checked")); - // Cookies.set("staff-editor", $editCheckBox.is(":checked")); - // }); + $editCheckBox.on("click", function () { + setEditorIcons($editCheckBox.is(":checked")); + Cookies.set("staff-editor", $editCheckBox.is(":checked")); + }); + + $i18nCheckBox.on("click", function () { + setI18NIcons($i18nCheckBox.is(':checked')); + Cookies.set("staff-i18n", $editCheckBox.is(":checked")); + }); $editCheckBox.prop("checked", Cookies.get("staff-editor") === "true") $i18nCheckBox.prop("checked", Cookies.get("staff-i18n") === "true"); diff --git a/src/aurora/core/static/edit.min.js b/src/aurora/core/static/edit.min.js index dcf1b914..c3242468 100644 --- a/src/aurora/core/static/edit.min.js +++ b/src/aurora/core/static/edit.min.js @@ -1 +1 @@ -(function($){$(function(){var TOOLBAR='
    '+'"+'"+''+"{{project.env}}"+""+'"+'
    '+''+''+"
    "+"
    ";var seed=Date.now()+Math.random();$(".staff-editor").hide();$.get("/api/user/me/?"+seed).done(function(resp){if(resp.canTranslate){$.get("/api/project/?"+seed).done(function(resp1){var html=TOOLBAR.replaceAll("{{project.version}}",resp1.version).replaceAll("{{project.build_date}}",resp1.build_date).replaceAll("{{project.env}}",resp1.env).replaceAll("{{admin_url}}",resp.adminUrl);$("body").prepend(html);i18n.displayIcons(resp);$editCheckBox=$("#staff-editor");$i18nCheckBox=$("#staff-i18n");var setI18NIcons=function(onOff){if(onOff){$(".staff-18n").show()}else{$(".staff-i18n").hide()}};var setEditorIcons=function(onOff){if(onOff){$(".staff-editor").show()}else{$(".staff-editor").hide()}};$(".staff-toolbar input[type=checkbox]").on("click",function(){var t=$(this).attr("id");if($(this).is(":checked")){$(`.${t}`).show()}else{$(`.${t}`).hide()}});$editCheckBox.prop("checked",Cookies.get("staff-editor")==="true");$i18nCheckBox.prop("checked",Cookies.get("staff-i18n")==="true");setEditorIcons($editCheckBox.is(":checked"));setI18NIcons($i18nCheckBox.is(":checked"))})}else{$(".staff-editor").html("").hide()}})})})($); +(function($){$(function(){var TOOLBAR='
    '+'"+'"+''+"{{project.env}}"+""+'"+'
    '+''+''+"
    "+"
    ";var seed=Date.now()+Math.random();$(".staff-editor").hide();$.get("/api/user/me/?"+seed).done(function(resp){if(resp.canTranslate){$.get("/api/project/?"+seed).done(function(resp1){var html=TOOLBAR.replaceAll("{{project.version}}",resp1.version).replaceAll("{{project.build_date}}",resp1.build_date).replaceAll("{{project.env}}",resp1.env).replaceAll("{{admin_url}}",resp.adminUrl);$("body").prepend(html);i18n.displayIcons(resp);$editCheckBox=$("#staff-editor");$i18nCheckBox=$("#staff-i18n");var setI18NIcons=function(onOff){if(onOff){$(".staff-18n").show()}else{$(".staff-i18n").hide()}};var setEditorIcons=function(onOff){if(onOff){$(".staff-editor").show()}else{$(".staff-editor").hide()}};$(".staff-toolbar input[type=checkbox]").on("click",function(){var t=$(this).attr("id");if($(this).is(":checked")){$(`.${t}`).show()}else{$(`.${t}`).hide()}});$editCheckBox.on("click",function(){setEditorIcons($editCheckBox.is(":checked"));Cookies.set("staff-editor",$editCheckBox.is(":checked"))});$i18nCheckBox.on("click",function(){setI18NIcons($i18nCheckBox.is(":checked"));Cookies.set("staff-i18n",$editCheckBox.is(":checked"))});$editCheckBox.prop("checked",Cookies.get("staff-editor")==="true");$i18nCheckBox.prop("checked",Cookies.get("staff-i18n")==="true");setEditorIcons($editCheckBox.is(":checked"));setI18NIcons($i18nCheckBox.is(":checked"))})}else{$(".staff-editor").html("").hide()}})})})($); diff --git a/src/aurora/core/static/smart_field.js b/src/aurora/core/static/smart_field.js index 903b2f56..c03c5e7b 100644 --- a/src/aurora/core/static/smart_field.js +++ b/src/aurora/core/static/smart_field.js @@ -110,7 +110,25 @@ self.fields = function () { if (_fields == null) { _fields = {} - $me.find(`:input[data-flex]`).each(function (i, e) { + $me.find(`:input[data-flex-name]`).each(function (i, e) { + var f = new aurora.Field(e); + _fields[f.name] = f; + }); + } + return _fields + }; + }, + FlexForm: function (name) { + var self = this; + const $me = $(".form-container." + name); + + self.$ = $me; + // var $container=$(".form-container"); + var _fields = null; + self.fields = function () { + if (_fields == null) { + _fields = {} + $me.find(`:input[data-flex-name]`).each(function (i, e) { var f = new aurora.Field(e); _fields[f.name] = f; }); diff --git a/src/aurora/core/static/smart_field.min.js b/src/aurora/core/static/smart_field.min.js index c8a6961c..3236caae 100644 --- a/src/aurora/core/static/smart_field.min.js +++ b/src/aurora/core/static/smart_field.min.js @@ -1 +1 @@ -(function($){const NUMBERS=["month","number","week"];const DATES=["date","time"];const STRINGS=["email","tel","text","url"];var highLight=function(onOff){if(onOff){this.__oldBorder=this.$.css("border");this.$.css("border","1px solid red")}else{this.$.css("border",this.__oldBorder)}return this};window.debug=function(arguments){var log;if(window.parent.document.getElementById("debugLog")){log=window.parent.document.getElementById("debugLog")}else{log=window.document.getElementById("debugLog")}if(log){$(log).append(arguments);$(log).append("\n")}};window.aurora={Module:function(config){var self=this;var errorsStack={};self.$=$("#formContainer");var $form=$("#registrationForm");self.$submit=$form.find("input[type=submit]");self.config=config;self.name=config.name;self.setError=function(msg){self.$.find(".alert-message").html(msg)};self.pushError=function(f){errorsStack[f]=true};self.popError=function(f){errorsStack[f]=false;var ret=_.filter(_.values(errorsStack),function(i){return i===true})};self.getError=function(f){return errorsStack[f]};self.getErrors=function(){return _.filter(_.keys(errorsStack),function(i,e){return errorsStack[i]===true})};var _fields=null;self.fields=function(){if(_fields==null){_fields={};$form.find(`:input[data-flex-name]`).each(function(i,e){var f=new aurora.Field(e);_fields[f.name]=f})}return _fields};self.isValid=function(){var ret=true;for(let i=0,keys=Object.keys(self.fields()),ii=keys.length;i
  • ${text}
  • `);module.pushError(self.name)}else{$fieldset.data("error","");$fieldset.find(".errors").html("");module.popError(self.name)}return self};self.assertBetween=function(min,max){};self.assertDateBetween=function(min,max){var limit1=Date.parse(min);var limit2=Date.parse(max);var dt=Date.parse(self.getValue());if(!(dtlimit2)){self.setError("the date cannot be before 1 dec 1930 or after 1 dec 2007")}else{self.setError("")}};self.sameAs=function(target){const $target=self.sibling(target);if($target.getValue()==self.getValue()){$input.css("background-color","#d7f6ca")}else{$input.css("background-color","#e1adad")}};self.setValue=function(value){$input.val(value);$input.trigger("change");return self};self.getValue=function(){var raw=$input.val();if(NUMBERS.indexOf(inputType)>=0){return parseInt(raw)}return raw};self.setDescription=function(value){$fieldset.find(".description").text(value);return self};self.setHint=function(value){$fieldset.find(".hint").text(value);return self};self.sibling=function(name){var $target=$formContainer.find(`:input[data-flex=${name}]`);if(!$target[0]){alert(`Cannot find "input[name=${name}]"`)}return new aurora.Field($target)};self.formsets=function(name){var $target=$formContainer.find(`input[data-flex=${name}]`);if(!$target[0]){alert(`Cannot find "input[name=${name}]"`)}return new aurora.Field($target)};self.setRequiredOnValue=function(value,targets){try{const cmp=value.toString().toLowerCase();const val=self.getValue();$form.find(targets).each(function(i,e){var $c=$(e).parents(".field-container");if(val==cmp){$(e).attr("required",true);$c.find(".required-label").show()}else{$(e).attr("required",false);$c.find(".required-label").hide()}})}catch(error){console.error(error)}};self.inspect=function(){console.log(`${id}: `,{value:self.getValue(),id:id,me:$me,form:$form,fieldset:$fieldset,input:$input})}}};$(function(){if(!window.module){window.module=new aurora.Module({})}$("[data-onload]").each(function(){eval($(this).data("onload"))})})})(jQuery||django.jQuery); +(function($){const NUMBERS=["month","number","week"];const DATES=["date","time"];const STRINGS=["email","tel","text","url"];var highLight=function(onOff){if(onOff){this.__oldBorder=this.$.css("border");this.$.css("border","1px solid red")}else{this.$.css("border",this.__oldBorder)}return this};window.debug=function(arguments){var log;if(window.parent.document.getElementById("debugLog")){log=window.parent.document.getElementById("debugLog")}else{log=window.document.getElementById("debugLog")}if(log){$(log).append(arguments);$(log).append("\n")}};window.aurora={Module:function(config){var self=this;var errorsStack={};self.$=$("#formContainer");var $form=$("#registrationForm");self.$submit=$form.find("input[type=submit]");self.config=config;self.name=config.name;self.setError=function(msg){self.$.find(".alert-message").html(msg)};self.pushError=function(f){errorsStack[f]=true};self.popError=function(f){errorsStack[f]=false;var ret=_.filter(_.values(errorsStack),function(i){return i===true})};self.getError=function(f){return errorsStack[f]};self.getErrors=function(){return _.filter(_.keys(errorsStack),function(i,e){return errorsStack[i]===true})};var _fields=null;self.fields=function(){if(_fields==null){_fields={};$form.find(`:input[data-flex-name]`).each(function(i,e){var f=new aurora.Field(e);_fields[f.name]=f})}return _fields};self.isValid=function(){var ret=true;for(let i=0,keys=Object.keys(self.fields()),ii=keys.length;i
  • ${text}
  • `);module.pushError(self.name)}else{$fieldset.data("error","");$fieldset.find(".errors").html("");module.popError(self.name)}return self};self.assertBetween=function(min,max){};self.assertDateBetween=function(min,max){var limit1=Date.parse(min);var limit2=Date.parse(max);var dt=Date.parse(self.getValue());if(!(dtlimit2)){self.setError("the date cannot be before 1 dec 1930 or after 1 dec 2007")}else{self.setError("")}};self.sameAs=function(target){const $target=self.sibling(target);if($target.getValue()==self.getValue()){$input.css("background-color","#d7f6ca")}else{$input.css("background-color","#e1adad")}};self.setValue=function(value){$input.val(value);$input.trigger("change");return self};self.getValue=function(){var raw=$input.val();if(NUMBERS.indexOf(inputType)>=0){return parseInt(raw)}return raw};self.setDescription=function(value){$fieldset.find(".description").text(value);return self};self.setHint=function(value){$fieldset.find(".hint").text(value);return self};self.sibling=function(name){var $target=$formContainer.find(`:input[data-flex=${name}]`);if(!$target[0]){alert(`Cannot find "input[name=${name}]"`)}return new aurora.Field($target)};self.formsets=function(name){var $target=$formContainer.find(`input[data-flex=${name}]`);if(!$target[0]){alert(`Cannot find "input[name=${name}]"`)}return new aurora.Field($target)};self.setRequiredOnValue=function(value,targets){try{const cmp=value.toString().toLowerCase();const val=self.getValue();$form.find(targets).each(function(i,e){var $c=$(e).parents(".field-container");if(val==cmp){$(e).attr("required",true);$c.find(".required-label").show()}else{$(e).attr("required",false);$c.find(".required-label").hide()}})}catch(error){console.error(error)}};self.inspect=function(){console.log(`${id}: `,{value:self.getValue(),id:id,me:$me,form:$form,fieldset:$fieldset,input:$input})}}};$(function(){if(!window.module){window.module=new aurora.Module({})}$("[data-onload]").each(function(){eval($(this).data("onload"))})})})(jQuery||django.jQuery); diff --git a/src/aurora/core/templates/admin/core/flexform/form_editor/main.html b/src/aurora/core/templates/admin/core/flexform/form_editor/main.html index 20bf7716..b0395a82 100644 --- a/src/aurora/core/templates/admin/core/flexform/form_editor/main.html +++ b/src/aurora/core/templates/admin/core/flexform/form_editor/main.html @@ -1,10 +1,11 @@ {% extends "admin_extra_buttons/action_page.html" %}{% load static itrans aurora field_editor %} {% block extrahead %} - - - - - {{ forms_media }} + + + + {{ media }} {% endblock %} {% block object-tools %}{% endblock %} {% block content_title %}

    Form: {{ original.name }}

    {% endblock %} @@ -13,30 +14,45 @@
    -
    +
    -
    - - -{# #} +
    + + + +
    -
    +
    - - + + {% for prefix, frm in forms.items %} + {% if prefix != "events" and prefix != "fields" %} + + {% endif %} + {% endfor %} - - + @@ -44,61 +60,34 @@ #} + {#
    Form attributes
    {{ frm.Meta.title }}FieldsStructureEvents{{ forms.events.Meta.title }} Usage
    {% csrf_token %} - - {{ form_frm }} -
    + {% for prefix, frm in forms.items %} + {% if prefix != "events" and prefix != "fields" %} + + + + + {{ frm }} + + {% endif %} + {% endfor %} - {% for fld in original.fields.all %} - - - - - {% endfor %} - - - - - - {% for fs in original.formset_set.all %} - - - - - {% endfor %} - - - - {% for fs in original.formsets.all %} - - - - - {% endfor %} - - - {% for usage in original.get_usage %} - - - - + {% for n,f in forms.fields.fields.items %} + {% with forms.fields|get:n as ff %} + + {% endwith %} +{# #} {% endfor %} + - + {#
    #} + {# {{ form_frm }}#} + {#
    #} + {# #} + {# {% for fld in original.fields.all %}#} + {# #} + {# #} + {# #} + {# #} + {# {% endfor %}#} + {# #} + {# #} + {# #} + {# #} + {# #} + {# {% for fs in original.formset_set.all %}#} + {# #} + {# #} + {# #} + {# #} + {# {% endfor %}#} + {# #} + {# #} + {# #} + {# {% for fs in original.formsets.all %}#} + {# #} + {# #} + {# #} + {# #} + {# {% endfor %}#} + {# #} + {# #} + {# {% for usage in original.get_usage %}#} + {# #} + {# #} + {# #} + {# #} + {# {% endfor %}#} + {# #} + {# #} + {# #} + {# #} + {# #} + {# #} + {# #} + {# #} + {# #} + {##}