Parcourir la source

feat: Page Builder based on Tailwind

- Website Theme based on: Bootstrap 4, Tailwind
- Web Page: Page Builder
- Web Template: Create templates for web pages
- Added some common Web Templates
- Components: Standard components directly usable in Jinja
- Purge Tailwind Classes in Production
- PostCSS Config to support tailwind
version-14
Faris Ansari il y a 5 ans
Parent
révision
2431767fdf
67 fichiers modifiés avec 1753 ajouts et 68 suppressions
  1. +1
    -0
      frappe/__init__.py
  2. +3
    -0
      frappe/public/build.json
  3. +47
    -0
      frappe/public/css/email.css
  4. +8
    -0
      frappe/public/tailwind.css
  5. +16
    -5
      frappe/templates/base.html
  6. +27
    -0
      frappe/templates/components/button.html
  7. +7
    -0
      frappe/templates/components/card.html
  8. +25
    -0
      frappe/templates/components/dropdown.html
  9. +20
    -0
      frappe/templates/components/navbar.html
  10. +3
    -0
      frappe/templates/components/navbar_link.html
  11. +5
    -0
      frappe/templates/components/web_page_section.html
  12. +6
    -3
      frappe/templates/includes/navbar/navbar.html
  13. +2
    -2
      frappe/templates/web.html
  14. +3
    -0
      frappe/utils/data.py
  15. +75
    -2
      frappe/utils/jinja.py
  16. +2
    -0
      frappe/website/context.py
  17. +6
    -0
      frappe/website/doctype/web_page/templates/web_page.html
  18. +29
    -5
      frappe/website/doctype/web_page/web_page.js
  19. +12
    -2
      frappe/website/doctype/web_page/web_page.json
  20. +19
    -0
      frappe/website/doctype/web_page/web_page.py
  21. +0
    -0
      frappe/website/doctype/web_page_block/__init__.py
  22. +51
    -0
      frappe/website/doctype/web_page_block/web_page_block.json
  23. +10
    -0
      frappe/website/doctype/web_page_block/web_page_block.py
  24. +0
    -0
      frappe/website/doctype/web_template/__init__.py
  25. +10
    -0
      frappe/website/doctype/web_template/test_web_template.py
  26. +8
    -0
      frappe/website/doctype/web_template/web_template.js
  27. +61
    -0
      frappe/website/doctype/web_template/web_template.json
  28. +45
    -0
      frappe/website/doctype/web_template/web_template.py
  29. +0
    -0
      frappe/website/doctype/web_template_field/__init__.py
  30. +10
    -0
      frappe/website/doctype/web_template_field/test_web_template_field.py
  31. +8
    -0
      frappe/website/doctype/web_template_field/web_template_field.js
  32. +47
    -0
      frappe/website/doctype/web_template_field/web_template_field.json
  33. +10
    -0
      frappe/website/doctype/web_template_field/web_template_field.py
  34. +69
    -1
      frappe/website/doctype/website_theme/website_theme.json
  35. +27
    -0
      frappe/website/render.py
  36. +0
    -0
      frappe/website/web_template/__init__.py
  37. +0
    -0
      frappe/website/web_template/full_width_image/__init__.py
  38. +1
    -0
      frappe/website/web_template/full_width_image/full_width_image.html
  39. +24
    -0
      frappe/website/web_template/full_width_image/full_width_image.json
  40. +0
    -0
      frappe/website/web_template/hero_with_right_image/__init__.py
  41. +39
    -0
      frappe/website/web_template/hero_with_right_image/hero_with_right_image.html
  42. +49
    -0
      frappe/website/web_template/hero_with_right_image/hero_with_right_image.json
  43. +0
    -0
      frappe/website/web_template/section_with_big_cards/__init__.py
  44. +19
    -0
      frappe/website/web_template/section_with_big_cards/section_with_big_cards.html
  45. +58
    -0
      frappe/website/web_template/section_with_big_cards/section_with_big_cards.json
  46. +0
    -0
      frappe/website/web_template/section_with_cta/__init__.py
  47. +7
    -0
      frappe/website/web_template/section_with_cta/section_with_cta.html
  48. +33
    -0
      frappe/website/web_template/section_with_cta/section_with_cta.json
  49. +0
    -0
      frappe/website/web_template/section_with_image/__init__.py
  50. +4
    -0
      frappe/website/web_template/section_with_image/section_with_image.html
  51. +33
    -0
      frappe/website/web_template/section_with_image/section_with_image.json
  52. +0
    -0
      frappe/website/web_template/section_with_left_image/__init__.py
  53. +9
    -0
      frappe/website/web_template/section_with_left_image/section_with_left_image.html
  54. +29
    -0
      frappe/website/web_template/section_with_left_image/section_with_left_image.json
  55. +0
    -0
      frappe/website/web_template/section_with_small_cards/__init__.py
  56. +19
    -0
      frappe/website/web_template/section_with_small_cards/section_with_small_cards.html
  57. +103
    -0
      frappe/website/web_template/section_with_small_cards/section_with_small_cards.json
  58. +0
    -0
      frappe/website/web_template/section_with_tabs/__init__.py
  59. +109
    -0
      frappe/website/web_template/section_with_tabs/section_with_tabs.html
  60. +83
    -0
      frappe/website/web_template/section_with_tabs/section_with_tabs.json
  61. +12
    -24
      frappe/website/website_theme/standard/standard.json
  62. +7
    -5
      package.json
  63. +23
    -0
      purgecss.js
  64. +1
    -1
      requirements.txt
  65. +17
    -11
      rollup/config.js
  66. +75
    -0
      tailwind.config.js
  67. +327
    -7
      yarn.lock

+ 1
- 0
frappe/__init__.py Voir le fichier

@@ -830,6 +830,7 @@ def rename_doc(*args, **kwargs):

