1
0
Fork 0
mirror of https://github.com/Oreolek/ifhub.club.git synced 2024-06-26 03:30:48 +03:00

Компоненты media и user-list-add перенесены из фреймворка

This commit is contained in:
Denis Shakhov 2015-03-15 20:32:51 +07:00
parent a0c76a8730
commit ae3513455c
21 changed files with 914 additions and 0 deletions

View file

@ -0,0 +1,3 @@
# Компонент media
Загрузка/управление медиа-файлами для последующей вставки в редактор (компонент editor).

View file

@ -0,0 +1,27 @@
{
"name": "media",
"version": "1.0.0",
"dependencies": {
"modal": "*",
"tabs": "*",
"uploader": "*"
},
"templates": {
"media-content": "media-content.tpl",
"media": "media.tpl",
"pane.insert": "panes/pane.insert.tpl",
"pane.photoset": "panes/pane.photoset.tpl",
"pane.preview": "panes/pane.preview.tpl",
"pane": "panes/pane.tpl",
"pane.url": "panes/pane.url.tpl",
"uploader-block.insert.image": "uploader/uploader-block.insert.image.tpl",
"uploader-block.photoset": "uploader/uploader-block.photoset.tpl",
"uploader": "uploader/uploader.tpl"
},
"scripts": {
"media": "js/media.js"
},
"styles": {
"media": "css/media.css"
}
}

View file

@ -0,0 +1,50 @@
/**
* Media
*/
.modal--media {
max-width: 1130px;
border: none;
}
.media {
background: #222;
}
/* Nav */
.media-nav .tab-list {
width: 160px;
padding-top: 15px;
}
.media-nav .tab {
padding: 10px 15px;
background: #222;
color: #eee;
}
.media-nav .tab:hover {
background: #2a2a2a;
}
.media-nav .tab.active {
background: #333;
}
/* Panes */
.media-nav .tabs-panes {
overflow: hidden;
margin-left: 160px;
background: #fff;
}
/* Content */
.media-pane-content {
min-height: 300px;
padding: 15px;
}
/* Footer */
.media-pane-footer {
padding: 15px;
background: #fafafa;
border-top: 1px solid #eee;
text-align: right;
}

View file

