You've already forked snikket-web-portal
@@ -402,24 +402,21 @@ div.form.layout-expanded {
|
||||
}
|
||||
|
||||
|
||||
/* icon support */
|
||||
|
||||
svg.icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
stroke-width: 0;
|
||||
stroke: currentColor;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
|
||||
/* form buttons */
|
||||
|
||||
.btn-edit:before {
|
||||
content: '✎';
|
||||
}
|
||||
|
||||
.btn-delete:before {
|
||||
content: '🗑';
|
||||
}
|
||||
|
||||
.btn-link:before {
|
||||
content: '+';
|
||||
}
|
||||
|
||||
.btn-more:before {
|
||||
content: '…';
|
||||
}
|
||||
|
||||
input[type="submit"], button, .button {
|
||||
margin: 0 $w-s2;
|
||||
padding: $w-s3 $w-s1;
|
||||
@@ -430,6 +427,23 @@ input[type="submit"], button, .button {
|
||||
margin: 0 $w-s4;
|
||||
padding: $w-s4 $w-s2;
|
||||
}
|
||||
|
||||
& > svg.icon:first-child {
|
||||
margin-right: $w-s2;
|
||||
|
||||
td & {
|
||||
margin-right: $w-s3;
|
||||
}
|
||||
}
|
||||
|
||||
& > svg.icon:last-child {
|
||||
margin-right: 0;
|
||||
margin-top: -$w-s4;
|
||||
|
||||
td & {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a.button {
|
||||
|
||||
90
snikket_web/static/img/icons.svg
Normal file
90
snikket_web/static/img/icons.svg
Normal file
@@ -0,0 +1,90 @@
|
||||
<svg aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<!-- These icons are sourced from Google’s Material Icons set,
|
||||
licensed under the terms of the Apache 2.0 License -->
|
||||
<!-- from: action/account_circle/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-profile" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z" />
|
||||
</symbol>
|
||||
<!-- from: action/done/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-done" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M9 16.2l-3.5-3.5c-.39-.39-1.01-.39-1.4 0-.39.39-.39 1.01 0 1.4l4.19 4.19c.39.39 1.02.39 1.41 0L20.3 7.7c.39-.39.39-1.01 0-1.4-.39-.39-1.01-.39-1.4 0L9 16.2z" />
|
||||
</symbol>
|
||||
<!-- from: action/logout/materialicons/24px.svg -->
|
||||
<symbol id="icon-logout" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z" />
|
||||
</symbol>
|
||||
<!-- from: action/login/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-login" viewBox="0 0 24 24">
|
||||
<g><rect fill="none" height="24" width="24" /><rect fill="none" height="24" width="24" /></g>
|
||||
<g><path d="M10.3,7.7L10.3,7.7c-0.39,0.39-0.39,1.01,0,1.4l1.9,1.9H3c-0.55,0-1,0.45-1,1v0c0,0.55,0.45,1,1,1h9.2l-1.9,1.9 c-0.39,0.39-0.39,1.01,0,1.4l0,0c0.39,0.39,1.01,0.39,1.4,0l3.59-3.59c0.39-0.39,0.39-1.02,0-1.41L11.7,7.7 C11.31,7.31,10.69,7.31,10.3,7.7z M20,19h-7c-0.55,0-1,0.45-1,1v0c0,0.55,0.45,1,1,1h7c1.1,0,2-0.9,2-2V5c0-1.1-0.9-2-2-2h-7 c-0.55,0-1,0.45-1,1v0c0,0.55,0.45,1,1,1h7V19z" /></g>
|
||||
</symbol>
|
||||
<!-- from: communication/vpn_key/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-passwd" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M12.65 10C11.7 7.31 8.9 5.5 5.77 6.12c-2.29.46-4.15 2.29-4.63 4.58C.32 14.57 3.26 18 7 18c2.61 0 4.83-1.67 5.65-4H17v2c0 1.1.9 2 2 2s2-.9 2-2v-2c1.1 0 2-.9 2-2s-.9-2-2-2h-8.35zM7 14c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z" />
|
||||
</symbol>
|
||||
<!-- from: content/add_circle_outline/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-add" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M12 7c-.55 0-1 .45-1 1v3H8c-.55 0-1 .45-1 1s.45 1 1 1h3v3c0 .55.45 1 1 1s1-.45 1-1v-3h3c.55 0 1-.45 1-1s-.45-1-1-1h-3V8c0-.55-.45-1-1-1zm0-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
|
||||
</symbol>
|
||||
<!-- from: content/add_link/materialicons/24px.svg -->
|
||||
<symbol id="icon-create_link" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0" fill="none" />
|
||||
<path d="M8 11h8v2H8zm12.1 1H22c0-2.76-2.24-5-5-5h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1zM3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM19 12h-2v3h-3v2h3v3h2v-3h3v-2h-3z" />
|
||||
</symbol>
|
||||
<!-- from: content/create/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-edit" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M3 17.46v3.04c0 .28.22.5.5.5h3.04c.13 0 .26-.05.35-.15L17.81 9.94l-3.75-3.75L3.15 17.1c-.1.1-.15.22-.15.36zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" />
|
||||
</symbol>
|
||||
<!-- from: content/remove_circle_outline/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-remove" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M7 12c0 .55.45 1 1 1h8c.55 0 1-.45 1-1s-.45-1-1-1H8c-.55 0-1 .45-1 1zm5-10C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
|
||||
</symbol>
|
||||
<!-- from: content/content_copy/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-copy" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M15 1H4c-1.1 0-2 .9-2 2v13c0 .55.45 1 1 1s1-.45 1-1V4c0-.55.45-1 1-1h10c.55 0 1-.45 1-1s-.45-1-1-1zm4 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-1 16H9c-.55 0-1-.45-1-1V8c0-.55.45-1 1-1h9c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1z" />
|
||||
</symbol>
|
||||
<!-- from: content/link_off/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-remove_link" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M21.94 11.23C21.57 8.76 19.32 7 16.82 7h-2.87c-.52 0-.95.43-.95.95s.43.95.95.95h2.9c1.6 0 3.04 1.14 3.22 2.73.17 1.43-.64 2.69-1.85 3.22l1.4 1.4c1.63-1.02 2.64-2.91 2.32-5.02zM4.12 3.56c-.39-.39-1.02-.39-1.41 0s-.39 1.02 0 1.41l2.4 2.4c-1.94.8-3.27 2.77-3.09 5.04C2.23 15.05 4.59 17 7.23 17h2.82c.52 0 .95-.43.95-.95s-.43-.95-.95-.95H7.16c-1.63 0-3.1-1.19-3.25-2.82-.15-1.72 1.11-3.17 2.75-3.35l2.1 2.1c-.43.09-.76.46-.76.92v.1c0 .52.43.95.95.95h1.78L13 15.27V17h1.73l3.3 3.3c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L4.12 3.56zM16 11.95c0-.52-.43-.95-.95-.95h-.66l1.49 1.49c.07-.13.12-.28.12-.44v-.1z" />
|
||||
</symbol>
|
||||
<!-- from: navigation/arrow_back/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-back" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M19 11H7.83l4.88-4.88c.39-.39.39-1.03 0-1.42-.39-.39-1.02-.39-1.41 0l-6.59 6.59c-.39.39-.39 1.02 0 1.41l6.59 6.59c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L7.83 13H19c.55 0 1-.45 1-1s-.45-1-1-1z" />
|
||||
</symbol>
|
||||
<!-- from: navigation/arrow_forward/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-forward" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M5 13h11.17l-4.88 4.88c-.39.39-.39 1.03 0 1.42.39.39 1.02.39 1.41 0l6.59-6.59c.39-.39.39-1.02 0-1.41l-6.58-6.6c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L16.17 11H5c-.55 0-1 .45-1 1s.45 1 1 1z" />
|
||||
</symbol>
|
||||
<!-- from: navigation/cancel/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-cancel" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" opacity=".87" />
|
||||
<path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm4.3 14.3c-.39.39-1.02.39-1.41 0L12 13.41 9.11 16.3c-.39.39-1.02.39-1.41 0-.39-.39-.39-1.02 0-1.41L10.59 12 7.7 9.11c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0L12 10.59l2.89-2.89c.39-.39 1.02-.39 1.41 0 .39.39.39 1.02 0 1.41L13.41 12l2.89 2.89c.38.38.38 1.02 0 1.41z" />
|
||||
</symbol>
|
||||
<!-- from: navigation/more_vert/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-more" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" />
|
||||
</symbol>
|
||||
<!-- from: social/groups/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-groups" viewBox="0 0 24 24">
|
||||
<rect fill="none" height="24" width="24" />
|
||||
<g><path d="M12,12.75c1.63,0,3.07,0.39,4.24,0.9c1.08,0.48,1.76,1.56,1.76,2.73L18,17c0,0.55-0.45,1-1,1H7c-0.55,0-1-0.45-1-1l0-0.61 c0-1.18,0.68-2.26,1.76-2.73C8.93,13.14,10.37,12.75,12,12.75z M4,13c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2s-2,0.9-2,2 C2,12.1,2.9,13,4,13z M5.13,14.1C4.76,14.04,4.39,14,4,14c-0.99,0-1.93,0.21-2.78,0.58C0.48,14.9,0,15.62,0,16.43L0,17 c0,0.55,0.45,1,1,1l3.5,0v-1.61C4.5,15.56,4.73,14.78,5.13,14.1z M20,13c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2s-2,0.9-2,2 C18,12.1,18.9,13,20,13z M24,16.43c0-0.81-0.48-1.53-1.22-1.85C21.93,14.21,20.99,14,20,14c-0.39,0-0.76,0.04-1.13,0.1 c0.4,0.68,0.63,1.46,0.63,2.29V18l3.5,0c0.55,0,1-0.45,1-1L24,16.43z M12,6c1.66,0,3,1.34,3,3c0,1.66-1.34,3-3,3s-3-1.34-3-3 C9,7.34,10.34,6,12,6z" /></g>
|
||||
</symbol>
|
||||
<!-- from: social/group_add/materialiconsround/24px.svg -->
|
||||
<symbol id="icon-create_group" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M7 10H5V8c0-.55-.45-1-1-1s-1 .45-1 1v2H1c-.55 0-1 .45-1 1s.45 1 1 1h2v2c0 .55.45 1 1 1s1-.45 1-1v-2h2c.55 0 1-.45 1-1s-.45-1-1-1zm11 1c1.66 0 2.99-1.34 2.99-3S19.66 5 18 5c-.32 0-.63.05-.91.14.57.81.9 1.79.9 2.86s-.34 2.04-.9 2.86c.28.09.59.14.91.14zm-5 0c1.66 0 2.99-1.34 2.99-3S14.66 5 13 5s-3 1.34-3 3 1.34 3 3 3zm0 2c-2 0-6 1-6 3v1c0 .55.45 1 1 1h10c.55 0 1-.45 1-1v-1c0-2-4-3-6-3zm6.62.16c.83.73 1.38 1.66 1.38 2.84v1.5c0 .17-.02.34-.05.5h2.55c.28 0 .5-.22.5-.5V16c0-1.54-2.37-2.49-4.38-2.84z" />
|
||||
</symbol>
|
||||
</defs></svg>
|
||||
|
After Width: | Height: | Size: 8.1 KiB |
@@ -1,5 +1,5 @@
|
||||
{% extends "admin_app.html" %}
|
||||
{% from "library.j2" import box %}
|
||||
{% from "library.j2" import box, form_button, standard_button %}
|
||||
{% block content %}
|
||||
<h1>{% trans user_name=target_user.localpart %}Delete user {{ user_name }}{% endtrans %}</h1>
|
||||
<div class="form layout-expanded"><form method="POST">
|
||||
@@ -20,8 +20,8 @@
|
||||
<p>The user and their data will be deleted irrevocably, permanently and immediately upon pushing thre below button. <strong>There is no way back!</strong></p>
|
||||
{% endcall %}
|
||||
<div class="f-bbox">
|
||||
<a class="button secondary" href="{{ url_for('.users') }}">Back</a>
|
||||
{{ form.action_delete(class="primary danger") }}
|
||||
{%- call standard_button("back", url_for(".index"), class="secondary") %}{% trans %}Back{% endtrans %}{% endcall -%}
|
||||
{%- call form_button("remove", form.action_delete, class="primary danger") %}{% endcall -%}
|
||||
</div>
|
||||
</form></div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "admin_app.html" %}
|
||||
{% from "library.j2" import showuri %}
|
||||
{% from "library.j2" import showuri, form_button, standard_button %}
|
||||
{% block head_lead %}
|
||||
{{ super() }}
|
||||
{% include "copy-snippet.html" %}
|
||||
@@ -18,11 +18,10 @@
|
||||
<dd>{% call showuri(invite.landing_page) %}{% endcall %}</dd>
|
||||
</dl>
|
||||
<div class="f-bbox">
|
||||
{#- -#}
|
||||
{{ form.action_revoke(class="button secondary danger") }}
|
||||
{#- -#}
|
||||
<a href="{{ url_for(".invitations") }}" class="button primary">{% trans %}Back{% endtrans %}</a>
|
||||
{#- -#}
|
||||
{%- call form_button("remove_link", form.action_revoke, class="secondary danger") %}{% endcall -%}
|
||||
{%- call standard_button("back", url_for(".invitations"), class="primary") %}
|
||||
{% trans %}Back{% endtrans %}
|
||||
{%- endcall %}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "admin_app.html" %}
|
||||
{% from "library.j2" import action_button, icon, clipboard_button, form_button, custom_form_button %}
|
||||
{% block head_lead %}
|
||||
{{ super() }}
|
||||
{% include "copy-snippet.html" %}
|
||||
@@ -10,7 +11,7 @@
|
||||
<h2 class="form-title">{% trans %}Create new invitation{% endtrans %}</h2>
|
||||
<p class="form-descr weak">{% trans %}Create a new invitation link to invite more users to your Snikket instance by clicking the button below.{% endtrans %}</p>
|
||||
<div class="f-bbox">
|
||||
{{ form.action_create_invite(class="primary") }}
|
||||
{%- call form_button("create_link", form.action_create_invite, class="primary") %}{% endcall -%}
|
||||
</div>
|
||||
</div>
|
||||
<h2>{% trans %}Pending invitations{% endtrans %}</h2>
|
||||
@@ -32,13 +33,15 @@
|
||||
<td>{{ invite.created_at | format_date }}</td>
|
||||
<td>{{ (invite.expires - now) | format_timedelta(add_direction=True) }}</td>
|
||||
<td style="white-space: nowrap;">
|
||||
{#- -#}
|
||||
<a href="{{ url_for(".edit_invite", id_=invite.id_) }}" class="button secondary btn-more" title="{% trans %}Show invite details{% endtrans %}"><span class="a11y-only">{% trans %}Show invite details{% endtrans %}</span></a>
|
||||
{#- -#}
|
||||
<a class="button primary" title="{% trans %}Copy invite link to clipboard{% endtrans %}" aria-label="{% trans %}Copy invite link to clipboard{% endtrans %}" data-cliptext="{{ invite.landing_page }}" onclick="copy_to_clipboard(this); return false;"><span>📋</span></a>
|
||||
{#- -#}
|
||||
<button type="submit" class="secondary danger btn-delete" name="{{ form.action_revoke.name }}" value="{{ invite.id_ }}"><span class="a11y-only">{% trans %}Delete invitation{% endtrans %}</span></button>
|
||||
{#- -#}
|
||||
{%- call action_button("more", url_for(".edit_invite", id_=invite.id_), class="secondary") -%}
|
||||
{% trans %}Show invite details{% endtrans %}
|
||||
{%- endcall -%}
|
||||
{%- call clipboard_button(invite.landing_page, class="primary") -%}
|
||||
{% trans %}Copy invite link to clipboard{% endtrans %}
|
||||
{%- endcall -%}
|
||||
{%- call custom_form_button("remove_link", form.action_revoke.name, invite.id_, class="secondary danger", slim=True) -%}
|
||||
{% trans %}Delete invitation{% endtrans %}
|
||||
{%- endcall -%}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "admin_app.html" %}
|
||||
{% from "library.j2" import icon %}
|
||||
{% macro value_or_hint(v, caller=None) %}
|
||||
{%- if v is not none -%}
|
||||
{{- v -}}
|
||||
@@ -26,7 +27,7 @@
|
||||
<td class="collapsible">{% call value_or_hint(user.email) %}{% endcall %}</td>
|
||||
<td class="collapsible">{% call value_or_hint(user.phone) %}{% endcall %}</td>
|
||||
<td>
|
||||
{#- -#}<a class="button secondary btn-delete" href="{{ url_for(".delete_user", localpart=user.localpart) }}" title="{% trans user_name=user.localpart %}Delete user {{ user_name }}{% endtrans %}"><span class="a11y-only">{% trans user_name=user.localpart %}Delete user {{ user_name }}{% endtrans %}</span></a>
|
||||
{#- -#}<a class="button secondary btn-delete" href="{{ url_for(".delete_user", localpart=user.localpart) }}">{% call icon("remove") %}{% trans user_name=user.localpart %}Delete user {{ user_name }}{% endtrans %}{% endcall %}</a>
|
||||
{#- -#}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ lang }}">
|
||||
<html lang="{{ lang }}" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
{% block head_lead %}{% endblock %}
|
||||
|
||||
@@ -73,6 +73,50 @@ var copy_to_clipboard = function(el) {
|
||||
});
|
||||
};
|
||||
|
||||
var get_current_icon = function(svg_el) {
|
||||
var use_el = svg_el.firstChild;
|
||||
// don’t ask. I hate js.
|
||||
var tmp = "" + use_el.getAttribute("xlink:href");
|
||||
return tmp.split("#")[1].split("-")[1];
|
||||
}
|
||||
|
||||
var change_icon = function(svg_el, new_icon) {
|
||||
var use_el = svg_el.firstChild;
|
||||
// don’t ask. I hate js.
|
||||
var tmp = "" + use_el.getAttribute("xlink:href");
|
||||
var base_url = tmp.split("#")[0];
|
||||
use_el.setAttributeNS(
|
||||
"http://www.w3.org/1999/xlink", "xlink:href",
|
||||
base_url + "#icon-" + new_icon
|
||||
);
|
||||
}
|
||||
|
||||
var copy_to_clipboard_btn = function(el) {
|
||||
var text = el.dataset.cliptext;
|
||||
if (!text) {
|
||||
console.error('copy_to_clipboard used on element without text to copy');
|
||||
}
|
||||
copyTextToClipboard(text, el, function(success) {
|
||||
var existing_result_el = document.getElementById("clipboard-result");
|
||||
if (existing_result_el !== null) {
|
||||
existing_result_el.parentNode.removeChild(existing_result_el);
|
||||
}
|
||||
|
||||
var icon = "done";
|
||||
var label = "{% trans %}Copied to clipboard{% endtrans %}";
|
||||
if (!success) {
|
||||
icon = "cancel";
|
||||
label = "{% trans %}Copy operation failed{% endtrans %}";
|
||||
}
|
||||
var icon_bak = get_current_icon(el.firstChild);
|
||||
change_icon(el.firstChild, icon);
|
||||
setTimeout(function() {
|
||||
change_icon(el.firstChild, icon_bak);
|
||||
el.blur();
|
||||
}, 1500);
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
document.body.classList.remove("no-copy");
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{% from "library.j2" import box %}
|
||||
{% from "library.j2" import box, icon %}
|
||||
{% set body_id = "no-lines" %}
|
||||
{% block head_lead %}
|
||||
<title>Theme Demo – Snikket Web Portal</title>
|
||||
@@ -238,5 +238,22 @@
|
||||
{% endfor %}
|
||||
#}
|
||||
</form></div>
|
||||
<h2>Icons</h2>
|
||||
<p>Icons can be used in a variety of ways. For example in an enumeration:</p>
|
||||
<ul>
|
||||
<li>{% call icon("add") %}{% endcall %} Yaycon</li>
|
||||
<li>{% call icon("remove") %}{% endcall %} Naycon</li>
|
||||
</ul>
|
||||
<p>Or, more importantly, on buttons:</p>
|
||||
<div class="form layout-expanded">
|
||||
<h3 class="form-title">Do the thing?</h3>
|
||||
<div class="f-bbox">
|
||||
{#- -#}
|
||||
<a class="button secondary">{% call icon("cancel") %}{% endcall %}No</a>
|
||||
{#- -#}
|
||||
<a class="button primary">{% call icon("done") %}{% endcall %}Yes</a>
|
||||
{#- -#}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -10,16 +10,65 @@
|
||||
{%- endif -%}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro clipboard_button(label=_("Copy link"), caller=None) -%}
|
||||
{%- set text = caller() -%}
|
||||
<a title="{% trans content=text %}Copy "{{ content }}" to clipboard{% endtrans %}" aria-label="{% trans content=text %}Copy "{{ content }}" to clipboard{% endtrans %}" class="copy-to-clipboard" onclick="copy_to_clipboard(this); return false;" data-cliptext="{{ text }}" href="#">{{ label }}</a>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro showuri(uri, caller=None) %}
|
||||
{%- if uri is none -%}
|
||||
<em>—</em>
|
||||
{%- else -%}
|
||||
<div><input type="text" readonly="readonly" value="{{ uri }}"></div>
|
||||
<div>{% call clipboard_button() %}{{ uri }}{% endcall %}</div>
|
||||
<div>{% call clipboard_button(uri, show_label=True) %}{% trans %}Copy link{% endtrans %}{% endcall %}</div>
|
||||
{%- endif -%}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro icon(name, caller=None) -%}
|
||||
{%- set alt = "" if caller is none else caller() -%}
|
||||
{%- if alt %}<span class="a11y-only">{{ alt }}</span>{% endif %}<svg class="icon icon-{{ name }}" aria-hidden="true"><use xlink:href="{{ url_for('static', filename='img/icons.svg' )}}#icon-{{ name }}"></use></svg>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro standard_button(icon_name, href, caller=None, class=None) -%}
|
||||
{%- set label = caller() -%}
|
||||
<a href="{{ href }}" class="button {% if class %}{{ class }}{% endif %}" aria-label="{{ a11y }}" title="{{ a11y }}">{% call icon(icon_name) %}{% endcall %}<span>{{ label }}</span></a>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro form_button(icon_name, button_obj, caller=None, class=None) -%}
|
||||
<button {% if class %}class="{{ class }}"{% endif %} id="{{ button_obj.id }}" name="{{ button_obj.name }}" type="submit" value="{{ button_obj.data or 'true' }}">{% call icon(icon_name) %}{% endcall %}<span>{{ button_obj.label.text }}</span></button>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro custom_form_button(icon_name, name, value, caller=None, slim=False, class=None) -%}
|
||||
{%- set text = caller() -%}
|
||||
<button
|
||||
{% if class %}class="{{ class }}"{% endif %}
|
||||
{% if name %}name="{{ name }}"{% endif %}
|
||||
type="submit"
|
||||
{% if value %} value="{{ value }}"{% endif %}
|
||||
{% if slim %}
|
||||
aria-label="{{ text }}"
|
||||
title="{{ text }}"
|
||||
{% endif %}
|
||||
>
|
||||
{%- call icon(icon_name) %}{% endcall -%}
|
||||
{%- if not slim -%}
|
||||
<span>{{ text }}</span>
|
||||
{%- endif -%}
|
||||
</button>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro action_button(icon_name, href, caller=None, class=None) -%}
|
||||
{%- set a11y = caller() -%}
|
||||
<a href="{{ href }}" class="button {% if class %}{{ class }}{% endif %}" aria-label="{{ a11y }}" title="{{ a11y }}">{% call icon(icon_name) %}{% endcall %}</a>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro clipboard_button(data, show_label=False, caller=None, class=None) -%}
|
||||
{%- set label = caller() -%}
|
||||
<a class="button{% if class %} {{ class }}{% endif %}"
|
||||
{% if not show_label %}
|
||||
aria-label="{{ label }}"
|
||||
title="{{ label }}"
|
||||
{% endif %}
|
||||
data-cliptext="{{ data }}"
|
||||
onclick="copy_to_clipboard_btn(this); return false;">
|
||||
{%- call icon("copy") %}{% endcall -%}
|
||||
{%- if show_label %}
|
||||
<span>{{ label }}</span>
|
||||
{% endif -%}
|
||||
</a>
|
||||
{%- endmacro %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{% from "library.j2" import box %}
|
||||
{% from "library.j2" import box, icon %}
|
||||
{% set body_id = "login" %}
|
||||
{% block head_lead %}
|
||||
<title>{{ _("Snikket Login") }}</title>
|
||||
@@ -28,7 +28,7 @@
|
||||
{{ form.password(placeholder=form.password.label.text) }}
|
||||
</div>
|
||||
<div class="f-bbox">
|
||||
<button type="submit" class="primary">{{ _("Log in") }}</button>
|
||||
<button type="submit" class="primary">{% call icon("login") %}{% endcall %}{{ _("Log in") }}</button>
|
||||
</div>
|
||||
</from>
|
||||
</div></main></div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "app.html" %}
|
||||
{% from "library.j2" import icon %}
|
||||
{% block head_lead %}
|
||||
<title>Snikket Web Portal</title>
|
||||
{% endblock %}
|
||||
@@ -9,9 +10,9 @@
|
||||
{{ form.csrf_token }}
|
||||
<div class="f-bbox">
|
||||
{#- -#}
|
||||
<a href="{{ url_for('user.index') }}" class="button secondary">{% trans %}Back{% endtrans %}</a>
|
||||
<a href="{{ url_for('user.index') }}" class="button secondary">{% call icon("back") %}{% endcall %}{% trans %}Back{% endtrans %}</a>
|
||||
{#- -#}
|
||||
<button type="submit" class="primary">{% trans %}Sign out{% endtrans %}</button>
|
||||
<button type="submit" class="primary">{% call icon("logout") %}{% endcall %}{% trans %}Sign out{% endtrans %}</button>
|
||||
{#- -#}
|
||||
</div>
|
||||
</form></div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "app.html" %}
|
||||
{% from "library.j2" import standard_button, custom_form_button %}
|
||||
{% block head_lead %}
|
||||
<title>Snikket Web Portal</title>
|
||||
{% endblock %}
|
||||
@@ -36,8 +37,10 @@
|
||||
<p>{% trans %}After changing your password, you will have to enter the new password on all of your devices.{% endtrans %}</p>
|
||||
</div>
|
||||
<div class="f-bbox">
|
||||
<a href="{{ url_for('.index') }}" class="button secondary">{% trans %}Back{% endtrans %}</a>
|
||||
<button type="submit" class="primary">{% trans %}Change password{% endtrans %}</button>
|
||||
{%- call standard_button("back", url_for('.index'), class="secondary") %}{% trans %}Back{% endtrans %}{% endcall -%}
|
||||
{%- call custom_form_button("done", "", "", class="primary") -%}
|
||||
{% trans %}Change password{% endtrans %}
|
||||
{%- endcall -%}
|
||||
</div>
|
||||
</form></div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "app.html" %}
|
||||
{% from "library.j2" import standard_button, form_button %}
|
||||
{% block head_lead %}
|
||||
<title>Snikket Web Portal</title>
|
||||
{% endblock %}
|
||||
@@ -21,7 +22,8 @@
|
||||
{{ form.profile_access_model }}
|
||||
</div>
|
||||
<div class="f-bbox">
|
||||
<a href="{{ url_for('user.index') }}" class="button secondary">{% trans %}Back{% endtrans %}</a><button type="submit" class="primary">{% trans %}Apply{% endtrans %}</button>
|
||||
{%- call standard_button("back", url_for('.index'), class="secondary") %}{% trans %}Back{% endtrans %}{% endcall -%}
|
||||
{%- call form_button("done", form.action_save, class="primary") %}{% endcall -%}
|
||||
</div>
|
||||
</form></div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -67,6 +67,10 @@ class ProfileForm(flask_wtf.FlaskForm): # type:ignore
|
||||
choices=_ACCESS_MODEL_CHOICES,
|
||||
)
|
||||
|
||||
action_save = wtforms.SubmitField(
|
||||
_l("Apply"),
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/")
|
||||
@client.require_session()
|
||||
|
||||
17
tools/icons.list
Normal file
17
tools/icons.list
Normal file
@@ -0,0 +1,17 @@
|
||||
action/account_circle:profile
|
||||
action/done:done
|
||||
action/logout:logout
|
||||
action/login:login
|
||||
communication/vpn_key:passwd
|
||||
content/add_circle_outline:add
|
||||
content/add_link:create_link
|
||||
content/create:edit
|
||||
content/remove_circle_outline:remove
|
||||
content/content_copy:copy
|
||||
content/link_off:remove_link
|
||||
navigation/arrow_back:back
|
||||
navigation/arrow_forward:forward
|
||||
navigation/cancel:cancel
|
||||
navigation/more_vert:more
|
||||
social/groups:groups
|
||||
social/group_add:create_group
|
||||
40
tools/import-icons.sh
Normal file
40
tools/import-icons.sh
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
# usage: import-icons.sh ROOT ICONLIST FLAVOR SVGOUT
|
||||
#
|
||||
# positional arguments:
|
||||
#
|
||||
# ROOT path to the checkout of https://github.com/google/material-design-icons
|
||||
# ICONLIST path to the icons.list file in the snikket-web-portal repository
|
||||
# FLAVOR one of '', 'round', 'sharp', 'outlined', 'twoshade'
|
||||
# SVGOUT path to the newly created SVG file
|
||||
root="$1/src"
|
||||
iconlist_file="$2"
|
||||
flavor="$3"
|
||||
output_file="$4"
|
||||
|
||||
printf '<svg aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">\n<defs>\n' > "$output_file"
|
||||
printf '<!-- These icons are sourced from Google’s Material Icons set,\nlicensed under the terms of the Apache 2.0 License -->\n' >> "$output_file"
|
||||
printf '<!DOCTYPE html>\n<html><body>'
|
||||
|
||||
IFS=$'\n'
|
||||
while read -r icondef; do
|
||||
path="$(cut -d':' -f1 <<<"$icondef")"
|
||||
name="$(cut -d':' -f2 <<<"$icondef")"
|
||||
src_path="$path/materialicons$flavor"
|
||||
if [ ! -d "$root/$src_path" ]; then
|
||||
printf 'warning: %q not found in flavor %q, falling back to default\n' "$path" "$flavor" >&2
|
||||
src_path="$path/materialicons"
|
||||
fi
|
||||
src_svg="$src_path/24px.svg"
|
||||
if [ ! -f "$root/$src_svg" ]; then
|
||||
printf 'error: failed to find source file for %q: %s: does not exist\n' "$path" "$src_svg" >&2
|
||||
fi
|
||||
printf '<!-- from: %s -->\n' "$src_svg" >> "$output_file"
|
||||
printf '<symbol id="icon-%s" viewBox="0 0 24 24">\n' "$name" >> "$output_file"
|
||||
xpath -q -e '/svg/*' "$root/$src_svg" >> "$output_file"
|
||||
printf '</symbol>\n' >> "$output_file"
|
||||
|
||||
printf '<p><svg><use xlink:href="#icon-%s"></use></svg></p>\n' "$name"
|
||||
done < "$iconlist_file"
|
||||
printf '</defs></svg>\n' >> "$output_file"
|
||||
Reference in New Issue
Block a user