示例#1
0
 def routes(self):
     return {
         f'{self._route_prefix}':
         m(
             app_container, {
                 'content':
                 self._list(),
                 'header_content':
                 self._header(
                     '/',
                     m(
                         button, {
                             'content': 'New',
                             'left_icon': 'solid/plus',
                             'route': f'{self._route_prefix}/new'
                         })),
             }),
         f'{self._route_prefix}/:id/view':
         self._detail(),
         f'{self._route_prefix}/new':
         m(
             app_container,
             {
                 'content':
                 self._create(),
                 'header_content':
                 self._header(
                     self._route_prefix,
                     # TODO onclick save the form
                     m(button, {
                         'content': 'Save',
                         'left_icon': 'solid/save'
                     }))
             }),
     }
示例#2
0
    def field_group(field_: str):
        t = get_type(field_)
        nested_form = issubclass(t, (ValueObject, Entity))
        children = []
        if config['labels']:
            children.append(label(field_))

        if nested_form:
            children.append = [
                m(Form(t(), {
                    'nested': True,
                    'required': is_required(field_)
                }))
            ]
        else:
            children.append(form_field(field_))

        if nested_form is False:
            children.append(error_container(field_))

        classes = ''
        if has_errors(field_):
            classes += '.error'

        if nested_form:
            return children
        else:
            return m(f'div.field-group{classes}', children)
示例#3
0
    def field_set(fields_: list, options: dict, legend: str):
        children = []
        if legend is not None:
            children.append(m('legend', legend))
        children.extend(list(map(field_group, fields_)))

        return m(f'fieldset', options, children)
示例#4
0
    def _header(self, return_route, right_button):
        def redirect():
            m.route.set(return_route or '/')

        return [
            m('div.w-8.invert-stroke.flex.flex-col.justify-center',
              m(Icon('solid/arrow-left', onclick=redirect))),
            right_button,
        ]
示例#5
0
def crud(entity: str, cls, route_prefix: str, form_config: dict = None):
    if '.' not in entity:
        raise ff.LogicError('entity must be formatted as "<context>.<entity>"')
    parts = entity.split('.')
    context = parts[0]
    entity = parts[1]

    def crud_list():
        entities = []

        def set_entities(e):
            nonlocal entities
            entities = e

        bus.request(f'{context}.{inflection.pluralize(entity)}').then(
            set_entities)

        return {
            'view':
            lambda: m('div', list(map(lambda e: e.to_dict(), entities)))
        }

    def crud_new():
        return {'view': lambda: m(form(cls(), form_config))}

    def redirect(return_route):
        def _redirect():
            m.route.set(return_route or '/')

        return _redirect

    add_route(
        route_prefix,
        compose(default_layout,
                crud_list,
                header=lambda: {
                    'view':
                    lambda: [
                        m('div.w-8.invert-stroke.flex.flex-col.justify-center',
                          m(Icon('solid/arrow-left', onclick=redirect('/')))),
                        m(
                            button({
                                'content': 'New',
                                'left_icon': 'solid/plus',
                                'route': f'{route_prefix}/new'
                            }))
                    ]
                }))

    add_route(f'{route_prefix}/new', compose(default_layout, crud_new))
示例#6
0
 def datetime_inputs(f: str, classes: str, conf: dict):
     return [
         m(
             f'input[type="date"][id="{f}_date"][name="{f}_date"]'
             f'[value="{moment(dto[f]).format("YYYY-MM-DD")}"]'
             f'.form-date-input.px-2.py-1.rounded-sm.h-12{classes}',
             conf,
         ),
         m(
             f'input[type="time"][id="{f}_date"][name="{f}_date"]'
             f'[value="{moment(dto[f]).format("HH:mm")}"]'
             f'.form-time-input.px-2.py-1.rounded-sm.h-12{classes}',
             conf,
         ),
     ]
示例#7
0
    def view():
        config = {'onsubmit': handle_submit}
        if 'fields' in config:
            keys = config['fields']
        else:
            keys = dto.keys()

        if 'exclude_fields' in config:
            keys = [x for x in keys if x not in config['exclude_fields']]

        if 'fieldsets' in config:
            form_fields = []
            for fieldset in config['fieldsets']:
                form_fields.append(
                    field_set(
                        fieldset['fields'], fieldset['options'],
                        fieldset['legend'] if 'legend' in fieldset else None))
        else:
            form_fields = list(map(field_group, keys))

        if 'nested' not in config or not config['nested']:
            form_fields.append(
                m('input[type="submit"][value="Submit"].hidden.md:block'))

        return _form(config, form_fields)