@ -0,0 +1,306 @@
/**
* Media
*
* @module ls/media
*
* @license GNU General Public License, version 2
* @copyright 2013 OOO "ЛС-СОФТ" {@link http://livestreetcms.com}
* @author Denis Shakhov <denis.shakhov@gmail.com>
*
* TODO: Фильтрация файлов по типу при переключении табов
*/
(function($) {
"use strict";
$.widget( "livestreet.lsMedia", {
/**
* Дефолтные опции
*/
options: {
// Редактор к которому привязано текущее окно
editor: $(),
// Ссылки
urls: {
// Вставка файла
insert: aRouter.ajax + 'media/submit-insert/',
// Вставка фотосета
photoset: aRouter.ajax + 'media/submit-create-photoset',
// Загрузка файла по ссылке
url_upload: aRouter.ajax + 'media/upload-link/',
// Вставка файла по ссылке
url_insert: aRouter.ajax + 'media/upload-insert/'
},
// Селекторы
selectors: {
uploader: '.js-media-uploader',
block: '.js-media-info-block',
insert: {
submit: '.js-media-insert-submit'
},
photoset: {
submit: '.js-media-photoset-submit'
},
url: {
form: '.js-media-url-form',
url: '.js-media-url-form-url',
type: '.js-media-url-type',
block_container: '.js-media-url-settings-blocks',
submit_upload: '.js-media-url-submit-upload',
submit_insert: '.js-media-url-submit-insert',
image_preview: '.js-media-url-image-preview'
}
},
uploader_options: {}
},
/**
* Конструктор
*
* @constructor
* @private
*/
_create: function () {
var _this = this;
! this.option( 'editor' ).length && this.option( 'editor', $( '#' + this.element.data( 'media-editor') ) );
this.elements = {
tabs: this.element.find( '[data-tab-type=tab-list] > [data-tab-type=tab]' ),
uploader: this.element.find( this.option( 'selectors.uploader' ) ),
blocks: this.element.find( this.option( 'selectors.uploader' ) + ' ' + this.option( 'selectors.block' ) ),
insert: {
submit: this.element.find( this.option( 'selectors.insert.submit' ) )
},
photoset: {
submit: this.element.find( this.option( 'selectors.photoset.submit' ) )
},
url: {
form: this.element.find( this.option( 'selectors.url.form' ) ),
url: this.element.find( this.option( 'selectors.url.url' ) ),
type: this.element.find( this.option( 'selectors.url.type' ) ),
block_container: this.element.find( this.option( 'selectors.url.block_container' ) ),
submit_upload: this.element.find( this.option( 'selectors.url.submit_upload' ) ),
submit_insert: this.element.find( this.option( 'selectors.url.submit_insert' ) ),
image_preview: this.element.find( this.option( 'selectors.url.image_preview' ) )
}
};
this.elements.url.blocks = this.elements.url.block_container.find( this.option( 'selectors.block' ) );
// Иниц-ия модального окна
this.element.lsModal({
aftershow: function () {
_this.elements.uploader.lsUploader( 'getElement', 'list' ).lsUploaderFileList( 'load' );
}
});
// Иниц-ия загрузчика
this.elements.uploader.lsUploader( $.extend( {}, this.option( 'uploader_options' ), {
autoload: false,
params: {
security_ls_key: LIVESTREET_SECURITY_KEY
},
file_options: {
beforeactivate: function ( event, context ) {
_this.activateInfoBlock( context.element );
}
}
}));
// Табы
this.elements.tabs.on( 'lstabactivate', function( event, tab ) {
this.elements.uploader.lsUploader( 'getElement', 'list' ).lsUploaderFileList( 'option', 'multiselect_ctrl', true );
// Перемещение галереи из одного таба в другой
if ( tab.element.hasClass( 'js-tab-show-gallery' ) ) {
this.elements.uploader.appendTo( this.element.find( '#' + tab.options.target + ' .js-media-pane-content' ) );
this.elements.uploader.lsUploader( 'getElement', 'list' ).lsUploaderFileList( 'resetFilter' );
this.elements.uploader.lsUploader( 'getElement', 'list' ).lsUploaderFileList( 'clearSelected' );
}
}.bind( this ));
this.elements.tabs.filter( '[data-media-name=photoset]' ).on( 'lstabactivate', function( event, tab ) {
this.elements.uploader.lsUploader( 'getElement', 'list' ).lsUploaderFileList( 'option', 'multiselect_ctrl', false );
this.elements.uploader.lsUploader( 'getElement', 'list' ).lsUploaderFileList( 'filterFilesByType', [ '1' ] );
}.bind( this ));
//
// INSERT
//
this.elements.insert.submit.on( 'click' + this.eventNamespace, function () {
var files = this.elements.uploader.lsUploader( 'getElement', 'list' ).lsUploaderFileList( 'getSelectedFiles' );
this.insertFiles( this.option( 'urls.insert' ), this.getSettings(), files );
}.bind( this ));
//
// PHOTOSET
//
this.elements.photoset.submit.on( 'click' + this.eventNamespace, function () {
var files = this.elements.uploader.lsUploader( 'getElement', 'list' ).lsUploaderFileList( 'getSelectedFiles' );
this.insertFiles( this.option( 'urls.photoset' ), this.getSettings(), files );
}.bind( this ));
//
// INSERT FROM URL
//
this.elements.url.type.on( 'change' + this.eventNamespace, this.onUrlTypeChange.bind( this ) );
this.elements.url.url.on( 'keyup change' + this.eventNamespace, this.onUrlChange.bind( this ) );
this.elements.url.submit_upload.on( 'click' + this.eventNamespace, this.urlInsert.bind( this, true ) );
this.elements.url.submit_insert.on( 'click' + this.eventNamespace, this.urlInsert.bind( this, false ) );
},
/**
*
*/
show: function() {
this.element.lsModal( 'show' );
},
/**
*
*/
hide: function() {
this.element.lsModal( 'hide' );
},
/**
*
*/
getSettings: function() {
return this.elements.blocks
.filter( ':visible' )
.find( 'form' )
.serializeJSON();
},
/**
* Вставляет выделенные файлы в редактор
*/
insertFiles: function( url, params, files ) {
if ( ! files.length ) return;
// Формируем список ID файлов
var ids = $.map( files, function ( file ) {
return $( file ).lsUploaderFile( 'getProperty', 'id' );
});
ls.ajax.load( url, $.extend( true, {}, { ids: ids }, params || {} ), function( response ) {
if ( response.bStateError ) {
ls.msg.error( response.sMsgTitle, response.sMsg );
} else {
this.option( 'editor' ).lsEditor( 'insert', response.sTextResult );
this.element.lsModal( 'hide' );
}
}.bind( this ));
},
/**
*
*/
activateInfoBlock: function( file ) {
this.elements.blocks.hide();
var block = this.elements.blocks.filter( '[data-type=' + this.getActiveTabName() + ']' ).show();
// Показываем блок настроек только для активного типа файла
this.elements.blocks
.filter( '[data-filetype]' )
.filter( ':not([data-filetype=' + file.lsUploaderFile( 'getProperty', 'type' ) + '])' )
.hide();
// Обновляем настройки
if ( file.lsUploaderFile( 'getProperty', 'type' ) == '1' ) {
var sizes = block.find( 'select[name=size]' );
sizes.find( 'option:not([value=original])' ).remove();
sizes.append($.map( file.data('mediaImageSizes'), function ( v, k ) {
// Расчитываем пропорциональную высоту изображения
var height = v.h || parseInt( v.w * file.lsUploaderFile( 'getProperty', 'height' ) / file.lsUploaderFile( 'getProperty', 'width' ) );
return '<option value="' + v.w + 'x' + ( v.h ? v.h : '' ) + ( v.crop ? 'crop' : '' ) + '">' + v.w + ' × ' + height + '</option>';
}).join( '' ));
}
// TODO: Add hook
},
/**
*
*/
getActiveTabName: function() {
return this.elements.tabs.filter( '.active' ).eq( 0 ).data( 'media-name' );
},
//
// INSERT FROM URL
//
/**
*
*/
onUrlTypeChange: function ( event ) {
this.elements.url.blocks.hide();
this.elements.url.blocks.filter( '[data-filetype=' + this.elements.url.type.val() + ']' ).show();
this.elements.url.url.val( '' );
this.elements.url.image_preview.hide().empty();
},
/**
*
*/
onUrlChange: function ( event ) {
var _this = this,
url = this.elements.url.url.val();
if ( this.elements.url.type.val() == 1 ) {
$('<img />', {
src: url,
style: 'max-width: 50%',
error: function () {
_this.elements.url.image_preview.hide().empty();
},
load: function () {
_this.elements.url.image_preview.show().html( $( this ) );
}
});
}
},
/**
*
*/
urlInsert: function ( upload ) {
var upload = upload || false,
params = $.extend(
{},
{ upload: upload },
this.elements.url.form.serializeJSON(),
this.elements.url.blocks.filter( ':visible' ).find('form').serializeJSON(),
this.elements.uploader.lsUploader( 'option', 'params' )
);
ls.ajax.load( this.option( 'urls.url_upload' ), params, function ( response ) {
if ( response.bStateError ) {
ls.msg.error( response.sMsgTitle, response.sMsg );
} else {
this.option( 'editor' ).lsEditor( 'insert', response.sText );
this.element.lsModal( 'hide' );
this.elements.uploader.lsUploader( 'getElement', 'list' ).lsUploaderFileList( 'load' );
}
}.bind( this ), {
// TODO: Fix validation
validate: false,
submitButton: this.elements.url[ upload ? 'submit_upload' : 'submit_insert' ]
});
}
});
})(jQuery);