Calls `frappe.model.rename_doc.rename_doc`
"""
kwargs.pop('cmd', None)
kwargs.pop('ignore_permissions', None)
from frappe.model.rename_doc import rename_doc
return rename_doc(*args, **kwargs)


+ 3
- 0
frappe/public/build.json Voir le fichier

@@ -350,5 +350,8 @@
],
"js/data_import_tools.min.js": [
"public/js/frappe/data_import/index.js"
],
"css/tailwind.css": [
"public/tailwind.css"
]
}

+ 47
- 0
frappe/public/css/email.css Voir le fichier

@@ -1,64 +1,82 @@
/* csslint ignore:start */

/* palette colors*/

body {
line-height: 1.5;
color: #36414c;
}

p {
margin: 1em 0 !important;
}

hr {
border-top: 1px solid #d1d8dd;
}

.body-table {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

.body-table td {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

.email-header,
.email-body,
.email-footer {
width: 100% !important;
min-width: 100% !important;
}

.email-body {
font-size: 14px;
}

.email-footer {
border-top: 1px solid #d1d8dd;
font-size: 12px;
}

.email-header {
border: 1px solid #d1d8dd;
border-radius: 4px 4px 0 0;
}

.email-header .brand-image {
width: 24px;
height: 24px;
display: block;
}

.email-header-title {
font-weight: bold;
}

.body-table.has-header .email-body {
border: 1px solid #d1d8dd;
border-radius: 0 0 4px 4px;
border-top: none;
}

.body-table.has-header .email-footer {
border-top: none;
}

.email-footer-container {
margin-top: 30px;
}

.email-footer-container > div:not(:last-child) {
margin-bottom: 5px;
}

.email-unsubscribe a {
color: #8d99a6;
text-decoration: underline;
}

.btn {
text-decoration: none;
padding: 7px 10px;
@@ -66,20 +84,24 @@ hr {
border: 1px solid;
border-radius: 3px;
}

.btn.btn-default {
color: #fff;
background-color: #f0f4f7;
border-color: transparent;
}

.btn.btn-primary {
color: #fff;
background-color: #5e64ff;
border-color: #444bff;
}

.table {
width: 100%;
border-collapse: collapse;
}

.table td,
.table th {
padding: 8px;
@@ -88,53 +110,68 @@ hr {
border-top: 1px solid #d1d8dd;
text-align: left;
}

.table th {
font-weight: bold;
}

.table > thead > tr > th {
vertical-align: middle;
border-bottom: 2px solid #d1d8dd;
}

.table > thead:first-child > tr:first-child > th {
border-top: none;
}

.table.table-bordered {
border: 1px solid #d1d8dd;
}

.table.table-bordered td,
.table.table-bordered th {
border: 1px solid #d1d8dd;
}

.more-info {
font-size: 80% !important;
color: #8d99a6 !important;
border-top: 1px solid #ebeff2;
padding-top: 10px;
}

.text-right {
text-align: right !important;
}

.text-center {
text-align: center !important;
}

.text-muted {
color: #8d99a6 !important;
}

.text-extra-muted {
color: #d1d8dd !important;
}

.text-regular {
font-size: 14px;
}

.text-medium {
font-size: 12px;
}

.text-small {
font-size: 10px;
}

.text-bold {
font-weight: bold;
}

.indicator {
width: 8px;
height: 8px;
@@ -143,33 +180,43 @@ hr {
display: inline-block;
margin-right: 5px;
}

.indicator.indicator-blue {
background-color: #5e64ff;
}

.indicator.indicator-green {
background-color: #98d85b;
}

.indicator.indicator-orange {
background-color: #ffa00a;
}

.indicator.indicator-red {
background-color: #ff5858;
}

.indicator.indicator-yellow {
background-color: #feef72;
}

.screenshot {
box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1);
border: 1px solid #d1d8dd;
margin: 8px 0;
max-width: 100%;
}

.list-unstyled {
list-style-type: none;
padding: 0;
}

/* auto email report */

.report-title {
margin-bottom: 20px;
}

/* csslint ignore:end */

+ 8
- 0
frappe/public/tailwind.css Voir le fichier

@@ -0,0 +1,8 @@
@tailwind base;
@tailwind components;

details.dropdown summary::-webkit-details-marker {
display: none;
}

@tailwind utilities;

+ 16
- 5
frappe/templates/base.html Voir le fichier

@@ -25,15 +25,26 @@
{{ head_html or "" }}
{%- endif %}

{% if theme.theme_url %}
<link type="text/css" rel="stylesheet" href="{{ theme.theme_url }}">
{%- if theme.based_on == 'Bootstrap 4' -%}
{%- if theme.theme_url -%}
<link type="text/css" rel="stylesheet" href="{{ theme.theme_url }}">
{%- else -%}
<link type="text/css" rel="stylesheet" href="/assets/css/frappe-web-b4.css">
{%- endif -%}
{% else %}
<link type="text/css" rel="stylesheet" href="/assets/css/frappe-web-b4.css">
{%- if developer_mode -%}
<!-- load the full list of tailwind classes in development -->
<link type="text/css" rel="stylesheet" href="/assets/css/tailwind.css">
{%- else -%}
<!-- load tree-shaken classes in production -->
<!-- container for processed tailwind styles -->
<!-- tailwind-styles -->
{% endif %}
{%- endif -%}

{%- for link in web_include_css %}
<link type="text/css" rel="stylesheet" href="{{ link|abs_url }}">
{%- endfor -%}
{% endif %}
{%- endblock -%}

{%- block head_include %}
@@ -50,7 +61,7 @@
}
window.dev_server = {{ dev_server }};
window.socketio_port = {{ frappe.socketio_port }};
</script>
</script>
</head>
<body frappe-session-status="{{ 'logged-in' if frappe.session.user != 'Guest' else 'logged-out'}}" data-path="{{ path | e }}" {%- if template and template.endswith('.md') %} frappe-content-type="markdown" {% endif -%}>
{%- block banner -%}


+ 27
- 0
frappe/templates/components/button.html Voir le fichier

@@ -0,0 +1,27 @@
---
name: "button"
variant: "secondary"
disabled: 0
url: null
---

{%- set static_classes = "px-4 py-2 border rounded-md inline-flex justify-center items-center focus:outline-none leading-5 font-medium text-sm" -%}
{%- set dynamic_classes = {
"opacity-50 cursor-not-allowed pointer-events-none": disabled,
"bg-blue-500 border-transparent hover:bg-blue-400 text-white focus:shadow-outline-blue focus:border-blue-700":
variant == "primary",
"bg-white border-gray-400 hover:text-gray-800 text-gray-900 focus:shadow-outline-blue focus:border-blue-300":
variant == "secondary",
"bg-red-500 border-transparent hover:bg-red-400 text-white focus:shadow-outline-red focus:border-red-700":
variant == "danger"
}
-%}
{%- set html_tag = "a" if url else "button" -%}

<{{html_tag}}
class="{{ resolve_class([static_classes, dynamic_classes, class]) }}"
{{ 'disabled' if disabled else '' }}
{{ ('href="' + url + '"') if url else '' }}
>
{{ label }}
</{{html_tag}}>

+ 7
- 0
frappe/templates/components/card.html Voir le fichier

@@ -0,0 +1,7 @@
<div class="px-6 py-4 border rounded-lg shadow">
<h1 class="text-2xl font-bold">{{ title }}</h1>
<p>{{ subtitle }}</p>
<div class="mt-4">
{{ actions }}
</div>
</div>

+ 25
- 0
frappe/templates/components/dropdown.html Voir le fichier

@@ -0,0 +1,25 @@
<details class="relative inline-block text-left dropdown">
<summary class="block cursor-pointer focus:outline-none">
<span class="inline-flex items-center justify-center text-gray-800 hover:text-gray-900">
{{ label }}
<svg class="w-5 h-5 ml-2 -mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd" />
</svg>
</span>
</summary>
<div
class="absolute right-0 w-56 mt-2 origin-top-right rounded-md shadow-lg">
<div class="bg-white rounded-md shadow-xs">
<div class="py-1">
{%- for item in items -%}
<a href="{{ item.link or item.url }}"
class="block px-4 py-2 text-sm leading-5 text-gray-800 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900">
{{ item.label }}
</a>
{%- endfor -%}
</div>
</div>
</div>
</details>

+ 20
- 0
frappe/templates/components/navbar.html Voir le fichier

@@ -0,0 +1,20 @@
<header class="px-4 py-4 bg-white border-b border-gray-200 sm:px-6 lg:px-8">
<div
class="container relative z-10 flex items-center justify-between ">
<div class="flex items-end">
<a href="/components" class="block">
<span>{{ brand_html or (frappe.get_hooks("brand_html") or [_("Home")])[0] }}</span>
</a>
</div>

<div class="flex items-center text-sm leading-5 space-x-8">
{%- for item in theme.navbar_items -%}
{{ c('navbar_link', label=item.label, url=item.url) }}
{%- endfor -%}
{{ c('navbar_link', label=_('Login'), url='/login') }}
{%- if theme.primary_action_label and theme.primary_action_url -%}
{{ c('button', label=theme.primary_action_label, url=theme.primary_action_url, variant="primary") }}
{%- endif -%}
</div>
</div>
</header>

+ 3
- 0
frappe/templates/components/navbar_link.html Voir le fichier

@@ -0,0 +1,3 @@
<a href="{{ url }}" class="font-medium text-gray-800 hover:text-gray-900">
{{ label }}
</a>

+ 5
- 0
frappe/templates/components/web_page_section.html Voir le fichier

@@ -0,0 +1,5 @@
<section class="py-32 {{ section.css_class or '' }}" data-section-title="{{ section.title | e }}">
<div class="container">
{{ section.rendered_html }}
</div>
</section>

+ 6
- 3
frappe/templates/includes/navbar/navbar.html Voir le fichier

@@ -1,4 +1,5 @@
<nav class="navbar navbar-light bg-white navbar-expand-lg sticky-top shadow-sm">
{%- if theme.based_on == 'Bootstrap 4' -%}
<nav class="bg-white shadow-sm navbar navbar-light navbar-expand-lg sticky-top">
<div class="container">
<a class="navbar-brand" href="{{ url_prefix }}{{ home_page or "/" }}">
<span>{{ brand_html or (frappe.get_hooks("brand_html") or [_("Home")])[0] }}</span>
@@ -8,8 +9,7 @@
data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>

@@ -18,3 +18,6 @@
</div>
</div>
</nav>
{%- else -%}
{{ c('navbar', theme=theme) }}
{%- endif -%}

+ 2
- 2
frappe/templates/web.html Voir le fichier

@@ -13,7 +13,7 @@
</div>

{% block page_container %}
<main class="container my-5">
<main class="{{ 'container my-5' if theme.based_on=='Bootstrap 4' else '' }}">
<div class="d-flex justify-content-between align-items-center">
<div class="page-header">
{% block header %}{% endblock %}
@@ -43,7 +43,7 @@ id="page-{{ name or route | e }}" data-path="{{ pathname | e }}" {%- if page_or_
{% endmacro %}

{% if show_sidebar %}
<div class="container">
<div class="{{ 'container' if theme.based_on=='Bootstrap 4' else '' }}">
<div class="row" {{ container_attributes() }}>
<div class="pt-4 col-sm-2 border-right sidebar-column d-none d-sm-block">
{% block page_sidebar %}


+ 3
- 0
frappe/utils/data.py Voir le fichier

@@ -1091,3 +1091,6 @@ def get_source_value(source, key):
def is_subset(list_a, list_b):
'''Returns whether list_a is a subset of list_b'''
return len(list(set(list_a) & set(list_b))) == len(list_a)

def generate_hash(*args, **kwargs):
return frappe.generate_hash(*args, **kwargs)

+ 75
- 2
frappe/utils/jinja.py Voir le fichier

@@ -11,12 +11,19 @@ def get_jenv():
from jinja2.sandbox import SandboxedEnvironment

# frappe will be loaded last, so app templates will get precedence
jenv = SandboxedEnvironment(loader = get_jloader(),
undefined=DebugUndefined)
jenv = SandboxedEnvironment(
loader=get_jloader(),
undefined=DebugUndefined
)
set_filters(jenv)

jenv.globals.update(get_safe_globals())
jenv.globals.update(get_jenv_customization('methods'))
jenv.globals.update({
'component': component,
'c': component,
'inspect': inspect
})

frappe.local.jenv = jenv

@@ -149,3 +156,69 @@ def get_jenv_customization(customization_type):
out[fn_name] = frappe.get_attr(fn_string)

return out


def component(name, **kwargs):
from jinja2 import TemplateNotFound

template_name = 'templates/components/' + name + '.html'
jenv = get_jenv()

try:
source = jenv.loader.get_source(jenv, template_name)[0]
except TemplateNotFound:
return '<pre>Component "{0}" not found</pre>'.format(name)

attributes, html = parse_front_matter_attrs_and_html(source)
context = {}
context.update(attributes)
context.update(kwargs)

if 'class' in context:
context['class'] = resolve_class(context['class'])
else:
context['class'] = ''
context['resolve_class'] = resolve_class

return get_jenv().from_string(html).render(context)

def resolve_class(classes):
import frappe

if isinstance(classes, frappe.string_types):
return classes

if isinstance(classes, (list, tuple)):
return ' '.join([resolve_class(c) for c in classes])

if isinstance(classes, dict):
return ' '.join([classname for classname in classes if classes[classname]])

return classes

def parse_front_matter_attrs_and_html(source):
from frontmatter import Frontmatter

html = source
attributes = {}

if not source.startswith('---'):
return attributes, html

try:
res = Frontmatter.read(source)
if res['attributes']:
attributes = res['attributes']
html = res['body']
except Exception as e:
print('Error parsing Frontmatter in template: ' + e)

return attributes, html

def inspect(var, render=True):
context = { "var": var }
if render:
html = "<pre>{{ var | pprint | e }}</pre>"
else:
html = ""
return get_jenv().from_string(html).render(context)

+ 2
- 0
frappe/website/context.py Voir le fichier

@@ -36,6 +36,8 @@ def get_context(path, args=None):
if frappe.conf.developer_mode:
context._context_dict = context

context.developer_mode = frappe.conf.developer_mode

return context

def update_controller_context(context, controller):


+ 6
- 0
frappe/website/doctype/web_page/templates/web_page.html Voir le fichier

@@ -15,6 +15,11 @@
{% endblock %}

{% block page_content %}
{%- if content_type == 'Page Builder' -%}
{%- for section in page_builder_sections -%}
{{ c('web_page_section', section=section) }}
{%- endfor -%}
{%- else -%}
<div class="webpage-content">
{% include "templates/includes/slideshow.html" %}
<article class="web-page-content" id="{{ name }}" {% if text_align -%}style="text-align: {{ text_align }}"{%- endif %}>
@@ -26,6 +31,7 @@
{% include 'templates/includes/comments/comments.html' %}
{%- endif %}
</div>
{%- endif -%}
{% endblock %}

{% block style %}


+ 29
- 5
frappe/website/doctype/web_page/web_page.js Voir le fichier

@@ -1,7 +1,7 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.ui.form.on("Web Page", {
frappe.ui.form.on('Web Page', {
title: function(frm) {
if (frm.doc.title && !frm.doc.route) {
frm.set_value('route', frappe.scrub(frm.doc.title, '-'));
@@ -26,7 +26,7 @@ frappe.ui.form.on("Web Page", {
frm.events.layout(frm);
}
},
published: function (frm) {
published: function(frm) {
// If current date is before end date,
// and web page is manually unpublished,
// set end date to current date.
@@ -35,13 +35,37 @@ frappe.ui.form.on("Web Page", {

// Set date a few seconds in the future to avoid throwing
// start and end date validation error
end_date.setSeconds(end_date.getSeconds() + 5)
end_date.setSeconds(end_date.getSeconds() + 5);

frm.set_value("end_date", end_date);
frm.set_value('end_date', end_date);
}
},

set_meta_tags(frm) {
frappe.utils.set_meta_tag(frm.doc.route);
}
})
});

frappe.ui.form.on('Web Page Block', {
edit_values(frm, cdt, cdn) {
let row = frm.selected_doc;
frappe.model.with_doc('Web Template', row.web_template).then(doc => {
let d = new frappe.ui.Dialog({
title: __('Edit Values'),
fields: doc.fields,
primary_action(values) {
frappe.model.set_value(
cdt,
cdn,
'web_template_values',
JSON.stringify(values)
);
d.hide();
}
});
let values = JSON.parse(row.web_template_values || '{}');
d.set_values(values);
d.show();
});
}
});

+ 12
- 2
frappe/website/doctype/web_page/web_page.json Voir le fichier

@@ -1,4 +1,5 @@
{
"actions": [],
"allow_guest_to_view": 1,
"allow_import": 1,
"creation": "2013-03-28 10:35:30",
@@ -22,6 +23,7 @@
"main_section",
"main_section_md",
"main_section_html",
"page_blocks",
"custom_javascript",
"insert_code",
"javascript",
@@ -105,7 +107,7 @@
"fieldname": "content_type",
"fieldtype": "Select",
"label": "Content Type",
"options": "Rich Text\nMarkdown\nHTML"
"options": "Rich Text\nMarkdown\nHTML\nPage Builder"
},
{
"depends_on": "eval:doc.content_type==='Rich Text'",
@@ -242,14 +244,22 @@
"fieldname": "dynamic_template",
"fieldtype": "Check",
"label": "Dynamic Template"
},
{
"depends_on": "eval:doc.content_type=='Page Builder'",
"fieldname": "page_blocks",
"fieldtype": "Table",
"label": "Page Building Blocks",
"options": "Web Page Block"
}
],
"has_web_view": 1,
"icon": "fa fa-file-alt",
"idx": 1,
"is_published_field": "published",
"links": [],
"max_attachments": 20,
"modified": "2019-10-02 13:58:50.825481",
"modified": "2020-04-16 22:57:35.414032",
"modified_by": "Administrator",
"module": "Website",
"name": "Web Page",


+ 19
- 0
frappe/website/doctype/web_page/web_page.py Voir le fichier

@@ -18,6 +18,7 @@ from frappe.website.router import resolve_route
from frappe.website.utils import (extract_title, find_first_image, get_comment_list,
get_html_content_based_on_type)
from frappe.website.website_generator import WebsiteGenerator
from frappe.website.doctype.web_template.web_template import get_rendered_template


class WebPage(WebsiteGenerator):
@@ -53,6 +54,7 @@ class WebPage(WebsiteGenerator):
self.set_metatags(context)
self.set_breadcrumbs(context)
self.set_title_and_header(context)
self.build_page_blocks(context)

return context

@@ -103,6 +105,23 @@ class WebPage(WebsiteGenerator):
if not context.title and context.header:
context.title = strip_html(context.header)

def build_page_blocks(self, context):
if self.content_type != 'Page Builder':
return

sections = []

for block in self.page_blocks:
values = frappe.parse_json(block.web_template_values)
rendered_html = get_rendered_template(block.web_template, values)
section = frappe._dict()
section.title = block.title
section.css_class = block.css_class
section.rendered_html = rendered_html
sections.append(section)

context.page_builder_sections = sections

def add_hero(self, context):
"""Add a hero element if specified in content or hooks.
Hero elements get full page width."""


+ 0
- 0
frappe/website/doctype/web_page_block/__init__.py Voir le fichier


+ 51
- 0
frappe/website/doctype/web_page_block/web_page_block.json Voir le fichier

@@ -0,0 +1,51 @@
{
"actions": [],
"creation": "2020-04-16 22:57:13.729460",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"web_template",
"edit_values",
"web_template_values",
"css_class"
],
"fields": [
{
"fieldname": "web_template",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Web Template",
"options": "Web Template"
},
{
"fieldname": "edit_values",
"fieldtype": "Button",
"in_list_view": 1,
"label": "Edit Values"
},
{
"fieldname": "web_template_values",
"fieldtype": "Code",
"label": "Web Template Values",
"options": "JSON"
},
{
"fieldname": "css_class",
"fieldtype": "Small Text",
"label": "CSS Class"
}
],
"istable": 1,
"links": [],
"modified": "2020-04-18 12:50:00.070761",
"modified_by": "Administrator",
"module": "Website",
"name": "Web Page Block",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
frappe/website/doctype/web_page_block/web_page_block.py Voir le fichier

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class WebPageBlock(Document):
pass

+ 0
- 0
frappe/website/doctype/web_template/__init__.py Voir le fichier


+ 10
- 0
frappe/website/doctype/web_template/test_web_template.py Voir le fichier

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals

# import frappe
import unittest

class TestWebTemplate(unittest.TestCase):
pass

+ 8
- 0
frappe/website/doctype/web_template/web_template.js Voir le fichier

@@ -0,0 +1,8 @@
// Copyright (c) 2020, Frappe Technologies and contributors
// For license information, please see license.txt

frappe.ui.form.on('Web Template', {
// refresh: function(frm) {

// }
});

+ 61
- 0
frappe/website/doctype/web_template/web_template.json Voir le fichier

@@ -0,0 +1,61 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "Prompt",
"creation": "2020-04-17 12:12:52.145708",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"standard",
"template",
"fields"
],
"fields": [
{
"depends_on": "eval:!doc.standard",
"fieldname": "template",
"fieldtype": "Code",
"in_list_view": 1,
"label": "Template",
"options": "HTML"
},
{
"fieldname": "fields",
"fieldtype": "Table",
"label": "Fields",
"options": "Web Template Field",
"reqd": 1
},
{
"default": "0",
"fieldname": "standard",
"fieldtype": "Check",
"label": "Standard"
}
],
"links": [],
"modified": "2020-04-17 14:05:28.499020",
"modified_by": "Administrator",
"module": "Website",
"name": "Web Template",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 45
- 0
frappe/website/doctype/web_template/web_template.py Voir le fichier

@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe
import os
from frappe.model.document import Document
from frappe.modules.export_file import export_to_files, create_folder, get_module_path, scrub_dt_dn


class WebTemplate(Document):
def validate(self):
for field in self.fields:
if not field.fieldname:
field.fieldname = frappe.scrub(field.label)

def on_update(self):
if self.standard and frappe.conf.developer_mode:
export_to_files(record_list=[["Web Template", self.name]], create_init=True)
self.create_template_file()

def create_template_file(self):
if self.standard:
folder = create_folder("Website", self.doctype, self.name, False)
path = os.path.join(folder, frappe.scrub(self.name) + '.html')
if not os.path.exists(path):
open(path, 'w').close()


def get_rendered_template(web_template, values):
standard = frappe.db.get_value("Web Template", web_template, "standard")
if standard:
module_path = get_module_path("Website")
dt, dn = scrub_dt_dn("Web Template", web_template)
scrubbed = frappe.scrub(web_template)
full_path = os.path.join("frappe", module_path, dt, dn, scrubbed + ".html")
root_app_path = os.path.abspath(os.path.join(frappe.get_app_path('frappe'), '..'))
template = os.path.relpath(full_path, root_app_path)
else:
template = frappe.db.get_value("Web Template", web_template, "template")

context = values or {}
context.update({'values': values})
return frappe.render_template(template, context)

+ 0
- 0
frappe/website/doctype/web_template_field/__init__.py Voir le fichier


+ 10
- 0
frappe/website/doctype/web_template_field/test_web_template_field.py Voir le fichier

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals

# import frappe
import unittest

class TestWebTemplateField(unittest.TestCase):
pass

+ 8
- 0
frappe/website/doctype/web_template_field/web_template_field.js Voir le fichier

@@ -0,0 +1,8 @@
// Copyright (c) 2020, Frappe Technologies and contributors
// For license information, please see license.txt

frappe.ui.form.on('Web Template Field', {
// refresh: function(frm) {

// }
});

+ 47
- 0
frappe/website/doctype/web_template_field/web_template_field.json Voir le fichier

@@ -0,0 +1,47 @@
{
"actions": [],
"creation": "2020-04-17 12:12:31.857277",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"label",
"fieldname",
"fieldtype"
],
"fields": [
{
"fieldname": "label",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Label",
"reqd": 1
},
{
"fieldname": "fieldname",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Fieldname"
},
{
"fieldname": "fieldtype",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Fieldtype",
"options": "Data\nSmall Text\nText\nAttach Image",
"reqd": 1
}
],
"istable": 1,
"links": [],
"modified": "2020-04-17 19:30:33.829203",
"modified_by": "Administrator",
"module": "Website",
"name": "Web Template Field",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
frappe/website/doctype/web_template_field/web_template_field.py Voir le fichier

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe
from frappe.model.document import Document

class WebTemplateField(Document):
pass

+ 69
- 1
frappe/website/doctype/website_theme/website_theme.json Voir le fichier

@@ -1,4 +1,5 @@
{
"actions": [],
"allow_import": 1,
"autoname": "field:theme",
"creation": "2015-02-18 12:46:38.168929",
@@ -7,11 +8,21 @@
"engine": "InnoDB",
"field_order": [
"theme",
"based_on",
"module",
"custom",
"tailwind_theme_section",
"brand_color",
"bootstrap_theme_section",
"theme_scss",
"theme_json",
"theme_url",
"navbar_section",
"navbar_items",
"primary_action_label",
"primary_action_url",
"footer_section",
"footer_items",
"custom_js_section",
"js"
],
@@ -68,9 +79,66 @@
"hidden": 1,
"label": "Theme JSON",
"options": "JSON"
},
{
"default": "Bootstrap 4",
"fieldname": "based_on",
"fieldtype": "Select",
"label": "Theme Based On",
"options": "Bootstrap 4\nTailwind"
},
{
"fieldname": "navbar_section",
"fieldtype": "Section Break",
"label": "Navbar"
},
{
"fieldname": "navbar_items",
"fieldtype": "Table",
"label": "Navbar Items",
"options": "Top Bar Item"
},
{
"depends_on": "eval:doc.based_on=='Tailwind'",
"fieldname": "tailwind_theme_section",
"fieldtype": "Section Break",
"label": "Tailwind Theme"
},
{
"depends_on": "eval:doc.based_on == 'Bootstrap 4'",
"fieldname": "bootstrap_theme_section",
"fieldtype": "Section Break",
"label": "Bootstrap Theme"
},
{
"fieldname": "brand_color",
"fieldtype": "Color",
"label": "Brand Color"
},
{
"fieldname": "primary_action_label",
"fieldtype": "Data",
"label": "Primary Action Label"
},
{
"fieldname": "primary_action_url",
"fieldtype": "Data",
"label": "Primary Action URL"
},
{
"fieldname": "footer_section",
"fieldtype": "Section Break",
"label": "Footer"
},
{
"fieldname": "footer_items",
"fieldtype": "Table",
"label": "Footer Items",
"options": "Top Bar Item"
}
],
"modified": "2019-06-14 18:36:21.283390",
"links": [],
"modified": "2020-04-17 21:23:13.735307",
"modified_by": "Administrator",
"module": "Website",
"name": "Website Theme",


+ 27
- 0
frappe/website/render.py Voir le fichier

@@ -202,6 +202,9 @@ def build_page(path):
if '{next}' in html:
html = html.replace('{next}', get_next_link(context.route))

if '<!-- tailwind-styles -->' in html and not frappe.conf.developer_mode:
html = add_processed_tailwind_css(context, html)

# html = frappe.get_template(context.base_template_path).render(context)

if can_cache(context.no_cache):
@@ -350,3 +353,27 @@ def raise_if_disabled(path):
_path = r.route.lstrip('/')
if path == _path and not r.enabled:
raise frappe.PermissionError

def add_processed_tailwind_css(context, html):
from subprocess import Popen, PIPE

print('asdfasdf')

# theme_file = 'css/frappe-web-b4.css'
# if context.theme.theme_url:
# theme_file = context.theme.theme_url.lstrip('/assets/')

# css_files = ','.join(['css/tailwind.css', theme_file])
replace_string = '<!-- tailwind-styles -->'
command = ['node', 'purgecss.js', 'css/tailwind.css', html]
process = Popen(command, cwd=frappe.get_app_path('frappe', '..'), stdout=PIPE, stderr=PIPE)

stdout, stderr = process.communicate()
if stderr:
stderr = frappe.safe_decode(stderr)
print(stderr)
else:
css = frappe.safe_decode(stdout)
html = html.replace(replace_string, '<style data-tailwind>{0}</style>'.format(css))

return html

+ 0
- 0
frappe/website/web_template/__init__.py Voir le fichier


+ 0
- 0
frappe/website/web_template/full_width_image/__init__.py Voir le fichier


+ 1
- 0
frappe/website/web_template/full_width_image/full_width_image.html Voir le fichier

@@ -0,0 +1 @@
<img class="w-full" src="{{ url }}" alt="{{ description }}">

+ 24
- 0
frappe/website/web_template/full_width_image/full_width_image.json Voir le fichier

@@ -0,0 +1,24 @@
{
"creation": "2020-04-17 16:03:35.676241",
"docstatus": 0,
"doctype": "Web Template",
"fields": [
{
"fieldname": "url",
"fieldtype": "Data",
"label": "URL"
},
{
"fieldname": "description",
"fieldtype": "Data",
"label": "Description"
}
],
"idx": 0,
"modified": "2020-04-17 16:03:35.676241",
"modified_by": "Administrator",
"name": "Full Width Image",
"owner": "Administrator",
"standard": 1,
"template": ""
}

+ 0
- 0
frappe/website/web_template/hero_with_right_image/__init__.py Voir le fichier


+ 39
- 0
frappe/website/web_template/hero_with_right_image/hero_with_right_image.html Voir le fichier

@@ -0,0 +1,39 @@
<div class="relative flex overflow-hidden bg-white">
<div class="mx-auto">
<div class="relative z-10 pb-8 bg-white sm:pb-16 md:pb-20 lg:max-w-2xl lg:w-full lg:pb-28 xl:pb-32">
<div class="pt-10 mx-auto sm:pt-12 md:pt-16 lg:pt-20 xl:pt-28">
<div class="sm:text-center lg:text-left">
<h2
class="text-4xl font-extrabold leading-10 tracking-tight text-black sm:text-5xl sm:leading-none md:text-5xl">
{{ title }}
</h2>
<p
class="mt-3 text-base text-gray-700 sm:mt-5 sm:text-lg sm:max-w-xl sm:mx-auto md:mt-5 md:text-xl lg:mx-0">
{{ subtitle }}
</p>
<div class="mt-5 sm:mt-8 sm:flex sm:justify-center lg:justify-start">
{%- if primary_action -%}
<div class="rounded-md shadow">
<a href="{{ primary_action }}"
class="flex items-center justify-center w-full px-8 py-3 text-base font-medium leading-6 text-white transition duration-150 ease-in-out bg-blue-500 border border-transparent rounded-md hover:bg-blue-400 focus:outline-none focus:shadow-outline md:py-4 md:text-lg md:px-10">
{{ primary_action_label }}
</a>
</div>
{%- endif -%}
{%- if secondary_action -%}
<div class="mt-3 sm:mt-0 sm:ml-3">
<a href="{{ secondary_action }}"
class="flex items-center justify-center w-full px-8 py-3 text-base font-medium leading-6 text-blue-700 transition duration-150 ease-in-out bg-blue-100 border border-transparent rounded-md hover:text-blue-600 hover:bg-blue-50 focus:outline-none focus:shadow-outline focus:border-blue-300 md:py-4 md:text-lg md:px-10">
{{ secondary_action_label }}
</a>
</div>
{%- endif -%}
</div>
</div>
</div>
</div>
</div>
<div class="lg:w-1/2">
<img class="object-cover w-full h-56 lg:h-full lg:w-full md:h-96 sm:h-72" src="{{ image }}" alt="">
</div>
</div>

+ 49
- 0
frappe/website/web_template/hero_with_right_image/hero_with_right_image.json Voir le fichier

@@ -0,0 +1,49 @@
{
"creation": "2020-04-17 12:18:04.376273",
"docstatus": 0,
"doctype": "Web Template",
"fields": [
{
"fieldname": "title",
"fieldtype": "Data",
"label": "Title"
},
{
"fieldname": "subtitle",
"fieldtype": "Text",
"label": "Subtitle"
},
{
"fieldname": "image",
"fieldtype": "Data",
"label": "Image"
},
{
"fieldname": "primary_action_label",
"fieldtype": "Data",
"label": "Primary Action Label"
},
{
"fieldname": "primary_action",
"fieldtype": "Data",
"label": "Primary Action"
},
{
"fieldname": "secondary_action_label",
"fieldtype": "Data",
"label": "Secondary Action Label"
},
{
"fieldname": "secondary_action",
"fieldtype": "Data",
"label": "Secondary Action"
}
],
"idx": 0,
"modified": "2020-04-17 14:18:24.738411",
"modified_by": "Administrator",
"name": "Hero with Right Image",
"owner": "Administrator",
"standard": 1,
"template": ""
}

+ 0
- 0
frappe/website/web_template/section_with_big_cards/__init__.py Voir le fichier


+ 19
- 0
frappe/website/web_template/section_with_big_cards/section_with_big_cards.html Voir le fichier

@@ -0,0 +1,19 @@
{%- macro card(title, content) -%}
<div class="p-8 bg-white border border-gray-100 rounded-xl">
<h3 class="text-2xl font-semibold">{{ title }}</h3>
<p class="mt-4 text-xl text-gray-800">{{ content }}</p>
</div>
{%- endmacro -%}


{%- if title -%}
<h2 class="mb-6 text-3xl font-bold">{{ title }}</h2>
{%- endif -%}
<div class="grid grid-cols-2 gap-8">
{%- for index in ['1', '2', '3', '4'] -%}
{%- if values['card_' + index + '_title'] -%}
{{ card(values['card_' + index + '_title'], values['card_' + index + '_content']) }}
{%- endif -%}
{%- endfor -%}
</div>


+ 58
- 0
frappe/website/web_template/section_with_big_cards/section_with_big_cards.json Voir le fichier

@@ -0,0 +1,58 @@
{
"creation": "2020-04-17 16:09:37.340176",
"docstatus": 0,
"doctype": "Web Template",
"fields": [
{
"fieldname": "title",
"fieldtype": "Data",
"label": "Title"
},
{
"fieldname": "card_1_title",
"fieldtype": "Data",
"label": "Card 1 Title"
},
{
"fieldname": "card_1_content",
"fieldtype": "Text",
"label": "Card 1 Content"
},
{
"fieldname": "card_2_title",
"fieldtype": "Data",
"label": "Card 2 Title"
},
{
"fieldname": "card_2_content",
"fieldtype": "Text",
"label": "Card 2 Content"
},
{
"fieldname": "card_3_title",
"fieldtype": "Data",
"label": "Card 3 Title"
},
{
"fieldname": "card_3_content",
"fieldtype": "Text",
"label": "Card 3 Content"
},
{
"fieldname": "card_4_title",
"fieldtype": "Data",
"label": "Card 4 Title"
},
{
"fieldname": "card_4_content",
"fieldtype": "Text",
"label": "Card 4 Content"
}
],
"idx": 0,
"modified": "2020-04-17 19:42:19.712813",
"modified_by": "Administrator",
"name": "Section with Big Cards",
"owner": "Administrator",
"standard": 1
}

+ 0
- 0
frappe/website/web_template/section_with_cta/__init__.py Voir le fichier


+ 7
- 0
frappe/website/web_template/section_with_cta/section_with_cta.html Voir le fichier

@@ -0,0 +1,7 @@
<div class="py-32 text-center bg-blue-100 rounded-xl">
<h2 class="max-w-xl mx-auto text-4xl font-bold leading-tight">{{ title }}</h2>
<p class="max-w-xl mx-auto mt-2 text-xl text-gray-800">{{ subtitle }}</p>
<p class="mt-8">
{{ c('button', label=cta_label, url=cta_url, variant="primary") }}
</p>
</div>

+ 33
- 0
frappe/website/web_template/section_with_cta/section_with_cta.json Voir le fichier

@@ -0,0 +1,33 @@
{
"creation": "2020-04-17 19:57:40.534914",
"docstatus": 0,
"doctype": "Web Template",
"fields": [
{
"fieldname": "title",
"fieldtype": "Data",
"label": "Title"
},
{
"fieldname": "subtitle",
"fieldtype": "Small Text",
"label": "Subtitle"
},
{
"fieldname": "cta_label",
"fieldtype": "Data",
"label": "CTA Label"
},
{
"fieldname": "cta_url",
"fieldtype": "Data",
"label": "CTA URL"
}
],
"idx": 0,
"modified": "2020-04-17 19:57:40.534914",
"modified_by": "Administrator",
"name": "Section with CTA",
"owner": "Administrator",
"standard": 1
}

+ 0
- 0
frappe/website/web_template/section_with_image/__init__.py Voir le fichier


+ 4
- 0
frappe/website/web_template/section_with_image/section_with_image.html Voir le fichier

@@ -0,0 +1,4 @@
<h2 class="text-3xl font-bold">{{ title }}</h2>
<p class="mt-2 text-xl text-gray-800">{{ subtitle }}</p>

<img class="w-full mt-8 rounded-xl" src="{{ image }}" alt="{{ image_description }}" />

+ 33
- 0
frappe/website/web_template/section_with_image/section_with_image.json Voir le fichier

@@ -0,0 +1,33 @@
{
"creation": "2020-04-17 19:29:21.186193",
"docstatus": 0,
"doctype": "Web Template",
"fields": [
{
"fieldname": "title",
"fieldtype": "Data",
"label": "Title"
},
{
"fieldname": "subtitle",
"fieldtype": "Small Text",
"label": "Subtitle"
},
{
"fieldname": "image",
"fieldtype": "Attach Image",
"label": "Image"
},
{
"fieldname": "image_description",
"fieldtype": "Data",
"label": "Image Description"
}
],
"idx": 0,
"modified": "2020-04-17 19:31:33.474017",
"modified_by": "Administrator",
"name": "Section with Image",
"owner": "Administrator",
"standard": 1
}

+ 0
- 0
frappe/website/web_template/section_with_left_image/__init__.py Voir le fichier


+ 9
- 0
frappe/website/web_template/section_with_left_image/section_with_left_image.html Voir le fichier

@@ -0,0 +1,9 @@
<div class="flex items-start">
<div class="w-1/2">
<img class="w-full rounded-xl" src="{{ image }}" alt="{{ title }}" />
</div>
<div class="w-1/2 py-4 ml-8">
<h2 class="text-2xl font-bold">{{ title }}</h2>
<p class="text-xl text-gray-800">{{ content }}</p>
</div>
</div>

+ 29
- 0
frappe/website/web_template/section_with_left_image/section_with_left_image.json Voir le fichier

@@ -0,0 +1,29 @@
{
"creation": "2020-04-18 12:58:28.670489",
"docstatus": 0,
"doctype": "Web Template",
"fields": [
{
"fieldname": "title",
"fieldtype": "Data",
"label": "Title"
},
{
"fieldname": "content",
"fieldtype": "Small Text",
"label": "Content"
},
{
"fieldname": "image",
"fieldtype": "Attach Image",
"label": "Image"
}
],
"idx": 0,
"modified": "2020-04-18 12:58:28.670489",
"modified_by": "Administrator",
"name": "Section with Left Image",
"owner": "Administrator",
"standard": 1,
"template": ""
}

+ 0
- 0
frappe/website/web_template/section_with_small_cards/__init__.py Voir le fichier


+ 19
- 0
frappe/website/web_template/section_with_small_cards/section_with_small_cards.html Voir le fichier

@@ -0,0 +1,19 @@
{%- macro card(title, content) -%}
<div class="p-6 bg-white border border-gray-200 cursor-pointer rounded-xl hover:shadow-xl">
<h3 class="text-xl font-semibold">{{ title }}</h3>
<p class="mt-4 text-base text-gray-800">{{ content }}</p>
</div>
{%- endmacro -%}


{%- if title -%}
<h2 class="mb-6 text-3xl font-bold">{{ title }}</h2>
{%- endif -%}
<div class="grid grid-cols-4 gap-8">
{%- for index in ['1', '2', '3', '4', '5', '6', '7', '8'] -%}
{%- if values['card_' + index + '_title'] -%}
{{ card(values['card_' + index + '_title'], values['card_' + index + '_content']) }}
{%- endif -%}
{%- endfor -%}
</div>


+ 103
- 0
frappe/website/web_template/section_with_small_cards/section_with_small_cards.json Voir le fichier

@@ -0,0 +1,103 @@
{
"creation": "2020-04-17 19:47:33.378013",
"docstatus": 0,
"doctype": "Web Template",
"fields": [
{
"fieldname": "title",
"fieldtype": "Data",
"label": "Title"
},
{
"fieldname": "subtitle",
"fieldtype": "Data",
"label": "Subtitle"
},
{
"fieldname": "card_1_title",
"fieldtype": "Data",
"label": "Card 1 Title"
},
{
"fieldname": "card_1_content",
"fieldtype": "Small Text",
"label": "Card 1 Content"
},
{
"fieldname": "card_2_title",
"fieldtype": "Data",
"label": "Card 2 Title"
},
{
"fieldname": "card_2_content",
"fieldtype": "Small Text",
"label": "Card 2 Content"
},
{
"fieldname": "card_3_title",
"fieldtype": "Data",
"label": "Card 3 Title"
},
{
"fieldname": "card_3_content",
"fieldtype": "Small Text",
"label": "Card 3 Content"
},
{
"fieldname": "card_4_title",
"fieldtype": "Data",
"label": "Card 4 Title"
},
{
"fieldname": "card_4_content",
"fieldtype": "Small Text",
"label": "Card 4 Content"
},
{
"fieldname": "card_5_title",
"fieldtype": "Data",
"label": "Card 5 Title"
},
{
"fieldname": "card_5_content",
"fieldtype": "Small Text",
"label": "Card 5 Content"
},
{
"fieldname": "card_6_title",
"fieldtype": "Data",
"label": "Card 6 Title"
},
{
"fieldname": "card_6_content",
"fieldtype": "Small Text",
"label": "Card 6 Content"
},
{
"fieldname": "card_7_title",
"fieldtype": "Data",
"label": "Card 7 Title"
},
{
"fieldname": "card_7_content",
"fieldtype": "Small Text",
"label": "Card 7 Content"
},
{
"fieldname": "card_8_title",
"fieldtype": "Data",
"label": "Card 8 Title"
},
{
"fieldname": "card_8_content",
"fieldtype": "Small Text",
"label": "Card 8 Content"
}
],
"idx": 0,
"modified": "2020-04-17 19:47:33.378013",
"modified_by": "Administrator",
"name": "Section with Small Cards",
"owner": "Administrator",
"standard": 1
}

+ 0
- 0
frappe/website/web_template/section_with_tabs/__init__.py Voir le fichier


+ 109
- 0
frappe/website/web_template/section_with_tabs/section_with_tabs.html Voir le fichier

@@ -0,0 +1,109 @@
<h2 class="font-bold text-3xl">{{ title }}</h2>
<p class="text-xl text-gray-800 mt-2">{{ subtitle }}</p>

<div class="mt-8">
<div class="tabs">
{% set ns = namespace(tabs=[]) %}

{%- for index in ['1', '2', '3', '4', '5', '6'] -%}

{%- set buttonid = 'id-' + frappe.utils.generate_hash('TabButton', 12) -%}
{%- set panelid = 'id-' + frappe.utils.generate_hash('TabPanel', 12) -%}

{%- set tab = {
'title': values['tab_' + index + '_title'],
'content': values['tab_' + index + '_content'],
'buttonid': buttonid,
'panelid': panelid, }
-%}

{%- if tab.title and tab.content -%}
{%- set ns.tabs = ns.tabs + [tab] -%}
{%- endif -%}

{%- endfor -%}
<div class="space-x-10" role="tablist" aria-label="{{ title or '' }}">
{%- for tab in ns.tabs -%}
{%- set first_tab = true if loop.index0 == 0 else false -%}
<button class="py-4 font-medium focus:outline-none focus:shadow-outline text-gray-800" role="tab" aria-selected="false" aria-controls="{{ tab.panelid }}" id="{{ tab.buttonid }}"
tabindex="{{ 0 if first_tab else -1 }}">
{{ tab.title }}
</button>
{%- endfor -%}
</div>
{%- for tab in ns.tabs -%}
{%- set first_tab = true if loop.index0 == 0 else false -%}
<div class="py-4" id="{{ tab.panelid }}" role="tabpanel" tabindex="0" aria-labelledby="{{ tab.buttonid }}" {{ 'hidden' if not first_tab else '' }}>
{{ tab.content }}
</div>
{%- endfor -%}
</div>
</div>

<script>
window.addEventListener("DOMContentLoaded", () => {
const tabs = document.querySelectorAll('[role="tab"]');
const tabList = document.querySelector('[role="tablist"]');

// Add a click event handler to each tab
tabs.forEach(tab => {
tab.addEventListener("click", changeTabs);
});

$(tabs[0]).click();

// Enable arrow navigation between tabs in the tab list
let tabFocus = 0;

tabList.addEventListener("keydown", e => {
// Move right
if (e.keyCode === 39 || e.keyCode === 37) {
tabs[tabFocus].setAttribute("tabindex", -1);
if (e.keyCode === 39) {
tabFocus++;
// If we're at the end, go to the start
if (tabFocus >= tabs.length) {
tabFocus = 0;
}
// Move left
} else if (e.keyCode === 37) {
tabFocus--;
// If we're at the start, move to the end
if (tabFocus < 0) {
tabFocus = tabs.length - 1;
}
}

tabs[tabFocus].setAttribute("tabindex", 0);
tabs[tabFocus].focus();
}
});
});

function changeTabs(e) {
const target = $(e.target);
const parent = target.parent();
const grandparent = $(parent.parent());
const activeClass = 'border-b-2 border-blue-500 text-blue-600';

// Remove all current selected tabs
parent
.find('[aria-selected="true"]')
.attr("aria-selected", false)
.removeClass(activeClass);

// Set this tab as selected
target.attr('aria-selected', true);
target.addClass(activeClass);

// Hide all tab panels
grandparent
.find('[role="tabpanel"]')
.attr("hidden", true);

// Show the selected panel
grandparent.parent()
.find(`#${target.attr("aria-controls")}`)
.removeAttr("hidden");
}
</script>

+ 83
- 0
frappe/website/web_template/section_with_tabs/section_with_tabs.json Voir le fichier

@@ -0,0 +1,83 @@
{
"creation": "2020-04-17 16:34:09.726205",
"docstatus": 0,
"doctype": "Web Template",
"fields": [
{
"fieldname": "title",
"fieldtype": "Data",
"label": "Title"
},
{
"fieldname": "subtitle",
"fieldtype": "Data",
"label": "Subtitle"
},
{
"fieldname": "tab_1_title",
"fieldtype": "Data",
"label": "Tab 1 Title"
},
{
"fieldname": "tab_1_content",
"fieldtype": "Text",
"label": "Tab 1 Content"
},
{
"fieldname": "tab_2_title",
"fieldtype": "Data",
"label": "Tab 2 Title"
},
{
"fieldname": "tab_2_content",
"fieldtype": "Text",
"label": "Tab 2 Content"
},
{
"fieldname": "tab_3_title",
"fieldtype": "Data",
"label": "Tab 3 Title"
},
{
"fieldname": "tab_3_content",
"fieldtype": "Text",
"label": "Tab 3 Content"
},
{
"fieldname": "tab_4_title",
"fieldtype": "Data",
"label": "Tab 4 Title"
},
{
"fieldname": "tab_4_content",
"fieldtype": "Text",
"label": "Tab 4 Content"
},
{
"fieldname": "tab_5_title",
"fieldtype": "Data",
"label": "Tab 5 Title"
},
{
"fieldname": "tab_5_content",
"fieldtype": "Text",
"label": "Tab 5 Content"
},
{
"fieldname": "tab_6_title",
"fieldtype": "Data",
"label": "Tab 6 Title"
},
{
"fieldname": "tab_6_content",
"fieldtype": "Text",
"label": "Tab 6 Content"
}
],
"idx": 0,
"modified": "2020-04-17 16:46:54.719420",
"modified_by": "Administrator",
"name": "Section with Tabs",
"owner": "Administrator",
"standard": 1
}

+ 12
- 24
frappe/website/website_theme/standard/standard.json Voir le fichier

@@ -1,26 +1,14 @@
{
"apply_style": 0,
"apply_text_styles": 0,
"creation": "2015-02-19 13:37:33.925909",
"css": ".navbar-header {\n display: none;\n}",
"custom": 0,
"docstatus": 0,
"doctype": "Website Theme",
"font_size": "14px",
"footer_color": "",
"footer_text_color": "",
"heading_style": "",
"heading_webfont": "",
"idx": 26,
"link_color": "",
"modified": "2016-12-29 05:40:17.289226",
"modified_by": "Administrator",
"module": "Website",
"name": "Standard",
"owner": "Administrator",
"text_color": "",
"text_webfont": "",
"theme": "Standard",
"top_bar_color": "",
"top_bar_text_color": ""
"based_on": "Bootstrap 4",
"creation": "2015-02-19 13:37:33.925909",
"custom": 0,
"docstatus": 0,
"doctype": "Website Theme",
"idx": 26,
"modified": "2020-04-16 18:43:01.567830",
"modified_by": "Administrator",
"module": "Website",
"name": "Standard",
"owner": "Administrator",
"theme": "Standard"
}

+ 7
- 5
package.json Voir le fichier

@@ -5,9 +5,7 @@
"production": "FRAPPE_ENV=production node rollup/build.js",
"watch": "node rollup/watch.js",
"cypress:run": "cypress run --record --key 4a48f41c-11b3-425b-aa88-c58048fa69eb",
"cypress:open": "cypress open",
"snyk-protect": "snyk protect",
"prepare": "yarn run snyk-protect"
"cypress:open": "cypress open"
},
"repository": {
"type": "git",
@@ -20,11 +18,13 @@
},
"homepage": "https://frappe.io",
"dependencies": {
"@tailwindcss/ui": "^0.1.3",
"ace-builds": "^1.4.8",
"air-datepicker": "http://github.com/frappe/air-datepicker",
"awesomplete": "^1.1.5",
"bootstrap": "^4.4.1",
"cookie": "^0.4.0",
"cssnano": "^4.1.10",
"express": "^4.17.1",
"fast-deep-equal": "^2.0.1",
"frappe-charts": "^1.3.0",
@@ -36,17 +36,19 @@
"jsbarcode": "^3.9.0",
"moment": "^2.20.1",
"moment-timezone": "^0.5.28",
"purgecss": "^2.1.0",
"quagga": "^0.12.1",
"quill": "2.0.0-dev.2",
"qz-tray": "^2.0.8",
"redis": "^2.8.0",
"showdown": "^1.9.1",
"snyk": "^1.297.4",
"socket.io": "^2.3.0",
"superagent": "^3.8.2",
"tailwindcss": "^1.2.0",
"touch": "^3.1.0",
"vue": "^2.6.11",
"vue-router": "^2.0.0",
"snyk": "^1.297.4"
"vue-router": "^2.0.0"
},
"devDependencies": {
"babel-runtime": "^6.26.0",


+ 23
- 0
purgecss.js Voir le fichier

@@ -0,0 +1,23 @@
const path = require('path');
const utils = require('./rollup/rollup.utils');
const { PurgeCSS } = require('purgecss');

let css_files = process.argv[2].split(',');
let css_file_paths = css_files.map(p => path.resolve(utils.assets_path, p));
let html_content = process.argv[3];
html_content = html_content.replace(/\\n/g, '\n');

new PurgeCSS()
.purge({
content: [
{
raw: html_content,
extension: 'html'
}
],
css: css_file_paths,
defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
})
.then(result => {
console.log(result[0].css);
});

+ 1
- 1
requirements.txt Voir le fichier

@@ -25,7 +25,7 @@ gunicorn==19.10.0
html2text==2016.9.19
html5lib==1.0.1
ipython==5.9.0
Jinja2==2.10.3
Jinja2==2.11.1
ldap3==2.7
markdown2==2.3.8
maxminddb-geolite2==2018.703


+ 17
- 11
rollup/config.js Voir le fichier

@@ -108,29 +108,34 @@ function get_rollup_options_for_js(output_file, input_files) {

function get_rollup_options_for_css(output_file, input_files) {
const output_path = path.resolve(assets_path, output_file);
const minimize_css = output_path.startsWith('css/') && production;
const minimize_css = output_path.includes('/assets/css/') && production;

const plugins = [
// enables array of inputs
multi_entry(),
// less -> css
postcss({
plugins: [
require('tailwindcss'),
require('autoprefixer'),
minimize_css ? require('cssnano')({ preset: 'default' }) : null
].filter(Boolean),
extract: output_path,
use: [
['less', {
// import other less/css files starting from these folders
paths: [
path.resolve(get_public_path('frappe'), 'less')
]
}],
[
'less',
{
// import other less/css files starting from these folders
paths: [path.resolve(get_public_path('frappe'), 'less')]
}
],
['sass', get_options_for_scss()]
],
include: [
path.resolve(bench_path, '**/*.less'),
path.resolve(bench_path, '**/*.scss'),
path.resolve(bench_path, '**/*.css')
],
minimize: minimize_css
]
})
];

@@ -138,11 +143,12 @@ function get_rollup_options_for_css(output_file, input_files) {
inputOptions: {
input: input_files,
plugins: plugins,
onwarn(warning) {
onwarn(warning, warn) {
// skip warnings
if (['EMPTY_BUNDLE'].includes(warning.code)) return;

// console.warn everything else
// console.warn everything else
log(warning)
log(chalk.yellow.underline(warning.code), ':', warning.message);
}
},


+ 75
- 0
tailwind.config.js Voir le fichier

@@ -0,0 +1,75 @@
const defaultTheme = require('tailwindcss/defaultTheme');
const plugin = require('tailwindcss/plugin');

module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['Inter var', ...defaultTheme.fontFamily.sans]
},
colors: {
black: '#112B42',
blue: {
'50': '#f4f9ff',
'100': '#E8F4FD',
'200': '#BDDDFA',
'300': '#88C3F6',
'400': '#54A8F2',
'500': '#2490EF',
'600': '#107CDB',
'700': '#0D66B5',
'800': '#0A518F',
'900': '#083B69'
},
gray: {
'100': '#f4f4f6',
'200': '#e9ebed',
'300': '#dfe1e2',
'400': '#cccfd1',
'500': '#b7bfc6',
'600': '#a1abb4',
'700': '#9fa5a8',
'800': '#7f878a',
'900': '#415668'
},
code: {
green: '#b5f4a5',
yellow: '#ffe484',
purple: '#d9a9ff',
red: '#ff8383',
blue: '#93ddfd',
white: '#fff'
}
},
borderRadius: {
'xl': '0.75rem'
},
container: {
center: true,
padding: '1.25rem'
},
screens: {
'xl': '1120px',
},
important: true
}
},
variants: {},
plugins: [
require('@tailwindcss/ui'),
plugin(function({ addUtilities, e, theme, variants }) {
let utilities = {};
Object.entries(theme('padding')).map(([modifier, size]) => {
utilities[`.${e(`space-y-${modifier}`)} > * + *`] = {
marginTop: `${size}`
};
utilities[`.${e(`space-x-${modifier}`)} > * + *`] = {
marginLeft: `${size}`
};
});
addUtilities(utilities, {
variants: variants('padding')
});
})
]
};

+ 327
- 7
yarn.lock Voir le fichier

@@ -173,6 +173,24 @@
semver-diff "^2.0.0"
xdg-basedir "^3.0.0"

"@tailwindcss/custom-forms@^0.2.1":
version "0.2.1"
resolved "https://registry.yarnpkg.com/@tailwindcss/custom-forms/-/custom-forms-0.2.1.tgz#40e5ed1fff6d29d8ed1c508a0b2aaf8da96962e0"
integrity sha512-XdP5XY6kxo3x5o50mWUyoYWxOPV16baagLoZ5uM41gh6IhXzhz/vJYzqrTb/lN58maGIKlpkxgVsQUNSsbAS3Q==
dependencies:
lodash "^4.17.11"
mini-svg-data-uri "^1.0.3"
traverse "^0.6.6"

"@tailwindcss/ui@^0.1.3":
version "0.1.3"
resolved "https://registry.yarnpkg.com/@tailwindcss/ui/-/ui-0.1.3.tgz#4cc46f8a5788fcad937eff1ab567f5688080eb24"
integrity sha512-ggEEFj0N6YY6fFjHtoGArv9j7wr5nj+TswpGzEezN2l15jHJK1NZ+Lnt0XKsbJy2zWFSNy/KKpPCbh3d6/66tw==
dependencies:
"@tailwindcss/custom-forms" "^0.2.1"
hex-to-rgba "^2.0.1"
postcss-selector-parser "^6.0.2"

"@types/agent-base@^4.2.0":
version "4.2.0"
resolved "https://registry.yarnpkg.com/@types/agent-base/-/agent-base-4.2.0.tgz#00644e8b395b40e1bf50aaf1d22cabc1200d5051"
@@ -216,6 +234,11 @@
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.0.8.tgz#d27600e9ba2f371e08695d90a0fe0408d89c7be7"
integrity sha512-m812CONwdZn/dMzkIJEY0yAs4apyTkTORgfB2UsMOxgkUbC205AHnm4T8I0I5gPg9MHrFc1dJ35iS75c0CJkjg==

"@types/color-name@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==

"@types/debug@^4.1.4":
version "4.1.5"
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd"
@@ -391,6 +414,20 @@ ace-builds@^1.4.8:
resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.4.8.tgz#d14be41d30294a2a12581f0bcfee4b696481ffdd"
integrity sha512-8ZVAxwyCGAxQX8mOp9imSXH0hoSPkGfy8igJy+WO/7axL30saRhKgg1XPACSmxxPA7nfHVwM+ShWXT+vKsNuFg==

acorn-node@^1.6.1:
version "1.8.2"
resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8"
integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==
dependencies:
acorn "^7.0.0"
acorn-walk "^7.0.0"
xtend "^4.0.2"

acorn-walk@^7.0.0:
version "7.1.1"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e"
integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==

acorn@^5.2.1:
version "5.7.4"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
@@ -401,6 +438,11 @@ acorn@^6.1.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f"
integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==

acorn@^7.0.0:
version "7.1.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf"
integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==

after@0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
@@ -500,6 +542,14 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"

ansi-styles@^4.1.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
dependencies:
"@types/color-name" "^1.1.1"
color-convert "^2.0.1"

ansicolors@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979"
@@ -629,6 +679,19 @@ atob@^2.1.1:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==

autoprefixer@^9.4.5:
version "9.7.5"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.5.tgz#8df10b9ff9b5814a8d411a5cfbab9c793c392376"
integrity sha512-URo6Zvt7VYifomeAfJlMFnYDhow1rk2bufwkbamPEAtQFcL11moLk4PnR7n9vlu7M+BkXAZkHFA0mIcY7tjQFg==
dependencies:
browserslist "^4.11.0"
caniuse-lite "^1.0.30001036"
chalk "^2.4.2"
normalize-range "^0.1.2"
num2fraction "^1.2.2"
postcss "^7.0.27"
postcss-value-parser "^4.0.3"

awesomplete@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/awesomplete/-/awesomplete-1.1.5.tgz#1b2b5dd106d3955595619c03da472a1dc0faf0af"
@@ -805,6 +868,16 @@ browserslist@^4.0.0:
electron-to-chromium "^1.3.113"
node-releases "^1.1.8"

browserslist@^4.11.0:
version "4.11.0"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.11.0.tgz#aef4357b10a8abda00f97aac7cd587b2082ba1ad"
integrity sha512-WqEC7Yr5wUH5sg6ruR++v2SGOQYpyUdYYd4tZoAq1F7y+QXoLoYGXVbxhtaIqWmAJjtNTRjVD3HuJc1OXTel2A==
dependencies:
caniuse-lite "^1.0.30001035"
electron-to-chromium "^1.3.380"
node-releases "^1.1.52"
pkg-up "^3.1.0"

buble@^0.19.6:
version "0.19.6"
resolved "https://registry.yarnpkg.com/buble/-/buble-0.19.6.tgz#915909b6bd5b11ee03b1c885ec914a8b974d34d3"
@@ -832,7 +905,7 @@ builtin-modules@^3.0.0:
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.0.0.tgz#1e587d44b006620d90286cc7a9238bbc6129cab1"
integrity sha512-hMIeU4K2ilbXV6Uv93ZZ0Avg/M91RaKXucQ+4me2Do1txxBDyDZWCBa5bJSLqoNTRpXTLwEzIk1KmloenDDjhg==

bytes@3.1.0:
bytes@3.1.0, bytes@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
@@ -883,6 +956,11 @@ callsites@^2.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=

camelcase-css@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==

camelcase-keys@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
@@ -926,6 +1004,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000939:
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000940.tgz#19f2b1497fbfa5b96b615963097c3757f27989ce"
integrity sha512-rp/086IBUfCsNgBpko6DGQv674jRjeXPesDatDB2kxrkmDfD+S5Gesw+uT8YjpRWvLKLMRBy72SLRZ8I0EgQFw==

caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001036:
version "1.0.30001038"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001038.tgz#44da3cbca2ab6cb6aa83d1be5d324e17f141caff"
integrity sha512-zii9quPo96XfOiRD4TrfYGs+QsGZpb2cGiMAzPjtf/hpFgB6zCPZgJb7I1+EATeMw/o+lG8FyRAnI+CWStHcaQ==

capture-stack-trace@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d"
@@ -965,6 +1048,14 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"

chalk@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"

chardet@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
@@ -1096,12 +1187,19 @@ color-convert@^1.9.0, color-convert@^1.9.1:
dependencies:
color-name "1.1.3"

color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
dependencies:
color-name "~1.1.4"

color-name@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=

color-name@^1.0.0:
color-name@^1.0.0, color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
@@ -1139,6 +1237,11 @@ commander@^2.19.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==

commander@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==

common-tags@1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.4.0.tgz#1187be4f3d4cf0c0427d43f74eef1f73501614c0"
@@ -1397,6 +1500,11 @@ cssesc@^2.0.0:
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703"
integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==

cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==

cssnano-preset-default@^4.0.7:
version "4.0.7"
resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76"
@@ -1455,7 +1563,7 @@ cssnano-util-same-parent@^4.0.0:
resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3"
integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==

cssnano@^4.1.8:
cssnano@^4.1.10, cssnano@^4.1.8:
version "4.1.10"
resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2"
integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==
@@ -1646,6 +1754,11 @@ define-property@^2.0.2:
is-descriptor "^1.0.2"
isobject "^3.0.1"

defined@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=

degenerator@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095"
@@ -1675,6 +1788,15 @@ destroy@~1.0.4:
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=

detective@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b"
integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==
dependencies:
acorn-node "^1.6.1"
defined "^1.0.0"
minimist "^1.1.1"

diff@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
@@ -1761,6 +1883,11 @@ electron-to-chromium@^1.3.113:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz#b1ccf619df7295aea17bc6951dc689632629e4a9"
integrity sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==

electron-to-chromium@^1.3.380:
version "1.3.390"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.390.tgz#a49e67dea22e52ea8027c475dd5447b1c00b1d4e"
integrity sha512-4RvbM5x+002gKI8sltkqWEk5pptn0UnzekUx8RTThAMPDSb8jjpm6SwGiSnEve7f85biyZl8DMXaipaCxDjXag==

elegant-spinner@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
@@ -2293,6 +2420,15 @@ fs-extra@4.0.1:
jsonfile "^3.0.0"
universalify "^0.1.0"

fs-extra@^8.0.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"

fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -2536,7 +2672,7 @@ got@^6.7.1:
unzip-response "^2.0.1"
url-parse-lax "^1.0.0"

graceful-fs@^4.1.11, graceful-fs@^4.1.2:
graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.2.0:
version "4.2.3"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
@@ -2608,6 +2744,11 @@ has-flag@^3.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=

has-flag@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==

has-glob@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-glob/-/has-glob-1.0.0.tgz#9aaa9eedbffb1ba3990a7b0010fb678ee0081207"
@@ -2678,6 +2819,11 @@ hex-color-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==

hex-to-rgba@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/hex-to-rgba/-/hex-to-rgba-2.0.1.tgz#4176977882a1cb32b83ce5ab1db6828ab84d5a13"
integrity sha512-5XqPJBpsEUMsseJUi2w2Hl7cHFFi3+OO10M2pzAvKB1zL6fc+koGMhmBqoDOCB4GemiRM/zvDMRIhVw6EkB8dQ==

highlight.js@^9.18.1:
version "9.18.1"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.1.tgz#ed21aa001fe6252bb10a3d76d47573c6539fe13c"
@@ -3325,6 +3471,13 @@ jsonfile@^3.0.0:
optionalDependencies:
graceful-fs "^4.1.6"

jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
optionalDependencies:
graceful-fs "^4.1.6"

jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
@@ -3550,6 +3703,11 @@ lodash.set@^4.3.2:
resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23"
integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=

lodash.toarray@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE=

lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
@@ -3785,6 +3943,11 @@ mimic-fn@^1.0.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==

mini-svg-data-uri@^1.0.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.1.3.tgz#9759ee5f4d89a4b724d089ce52eab4b623bfbc88"
integrity sha512-EeKOmdzekjdPe53/GdxmUpNgDQFkNeSte6XkJmOBt4BfWL6FQ9G9RtLNh+JMjFS3LhdpSICMIkZdznjiecASHQ==

minimatch@^3.0.4, minimatch@~3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
@@ -3802,6 +3965,11 @@ minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=

minimist@^1.1.1:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==

mixin-deep@^1.2.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
@@ -3938,6 +4106,13 @@ node-bitmap@0.0.1:
resolved "https://registry.yarnpkg.com/node-bitmap/-/node-bitmap-0.0.1.tgz#180eac7003e0c707618ef31368f62f84b2a69091"
integrity sha1-GA6scAPgxwdhjvMTaPYvhLKmkJE=

node-emoji@^1.8.1:
version "1.10.0"
resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da"
integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==
dependencies:
lodash.toarray "^4.4.0"

node-gyp@^3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c"
@@ -3956,6 +4131,11 @@ node-gyp@^3.8.0:
tar "^2.0.0"
which "1"

node-releases@^1.1.52:
version "1.1.53"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.53.tgz#2d821bfa499ed7c5dffc5e2f28c88e78a08ee3f4"
integrity sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==

node-releases@^1.1.8:
version "1.1.9"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.9.tgz#70d0985ec4bf7de9f08fc481f5dae111889ca482"
@@ -4010,11 +4190,21 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"

normalize-range@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=

normalize-url@^3.0.0, normalize-url@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==

normalize.css@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3"
integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==

npm-run-path@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
@@ -4039,6 +4229,11 @@ nth-check@^1.0.2:
dependencies:
boolbase "~1.0.0"

num2fraction@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=

number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
@@ -4054,7 +4249,7 @@ oauth-sign@~0.9.0:
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==

object-assign@^4.0.1, object-assign@^4.1.0:
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
@@ -4438,6 +4633,13 @@ pinkie@^2.0.0:
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=

pkg-up@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
dependencies:
find-up "^3.0.0"

pngjs@^3.3.3:
version "3.4.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f"
@@ -4505,6 +4707,24 @@ postcss-discard-overridden@^4.0.1:
dependencies:
postcss "^7.0.0"

postcss-functions@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/postcss-functions/-/postcss-functions-3.0.0.tgz#0e94d01444700a481de20de4d55fb2640564250e"
integrity sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=
dependencies:
glob "^7.1.2"
object-assign "^4.1.1"
postcss "^6.0.9"
postcss-value-parser "^3.3.0"

postcss-js@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-2.0.3.tgz#a96f0f23ff3d08cec7dc5b11bf11c5f8077cdab9"
integrity sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==
dependencies:
camelcase-css "^2.0.1"
postcss "^7.0.18"

postcss-load-config@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.0.0.tgz#f1312ddbf5912cd747177083c5ef7a19d62ee484"
@@ -4629,6 +4849,14 @@ postcss-modules@^1.4.1:
postcss "^7.0.1"
string-hash "^1.1.1"

postcss-nested@^4.1.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-4.2.1.tgz#4bc2e5b35e3b1e481ff81e23b700da7f82a8b248"
integrity sha512-AMayXX8tS0HCp4O4lolp4ygj9wBn32DJWXvG6gCv+ZvJrEa00GUxJcJEEzMh87BIe6FrWdYkpR2cuyqHKrxmXw==
dependencies:
postcss "^7.0.21"
postcss-selector-parser "^6.0.2"

postcss-normalize-charset@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4"
@@ -4757,6 +4985,15 @@ postcss-selector-parser@^5.0.0, postcss-selector-parser@^5.0.0-rc.4:
indexes-of "^1.0.1"
uniq "^1.0.1"

postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c"
integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==
dependencies:
cssesc "^3.0.0"
indexes-of "^1.0.1"
uniq "^1.0.1"

postcss-svgo@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258"
@@ -4776,11 +5013,16 @@ postcss-unique-selectors@^4.0.1:
postcss "^7.0.0"
uniqs "^2.0.0"

postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.1:
postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==

postcss-value-parser@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz#651ff4593aa9eda8d5d0d66593a2417aeaeb325d"
integrity sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==

postcss@6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.1.tgz#000dbd1f8eef217aa368b9a212c5fc40b2a8f3f2"
@@ -4790,6 +5032,15 @@ postcss@6.0.1:
source-map "^0.5.6"
supports-color "^3.2.3"

postcss@7.0.27, postcss@^7.0.11, postcss@^7.0.18, postcss@^7.0.21, postcss@^7.0.27:
version "7.0.27"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9"
integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==
dependencies:
chalk "^2.4.2"
source-map "^0.6.1"
supports-color "^6.1.0"

postcss@^5.2.5:
version "5.2.18"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5"
@@ -4800,7 +5051,7 @@ postcss@^5.2.5:
source-map "^0.5.6"
supports-color "^3.2.3"

postcss@^6.0.1, postcss@^6.0.20, postcss@^6.0.22:
postcss@^6.0.1, postcss@^6.0.20, postcss@^6.0.22, postcss@^6.0.9:
version "6.0.23"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==
@@ -4838,6 +5089,11 @@ prettier@^1.13.0:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717"
integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==

pretty-hrtime@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=

process-nextick-args@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
@@ -4925,6 +5181,16 @@ punycode@^2.1.0:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==

purgecss@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-2.1.0.tgz#6da655d166073824efe2532b0c6466c740d939d6"
integrity sha512-QnXhowNjeWo9vNnGES2LVzDXdRR/8EvG/O03m4bYOWfAX0ShmG/Pmj7brVtVBy2eaaRAmNy23L+GBc4SpDFUeQ==
dependencies:
commander "^4.0.0"
glob "^7.0.0"
postcss "7.0.27"
postcss-selector-parser "^6.0.2"

q@^1.1.2:
version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
@@ -5113,6 +5379,14 @@ redis@^2.8.0:
redis-commands "^1.2.0"
redis-parser "^2.6.0"

reduce-css-calc@^2.1.6:
version "2.1.7"
resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.7.tgz#1ace2e02c286d78abcd01fd92bfe8097ab0602c2"
integrity sha512-fDnlZ+AybAS3C7Q9xDq5y8A2z+lT63zLbynew/lur/IR24OQF5x98tfNwf79mzEdfywZ0a2wpM860FhFfMxZlA==
dependencies:
css-unit-converter "^1.1.1"
postcss-value-parser "^3.3.0"

regenerate-unicode-properties@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz#107405afcc4a190ec5ed450ecaa00ed0cafa7a4c"
@@ -5329,6 +5603,13 @@ resolve@^1.10.0, resolve@^1.4.0, resolve@^1.5.0:
dependencies:
path-parse "^1.0.6"

resolve@^1.14.2:
version "1.15.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"
integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==
dependencies:
path-parse "^1.0.6"

restore-cursor@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
@@ -6307,6 +6588,13 @@ supports-color@^6.1.0:
dependencies:
has-flag "^3.0.0"

supports-color@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1"
integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
dependencies:
has-flag "^4.0.0"

svgo@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.2.0.tgz#305a8fc0f4f9710828c65039bb93d5793225ffc3"
@@ -6332,6 +6620,28 @@ symbol-observable@1.0.1:
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4"
integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=

tailwindcss@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-1.2.0.tgz#5df317cebac4f3131f275d258a39da1ba3a0f291"
integrity sha512-CKvY0ytB3ze5qvynG7qv4XSpQtFNGPbu9pUn8qFdkqgD8Yo/vGss8mhzbqls44YCXTl4G62p3qVZBj45qrd6FQ==
dependencies:
autoprefixer "^9.4.5"
bytes "^3.0.0"
chalk "^3.0.0"
detective "^5.2.0"
fs-extra "^8.0.0"
lodash "^4.17.15"
node-emoji "^1.8.1"
normalize.css "^8.0.1"
postcss "^7.0.11"
postcss-functions "^3.0.0"
postcss-js "^2.0.0"
postcss-nested "^4.1.1"
postcss-selector-parser "^6.0.0"
pretty-hrtime "^1.0.3"
reduce-css-calc "^2.1.6"
resolve "^1.14.2"

tar-stream@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3"
@@ -6496,6 +6806,11 @@ tough-cookie@~2.4.3:
psl "^1.1.24"
punycode "^1.4.1"

traverse@^0.6.6:
version "0.6.6"
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=

tree-kill@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
@@ -6885,6 +7200,11 @@ xregexp@2.0.0:
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943"
integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=

xtend@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==

y18n@^3.2.0, y18n@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"


Chargement…
Annuler
Enregistrer