示例#8
0
 def text_input(f: str,
                classes: str,
                conf: dict,
                children,
                input_type: str = 'text'):
     return m(
         f'input[type="{input_type}"][id="{f}"][name="{f}"][value="{dto[f]}"][placeholder="{humanize(f)}"]'
         f'.form-text-input.px-2.py-1.rounded-sm{classes}.h-12', conf,
         children)
示例#9
0
    def view():
        options = {}
        if onclick is not None:
            options['onclick'] = onclick

        item = m(
            'div.ff-card.flex.flex-row.justify-start.h-16.md:justify-center.md:flex-col.md:w-56.md:ml-3.md:h-32',
            options, [
                m(
                    'div.w-8.h-full.invert-stroke.ml-2.py-3.md:ml-0.md:h-20.md:w-full.md:flex.md:flex-row.md:justify-center',
                    m(Icon(icon))),
                m(
                    'div.w-10/12.ml-3.flex.flex-col.justify-center.md:w-full.md:ml-0.md:flex-row.md:justify-center',
                    text),
            ])

        if route is not None:
            item = m(m.route.Link, {'href': route}, item)

        return item
示例#10
0
    def view():
        config = {}
        if 'onclick' in data:
            config['onclick'] = data['onclick']
        elif 'route' in data:

            def redirect():
                m.route.set(data['route'])

            config['onclick'] = redirect

        content = [
            m('span.flex.flex-col.justify-center.font-bold', data['content'])
        ]
        if 'left_icon' in data:
            content.insert(
                0,
                m('div.invert-stroke.my-auto.w-5.mr-2',
                  m(Icon(data['left_icon']))))
        if 'right_icon' in data:
            content.append(
                m('div.invert-stroke.my-auto.w-5.ml-2',
                  m(Icon(data['right_icon']))))

        classes = ''
        if style == 'horizontal':
            classes += '.flex.flex-row.justify-between'

        return m(
            f'button[type="button"]{classes}.h-10.border.rounded.px-2.my-auto',
            config, content)
示例#11
0
    def select(f: str, options, classes: str, conf: dict):
        value = dto[f]
        options = list(options)
        options.insert(0, humanize(f))

        def option(f):
            selected = ''
            if f == value:
                selected = '[selected="selected"]'
            return m(f'option[value="{f}"]{selected}', f)

        return m(f'select.form-select{classes}.h-12', conf,
                 list(map(option, options)))
示例#12
0
    def crud_list():
        entities = []

        def set_entities(e):
            nonlocal entities
            entities = e

        bus.request(f'{context}.{inflection.pluralize(entity)}').then(
            set_entities)

        return {
            'view':
            lambda: m('div', list(map(lambda e: e.to_dict(), entities)))
        }
示例#13
0
    def _list(self):
        context = self._context
        entity = self._entity
        entities = []

        class ListEntity:
            @staticmethod
            def oninit():
                def set_entities(e):
                    nonlocal entities
                    entities = e
                    m.redraw()

                # TODO Use a QueryStream
                bus.request(f'{context}.{inflection.pluralize(entity)}').then(
                    set_entities)

            @staticmethod
            def view():
                return m('div', list(map(lambda e: e.to_dict(), entities)))

        return m(ListEntity())
示例#14
0
 def error_container(f: str, conf=None):
     return m(f'span[id="{f}_error"].errors', conf, error_messages(f))
示例#15
0
 def label(f: str, conf=None):
     return f'label', m(f'label[for="{f}"].block', conf, humanize(f))
示例#16
0
    def _form(conf=None, children=None):
        if 'nested' in config and config['nested']:
            return children

        return m('div.form-container.flex.flex-col',
                 m(f'form#{config["id"]}', conf, children))
示例#17
0
 def crud_new():
     return {'view': lambda: m(form(cls(), form_config))}
示例#18
0
 def option(f):
     selected = ''
     if f == value:
         selected = '[selected="selected"]'
     return m(f'option[value="{f}"]{selected}', f)
示例#19
0
 def _create(self):
     return m(Form(self._class(), self._form_config))