View file

@ -0,0 +1,11 @@
{**
* Media content
*}
<div class="media clearfix {$smarty.local.classes}">
{component 'tabs' classes='media-nav js-modal-tabs' mods='align-left' tabs=[
[ 'text' => {lang 'media.nav.insert'}, 'body' => {include './panes/pane.insert.tpl'}, 'classes' => 'js-tab-show-gallery', 'attributes' => 'data-media-name="insert"' ],
[ 'text' => {lang 'media.nav.photoset'}, 'body' => {include './panes/pane.photoset.tpl'}, 'classes' => 'js-tab-show-gallery', 'attributes' => 'data-media-name="photoset"' ],
[ 'text' => {lang 'media.nav.url'}, 'body' => {include './panes/pane.url.tpl'}, 'attributes' => 'data-media-name="url"' ]
]}
</div>

View file

@ -0,0 +1,14 @@
{**
* Загрузка медиа-файлов
*}
{extends 'Component@modal.modal'}
{block 'modal_options' append}
{$mods = "$mods media"}
{$classes = "$classes js-modal-media"}
{$title = {lang name='media.title'}}
{$options = array_merge( $options|default:[], [ 'center' => 'false' ] )}
{$showFooter = false}
{$body = {include './media-content.tpl'}}
{/block}

