Centralised file input selection.

This commit is contained in:
flash 2018-10-27 20:50:34 +02:00
parent 46bca5bfca
commit f3ba61418d
13 changed files with 202 additions and 48 deletions

View file

@ -0,0 +1,9 @@
@keyframes background-slide {
0% {
background-position: 0 0;
}
100% {
background-position: var(--background-width) var(--background-height);
}
}

View file

@ -1,4 +1,5 @@
.input__select { .input__select {
margin: 1px 0;
border: 1px solid #222; border: 1px solid #222;
padding: 5px 10px; padding: 5px 10px;
background: #222; background: #222;

View file

@ -1,7 +1,31 @@
.input__upload { .input__upload {
display: none; display: inline-block;
cursor: pointer;
margin: 1px 0;
&__label { &__input {
.input__button(); display: inline-block;
position: absolute;
z-index: -1000;
}
&__selection {
text-align: center;
font-size: 1.2em;
border: 1px solid #222;
padding: 5px 10px;
background: #222;
color: #fff;
border-radius: 2px;
box-shadow: inset 0 0 4px #111;
transition: border-color .2s;
overflow: none;
word-wrap: break-word;
}
&:focus-within &__selection,
&__input:focus ~ &__selection,
&__input:active ~ &__selection {
border-color: var(--accent-colour);
} }
} }

View file

@ -39,6 +39,7 @@ body {
background-color: var(--background-colour); background-color: var(--background-colour);
font: 12px/20px @mio-font-regular; font: 12px/20px @mio-font-regular;
color: var(--text-colour); color: var(--text-colour);
background-attachment: fixed;
&__wrapper { &__wrapper {
max-width: var(--site-max-width); max-width: var(--site-max-width);
@ -48,26 +49,34 @@ body {
} }
&--bg-blend { &--bg-blend {
background-color: var(--accent-colour);
background-blend-mode: multiply; background-blend-mode: multiply;
} }
/*&--bg-slide { &--bg-slide {
}*/ animation: background-slide infinite linear 2s;
}
&--bg-cover { &--bg-cover {
background-repeat: no-repeat; background-size: cover;
}
&--bg-contain {
background-size: contain;
} }
&--bg-stretch { &--bg-stretch {
background-size: 100% 100%; background-size: 100% 100%;
background-repeat: no-repeat;
} }
&--bg-tile { &--bg-tile {
background-repeat: repeat; background-size: auto;
} }
} }
// Misc
@import "animations";
// Input elements // Input elements
@import "classes/input/button"; @import "classes/input/button";
@import "classes/input/select"; @import "classes/input/select";

View file

@ -262,7 +262,7 @@ switch ($mode) {
SELECT SELECT
u.`user_id`, u.`username`, u.`user_country`, u.`user_id`, u.`username`, u.`user_country`,
u.`created_at`, u.`last_seen`, u.`created_at`, u.`last_seen`,
u.`user_about_parser`, u.`user_about_content`, u.`user_about_parser`, u.`user_about_content`, u.`user_background_settings`,
%1$s, %1$s,
COALESCE(u.`user_title`, r.`role_title`) as `user_title`, COALESCE(u.`user_title`, r.`role_title`) as `user_title`,
COALESCE(u.`user_colour`, r.`role_colour`) as `user_colour`, COALESCE(u.`user_colour`, r.`role_colour`) as `user_colour`,
@ -303,13 +303,27 @@ switch ($mode) {
$getProfile->bindValue('user_id', $userId); $getProfile->bindValue('user_id', $userId);
$profile = $getProfile->execute() ? $getProfile->fetch(PDO::FETCH_ASSOC) : []; $profile = $getProfile->execute() ? $getProfile->fetch(PDO::FETCH_ASSOC) : [];
$backgroundPath = build_path(MSZ_STORAGE, 'backgrounds/original', "{$profile['user_id']}.msz");
if (is_file($backgroundPath)) {
$backgroundInfo = getimagesize($backgroundPath);
if ($backgroundInfo) {
tpl_var('site_background', [
'url' => "/profile.php?m=background&u={$userId}",
'width' => $backgroundInfo[0],
'height' => $backgroundInfo[1],
'settings' => $profile['user_background_settings'],
]);
}
}
echo tpl_render('user.profile', [ echo tpl_render('user.profile', [
'profile' => $profile, 'profile' => $profile,
'profile_notices' => $notices, 'profile_notices' => $notices,
'can_edit' => $canEdit, 'can_edit' => $canEdit,
'is_editing' => $isEditing, 'is_editing' => $isEditing,
'profile_fields' => user_session_active() ? user_profile_fields_display($profile, !$isEditing) : [], 'profile_fields' => user_session_active() ? user_profile_fields_display($profile, !$isEditing) : [],
'has_background' => is_file(build_path(MSZ_STORAGE, 'backgrounds/original', "{$profile['user_id']}.msz")),
'friend_info' => user_session_active() ? user_relation_info(user_session_current('user_id', 0), $profile['user_id']) : [], 'friend_info' => user_session_active() ? user_relation_info(user_session_current('user_id', 0), $profile['user_id']) : [],
]); ]);
break; break;

View file

@ -10,18 +10,21 @@ define('MSZ_USER_BACKGROUND_ATTACHMENT_NONE', 0);
define('MSZ_USER_BACKGROUND_ATTACHMENT_COVER', 1); define('MSZ_USER_BACKGROUND_ATTACHMENT_COVER', 1);
define('MSZ_USER_BACKGROUND_ATTACHMENT_STRETCH', 2); define('MSZ_USER_BACKGROUND_ATTACHMENT_STRETCH', 2);
define('MSZ_USER_BACKGROUND_ATTACHMENT_TILE', 3); define('MSZ_USER_BACKGROUND_ATTACHMENT_TILE', 3);
define('MSZ_USER_BACKGROUND_ATTACHMENT_CONTAIN', 4);
define('MSZ_USER_BACKGROUND_ATTACHMENTS', [ define('MSZ_USER_BACKGROUND_ATTACHMENTS', [
MSZ_USER_BACKGROUND_ATTACHMENT_NONE, MSZ_USER_BACKGROUND_ATTACHMENT_NONE,
MSZ_USER_BACKGROUND_ATTACHMENT_COVER, MSZ_USER_BACKGROUND_ATTACHMENT_COVER,
MSZ_USER_BACKGROUND_ATTACHMENT_STRETCH, MSZ_USER_BACKGROUND_ATTACHMENT_STRETCH,
MSZ_USER_BACKGROUND_ATTACHMENT_TILE, MSZ_USER_BACKGROUND_ATTACHMENT_TILE,
MSZ_USER_BACKGROUND_ATTACHMENT_CONTAIN,
]); ]);
define('MSZ_USER_BACKGROUND_ATTACHMENTS_NAMES', [ define('MSZ_USER_BACKGROUND_ATTACHMENTS_NAMES', [
MSZ_USER_BACKGROUND_ATTACHMENT_COVER => 'cover', MSZ_USER_BACKGROUND_ATTACHMENT_COVER => 'cover',
MSZ_USER_BACKGROUND_ATTACHMENT_STRETCH => 'stretch', MSZ_USER_BACKGROUND_ATTACHMENT_STRETCH => 'stretch',
MSZ_USER_BACKGROUND_ATTACHMENT_TILE => 'tile', MSZ_USER_BACKGROUND_ATTACHMENT_TILE => 'tile',
MSZ_USER_BACKGROUND_ATTACHMENT_CONTAIN => 'contain',
]); ]);
define('MSZ_USER_BACKGROUND_ATTRIBUTE_BLEND', 0x10); define('MSZ_USER_BACKGROUND_ATTRIBUTE_BLEND', 0x10);
@ -80,9 +83,11 @@ function user_background_delete(int $userId): void
define('MSZ_USER_BACKGROUND_TYPE_PNG', IMAGETYPE_PNG); define('MSZ_USER_BACKGROUND_TYPE_PNG', IMAGETYPE_PNG);
define('MSZ_USER_BACKGROUND_TYPE_JPG', IMAGETYPE_JPEG); define('MSZ_USER_BACKGROUND_TYPE_JPG', IMAGETYPE_JPEG);
define('MSZ_USER_BACKGROUND_TYPE_GIF', IMAGETYPE_GIF);
define('MSZ_USER_BACKGROUND_TYPES', [ define('MSZ_USER_BACKGROUND_TYPES', [
MSZ_USER_BACKGROUND_TYPE_PNG, MSZ_USER_BACKGROUND_TYPE_PNG,
MSZ_USER_BACKGROUND_TYPE_JPG, MSZ_USER_BACKGROUND_TYPE_JPG,
MSZ_USER_BACKGROUND_TYPE_GIF,
]); ]);
function user_background_is_allowed_type(int $type): bool function user_background_is_allowed_type(int $type): bool

View file

@ -44,6 +44,8 @@
{% endmacro %} {% endmacro %}
{% macro comments_entry(comment, indent, category, user, perms) %} {% macro comments_entry(comment, indent, category, user, perms) %}
{% from '_layout/input.twig' import input_checkbox_raw %}
{% if comment.comment_deleted is null or comment.comment_replies|length > 0 %} {% if comment.comment_deleted is null or comment.comment_replies|length > 0 %}
<div class="comment" id="comment-{{ comment.comment_id }}"> <div class="comment" id="comment-{{ comment.comment_id }}">
<div class="comment__container"> <div class="comment__container">
@ -114,7 +116,7 @@
<div class="comment__replies comment__replies--indent-{{ indent }}" id="comment-{{ comment.comment_id }}-replies"> <div class="comment__replies comment__replies--indent-{{ indent }}" id="comment-{{ comment.comment_id }}-replies">
{% from _self import comments_entry, comments_input %} {% from _self import comments_entry, comments_input %}
{% if user|default(null) is not null and category|default(null) is not null and perms|default(null) is not null and perms.can_comment %} {% if user|default(null) is not null and category|default(null) is not null and perms|default(null) is not null and perms.can_comment %}
<input type="checkbox" class="comment__reply-toggle" id="comment-reply-toggle-{{ comment.comment_id }}"> {{ input_checkbox_raw('', false, 'comment__reply-toggle', '', false, {'id':'comment-reply-toggle-' ~ comment.comment_id}) }}
{{ comments_input(category, user, perms, comment) }} {{ comments_input(category, user, perms, comment) }}
{% endif %} {% endif %}
{% if comment.comment_replies is defined and comment.comment_replies|length > 0 %} {% if comment.comment_replies is defined and comment.comment_replies|length > 0 %}

View file

@ -1,3 +1,4 @@
{% from '_layout/input.twig' import input_checkbox_raw %}
{% set in_manage = manage_menu is defined %} {% set in_manage = manage_menu is defined %}
<nav class="header"> <nav class="header">
@ -17,7 +18,7 @@
for="toggle-mobile-header-user"></label> for="toggle-mobile-header-user"></label>
</div> </div>
<input type="checkbox" class="header__menu-toggle" id="toggle-mobile-header-menu"> {{ input_checkbox_raw('', false, 'header__menu-toggle', '', false, {'id':'toggle-mobile-header-menu'}) }}
<ul class="header__menu"> <ul class="header__menu">
<li class="header__menu__item"> <li class="header__menu__item">
<a href="/" class="header__menu__link">Home</a> <a href="/" class="header__menu__link">Home</a>
@ -41,7 +42,7 @@
<li class="header__menu__item"><a href="https://chat.flashii.net" class="header__menu__link">Chat</a></li> <li class="header__menu__item"><a href="https://chat.flashii.net" class="header__menu__link">Chat</a></li>
</ul> </ul>
<input type="checkbox" class="header__user-toggle" id="toggle-mobile-header-user"> {{ input_checkbox_raw('', false, 'header__user-toggle', '', false, {'id':'toggle-mobile-header-user'}) }}
<div class="header__user"> <div class="header__user">
{% if current_user is defined %} {% if current_user is defined %}
<a href="/profile.php?u={{ current_user.user_id }}" <a href="/profile.php?u={{ current_user.user_id }}"

View file

@ -23,13 +23,23 @@
{% endspaceless %} {% endspaceless %}
{% endmacro %} {% endmacro %}
{% macro input_checkbox(name, text, checked, class, value, radio) %} {% macro input_checkbox_raw(name, checked, class, value, radio, attributes) %}
{% spaceless %} {% spaceless %}
<label class="input__checkbox{% if radio %} input__checkbox--radio{% endif %}{{ class|length > 0 ? ' ' ~ class : '' }}"> <input type="{{ radio ? 'radio' : 'checkbox' }}" class="{{ class|length > 0 ? class : 'input__checkbox__input' }}"
<input type="{{ radio ? 'radio' : 'checkbox' }}" class="input__checkbox__input"
{% if name|length > 0 %}name="{{ name }}"{% endif %} {% if name|length > 0 %}name="{{ name }}"{% endif %}
{% if checked %}checked{% endif %} {% if checked %}checked{% endif %}
{% if value|length > 0 %}value="{{ value }}"{% endif %}> {% if value|length > 0 %}value="{{ value }}"{% endif %}
{% for name, value in attributes|default([]) %}
{{ name }}{% if value|length > 0 %}="{{ value }}"{% endif %}
{% endfor %}>
{% endspaceless %}
{% endmacro %}
{% macro input_checkbox(name, text, checked, class, value, radio, attributes) %}
{% from _self import input_checkbox_raw %}
{% spaceless %}
<label class="input__checkbox{% if radio %} input__checkbox--radio{% endif %}{{ class|length > 0 ? ' ' ~ class : '' }}">
{{ input_checkbox_raw(name, checked, '', value, radio, attributes) }}
<div class="input__checkbox__display"> <div class="input__checkbox__display">
<div class="input__checkbox__display__icon"></div> <div class="input__checkbox__display__icon"></div>
</div> </div>
@ -41,3 +51,32 @@
</label> </label>
{% endspaceless %} {% endspaceless %}
{% endmacro %} {% endmacro %}
{% macro input_file_raw(name, class, accepts, attributes) %}
{% spaceless %}
<input type="file" {% if name|length > 0 %}name="{{ name }}"{% endif %}
class="{{ class|length > 0 ? class : 'input__upload__input' }}"
{% if accepts|length > 0 %}accept="{{ accepts|join(',') }}"{% endif %}
{% for name, value in attributes|default([]) %}
{{ name }}{% if value|length > 0 %}="{{ value }}"{% endif %}
{% endfor %}>
{% endspaceless %}
{% endmacro %}
{% macro input_file(name, class, accepts, attributes) %}
{% from _self import input_file_raw %}
{% spaceless %}
<label class="input__upload">
{{ input_file_raw(name, class, accepts, attributes) }}
<div class="input__upload__selection">
Click here to select a file!
</div>
<script>
const parent = document.currentScript.parentNode,
input = parent.querySelector('input[type="file"]'),
display = parent.querySelector('.input__upload__selection');
input.addEventListener('change', ev => display.textContent = Array.from(ev.target.files).map(f => f.name).join(', '));
</script>
</label>
{% endspaceless %}
{% endmacro %}

View file

@ -1,7 +1,7 @@
{% extends 'manage/users/master.twig' %} {% extends 'manage/users/master.twig' %}
{% from 'macros.twig' import container_title %} {% from 'macros.twig' import container_title %}
{% from 'manage/macros.twig' import permissions_table %} {% from 'manage/macros.twig' import permissions_table %}
{% from '_layout/input.twig' import input_csrf, input_text, input_checkbox %} {% from '_layout/input.twig' import input_csrf, input_text, input_checkbox, input_file %}
{% set site_link = '/profile.php?u=' ~ view_user.user_id %} {% set site_link = '/profile.php?u=' ~ view_user.user_id %}
@ -76,7 +76,7 @@
<label class="form__label"> <label class="form__label">
<div class="form__label__text">New Avatar</div> <div class="form__label__text">New Avatar</div>
<div class="form__label__input"> <div class="form__label__input">
<input class="input__text" type="file" name="avatar[file]"> {{ input_file('avatar[file]') }}
</div> </div>
</label> </label>

View file

@ -6,17 +6,19 @@
{% include '_layout/meta.twig' %} {% include '_layout/meta.twig' %}
<link href="{{ '/css/style.css'|asset_url }}" rel="stylesheet"> <link href="{{ '/css/style.css'|asset_url }}" rel="stylesheet">
<link href="{{ '/css/libraries.css'|asset_url }}" rel="stylesheet"> <link href="{{ '/css/libraries.css'|asset_url }}" rel="stylesheet">
{% if site_background_url is defined %} {% if site_background is defined %}
<style> <style>
:root { :root {
--background-image: url('{{ site_background_url|raw }}'); --background-width: {{ site_background.width }}px;
--background-height: {{ site_background.height }}px;
--background-image: url('{{ site_background.url|raw }}');
} }
</style> </style>
{% endif %} {% endif %}
</head> </head>
<body <body
class="main main--default{% if current_user.user_background_settings is defined %} {{ current_user.user_background_settings|bg_settings('main--bg-%s')|join(' ') }}{% endif %}" class="main{% if site_background is defined %} {{ site_background.settings|bg_settings('main--bg-%s')|join(' ') }}{% endif %}"
style="{% if global_accent_colour is defined %}{{ global_accent_colour|html_colour('--accent-colour') }}{% endif %}"> style="{% if global_accent_colour is defined %}{{ global_accent_colour|html_colour('--accent-colour') }}{% endif %}">
{% include '_layout/header.twig' %} {% include '_layout/header.twig' %}

View file

@ -1,7 +1,8 @@
{% from '_layout/input.twig' import input_checkbox_raw %}
<div class="container profile__header"> <div class="container profile__header">
<div class="profile__header__details"> <div class="profile__header__details">
<div class="profile__header__avatar"> <div class="profile__header__avatar">
{% if is_editing and perms.edit_avatar %} {% if is_editing and perms.edit_avatar %}
<label class="avatar profile__header__avatar__image profile__header__avatar__image--edit" <label class="avatar profile__header__avatar__image profile__header__avatar__image--edit"
style="background-image:url('{{ image }}')" style="background-image:url('{{ image }}')"
@ -13,10 +14,7 @@
Select Select
</label> </label>
<input type="checkbox" {{ input_checkbox_raw('avatar[delete]', false, 'profile__header__avatar__check', '', false, {'id':'avatar-delete'}) }}
class="profile__header__avatar__check"
name="avatar[delete]"
id="avatar-delete">
<label class="profile__header__avatar__option profile__header__avatar__option--delete" <label class="profile__header__avatar__option profile__header__avatar__option--delete"
for="avatar-delete"> for="avatar-delete">
Remove Remove

View file

@ -1,16 +1,12 @@
{% extends 'user/master.twig' %} {% extends 'user/master.twig' %}
{% from 'macros.twig' import container_title %} {% from 'macros.twig' import container_title %}
{% from '_layout/input.twig' import input_hidden, input_csrf, input_text, input_checkbox %} {% from '_layout/input.twig' import input_hidden, input_csrf, input_text, input_checkbox, input_file, input_file_raw %}
{% set image = '/profile.php?u=' ~ profile.user_id ~ '&m=avatar' %} {% set image = '/profile.php?u=' ~ profile.user_id ~ '&m=avatar' %}
{% set canonical_url = '/profile.php?u=' ~ profile.user_id %} {% set canonical_url = '/profile.php?u=' ~ profile.user_id %}
{% set title = 'Profile of ' ~ profile.username %} {% set title = 'Profile of ' ~ profile.username %}
{% set manage_link = '/manage/users.php?v=view&u=' ~ profile.user_id %} {% set manage_link = '/manage/users.php?v=view&u=' ~ profile.user_id %}
{% if has_background %}
{% set site_background_url = '/profile.php?m=background&u=' ~ profile.user_id %}
{% endif %}
{% set stats = [ {% set stats = [
{ {
'title': 'Joined', 'title': 'Joined',
@ -47,11 +43,7 @@
{{ input_csrf('profile') }} {{ input_csrf('profile') }}
{% if perms.edit_avatar %} {% if perms.edit_avatar %}
<input class="profile__hidden" {{ input_file_raw('avatar[file]', 'profile__hidden', ['image/png', 'image/jpeg', 'image/gif'], {'id':'avatar-selection'}) }}
accept="image/png,image/jpeg,image/gif"
type="file"
name="avatar[file]"
id="avatar-selection">
<script> <script>
function updateAvatarPreview(name, url, preview) { function updateAvatarPreview(name, url, preview) {
@ -118,15 +110,9 @@
{{ container_title('Background') }} {{ container_title('Background') }}
<div class="profile__background-settings__content"> <div class="profile__background-settings__content">
<label> {{ input_file('background[file]', '', ['image/png', 'image/jpeg', 'image/gif'], {'id':'background-selection'}) }}
<input
accept="image/png,image/jpeg,image/gif"
type="file"
name="background[file]"
id="background-selection">
</label>
<select name="background[attach]" class="input__select"> <select name="background[attach]" class="input__select" onchange="profileChangeBackgroundAttach(this.value)">
{% for key, value in background_attachments %} {% for key, value in background_attachments %}
<option value="{{ value }}"{% if profile.user_background_attachment == key %} selected{% endif %}> <option value="{{ value }}"{% if profile.user_background_attachment == key %} selected{% endif %}>
{{ value|capitalize }} {{ value|capitalize }}
@ -134,9 +120,9 @@
{% endfor %} {% endfor %}
</select> </select>
{{ input_checkbox('background[delete]', 'Delete') }} {{ input_checkbox('background[delete]', 'Delete', false, '', '', false, {'onchange':'profileToggleBackground(this.checked)'}) }}
{{ input_checkbox('background[attr][blend]', 'Blend', profile.user_background_blend) }} {{ input_checkbox('background[attr][blend]', 'Blend', profile.user_background_blend, '', '', false, {'onchange':'profileToggleBackgroundAttr(\'blend\', this.checked)'}) }}
{{ input_checkbox('background[attr][slide]', 'Slide', profile.user_background_slide) }} {{ input_checkbox('background[attr][slide]', 'Slide', profile.user_background_slide, '', '', false, {'onchange':'profileToggleBackgroundAttr(\'slide\', this.checked)'}) }}
</div> </div>
</div> </div>
{% endif %} {% endif %}
@ -201,6 +187,70 @@
</div> </div>
{% if is_editing %} {% if is_editing %}
</form> </form>
<script>
let profilePreviousBackground = null;
function profileToggleBackground(checked) {
let currentBg = document.body.style.getPropertyValue('--background-image');
if (currentBg != 'initial' && checked) {
profilePreviousBackground = currentBg;
currentBg = 'initial';
} else if (!checked) {
currentBg = profilePreviousBackground;
}
document.body.style.setProperty('--background-image', currentBg);
}
function profileChangeBackgroundAttach(mode) {
const modes = [
'cover',
'stretch',
'tile',
'contain',
];
if (modes.indexOf(mode) < 0)
return;
for (let i = 0; i < modes.length; i++)
document.body.classList.remove('main--bg-' + modes[i]);
document.body.classList.add('main--bg-' + mode);
}
function profileToggleBackgroundAttr(attr, mode) {
let className = '';
switch (attr) {
case 'blend':
className = 'main--bg-blend';
break;
case 'slide':
className = 'main--bg-slide';
break;
}
if (className) {
if (mode)
document.body.classList.add(className);
else
document.body.classList.remove(className);
}
}
document.getElementById('background-selection').addEventListener('change', ev => {
const image = new Image();
image.src = URL.createObjectURL(ev.target.files[0]);
image.addEventListener('load', () => {
document.body.style.setProperty('--background-image', 'url(%)'.replace('%', image.src));
document.body.style.setProperty('--background-width', '%px'.replace('%', image.width));
document.body.style.setProperty('--background-height', '%px'.replace('%', image.height));
});
});
</script>
{% else %} {% else %}
</div> </div>
{% endif %} {% endif %}