示例#20
0
 def boolean_input(f: str, classes: str, conf: dict):
     if dto[f]:
         conf['checked'] = 'checked'
     return m(
         f'input[type="checkbox"][id="{f}"][name="{f}"].form-checkbox-input{classes}',
         conf)
示例#21
0
 def error_messages(f: str):
     if f not in errors or not has_value(f):
         return []
     return m('ul', map(lambda msg: m('li', msg), errors[f]))
示例#22
0
def default_layout(main, menu, header, drawer, footer):
    drawer_is_open = False

    def close_drawer():
        nonlocal drawer_is_open
        drawer_is_open = False

    def open_drawer():
        nonlocal drawer_is_open
        drawer_is_open = True

    return lambda: [
        m('div.app.flex.flex-col', [
            m(
                'div.ff-header.fixed.flex.flex-row.justify-between.h-20.w-full.z-30.px-5.md:ml-20.md:pr-20',
                [
                    m('div.my-auto', m(firefly_logo))
                    if header is None else m(header),
                ]),
            m('div.flex.flex-row', [
                m(
                    'div.ff-menu.fixed.z-30.w-full.h-20.bottom-0.flex.flex-row.justify-between'
                    '.md:left-0.md:bottom-auto.md:flex-col.md:justify-start.md:w-20.md:h-full',
                    menu or ''),
                m(
                    'div.ff-content.z-0.w-full.mb-20.mt-20.flex.flex-col.justify-between'
                    '.md:ml-20.md:mb-0.md:flex-row.md:flex-wrap.md:justify-start',
                    m(main)
                    if callable(main) or hasattr(main, 'view') else main)
            ]),
            m('div.ff-footer', footer) if footer is not None else '',
        ]),
        m(
            f'div.ff-drawer-bg.z-40{".open" if drawer_is_open is True else ""}',
            {'onclick': close_drawer},
            m(
                'div.ff-drawer', {'onclick': lambda e: e.stopPropagation()},
                m('div.flex.flex-row.h-full', [
                    m('div.w-10/12', drawer or ''),
                    m(
                        'div.w-2/12.h-full.px-1.flex.flex-col.justify-center',
                        m('div.invert-stroke.w-full.h-10', {
                            'onclick': close_drawer
                        }, m(Icon('solid/chevron-left'))))
                ])))
    ]
示例#23
0
 def view():
     return m('div', list(map(lambda e: e.to_dict(), entities)))
示例#24
0
#  Firefly is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
#  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
#  Public License for more details. You should have received a copy of the GNU Lesser General Public
#  License along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#  You should have received a copy of the GNU General Public License along with Firefly. If not, see
#  <http://www.gnu.org/licenses/>.

from firefly.presentation.web.js_libs.mithril import m
from firefly.presentation.web.polyfills import *  # __:skip


firefly_icon = {
    'view': lambda: m('div.logo-image.rounded-full', [
        m('div.firefly.one'),
        m('div.firefly.two'),
        m('div.firefly.three'),
    ])
}


firefly_logo_text = {
    'view': lambda: [
        m('span.fire.inline-block.ml-2', 'Fire'),
        m('span.fly.inline-block', 'fly')
    ]
}


firefly_logo = {
    'view': lambda: m('div.logo.font-sans.font-bold.text-shadow.rambla.flex.flex-row.justify-start', [
示例#25
0
#  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
#  Public License for more details. You should have received a copy of the GNU Lesser General Public
#  License along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#  You should have received a copy of the GNU General Public License along with Firefly. If not, see
#  <http://www.gnu.org/licenses/>.

from firefly.presentation.web.components.layouts.default import menu_item, compose, default_layout
from firefly.presentation.web.js_libs.mithril import m
from firefly.presentation.web.plugins import add_route, add_menu_item
from firefly.presentation.web.polyfills import *  # __:skip

# __pragma__('opov')

m.route.prefix = '/admin'

add_menu_item(m('div.ff-title', 'Kernel'), 0)
add_menu_item(m(menu_item('Configuration', icon='solid/cog')), 1)
add_menu_item(m(menu_item('System Health', icon='solid/heart')), 2)
add_menu_item(m(menu_item('Services', icon='solid/wifi')), 3)

add_route('/', compose(default_layout, lambda: window.ff_menu))

m.route(document.body, '/', window.ff_routes)
"""
__pragma__('js', '{}', '''
if (module.hot) {
  module.hot.accept();
}
''')
"""