View file

@ -0,0 +1,22 @@
{extends './pane.tpl'}
{block 'media_pane_options' append}
{$id = 'tab-media-insert'}
{/block}
{block 'media_pane_content'}
{include './../uploader/uploader.tpl'
attributes = [ 'id' => 'media-uploader' ]
classes = 'js-media-uploader'
targetParams = $aTargetParams
targetType = $sMediaTargetType
targetId = $sMediaTargetId
targetTmp = $sMediaTargetTmp}
{/block}
{block 'media_pane_footer' prepend}
{component 'button'
mods = 'primary'
classes = 'js-media-insert-submit'
text = {lang name='media.insert.submit'}}
{/block}

View file

@ -0,0 +1,12 @@
{extends './pane.tpl'}
{block 'media_pane_options' append}
{$id = 'tab-media-photoset'}
{/block}
{block 'media_pane_footer' prepend}
{component 'button'
mods = 'primary'
classes = 'js-media-photoset-submit'
text = {lang name='media.photoset.submit'}}
{/block}

View file

@ -0,0 +1,23 @@
{extends './pane.tpl'}
{block 'media_pane_options' append}
{$id = 'tab-media-preview'}
{/block}
{block 'media_pane_content'}
{if $aTargetItems}
{foreach $aTargetItems as $oTarget}
<p class="mb-20">
<a href="#" class="button" onclick="ls.media.removePreviewFile({$oTarget->getMediaId()}); return false;">Удалить превью</a>
</p>
{$aPreview = $oTarget->getPreviewImageItemsWebPath()}
{foreach $aPreview as $sPreviewFile}
<img src="{$sPreviewFile}" alt=""><br>
{/foreach}
{/foreach}
{else}
Превью можно <a href="#" onclick="jQuery('.js-tab-show-gallery').first().click(); return false;">выбрать из галереи</a>.
{/if}
{/block}

View file

@ -0,0 +1,11 @@
{block 'media_pane_options'}
{$id = $smarty.local.id}
{/block}
<div class="media-pane-content js-media-pane-content">
{block 'media_pane_content'}{/block}
</div>
<div class="media-pane-footer">
{block 'media_pane_footer'}{/block}
</div>

View file

@ -0,0 +1,44 @@
{extends './pane.tpl'}
{block 'media_pane_options' append}
{$id = 'tab-media-url'}
{/block}
{block 'media_pane_content'}
<form method="post" action="" enctype="multipart/form-data" class="mb-20 js-media-url-form">
{* Типы файлов *}
{* TODO: Add hook *}
{component 'field' template='select'
name = 'filetype'
label = 'Type'
inputClasses = 'width-300 js-media-url-type'
items = [
[ 'value' => '1', 'text' => 'Image' ]
]}
{* Ссылка *}
{component 'field' template='text'
name = 'url'
value = 'http://'
inputClasses = 'js-media-url-form-url'
label = {lang 'media.url.fields.url.label'}}
</form>
<div class="mb-15 js-media-url-image-preview" style="display: none"></div>
<div class="js-media-url-settings-blocks">
{include '../uploader/uploader-block.insert.image.tpl' useSizes=false}
</div>
{/block}
{block 'media_pane_footer' prepend}
{component 'button'
mods = 'primary'
classes = 'js-media-url-submit-insert'
text = {lang 'media.url.submit_insert'}}
{component 'button'
mods = 'primary'
classes = 'js-media-url-submit-upload'
text = {lang 'media.url.submit_upload'}}
{/block}

View file

@ -0,0 +1,34 @@
{**
* Опции вставки
*
* @param boolean $useSizes
*}
{capture 'block_content'}
<form method="post" action="" enctype="multipart/form-data">
{* Выравнивание *}
{component 'field' template='select'
name = 'align'
label = {lang name='media.image_align.title'}
items = [
[ 'value' => '', 'text' => {lang name='media.image_align.no'} ],
[ 'value' => 'left', 'text' => {lang name='media.image_align.left'} ],
[ 'value' => 'right', 'text' => {lang name='media.image_align.right'} ],
[ 'value' => 'center', 'text' => {lang name='media.image_align.center'} ]
]}
{* Размер *}
{if $smarty.local.useSizes|default:true}
{component 'field' template='select'
name = 'size'
label = {lang name='media.insert.settings.fields.size.label'}
items = [[ 'value' => 'original', 'text' => {lang name='media.insert.settings.fields.size.original'} ]]}
{/if}
</form>
{/capture}
{component 'uploader' template='block'
title = {lang 'media.insert.settings.title'}
content = $smarty.capture.block_content
classes = 'js-media-info-block'
attributes = [ 'data-type' => 'insert', 'data-filetype' => '1' ]}

View file

@ -0,0 +1,24 @@
{**
* Опции фотосета
*}
{capture 'block_content'}
<form method="post" action="" enctype="multipart/form-data">
{* Показывать ленту с превьюшками *}
{component 'field' template='checkbox'
name = 'use_thumbs'
checked = true
label = {lang name='media.photoset.settings.fields.use_thumbs.label'}}
{* Показывать описания фотографий *}
{component 'field' template='checkbox'
name = 'show_caption'
label = {lang name='media.photoset.settings.fields.show_caption.label'}}
</form>
{/capture}
{component 'uploader' template='block'
title = {lang 'media.photoset.settings.title'}
content = $smarty.capture.block_content
classes = 'js-media-info-block'
attributes = [ 'data-type' => 'photoset' ]}

View file

@ -0,0 +1,17 @@
{extends 'Component@uploader.uploader'}
{block 'uploader_options' append}
{$attributes = array_merge( $attributes|default:[], [
'data-param-target_type' => {json var=$smarty.local.targetType},
'data-param-target_id' => {json var=$smarty.local.targetId},
'data-param-target_tmp' => {json var=$smarty.local.targetTmp}
])}
{/block}
{block 'uploader_aside' append}
{* Основные настройки *}
{include './uploader-block.insert.image.tpl'}
{* Опции фотосета *}
{include './uploader-block.photoset.tpl'}
{/block}

View file

@ -0,0 +1 @@
# Компонент user-list-add

View file

@ -0,0 +1,20 @@
{
"name": "user-list-add",
"version": "1.0.0",
"dependencies": {
"ls-component": "*",
"user": "*",
"field": "*"
},
"templates": {
"item": "item.tpl",
"list": "list.tpl",
"user-list-add": "user-list-add.tpl"
},
"scripts": {
"user-list-add": "js/user-list-add.js"
},
"styles": {
"user-list-add": "css/user-list-add.css"
}
}

View file

@ -0,0 +1,25 @@
/**
* Список пользователей
*/
.user-list-add {
margin-bottom: 15px;
}
.user-list-add:last-child {
margin-bottom: 0;
}
.user-list-add-title {
font-size: 18px;
margin-bottom: 15px;
}
.user-list-add-form {
padding: 20px;
margin-bottom: 5px;
background: #f7f7f7;
border: 1px solid #eee;
}
.user-list-add-note {
margin-bottom: 20px;
color: #aaa;
font-size: 13px;
}

View file

@ -0,0 +1,20 @@
{extends 'Component@user.user-list-small-item'}
{block 'user_list_small_item_options' append}
{block 'user_list_add_item_options'}{/block}
{/block}
{block 'user_list_small_item_content' append}
{block 'user_list_add_item_content'}
{* Действия *}
{if $smarty.local.showActions}
<ul class="{$component}-actions js-user-list-small-actions">
{block 'user_list_add_item_actions'}
{if $smarty.local.showRemove|default:true}
<li class="icon-remove js-user-list-add-user-remove" title="{$aLang.common.remove}" data-user-id="{$userId}"></li>
{/if}
{/block}
</ul>
{/if}
{/block}
{/block}

View file

@ -0,0 +1,176 @@
/**
* Пополняемый список пользователей
*
* @module ls/user-list-add
*
* @license GNU General Public License, version 2
* @copyright 2013 OOO "ЛС-СОФТ" {@link http://livestreetcms.com}
* @author Denis Shakhov <denis.shakhov@gmail.com>
*/
(function($) {
"use strict";
$.widget( "livestreet.lsUserListAdd", $.livestreet.lsComponent, {
/**
* Дефолтные опции
*/
options: {
urls: {
add: null,
remove: null
},
// Селекторы
selectors: {
// Блок со списком объектов
list: '.js-user-list-add-users',
// Объект
item: '.js-user-list-small-item',
// Кнопка удаления объекта
item_remove: '.js-user-list-add-user-remove',
// Сообщение о пустом списке
empty: '.js-user-list-small-empty',
// Форма добавления
form: '.js-user-list-add-form',
// Форма добавления
form_text: '.js-user-list-add-form input[type=text]'
},
// Анимация при скрытии объекта
hide: {
effect: 'slide',
duration: 200,
direction: 'left'
},
// Ajax параметры
params: {}
},
/**
* Конструктор
*
* @constructor
* @private
*/
_create: function () {
var _this = this;
this._super();
// Удаление пользователя из списка
this.elements.list.on('click' + this.eventNamespace, this.options.selectors.item_remove, function (e) {
_this.remove( $(this).data('user-id') );
e.preventDefault();
});
// Добавление пользователя в список
this.elements.form.on('submit' + this.eventNamespace, function (e) {
var items = _this.getItems();
if ( items.length ) {
ls.utils.formLock( _this.elements.form );
_this.add( items );
}
e.preventDefault();
});
},
/**
* Получает объекты для добавления
*
* @return {Array} Массив с объектами
*/
getItems: function () {
return $.map( this.elements.form_text.val().split( ',' ), function( item ) {
return $.trim( item ) || null;
});
},
/**
* Добавление объекта
*/
add: function( users ) {
if ( ! users ) return;
this._load( 'add', { 'users': users }, '_onAdd' );
},
/**
* Коллбэк вызываемый при добавлении объекта
*/
_onAdd: function ( response ) {
var users = this._getUsersAll();
// Составляем список добавляемых объектов
var itemsHtml = $.map( response.users, function ( item ) {
if ( item.bStateError ) {
ls.msg.error( null, item.sMsg );
} else {
ls.msg.notice( null, ls.lang.get( 'common.success.add' ) );
this._trigger( "afteruseradd", null, { context: this, item: item, response: response } );
this._onUserAdd( item );
// Не добавляем юзера если он уже есть в списке
return users.filter( '[data-user-id=' + item.user_id + ']' ).length ? null : item.html;
}
}.bind(this)).join('');
if ( itemsHtml ) {
this.elements.empty.hide();
this.elements.list.show().prepend( itemsHtml );
}
ls.utils.formUnlock( this.elements.form );
this.elements.form_text.focus().val('');
this._trigger( "afteradd", null, { context: this, response: response } );
},
_onUserAdd: function (item) {
return;
},
/**
* Удаление объекта
*/
remove: function( userId ) {
if ( ! this.options.urls.remove ) return;
var _this = this;
this._load( 'remove', { user_id: userId }, function( response ) {
this._hide( this._getUserById( userId ), this.options.hide, function () {
$( this ).remove();
// Скрываем список если объектов в нем больше нет
if ( ! _this.elements.list.find( _this.options.selectors.item ).length ) {
_this.elements.list.hide();
_this.elements.empty.show();
}
});
this._trigger( "afterremove", null, { context: this, response: response } );
});
},
/**
* Получает пользователя по ID
*
* @private
* @param {Number} userId , ID объекта
* @return {jQuery} Объект
*/
_getUserById: function( userId ) {
return this.elements.list.find( this.options.selectors.item + '[data-user-id=' + userId + ']' );
},
/**
* Получает всех пользователей
*/
_getUsersAll: function() {
return this.elements.list.find( this.options.selectors.item );
}
});
})(jQuery);

View file

@ -0,0 +1,17 @@
{**
* Список пользователей
*
* @param object $users
* @param string $title
* @param boolean $hideableEmptyAlert
* @param boolean $show
* @param array $exclude
*}
{extends 'Component@user.user-list-small'}
{block 'user_list_small_item'}
{block 'user_list_add_item'}
{component 'user-list-add' template='item' user=$user showActions=true}
{/block}
{/block}

View file

@ -0,0 +1,57 @@
{**
* Пополняемый список пользователей
*
* @param array $users
* @param string $title
* @param string $note
* @param boolean $editable
*
* @param string $classes
* @param array $attributes
* @param array $mods
*}
{* Название компонента *}
{$component = 'user-list-add'}
{* Форма добавления *}
<div class="{$component} {cmods name=$component mods=$smarty.local.mods} {$smarty.local.classes}"
{cattr list=$smarty.local.attributes}>
{* Заголовок *}
{if $smarty.local.title}
<h3 class="{$component}-title">{$smarty.local.title}</h3>
{/if}
{* Описание *}
{if $smarty.local.note}
<p class="{$component}-note">{$smarty.local.note}</p>
{/if}
{* Форма добавления *}
{if $smarty.local.editable|default:true}
<form class="{$component}-form js-{$component}-form">
{$uid = "js-$component-form-users-"|cat:rand(0, 10e10)}
{component 'field' template='text'
name = 'add'
inputClasses = "autocomplete-users-sep {$uid}"
label = $aLang.user_list_add.form.fields.add.label
note = "<a href=\"#\" class=\"link-dotted\" data-type=\"modal-toggle\" data-modal-url=\"{router page='ajax/modal-friend-list'}\" data-param-selectable=\"true\" data-param-target=\".{$uid}\">Выбрать из списка друзей</a>"}
{component 'button' text=$aLang.common.add mods='primary' classes="js-$component-form-submit"}
</form>
{/if}
{* Список пользователей *}
{* TODO: Изменить порядок вывода - сначало новые *}
{block 'user_list_add_list'}
{component 'user-list-add' template='list'
hideableEmptyAlert = true
users = $smarty.local.users
showActions = true
show = !! $smarty.local.users
classes = "js-$component-users"
itemClasses = "js-$component-user"}
{/block}
</div>