Commit 1879bcf6 authored by Evgeny Talagaev's avatar Evgeny Talagaev

start project

parents
Pipeline #87 failed with stages
# Универсальные настройки редактора
#
# http://editorconfig.org
# https://habrahabr.ru/post/220131/
# https://packagecontrol.io/packages/EditorConfig
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
src/js/head-script.js
{
"parser": "babel-eslint",
"extends": "eslint:recommended",
"globals": {
"require": true,
},
"rules": {
"strict": 0
}
}
* text=auto
design/
build/*
!build/readme.md
src/pug/mixins.pug
src/js/entry.js
src/scss/style.scss
src/blocks/sprite-svg/img/sprite.svg
src/blocks/sprite-png/sprite-png.scss
src/blocks/sprite-png/img/sprite-*.png
__MACOSX
.Saved/
_arc/
bower_components/
node_modules/
.vscode/
.publish/
.DS_Store
*.log
*.sublime-*
*.rar
*.zip
.idea
ex_files
Thumbs.db
{
"validateExtensions": true,
"validateDivTags": true,
"validateAttributeSeparator": { "separator": ", ", "multiLineSeparator": ",\n " },
"validateAttributeQuoteMarks": "'",
"requireSpecificAttributes": [ { "img": [ "src", "alt" ] } ],
"requireSpaceAfterCodeOperator": true,
"requireLowerCaseTags": true,
"requireClassLiteralsBeforeIdLiterals": true,
"requireClassLiteralsBeforeAttributes": true,
"disallowTrailingSpaces": true,
"disallowSpacesInsideAttributeBrackets": true,
"disallowLegacyMixinCall": true,
"disallowDuplicateAttributes": true
}
src/scss/style.scss
src/blocks/sprite-png/sprite-png.scss
This diff is collapsed.
language: node_js
node_js:
- "14.15"
notifications:
email: true
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
This diff is collapsed.
**Не редактируйте файлы в этой папке**, все такие изменения будут затёрты при следующей сборке проекта.
Для любых изменений создавайте новые файлы (стили, скрипты etc.) или обратитесь к автору.
Проект собирается автоматикой из исходников, хранящихся в репозитории. Адрес можно узнать у автора.
/* global module */
let config = {
'notGetBlocks': [
'blocks-demo.html',
],
'ignoredBlocks': [
'no-js',
],
'alwaysAddBlocks': [
// 'sprite-svg',
// 'sprite-png',
],
'addStyleBefore': [
'src/scss/variables.scss',
'src/scss/mixins.scss',
// 'somePackage/dist/somePackage.css', // для 'node_modules/somePackage/dist/somePackage.css',
],
'addStyleAfter': [
// 'src/scss/print.scss',
],
'addJsBefore': [
// 'somePackage/dist/somePackage.js', // для 'node_modules/somePackage/dist/somePackage.js',
],
'addJsAfter': [
'./script.js',
],
'addAssets': {
'src/img/*.{png,svg,jpg,jpeg}': 'img/',
'src/fonts/*.{woff,woff2}': 'fonts/',
// 'src/favicon/*.{png,ico,svg,xml,webmanifest}': 'img/favicon',
// 'node_modules/somePackage/images/*.{png,svg,jpg,jpeg}': 'img/',
},
'dir': {
'src': 'src/',
'build': 'build/',
'blocks': 'src/blocks/'
}
};
module.exports = config;
/* eslint-disable */
'use strict';
// Генератор файлов блока
// Использование: node createBlock.js [имя блока] [доп. расширения через пробел]
const fs = require('fs');
const projectConfig = require('./config.js');
const dir = projectConfig.dir;
const mkdirp = require('mkdirp');
const blockName = process.argv[2];
const defaultExtensions = ['scss', 'img']; // расширения по умолчанию
const extensions = uniqueArray(defaultExtensions.concat(process.argv.slice(3)));
// Если есть имя блока
if (blockName) {
const dirPath = `${dir.blocks}${blockName}/`; // полный путь к создаваемой папке блока
const made = mkdirp.sync(dirPath);
console.log(`[NTH] Создание папки: ${made}`);
// Обходим массив расширений и создаем файлы, если они еще не созданы
extensions.forEach((extension) => {
const filePath = `${dirPath + blockName}.${extension}`; // полный путь к создаваемому файлу
let fileContent = ''; // будущий контент файла
let fileCreateMsg = ''; // будущее сообщение в консоли при создании файла
if (extension === 'scss') {
fileContent = `// В этом файле должны быть стили для БЭМ-блока ${blockName}, его элементов,\n// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...\n// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority\n\n.${blockName} {\n\n $block-name: &; // #{$block-name}__element\n}\n`;
// fileCreateMsg = '';
}
else if (extension === 'js') {
fileContent = `/* global document */\n\n// import ready from 'Utils/documentReady.js';\n\n// ready(function() {\n// \n// });\n`;
}
else if (extension === 'md') {
fileContent = '';
}
else if (extension === 'pug') {
fileContent = `//- Все примеси в этом файле должны начинаться c имени блока (${blockName})\n\nmixin ${blockName}(text, mods)\n\n //- Принимает:\n //- text {string} - текст\n //- mods {string} - список модификаторов\n //- Вызов:\n +${blockName}('Текст', 'some-mod')\n\n -\n // список модификаторов\n var allMods = '';\n if(typeof(mods) !== 'undefined' && mods) {\n var modsList = mods.split(',');\n for (var i = 0; i < modsList.length; i++) {\n allMods = allMods + ' ${blockName}--' + modsList[i].trim();\n }\n }\n\n .${blockName}(class=allMods)&attributes(attributes)\n .${blockName}__inner\n block\n`;
}
else if (extension === 'img') {
const imgFolder = `${dirPath}img/`;
if (fileExist(imgFolder) === false) {
const made = mkdirp.sync(imgFolder);
console.log(`[NTH] Создание папки: ${made}`);
} else {
console.log(`[NTH] Папка ${imgFolder} НЕ создана (уже существует) `);
}
}
if (fileExist(filePath) === false && extension !== 'img' && extension !== 'md') {
fs.writeFile(filePath, fileContent, (err) => {
if (err) {
return console.log(`[NTH] Файл НЕ создан: ${err}`);
}
console.log(`[NTH] Файл создан: ${filePath}`);
if (fileCreateMsg) {
console.warn(fileCreateMsg);
}
});
}
else if (extension !== 'img' && extension !== 'md') {
console.log(`[NTH] Файл НЕ создан: ${filePath} (уже существует)`);
}
else if (extension === 'md') {
fs.writeFile(`${dirPath}readme.md`, fileContent, (err) => {
if (err) {
return console.log(`[NTH] Файл НЕ создан: ${err}`);
}
console.log(`[NTH] Файл создан: ${dirPath}readme.md`);
if (fileCreateMsg) {
console.warn(fileCreateMsg);
}
});
}
});
} else {
console.log('[NTH] Отмена операции: не указан блок');
}
function uniqueArray(arr) {
const objectTemp = {};
for (let i = 0; i < arr.length; i++) {
const str = arr[i];
objectTemp[str] = true;
}
return Object.keys(objectTemp);
}
function fileExist(path) {
const fs = require('fs');
try {
fs.statSync(path);
} catch (err) {
return !(err && err.code === 'ENOENT');
}
}
This diff is collapsed.
This diff is collapsed.
{
"name": "NTH-start-project",
"version": "4.1.0",
"description": "Startkit for HTML/CSS/JS pages layout.",
"author": "Nikolay Gromov",
"license": "WTFPL",
"repository": {
"type": "git",
"url": "https://github.com/nicothin/NTH-start-project.git"
},
"scripts": {
"test": "npm run test:pug && npm run test:style && npm run test:js",
"test:pug": "glob-exec \"src/**/*.pug\" -- \"pug-lint {{files.join(' ')}}\"",
"test:style": "stylelint \"src/**/*.scss\" --syntax=scss",
"test:js": "eslint src/blocks/**/*.js src/js/**/*.js",
"start": "gulp",
"wlib": "cross-env BUILD_LIBRARY=true gulp",
"build": "cross-env MODE=production gulp build",
"lint-staged": "lint-staged"
},
"browserslist": [
"> 1%",
"last 2 versions"
],
"lint-staged": {
"*.{js,pug,md}": "editorconfig-cli",
"*.pug": "pug-lint",
"*.scss": "stylelint --syntax=scss",
"*.js": "eslint"
},
"devDependencies": {
"@babel/core": "^7.6.2",
"@babel/preset-env": "^7.6.2",
"@htmlacademy/editorconfig-cli": "^1.0.0",
"autoprefixer": "^10.2.4",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"browser-sync": "^2.26.7",
"cpy": "^8.1.0",
"cross-env": "^7.0.2",
"css-mqpacker": "^7.0.0",
"del": "^6.0.0",
"eslint": "^7.19.0",
"get-classes-from-html": "^1.0.1",
"gh-pages": "^3.1.0",
"glob-exec": "^0.1.1",
"gulp": "^4.0.2",
"gulp-concat": "^2.6.1",
"gulp-csso": "^4.0.1",
"gulp-debug": "^4.0.0",
"gulp-imagemin": "^7.1.0",
"gulp-plumber": "^1.2.1",
"gulp-postcss": "^9.0.0",
"gulp-pretty-html": "^2.0.10",
"gulp-pug": "^4.0.1",
"gulp-rename": "^2.0.0",
"gulp-replace": "^1.0.0",
"gulp-sass": "^4.0.1",
"gulp-sourcemaps": "^3.0.0",
"gulp-svgmin": "^3.0.0",
"gulp-svgstore": "^7.0.1",
"gulp.spritesmith": "^6.11.0",
"husky": "^4.2.3",
"jstransformer-markdown-it": "^2.0.0",
"lint-staged": "^10.0.9",
"merge-stream": "^2.0.0",
"mkdirp": "^1.0.3",
"path": "^0.12.7",
"postcss": "^8.2.5",
"postcss-import": "^14.0.0",
"postcss-inline-svg": "^5.0.0",
"pug-lint": "^2.6.0",
"stylelint": "^13.2.1",
"stylelint-order": "^4.0.0",
"stylelint-selector-bem-pattern": "^2.1.0",
"through2": "^4.0.2",
"vinyl-buffer": "^1.0.1",
"webpack": "^5.21.1",
"webpack-stream": "^6.1.2"
},
"dependencies": {
"autosize": "^4.0.2",
"baron": "^3.0.3",
"choices.js": "^9.0.1",
"closest": "0.0.1",
"svg4everybody": "^2.1.8"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
}
//- Все примеси в этом файле должны начинаться c имени блока (alert)
mixin alert(title, mods)
//- Принимает:
//- title {string} - заголовок
//- mods {string} - стилевые модификаторы
//- Вызов:
+alert()
p Текст
+alert('Внимание', 'warning, some-mod-name')
p Предупреждение
+close('Закрыть')(class='alert__close')
+alert('Внимание', 'danger')
p Проблема
+close('Закрыть')(class='alert__close')
+alert('Внимание', 'success')
p Успех
+close('Закрыть')(class='alert__close')
-
//- список модификаторов
var allMods = '';
if(typeof(mods) === 'string') {
var modsList = mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' alert--' + modsList[i].trim();
}
}
.alert(class=allMods, role='alert')&attributes(attributes)
if (typeof(title) !== 'undefined' && title !== '')
h4.alert__header!= title
block
// В этом файле должны быть стили для БЭМ-блока alert, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.alert {
position: relative;
margin-top: $typo-margin-vertical;
margin-bottom: $typo-margin-vertical;
padding: 0.7em 1.5em 0.7em 1.4em;
border-radius: $border-radius;
color: $text-color;
background-color: lighten($color-warning, 30%);
& > * { // stylelint-disable-line selector-combinator-blacklist
margin-top: 0;
margin-bottom: 0;
}
& > * + * { // stylelint-disable-line selector-combinator-blacklist
margin-top: $typo-margin-vertical;
}
&__header {
display: block;
margin-top: 0;
margin-bottom: 0.5rem;
font-size: $font-size-h3;
font-weight: 400;
line-height: 1.2;
color: inherit;
& + * {
margin-top: 0;
}
}
&__close.close {
position: absolute;
top: 0;
right: 0;
margin: 0;
}
// &--warning {
// background-color: lighten($color-warning, 30%);
// color: $text-color;
// }
// &--danger {
// background-color: $color-danger;
// color: #fff;
// a {
// color: inherit;
// text-decoration: underline;
// &:hover,
// &:focus {
// text-decoration: none;
// }
// }
// }
// &--success {
// background-color: $color-success;
// color: #fff;
// a {
// color: inherit;
// text-decoration: underline;
// &:hover,
// &:focus {
// text-decoration: none;
// }
// }
// }
}
import ready from 'Utils/documentReady.js';
import baron from 'baron';
ready(function(){
baron({
root: '#baron-demo',
scroller: '.baron__scroller',
bar: '.baron__bar',
scrollingCls: 'baron--scrolling',
draggingCls: 'baron--dragging',
barOnCls: 'baron--scrollbar',
});
});
//- Блок для кастомного скролла baron: https://github.com/Diokuz/baron
//- Нуждается в соотв. зависимости: https://www.npmjs.com/package/baron
//- (должна быть указана в ./projectConfig.json, есть по умолчанию)
//- ВНИМАНИЕ! Помимо вызова примеси, нужно включить baron на блоке! см. blocks/baron/baron.js
mixin baron(id, mods, tag)
//- Принимает:
//- id {string} - ID этого конкретного блока с кастомным скроллом
//- mods {string} - список модификаторов
//- tag {string} - тег
//- Вызов:
+baron('some-mod')(style='width:100px; height: 100px')
p Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dicta aliquid nemo, sit possimus, eveniet tempore minus, doloremque incidunt, nihil quos aperiam ab iure quia. Ipsa sit porro incidunt, sed!
.some-block
+baron('', 'section')(class='some-block__demo')
p Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dicta aliquid nemo, sit possimus, eveniet tempore minus, doloremque incidunt, nihil quos aperiam ab iure quia. Ipsa sit porro incidunt, sed!
-
// список модификаторов
var allMods = '';
if(typeof(mods) !== 'undefined' && mods) {
var modsList = mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' baron--' + modsList[i].trim();
}
}
var parentTag = 'div';
if(typeof(tag) !== 'undefined' && tag) {
parentTag = tag;
}
#{parentTag}.baron(id=id, class=allMods)&attributes(attributes)
.baron__scroller
block
.baron__track
//- .baron__control.baron__up ▲
.baron__free
.baron__bar
//- .baron__control.baron__down ▼
// В этом файле должны быть стили для БЭМ-блока baron, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Оригинальные темы оформления: https://github.com/Diokuz/baron/blob/master/skins/styles.css
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.baron {
$block-name: &; // #{$block-name}__element
$scrollbar-width: 14px;
position: relative;
overflow: hidden;
&__scroller {
box-sizing: border-box;
width: 100%;
height: 100%;
overflow-y: scroll;
margin: 0;
border: 0;
padding: 0;
padding-right: $scrollbar-width;
-webkit-overflow-scrolling: touch; // remove line to customize scrollbar in iOs
&::-webkit-scrollbar {
width: 0;
height: 0;
}
}
&__track {
position: absolute;
z-index: 3;
top: 0;
right: 0;
bottom: 0;
width: $scrollbar-width;
opacity: 0;
background-color: $gray-lightest;
#{$block-name}--scrollbar & {
opacity: 1;
}
}
&__free {
position: absolute;
top: 0;
right: 0;
bottom: 0;
}
&__bar {
box-sizing: border-box;
display: none;
position: absolute;
z-index: 1;
left: ($scrollbar-width * -1 + 1);
width: ($scrollbar-width - 2);
opacity: 1;
pointer-events: auto;
background: $gray-lighten;
#{$block-name}--scrollbar & {
display: block;
}
#{$block-name}--dragging &,
&:hover {
background-color: $gray-light;
}
}
&__control {
display: none;
}
}
Кастомный скролл c [baron](https://www.npmjs.com/package/baron). [Демо кастомного скролла](http://diokuz.github.io/baron/).
Для каждого блока нужно включать baron в `blocks/baron/baron.js`.
Оставлена одна тема оформления (используется по умолчанию). Стили берутся из `blocks/baron/baron.scss`. См. [стилизацию оригинальных тем](https://github.com/Diokuz/baron/blob/master/skins/styles.css).
require('../main-nav/main-nav.js');
require('../burger/burger.js');
require('../field-text/field-text.js');
require('../field-file/field-file.js');
require('../field-num/field-num.js');
require('../field-select/field-select.js');
require('../scroll-link/scroll-link.js');
require('../to-top/to-top.js');
require('../off-canvas/off-canvas.js');
require('../modal/modal.js');
require('../tabs/tabs.js');
require('../baron/baron.js');
require('../form-validation/form-validation.js');
require('../../js/script.js');
/* stylelint-disable */
@import "../../scss/variables.scss";
@import "../../scss/mixins.scss";
@import "../alert/alert.scss";
@import "../baron/baron.scss";
@import "../breadcrumbs/breadcrumbs.scss";
@import "../btn/btn.scss";
@import "../burger/burger.scss";
@import "../close/close.scss";
@import "../code/code.scss";
@import "../comment/comment.scss";
@import "../embed-responsive/embed-responsive.scss";
@import "../field-actions/field-actions.scss";
@import "../field-checkbox/field-checkbox.scss";
@import "../field-file/field-file.scss";
@import "../field-radio/field-radio.scss";
@import "../field-range/field-range.scss";
@import "../field-select/field-select.scss";
@import "../field-text/field-text.scss";
@import "../field-num/field-num.scss";
@import "../field-toggler/field-toggler.scss";
@import "../form/form.scss";
@import "../label/label.scss";
@import "../loader/loader.scss";
@import "../logo/logo.scss";
@import "../main-nav/main-nav.scss";
@import "../modal/modal.scss";
@import "../off-canvas/off-canvas.scss";
@import "../or/or.scss";
@import "../page/page.scss";
@import "../page-footer/page-footer.scss";
@import "../page-header/page-header.scss";
@import "../pagination/pagination.scss";
@import "../progress/progress.scss";
// @import "../sprite-png/sprite-png.scss";
@import "../table/table.scss";
@import "../table-responsive/table-responsive.scss";
@import "../tabs/tabs.scss";
@import "../social/social.scss";
@import "../to-top/to-top.scss";
@import "../card/card.scss";
@import "../../scss/print.scss";
.blocks-library {
$block-name: &; // #{$block-name}__element
&__title {
font-weight: 400;
}
&__item {
position: relative;
padding: 1rem;
border: 2px dashed $gray-lighter;
margin: 2.5rem 0;
@media (min-width: $screen-md) {
padding: 1.5rem 2rem 0.5rem 3rem;
}
&:before {
content: '.' attr(data-id);
position: absolute;
top: -1.2rem;
left: 0;
font-weight: 700;
text-transform: lowercase;
font-size: 1em;
line-height: 1;
height: 1em;
text-align: right;
white-space: nowrap;
color: $gray-lighter;
@media (min-width: $screen-md) {
left: 1.4rem;
top: 20.5rem;
width: 21rem;
transform: rotate(-90deg);
transform-origin: 0 100%;
}
}
}
&__item-title {
margin: 0 0 0.5rem;
}
&__item-title-link {
display: inline-block;
margin-right: 0.8rem;
svg {
display: block;
fill: $gray;
}
}
&__code-wrapper {
position: relative;
}
&__code-show-trigger {
display: block;
width: 30px;
height: 30px;
cursor: pointer;
margin-bottom: 0.3em;
@media (min-width: $screen-md) {
position: absolute;
top: -1.65rem;
left: -2.5rem;
}
}
&__code-icon {
display: block;
width: 30px;
height: 3px;
background-color: $gray-lighter;
&:before,
&:after {
position: absolute;
background-color: $gray-lighter;
width: 30px;
height: 3px;
left: 0;
content: '';
}
&:before {
top: 10px;
}
&:after {
top: 20px;
}
}
&__code {
overflow: hidden;
.js & {
max-height: 0;
transition-duration: $transition-time;
transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
}
.js &--shown {
transition-duration: $transition-time;
transition-timing-function: ease-in;
max-height: 1000px;
}
pre {
max-height: 800px;
}
}
&__menu-toggler {
position: fixed !important;
backface-visibility: hidden;
bottom: 0;
left: 0;
background-color: #000 !important;
width: 40px !important;
height: 40px;
padding: 5px !important;
.off-canvas--open & {
display: none;
}
& > span,
& > span:before,
& > span:after {
background-color: #fff !important;
}
}
}
/* stylelint-enable */
Оформление одного блока при описании в библиотеке блоков.
//- Все примеси в этом файле должны начинаться c имени блока (breadcrumbs)
mixin breadcrumbs(mods)
//- Принимает:
//- mods {string} - список модификаторов
//- Вызов:
+breadcrumbs()
+breadcrumbs-item('Главная', '/home')
+breadcrumbs-item('Услуги', '/services')
+breadcrumbs-item('Стриптиз')
-
// список модификаторов
var allMods = '';
if(typeof(mods) !== 'undefined' && mods) {
var modsList = mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' breadcrumbs--' + modsList[i].trim();
}
}
ul.breadcrumbs(class=allMods, aria-label='Breadcrumb', role='navigation')&attributes(attributes)
block
mixin breadcrumbs-item(text, href, mods)
//- Принимает:
//- text {string} - содержимое пункта
//- href {string} - ссылка этого пункта (если пустая, то это не ссылка, а span)
//- mods {string} - список модификаторов
-
// список модификаторов
var allMods = '';
if(typeof(mods) !== 'undefined' && mods) {
var modsList = mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' breadcrumbs__item--' + modsList[i].trim();
}
}
li.breadcrumbs__item(class=allMods)&attributes(attributes)
if(typeof(href) !== 'undefined' && href)
a(href=href)!= text
else
span!= text
// В этом файле должны быть стили для БЭМ-блока breadcrumbs, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.breadcrumbs {
list-style: none;
margin-top: $typo-margin-vertical;
margin-bottom: $typo-margin-vertical;
padding: 0;
color: $text-color;
a {
color: $text-color;
text-decoration: none;
&:hover,
&:focus {
color: $text-color;
text-decoration: none;
}
}
&__item {
display: inline-block;
margin-right: 0.6em;
white-space: nowrap;
&:not(:last-child):after {
content: '>';
display: inline-block;
margin-left: 0.8em;
color: $gray;
}
}
}
//- Все примеси в этом файле должны начинаться c имени блока (btn)
mixin btn(text, mods, isInput)
//- Принимает:
//- text {string} - текст кнопки
//- mods {string} - список модификаторов
//- isInput {bool} - флаг «это тег input»
//- Вызов:
+btn('Кнопка-ссылка')(href='/') - есть href, это точно ссылка
+btn('Кнопка-input', '', true) - есть флаг isInput, это input
+btn('Кнопка-button', 'success') - нет href, нет isInput — это button
-
// список модификаторов
var allMods = '';
if(typeof(mods) !== 'undefined' && mods) {
var modsList = mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' btn--' + modsList[i].trim();
}
}
//- передан href — это ссылка
if (attributes.href)
a.btn(class=allMods)&attributes(attributes)
block
!= text
//- иначе, если передан isInput и он true, это input
else if (typeof(isInput) !== 'undefined' && isInput)
input.btn(class=allMods, value=text, type='button')&attributes(attributes)
//- иначе это button
else
button.btn(class=allMods)&attributes(attributes)
block
!= text
// В этом файле должны быть стили для БЭМ-блока btn, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.btn {
$block-name: &;
box-sizing: border-box;
display: inline-block;
max-width: 100%;
margin: 0;
border-radius: $border-radius;
border: 1px solid var(--m-color-1);
padding: 15px;
vertical-align: middle;
white-space: nowrap;
text-overflow: ellipsis;
user-select: none;
text-align: center;
font: 600 14px/120% $font-family;
text-decoration: none;
color: #fff;
background-color: var(--m-color-1);
background-image: none;
cursor: pointer;
overflow: hidden;
transition: background-color $transition-time, color $transition-time,
border-color $transition-time;
@media (min-width: $screen-xl) {
padding-left: 30px;
padding-right: 30px;
font-size: 16px;
}
&:focus,
&:hover {
background-color: transparent;
color: var(--m-color-1);
}
&__del {
@media (max-width: ($screen-xl - 1)) {
display: none;
}
}
&--disabled,
&:disabled {
opacity: $opacity;
cursor: not-allowed;
color: $text-color;
background-color: $gray-lightest;
@at-root a.btn--disabled {
pointer-events: none;
}
}
&--ico {
display: inline-flex;
justify-content: center;
align-items: center;
svg {
margin-right: 10px;
rect {
transition: fill $transition-time;
}
}
&:focus,
&:hover {
svg {
rect {
fill: var(--m-color-1);
}
}
}
}
&--w-100 {
width: 100%;
}
&--big {
@media (min-width: $screen-xxl) {
}
@media (min-width: $screen-xl) {
font-size: 20px;
}
}
&--red {
border-color: var(--m-color-3);
background-color: var(--m-color-3);
&:hover,
&:focus {
border-color: var(--m-color-2);
background-color: var(--m-color-2);
color: #fff;
}
}
&--transparent {
border-color: var(--m-color-3);
background-color: transparent;
color: var(--m-color-3);
&:hover,
&:focus {
border-color: var(--m-color-3);
background-color: var(--m-color-3);
color: #fff;
}
}
}
/* global document */
import ready from 'Utils/documentReady.js';
ready(function(){
var burgers = document.querySelectorAll('.burger');
for (var i = 0; i < burgers.length; i++) {
var burger = burgers[i];
burger.addEventListener('click', showBurgerTarget);
}
function showBurgerTarget() {
var targetId = this.getAttribute('data-target-id');
var targetClassToggle = this.getAttribute('data-target-class-toggle');
if (targetId && targetClassToggle) {
this.classList.toggle('burger--close');
document.getElementById(targetId).classList.toggle(targetClassToggle);
document.getElementsByTagName('body')[0].classList.add('of-h');
}
}
});
//- Все примеси в этом файле должны начинаться c имени блока (burger)
mixin burger(label, targetId, switchableClass)
//- Принимает:
//- label {string} - описание, значение атрибута aria-label
//- targetId {string} - атрибут id целевого элемента (без символа #), на котором по клику будет меняться класс
//- switchableClass {string} - класс, добавляемый/убираемый с целевого элемента
//- Вызов:
+burger('Показать пример кода', 'burger-code', 'blocks-library__code--shown')
+burger('Показать ничто')(data-some="SOME")
-
label = label || 'Toggle block ' + targetId
if (typeof(targetId) !== 'undefined' && targetId !== '') attributes['data-target-id'] = targetId
if (typeof(switchableClass) !== 'undefined' && switchableClass !== '') attributes['data-target-class-toggle'] = switchableClass
button.burger(aria-label=label)&attributes(attributes)
span= label
// В этом файле должны быть стили для БЭМ-блока burger, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.burger {
position: relative;
z-index: 1;
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
min-width: 20px;
height: 14px;
border: none;
padding: 0;
background: transparent;
user-select: none;
cursor: pointer;
&:hover {
> span {
background-color: #494f54;
&:before,
&:after {
background-color: #494f54;
}
}
}
> span {
display: block;
position: relative;
width: 100%;
height: $toggler-part-height;
font-size: 0;
color: transparent;
background: #494f54;
transition: background-color $transition-time;
&:before,
&:after {
content: "";
position: absolute;
left: 0;
width: 100%;
height: $toggler-part-height;
background: #494f54;
transform-origin: 50% 50%;
transition: background-color $transition-time;
}
&:before {
top: -7px;
}
&:after {
top: 7px;
}
}
// &--close {
// > span {
// transition: background $transition-time 0s;
// background: transparent;
// &:before,
// &:after {
// top: 0;
// transition: top $transition-time,
// transform $transition-time $transition-time;
// }
// &:before {
// transform: rotate3d(0, 0, 1, 45deg);
// }
// &:after {
// transform: rotate3d(0, 0, 1, -45deg);
// }
// }
// }
}
Анимированное превращение в крестик по добавлению модификатора. <br />
JS блока берет переданный в data-атрибуте идентификатор и, по клику на бургере, добавляет переданный в data-атрибуте класс на элемент с этим модификатором.
//- Все примеси в этом файле должны начинаться c имени блока (card)
mixin card(data, mods)
//- Принимает:
//- data {
//- url: {string}, - адрес для ссылки
//- img: {string}, - адрес картинки товара
//- name: {string}, - название
//- descr: {string}, - описание
//- price: {number}, - цена
//- new: {boolean}, - показать метку «новинка»
//- }
//- mods {string} - модификаторы
//- Вызов:
+card({
url: 'good01',
img: 'img/demo-product.jpg',
name: 'Товар1',
descr: 'Описание1',
price: 7777,
new: true,
}, 'mod')
+card({
img: 'img/demo-product.jpg',
name: 'Товар2',
price: 12000,
})
-
// список модификаторов
var allMods = '';
if(typeof(mods) !== 'undefined' && mods) {
var modsList = mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' card--' + modsList[i].trim();
}
}
var newLabel = (data.new !== 'undefined' && data.new) ? '<div class="card__new">Новинка!</div>' : '';
var price = '' + data.price;
var price = price.replace(/(\d)(?=(\d\d\d)+([^\d]|$))/g, '$1&thinsp;') + ' ₽';
article.card(class=allMods)&attributes(attributes)
!= newLabel
if (data.url !== 'undefined' && data.url)
.card__inner
a.card__img-wrap(href=data.url)
img.card__img(src=data.img, alt=data.name)
h3.card__title
a(href=data.url)!= data.name
if (data.descr !== 'undefined' && data.descr)
p.card__descr!= data.descr
p.card__price!= price
else
.card__inner
span.card__img-wrap
img.card__img(src=data.img, alt=data.name)
h3.card__title!= data.name
if (data.descr !== 'undefined' && data.descr)
p.card__descr!= data.descr
p.card__price!= price
+btn('Купить', '')(class='card__buy')
// В этом файле должны быть стили для БЭМ-блока card, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.card {
$block-name: &; // #{$block-name}__element
display: flex;
flex-direction: column;
align-items: center;
position: relative;
z-index: 0;
min-width: 200px;
padding: 0 15px;
margin-bottom: 30px;
&__inner {
display: flex;
flex-direction: column;
flex-grow: 1;
align-items: center;
position: relative;
width: 100%;
margin-bottom: 20px;
}
&__img-wrap {
display: block;
flex-shrink: 0;
margin-bottom: 20px;
}
&__img {
display: block;
}
&__title {
margin: 0 0 20px;
}
&__new {
position: absolute;
z-index: 1;
top: 0;
left: 50%;
padding: 5px 10px;
line-height: 1;
font-weight: 700;
color: #fff;
background: $color-success;
transform: translateX(-50%) rotate(-10deg);
}
&__price {
flex-shrink: 0;
margin: auto 0 0;
font-size: $font-size-h3;
font-weight: 700;
line-height: 1;
}
&__buy {
flex-shrink: 0;
}
}
Карточка товара (миниатюра-ссылка на страницу товара).
//- Все примеси в этом файле должны начинаться c имени блока (close)
mixin close(label, mods)
//- Принимает:
//- label {string} - описание, значение атрибута aria-label
//- mods {string} - стилевые модификаторы
//- Вызов:
+close('Закрыть')
+close('Закрыть', 'sm')
-
// список модификаторов
var allMods = '';
if(typeof(mods) !== 'undefined' && mods) {
var modsList = mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' close--' + modsList[i].trim();
}
}
button.close(class=allMods, type='button', aria-label=label, title=label)&attributes(attributes)
span= label
// В этом файле должны быть стили для БЭМ-блока close, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.close {
position: relative;
z-index: 1;
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border: none;
padding: 0;
background: transparent;
user-select: none;
cursor: pointer;
span {
position: relative;
display: block;
width: 60%;
height: $toggler-part-height;
font-size: 0;
color: transparent;
background: transparent;
&:before,
&:after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: $toggler-part-height;
background: $gray-dark;
transform-origin: 50% 50%;
}
&:before {
transform: rotate3d(0, 0, 1, 45deg);
}
&:after {
transform: rotate3d(0, 0, 1, -45deg);
}
}
}
Иконка закрытия.
//- Все примеси в этом файле должны начинаться c имени блока (code)
mixin code()
//- Вызов:
+code()
code &lt;div class="some">...&lt;/div>
code &lt;div class="some">...&lt;/div>
pre.code&attributes(attributes)
block
// В этом файле должны быть стили для БЭМ-блока code, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.code {
display: block;
width: 100%;
max-height: 500px;
margin-top: $typo-margin-vertical;
margin-bottom: $typo-margin-vertical;
border-radius: $border-radius;
border: 1px solid $gray-lighter;
padding: 0.5em 1em;
overflow-x: auto;
background-color: $gray-lightest;
white-space: normal;
tab-size: 2;
code {
display: block;
min-height: 1em;
padding: 0;
white-space: pre;
background-color: transparent;
p,
div {
display: inline-block;
margin: 0;
}
}
}
Оформление блочных вставок кода.
//- Все примеси в этом файле должны начинаться c имени блока (comment)
mixin comment(props)
//- Принимает:
//- props {object}
//- username {string} - имя пользователя
//- avatarURL {string} - адрес изображения с аватаром
//- authorURL {string} - адрес изображения с аватаром
//- content {string} - html текста коммента
//- mods {string} - список модификаторов
//- Вызов:
+comment({
username: 'Джокер',
avatarURL: 'img/joker.jpg',
authorURL: '/users/joker',
content: '<p>Далеко-далеко за словесными горами в стране, гласных и согласных живут рыбные тексты.</p>',
mods: 'admin',
})
+comment({
username: 'Докер',
content: '<p>Далеко-далеко за словесными горами в стране, гласных и согласных живут рыбные тексты.</p>',
})
-
// список модификаторов
var allMods = '';
if(typeof(props.mods) !== 'undefined' && props.mods) {
var modsList = props.mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' comment--' + modsList[i].trim();
}
}
if(typeof(props.username) === 'undefined') props.username = 'anonymous';
if(typeof(props.avatarURL) === 'undefined') props.avatarURL = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60"%3E%3Ccircle r="50%25" cx="50%25" cy="50%25" fill="#ccc"/%3E%3C/svg%3E';
article.comment(class=allMods)&attributes(attributes)
.comment__inner
if(typeof(props.authorURL) !== 'undefined' && props.authorURL)
a.comment__avatar-wrap(href=props.authorURL)
img(src=props.avatarURL, alt=props.username)
else
span.comment__avatar-wrap
img(src=props.avatarURL, alt=props.username)
.comment__text
header.comment__header #[b.comment__author= props.username] #[time.comment__date(datetime='2017-04-25T05:45') 25.04.2017 в 05:45]
.comment__body!= props.content
footer.comment__footer.
#[a.comment__reply(href='') Reply] #[a.comment__reply(href='') Admin]
block
// В этом файле должны быть стили для БЭМ-блока comment, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.comment {
margin-top: $typo-margin-vertical;
margin-bottom: $typo-margin-vertical;
@media (min-width: $screen-md) {
& & {
padding-left: calc(60px + 1em);
}
& & & & { // stylelint-disable-line selector-max-compound-selectors, selector-max-class, selector-max-combinators
padding-left: 0;
}
}
&__inner {
display: flex;
}
&__avatar-wrap {
flex-shrink: 0;
margin-right: 1em;
img {
display: block;
width: 60px;
height: 60px;
object-fit: cover;
}
}
&__text {
flex-grow: 1;
*:first-child {
margin-top: 0;
}
}
&__header {}
&__body {
& > *:last-child { // stylelint-disable-line selector-combinator-blacklist
margin-bottom: 0;
}
}
&__footer {}
}
//- Все примеси в этом файле должны начинаться c имени блока (embed-responsive)
mixin embed-responsive(mods)
//- Принимает:
//- mods {string} - список модификаторов
//- Вызов:
+embed-responsive()
iframe(src='https://www.youtube.com/embed/B0Q1rKpDNE4', frameborder='0', allowfullscreen='')
+embed-responsive('4-3')
iframe(src='https://www.youtube.com/embed/7pOr3dBFAeY', frameborder='0', allowfullscreen='')
-
// список модификаторов
var allMods = '';
if(typeof(mods) !== 'undefined' && mods) {
var modsList = mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' embed-responsive--' + modsList[i].trim();
}
}
.embed-responsive(class=allMods)&attributes(attributes)
block
// В этом файле должны быть стили только для БЭМ-блока embed-responsive, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Не пишите здесь другие селекторы.
.embed-responsive {
display: block;
position: relative;
width: 100%;
margin-top: $typo-margin-vertical;
margin-bottom: $typo-margin-vertical;
padding: 0;
overflow: hidden;
&::before {
content: '';
display: block;
padding-top: percentage(9 / 16); // по умолчанию ожидается вставка в формате 16/9
}
// embed,
// object,
iframe,
video {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
}
&--4-3 { // модификатор на случай вставки 4/3
&::before {
padding-top: percentage(3 / 4);
}
}
}
По умолчанию предназначен для встраивания роликов 16:9. C модификатором позволяет встраивать ролики 4:3.
//- Все примеси в этом файле должны начинаться c имени блока (field-actions)
mixin field-actions(text, mods)
//- Принимает:
//- text: '' {string} - текст пояснения
//- mods: '' {string} - модификаторы блока
//- Вызов:
+field-actions('* — обязательные поля', 'some-mod')
p КнопкиТут
-
var allMods = '';
if(typeof(mods) !== 'undefined' && mods) {
var modsList = mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' field-actions--' + modsList[i].trim();
}
}
.field-actions(class=allMods)&attributes(attributes)
if(typeof(text) !== 'undefined' && text)
.field-actions__text!= text
block
// В этом файле должны быть стили для БЭМ-блока field-actions, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.field-actions {
display: block;
margin-top: $typo-margin-vertical;
margin-bottom: $typo-margin-vertical;
&__text {
display: block;
margin-top: $typo-margin-vertical;
margin-bottom: $typo-margin-vertical;
font-size: $font-size-sm;
color: $text-color-muted;
line-height: 1.2em;
}
}
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><path d="M7.984 15.017l9-9-1.406-1.454-7.594 7.595L4.42 8.594 3.018 10zm9-14.017q.844 0 1.43.61T19 3.014v13.97q0 .797-.586 1.406-.586.61-1.43.61H3.014q-.843 0-1.43-.61T1 16.984V3.014q0-.796.585-1.404Q2.17 1 3.015 1h13.97z" fill="#2d2d2d"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><path d="M16.984 1q.797 0 1.406.61.61.61.61 1.404v13.97q0 .797-.61 1.406-.61.61-1.406.61H3.014q-.796 0-1.405-.61Q1 17.78 1 16.984V3.014q0-.796.61-1.404Q2.22 1 3.014 1h13.97zm0 2.016H3.014v13.97h13.97V3.016z" fill="#2d2d2d"/></svg>
\ No newline at end of file
//- Все примеси в этом файле должны начинаться c имени блока (field-checkbox)
mixin field-checkbox(checkboxes, title)
//- Принимает:
//- checkboxes {array}
//- {object}
//- title: '' {string} - текст рядом с чекбоксом
//- helpText: '' {string} - пояснение под полем
//- mods: '' {string} - модификаторы обертки чекбокса
//- attrs: {object} - любые атрибуты для input
//- name: {string}
//- ...
//- Вызов:
+field-checkbox([
{
title: 'Чекбокс0',
helpText: 'Подсказка',
attrs: {
name: 'check0',
}
},
])
+field-checkbox([
{
title: 'Чекбокс1',
helpText: 'Подсказка',
mods: 'error',
attrs: {
name: 'check1',
checked: true,
}
},
{
title: 'Чекбокс2',
helpText: 'Подсказка',
mods: 'error',
attrs: {
name: 'check2',
}
},
], 'ОбщееНазваниеБлока')
.field-checkbox&attributes(attributes)
if(typeof(title) !== 'undefined' && title)
.field-checkbox__title!= title
each checkbox in checkboxes
-
var allMods = '';
if(typeof(checkbox.mods) !== 'undefined' && checkbox.mods) {
var modsList = checkbox.mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' field-checkbox__input-wrap--' + modsList[i].trim();
}
}
.field-checkbox__input-wrap(class=allMods)
label.field-checkbox__name
input.field-checkbox__input(type='checkbox')&attributes(checkbox.attrs)
span.field-checkbox__name-text!= checkbox.title
if(typeof(checkbox.helpText) !== 'undefined' && checkbox.helpText)
.field-checkbox__help-text-wrap
.field-checkbox__help-text!= checkbox.helpText
// В этом файле должны быть стили для БЭМ-блока field-checkbox, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.field-checkbox {
$block-name: &; // #{$block-name}__element
display: block;
margin-bottom: $typo-margin-vertical;
&__title {
@include field-name;
}
&__input-wrap {
& + & {
margin-top: $typo-margin-vertical;
}
&--error {
#{$block-name}__name-text,
#{$block-name}__help-text {
color: $color-danger;
}
}
}
&__name {
display: inline-block;
position: relative;
padding-left: 1.5em;
margin-right: 0.6em;
font-weight: 400;
line-height: $line-height;
}
&__name-text {
display: inline-block;
// свой чекбокс с картинкой
// &:before {
// content: '';
// position: absolute;
// top: 5px;
// left: 0;
// width: 14px;
// height: 14px;
// background-image: svg-load('../blocks/field-checkbox/bg-img/checkbox.svg');
// background-size: 100%;
// }
// @at-root input:checked ~ &:before {
// background-image: svg-load('../blocks/field-checkbox/bg-img/checkbox--checked.svg');
// }
// / свой чекбокс с картинкой
// свой чекбокс без картинок
@at-root input:disabled ~ & {
cursor: not-allowed;
}
&:before {
content: '';
position: absolute;
top: 5px;
left: 0;
width: 14px;
height: 14px;
border: 2px solid currentColor;
border-radius: $border-radius;
}
@at-root input:focus ~ &:before {
@include field-focus;
}
@at-root input:disabled ~ &:before {
border-color: $gray-light;
background: $gray-lighter;
}
&:after {
content: '';
position: absolute;
top: 6px;
left: 3px;
width: 14px;
height: 6px;
opacity: 0;
border-left: 2px solid currentColor;
border-bottom: 2px solid currentColor;
transform: rotate(-45deg);
transition: opacity $transition-time;
box-shadow: 1px 2px 0 #fff, inset 0 -2px 0 #fff;
@at-root input:checked ~ & {
opacity: 1;
}
@at-root input:checked:disabled ~ & {
border-color: $gray-light;
}
}
// / свой чекбокс без картинок
}
&__input {
position: absolute;
top: 0.7em;
left: 0;
margin: 0;
padding: 0;
transform: translateY(-50%);
&:focus,
&:active {
@include field-focus;
}
// сокрытие инпута в случае использования своего чекбокса
opacity: 0;
}
&__help-text-wrap {
padding-left: 1.5em;
}
&__help-text {
@include field-help-text;
}
}
В стилевом файле есть закомментированные фрагменты для замены нативного элемента своим (svg).
/* global document */
import closest from 'closest';
import ready from 'Utils/documentReady.js';
ready(function() {
/*
Форма: работа стилизованного input[type="file"]
Автор: Osvaldas Valutis, www.osvaldas.info (адаптировано под используемую разметку)
Available for use under the MIT License
*/
var inputs = document.querySelectorAll( '.field-file__input:not([disabled])' );
Array.prototype.forEach.call( inputs, function( input )
{
var label = closest(input, '.field-file').querySelector( '.field-file__name-text' ),
labelVal = label.innerHTML;
input.addEventListener( 'change', function( e ) {
var fileName = '';
if( this.files && this.files.length > 1 ) {
fileName = ( this.getAttribute( 'data-multiple-caption' ) || '' ).replace( '{count}', this.files.length );
}
else {
fileName = e.target.value.split( '\\' ).pop();
}
if( fileName ) {
label.innerHTML = fileName;
}
else {
label.innerHTML = labelVal;
}
});
});
});
//- Все примеси в этом файле должны начинаться c имени блока (field-file)
mixin field-file(props)
//- Принимает:
//- props {object}
//- title: '' {string} - текст с названием (выводится над полем)
//- helpText: '' {string} - пояснение под полем
//- mods: '' {string} - модификаторы блока
//- attrs: {object} - любые атрибуты для input
//- name: {string}
//- text: {object} - тексты
//- selectText: {string} - текст на кнопке «выберите файл(ы)»
//- nothingText: {string} - текст, показываемый, пока ничего не выбрано
//- flesSelectedText: {string} - текст, показываемый, если выбрано более одного файла («файлов выбрано: 2»)
//- Вызов:
+field-file({
title: 'Название',
helpText: 'Подсказка',
mods: '',
attrs: {
name: 'commentFile',
multiple: true,
},
text: {
selectText: 'Выберите файл(ы)',
nothingText: 'Ничего не выбрано',
flesSelectedText: 'Выбрано файлов:',
},
})
-
if(typeof(props) === 'undefined') {
var props = {};
}
var allMods = '';
if(typeof(props.mods) !== 'undefined' && props.mods) {
var modsList = props.mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' field-file--' + modsList[i].trim();
}
}
var selectText = 'Выберите файл(ы)'; // Select file(s)
var nothingText = ''; // Nothing selected
var flesSelectedText = 'Выбрано файлов: {count}'; // Selected files: {count}
if (typeof(props.text) !== 'undefined' && props.text !== '') {
if (typeof(props.text.selectText) !== 'undefined') selectText = props.text.selectText;
if (typeof(props.text.nothingText) !== 'undefined') nothingText = props.text.nothingText;
if (typeof(props.text.flesSelectedText) !== 'undefined') flesSelectedText = props.text.flesSelectedText + ' {count}';
}
.field-file(class=allMods)&attributes(attributes)
if(typeof(props.title) !== 'undefined' && props.title)
.field-file__name!= props.title
label.field-file__input-wrap
input.field-file__input(type='file', data-multiple-caption=flesSelectedText)&attributes(props.attrs)
.field-file__name-text(data-button-text=selectText)= nothingText
if(typeof(props.helpText) !== 'undefined' && props.helpText)
.field-file__help-text!= props.helpText
block
// В этом файле должны быть стили для БЭМ-блока field-file, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.field-file {
$block-name: &; // #{$block-name}__element
display: block;
margin-bottom: $typo-margin-vertical;
&__name {
@include field-name;
}
&__input-wrap {
display: block;
position: relative;
}
&__input {
position: absolute;
width: 0;
height: 0;
opacity: 0;
&:active ~ .field-file__name-text,
&:focus ~ .field-file__name-text {
@include field-focus;
}
}
&__name-text {
display: flex;
align-items: baseline;
position: relative;
width: 100%;
border: 1px solid $border-color;
border-radius: $border-radius;
background-color: #fff;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
overflow: hidden;
@at-root input:disabled ~ & {
background: $gray-lighter;
cursor: not-allowed;
}
&:before {
content: attr(data-button-text);
display: block;
background-color: $gray-lightest;
border-right: 1px solid $border-color;
margin-right: $field-padding-horizontal;
line-height: $line-height;
padding: $field-padding-vertical $field-padding-horizontal;
border-top-left-radius: $border-radius;
border-bottom-left-radius: $border-radius;
}
&:hover,
&:focus {
&:before {
background-color: darken($gray-lightest, 10%); // как у обычных кнопок
}
}
}
&__help-text {
@include field-help-text;
}
&--error {
#{$block-name}__name,
#{$block-name}__name-text,
#{$block-name}__input,
#{$block-name}__help-text {
color: $color-danger;
}
#{$block-name}__name-text {
border-color: $color-danger;
background-color: lighten($color-danger, 35%);
}
}
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 357 357"><path d="M357 204H0v-51h357v51z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 357 357"><path d="M357 204H204v153h-51V204H0v-51h153V0h51v153h153v51z"/></svg>
/* global document */
import ready from 'Utils/documentReady.js';
ready(function(){
var fields = document.querySelectorAll( '.field-num' );
if(fields.length) {
Array.prototype.forEach.call( fields, function( field ) {
const input = field.querySelector('.field-num__input');
const valueMin = input.getAttribute('min') ? +input.getAttribute('min') : -Infinity;
const valueMax = input.getAttribute('max') ? +input.getAttribute('max') : Infinity;
const valueStep = input.getAttribute('step') ? +input.getAttribute('step') : 1;
field.addEventListener('click', function(event){
if(event.target.classList.contains('field-num__btn') && !input.getAttribute('disabled')) {
let num = parseInt(input.value);
if(isNaN(num)) num = 0;
if(event.target.classList.contains('field-num__btn--plus')) {
if (num < valueMax) input.value = num + valueStep;
}
if(event.target.classList.contains('field-num__btn--minus')) {
if (num > valueMin) input.value = num - valueStep;
}
}
});
});
}
});
//- Все примеси в этом файле должны начинаться c имени блока (field-num)
mixin field-num(props)
//- Принимает:
//- props {
//- title: '' {string} - текст с названием (выводится над полем)
//- helpText: '' {string} - пояснение под полем
//- mods: '' {string} - модификаторы блока
//- val: {number} - количество в поле
//- attrs: {object} - любые атрибуты для input
//- placeholder: {string}
//- Вызов:
+field-num({
title: 'Количество',
helpText: 'От 1 до 10, шаг 1',
mods: '',
val: '9',
attrs: {
name: 'quantity',
max: '10',
min: '1',
step: '1',
}
})
+field-num({
mods: 'error',
attrs: {
name: 'quantity',
}
})
-
if(typeof(props) === 'undefined') { var props = {}; }
var allMods = '';
if(typeof(props.mods) !== 'undefined' && props.mods) {
var modsList = props.mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' field-num--' + modsList[i].trim();
}
}
if(props.attrs.disabled) allMods = allMods + ' field-num--disabled';
var val = parseInt(props.val);
if(isNaN(val)) val = 0;
.field-num(class=allMods)&attributes(attributes)
if(typeof(props.title) !== 'undefined' && props.title)
span.field-num__name!= props.title
span.field-num__input-wrap
span.field-num__input-and-btns
button.field-num__btn.field-num__btn--minus(type='button') -
button.field-num__btn.field-num__btn--plus(type='button') +
input.field-num__input(type='number', value=val)&attributes(props.attrs)
if(typeof(props.helpText) !== 'undefined' && props.helpText)
span.field-num__help-text!= props.helpText
block
// В этом файле должны быть стили для БЭМ-блока field-num, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.field-num {
$block-name: &; // #{$block-name}__element
display: block;
margin-bottom: $typo-margin-vertical;
&__name {
@include field-name;
}
&__input-wrap {
display: block;
}
&__input-and-btns {
display: block;
position: relative;
width: 140px;
}
&__input {
@include field-text;
padding: $field-padding-vertical 45px;
text-align: center;
#{$block-name}:not(#{$block-name}--disabled) button:focus ~ & { // stylelint-disable-line
@include field-focus;
}
}
&__btn {
position: absolute;
top: 1px;
bottom: 1px;
width: 40px;
border: none;
padding: 0;
border-top-right-radius: $border-radius;
border-bottom-right-radius: $border-radius;
font: 0/0 a; // stylelint-disable-line
color: transparent;
background: transparent;
&:hover,
&:focus {
background-color: $gray-lightest;
cursor: pointer;
}
&:focus {
outline: none;
}
&--plus {
right: 1px;
background: svg-load('../field-num/bg-img/plus.svg') center no-repeat;
background-size: 14px 14px;
}
&--minus {
left: 1px;
background: svg-load('../field-num/bg-img/minus.svg') center no-repeat;
background-size: 14px 14px;
}
#{$block-name}--disabled & {
cursor: not-allowed;
&:hover,
&:focus {
background-color: transparent;
}
}
}
&__help-text {
@include field-help-text;
}
&--error {
#{$block-name}__name,
#{$block-name}__input,
#{$block-name}__help-text {
color: $color-danger;
}
#{$block-name}__input {
border-color: $color-danger;
background-color: lighten($color-danger, 35%);
}
}
}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#2D2D2D" d="M12 20.016q3.28 0 5.648-2.367T20.015 12t-2.367-5.647T12 3.986 6.352 6.353 3.985 12t2.367 5.65T12 20.015zm0-18q4.125 0 7.055 2.93T21.985 12t-2.93 7.056T12 21.986t-7.055-2.93T2.015 12t2.93-7.054T12 2.016zm0 4.968q2.063 0 3.54 1.477T17.015 12t-1.477 3.54T12 17.015 8.46 15.54 6.985 12 8.46 8.46 12 6.985z"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#2D2D2D" d="M12 20.016q3.28 0 5.648-2.367T20.015 12t-2.367-5.647T12 3.986 6.352 6.353 3.985 12t2.367 5.65T12 20.015zm0-18q4.125 0 7.055 2.93T21.985 12t-2.93 7.056T12 21.986t-7.055-2.93T2.015 12t2.93-7.054T12 2.016z"/></svg>
\ No newline at end of file
//- Все примеси в этом файле должны начинаться c имени блока (field-radio)
mixin field-radio(radiobuttons, title)
//- Принимает:
//- radiobuttons {array}
//- {object}
//- title: '' {string} - текст рядом с радиокнопкой
//- helpText: '' {string} - пояснение под полем
//- mods: '' {string} - модификаторы обертки радиокнопки
//- attrs: {object} - любые атрибуты для input
//- name: {string}
//- ...
//- Вызов:
+field-radio([
{
title: 'Радиокнопка1',
helpText: 'Подсказка',
mods: '',
attrs: {
name: 'radio',
checked: true,
}
},
{
title: 'Радиокнопка2',
helpText: 'Подсказка',
mods: '',
attrs: {
name: 'radio',
}
},
], 'ОбщееНазваниеБлока')
.field-radio&attributes(attributes)
if(typeof(title) !== 'undefined' && title)
.field-radio__title!= title
each checkbox in radiobuttons
-
var allMods = '';
if(typeof(checkbox.mods) !== 'undefined' && checkbox.mods) {
var modsList = checkbox.mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' field-radio__input-wrap--' + modsList[i].trim();
}
}
.field-radio__input-wrap(class=allMods)
label.field-radio__name
input.field-radio__input(type='radio')&attributes(checkbox.attrs)
span.field-radio__name-text!= checkbox.title
if(typeof(checkbox.helpText) !== 'undefined' && checkbox.helpText)
.field-radio__help-text-wrap
.field-radio__help-text!= checkbox.helpText
// В этом файле должны быть стили для БЭМ-блока field-radio, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.field-radio {
$block-name: &; // #{$block-name}__element
display: block;
margin-bottom: $typo-margin-vertical;
&__title {
@include field-name;
}
&__input-wrap {
& + & {
margin-top: $typo-margin-vertical;
}
&--error {
#{$block-name}__name-text,
#{$block-name}__help-text {
color: $color-danger;
}
}
}
&__name {
display: inline-block;
position: relative;
margin-right: 0.6em;
padding-left: 1.5em;
font-weight: 400;
line-height: $line-height;
}
&__name-text {
display: inline-block;
// своя радиокнопка с картинкой
// &:before {
// content: '';
// position: absolute;
// top: 5px;
// left: 0;
// width: 14px;
// height: 14px;
// background-image: svg-load('../blocks/field-radio/bg-img/radiobutton.svg');
// background-size: 100%;
// }
// @at-root input:checked ~ &:before {
// background-image: svg-load('../blocks/field-radio/bg-img/radiobutton--checked.svg');
// }
// / своя радиокнопка с картинкой
// своя радиокнопка без картинки
@at-root input:disabled ~ & {
cursor: not-allowed;
}
&:before {
content: '';
position: absolute;
top: 5px;
left: 0;
width: 14px;
height: 14px;
border: 2px solid currentColor;
border-radius: 50%;
}
@at-root input:focus ~ &:before {
@include field-focus;
}
@at-root input:disabled ~ &:before {
border-color: $gray-light;
background: $gray-lighter;
}
&:after {
content: '';
position: absolute;
top: 9px;
left: 4px;
width: 6px;
height: 6px;
opacity: 0;
border-radius: 50%;
background-color: currentColor;
transition: opacity $transition-time;
@at-root input:checked ~ & {
opacity: 1;
}
@at-root input:checked:disabled ~ & {
background-color: $gray-light;
}
}
// / своя радиокнопка без картинки
}
&__input {
position: absolute;
top: 0.8em;
left: 0;
margin: 0;
padding: 0;
transform: translateY(-50%);
&:focus,
&:active {
@include field-focus;
}
// сокрытие инпута в случае использования своей радиокнопки
opacity: 0;
}
&__help-text-wrap {
padding-left: 1.5em;
}
&__help-text {
@include field-help-text;
}
}
В стилевом файле есть закомментированные фрагменты для замены нативного элемента своим (svg).
//- Все примеси в этом файле должны начинаться c имени блока (field-range)
mixin field-range(props)
//- Принимает:
//- props {object}
//- title: '' {string} - текст с названием (выводится над полем)
//- helpText: '' {string} - пояснение под полем
//- mods: '' {string} - модификаторы блока
//- attrs: {object} - любые атрибуты для input
//- name: {string}
//- Вызов:
+field-range({
title: 'Название',
helpText: 'Подсказка',
mods: '',
attrs: {
name: 'counter',
min: '1',
max: '100',
step: '1',
value: '40',
}
})
-
if(typeof(props) === 'undefined') {
var props = {};
}
var allMods = '';
if(typeof(props.mods) !== 'undefined' && props.mods) {
var modsList = props.mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' field-range--' + modsList[i].trim();
}
}
label.field-range(class=allMods)&attributes(attributes)
if(typeof(props.title) !== 'undefined' && props.title)
span.field-range__name!= props.title
span.field-range__input-wrap
input.field-range__input(type='range')&attributes(props.attrs)
if(typeof(props.helpText) !== 'undefined' && props.helpText)
span.field-range__help-text!= props.helpText
block
// В этом файле должны быть стили для БЭМ-блока field-range, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.field-range {
display: block;
margin-bottom: $typo-margin-vertical;
&__name {
@include field-name;
}
&__input-wrap {
display: block;
position: relative;
}
&__input {
align-self: center;
width: 100%;
height: 2em;
border-radius: 0.25em;
border: 0;
padding: 0;
background: none;
font-size: inherit;
cursor: pointer;
appearance: none;
&::-webkit-slider-runnable-track,
&::-webkit-slider-thumb {
-webkit-appearance: none; // stylelint-disable-line property-no-vendor-prefix
}
&:focus {
@include field-focus;
}
// Фоновая линия ползунка
// Да, да, и это — локальная примесь.
@mixin field-range-runnable-track {
box-sizing: border-box;
width: 100%;
height: 0.75em;
border-radius: $border-radius;
background: #fff;
border: 1px solid $border-color;
}
// Увы, тут похожие наборы свойств приходится писать порознь.
&::-webkit-slider-runnable-track {
@include field-range-runnable-track; // локальная примесь!
}
&::-moz-range-track {
@include field-range-runnable-track; // локальная примесь!
}
&::-ms-track {
@include field-range-runnable-track; // локальная примесь!
color: transparent;
}
// Смещающийся элемент ползунка
// Да, да, и это — локальная примесь.
@mixin field-range-slider-thumb {
box-sizing: border-box;
width: 2em;
height: 2em;
border: 1px solid $border-color;
border-radius: 1em;
padding: 0;
box-shadow: none;
cursor: ew-resize;
background: $gray-lightest;
}
&::-webkit-slider-thumb {
@include field-range-slider-thumb; // локальная примесь!
margin-top: -0.68em;
}
&::-moz-range-thumb {
@include field-range-slider-thumb; // локальная примесь!
}
&::-ms-thumb {
@include field-range-slider-thumb; // локальная примесь!
}
&::-ms-fill-lower,
&::-ms-tooltip {
display: none;
}
}
&__help-text {
@include field-help-text;
padding-top: 0;
}
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 11" width="17" height="11"><path fill="#B1B1B1" d="M.202 1.97L1.956.204A.61.61 0 0 1 2.43 0a.61.61 0 0 1 .47.204L8.5 5.84 14.1.204A.61.61 0 0 1 14.57 0a.61.61 0 0 1 .474.204l1.754 1.766a.618.618 0 0 1 .202.473.62.62 0 0 1-.202.476l-7.826 7.875A.61.61 0 0 1 8.5 11a.61.61 0 0 1-.472-.204L.202 2.92A.618.618 0 0 1 0 2.444c0-.203.067-.34.202-.475z"/></svg>
/* global document */
import ready from 'Utils/documentReady.js';
import Choices from 'choices.js';
ready(function(){
if (typeof Object.assign != 'function') {
Object.assign = function(target) {
'use strict';
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object');
}
target = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (source != null) {
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
}
return target;
};
}
// Включим на каком-то конкретном отдельно
// const choices = new Choices('#some-if', {/* options */});
// Или тупо найдём все селекты и включим на них Choices
const selects = document.querySelectorAll('.field-select__select');
selects.forEach(function(item){
new Choices(item, {
searchEnabled: false,
placeholderValue: 'Выберите',
});
});
});
//- Все примеси в этом файле должны начинаться c имени блока (field-select)
mixin field-select(title, attrs, options, helpText, mods)
//- Принимает:
//- title: '' {string} - название селекта
//- attrs: {object} - атрибуты селекта
//- name: '' {string}
//- options {array}
//- {object}
//- title: '' {string} - текст пункта
//- attrs: {object} - любые атрибуты пункта
//- value: {string}
//- {object}
//- attrs: {object} - любые атрибуты пункта
//- label: {string}
//- child: {array} - потомки (если есть этот элемент, то его родитель — optgroup)
//- {object}
//- title: '' {string}
//- attrs: {object}
//- value: {string}
//- {object}
//- title: '' {string} - текст пункта
//- attrs: {object} - любые атрибуты пункта
//- value: {string}
//- helpText: '' {string} - текст подсказки
//- mods: '' {string} - модификаторы блока
//- Вызов:
+field-select(
'Название',
{
name: 'select',
id: 'select'
},
[
{
title: 'Опция1',
attrs: {
value: '',
}
},
{
title: 'Опция2',
attrs: {
value: '',
}
},
],
'Подсказка',
''
)
+field-select(
'Название',
{
name: '',
id: ''
},
[
{
attrs: {
label: 'Группа',
},
child: [
{
title: 'Опция1',
attrs: {
value: 'val01',
}
},
{
title: 'Опция2',
attrs: {
value: 'val02',
}
},
]
},
{
title: 'Опция3',
attrs: {
value: 'val03',
}
}
],
'Подсказка',
''
)
-
var allMods = '';
if(typeof(mods) !== 'undefined' && mods) {
var modsList = mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' field-select--' + modsList[i].trim();
}
}
.field-select(class=allMods)&attributes(attributes)
if(typeof(title) !== 'undefined' && title)
.field-select__name!= title
.field-select__select-wrap
select.field-select__select&attributes(attrs)
each option in options
//- option= option
if(typeof(option.child) !== 'undefined' && option.child)
optgroup(label=option.attrs.label)
each subOption in option.child
option&attributes(subOption.attrs)= subOption.title
else if(typeof(option.title) !== 'undefined' && option.title)
option&attributes(option.attrs)= option.title
if(typeof(helpText) !== 'undefined' && helpText)
span.field-select__help-text!= helpText
// В этом файле должны быть стили для БЭМ-блока field-select, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.field-select {
$block-name: &; // #{$block-name}__element
display: block;
margin-bottom: $typo-margin-vertical;
&__name {
@include field-name;
}
&__select-wrap {}
// Оформление штатного селекта
// &__select {
// display: inline-block;
// vertical-align: middle;
// width: 100%;
// line-height: $line-height;
// padding: $field-padding-vertical $field-padding-horizontal;
// padding-right: 35px;
// font-size: inherit;
// font-family: inherit;
// border: 1px solid $border-color;
// border-radius: $border-radius;
// appearance: none;
// background-color: #fff;
// &[multiple] {
// background-image: none;
// min-height: 6.8em;
// }
// &::-ms-expand {
// display: none;
// }
// &:active,
// &:focus {
// @include field-focus;
// }
// &[disabled],
// &[readonly] {
// cursor: default;
// background-color: $gray-lightest;
// }
// }
&__help-text {
@include field-help-text;
}
&--error {
color: $color-danger;
#{$block-name}__name,
#{$block-name}__select,
#{$block-name}__help-text {
color: $color-danger;
}
#{$block-name}__select,
.choices__inner,
.choices:after {
border-color: $color-danger;
background-color: lighten($color-danger, 35%);
}
}
}
/** @define choices */
.choices {
$block-name: &; // #{$block-name}__element
position: relative;
&:focus {
outline: 0;
&:not(.is-disabled) {
#{$block-name}__inner {
@include field-focus;
}
}
}
&:after {
content: '';
position: absolute;
top: 7px;
right: 16px;
width: 14px;
height: 14px;
border-left: 1px solid $border-color;
border-bottom: 1px solid $border-color;
pointer-events: none;
transform: rotate(-45deg);
transition: all 0.3s;
}
&.is-open:after {
transform: translateY(7px) rotate(135deg);
}
.is-hidden {
display: none;
}
&__inner {
border: 1px solid $border-color;
border-radius: $border-radius;
padding: $field-padding-vertical 40px $field-padding-vertical $field-padding-horizontal;
background: #fff;
font-size: 1em;
line-height: 1.5;
cursor: pointer;
#{$block-name}__item--selectable {
max-width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
&__list--dropdown {
display: none;
position: absolute;
z-index: 1;
top: 100%;
width: 100%;
margin-top: -1px;
border: 1px solid $border-color;
border-bottom-left-radius: $border-radius;
border-bottom-right-radius: $border-radius;
background-color: #fff;
word-break: break-all;
overflow: hidden;
#{$block-name}__item {
padding: $field-padding-vertical $field-padding-horizontal;
cursor: pointer;
&.is-highlighted {
background: $gray-lightest;
}
}
}
&__heading {
padding: $field-padding-vertical $field-padding-horizontal;
font-size: $font-size-sm;
font-weight: 700;
opacity: 0.5;
cursor: default;
}
&.is-open {
#{$block-name}__list--dropdown {
display: block;
}
}
&.is-disabled {
#{$block-name}__inner {
background-color: $gray-lighter;
cursor: default;
}
}
}
/* global document */
import ready from 'Utils/documentReady.js';
import autosize from 'autosize';
ready(function() {
autosize(document.querySelectorAll('textarea'));
});
//- Все примеси в этом файле должны начинаться c имени блока (field-text)
mixin field-text(props)
//- Принимает:
//- props {
//- title: '' {string} - текст с названием (выводится над полем)
//- isTextarea: false {bool} - флаг input/textarea
//- helpText: '' {string} - пояснение под полем
//- mods: '' {string} - модификаторы блока
//- val: '' {string} - текст в поле
//- attrs: {object} - любые атрибуты для input/textarea
//- type: {string}
//- placeholder: {string}
//- Вызов:
+field-text({
title: 'Название',
isTextarea: true,
helpText: 'Подсказка',
mods: '',
val: '',
attrs: {
name: 'comment',
}
})
-
if(typeof(props) === 'undefined') {
var props = {};
}
var allMods = '';
if(typeof(props.mods) !== 'undefined' && props.mods) {
var modsList = props.mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' field-text--' + modsList[i].trim();
}
}
label.field-text(class=allMods)&attributes(attributes)
if(typeof(props.title) !== 'undefined' && props.title)
span.field-text__name!= props.title
span.field-text__input-wrap
if(typeof(props.isTextarea) !== 'undefined' && props.isTextarea)
textarea.field-text__input&attributes(props.attrs)= props.val
else
input.field-text__input(type=(typeof(props.attrs) !== 'undefined' && props.attrs.type) ? props.attrs.type : 'text', value=props.val)&attributes(props.attrs)
if(typeof(props.helpText) !== 'undefined' && props.helpText)
span.field-text__help-text!= props.helpText
block
// В этом файле должны быть стили для БЭМ-блока field-text, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.field-text {
$block-name: &;
display: block;
margin-bottom: $typo-margin-vertical;
&__name {
@include field-name;
}
&__input-wrap {
display: block;
}
&__input {
@include field-text;
@at-root textarea#{&} {
$textarea-height: ($line-height * 2) + ($field-padding-vertical * 2);
height: auto;
min-height: calc(#{$textarea-height} + 2px);
resize: vertical;
}
}
&__help-text {
@include field-help-text;
}
&--error {
#{$block-name}__name,
#{$block-name}__input,
#{$block-name}__help-text {
color: $color-danger;
}
#{$block-name}__input {
border-color: $color-danger;
background-color: lighten($color-danger, 35%);
}
}
}
Текстовые поля любого типа. В том числе, с использованием не `input`, а `textarea`. Для `textarea` использован [autosize](https://github.com/jackmoore/autosize).
//- Все примеси в этом файле должны начинаться c имени блока (field-toggler)
mixin field-toggler(togglers, title, isRadio)
//- Принимает:
//- togglers {array}
//- {object}
//- title: '' {string} - текст рядом с переключателем
//- helpText: '' {string} - пояснение под полем
//- mods: '' {string} - модификаторы обёртки переключателя
//- attrs: {object} - любые атрибуты для input
//- name: {string}
//- ...
//- title: '' {string} - общее название группы переключателей
//- isRadio: false {bool} - флаг «это радиокнопки, а не чекбоксы»
//- Вызов:
+field-toggler([
{
title: 'Переключатель0',
helpText: 'Подсказка',
attrs: {
name: 'check0',
}
},
])
+field-toggler([
{
title: 'Переключатель1',
helpText: 'Подсказка',
attrs: {
name: 'check1',
checked: true,
}
},
{
title: 'Переключатель2',
helpText: 'Подсказка',
mods: 'some',
attrs: {
name: 'check2',
}
},
], 'ОбщееНазваниеБлока', true)
-
if(typeof(isRadio) !== 'undefined' && isRadio)
var type = 'radio';
else
var type = 'checkbox';
.field-toggler&attributes(attributes)
if(typeof(title) !== 'undefined' && title)
.field-toggler__title!= title
each toggler in togglers
-
var allMods = '';
if(typeof(toggler.mods) !== 'undefined' && toggler.mods) {
var modsList = toggler.mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' field-toggler__input-wrap--' + modsList[i].trim();
}
}
.field-toggler__input-wrap(class=allMods)
label.field-toggler__name
input.field-toggler__input(type=type)&attributes(toggler.attrs)
span.field-toggler__name-text!= toggler.title
if(typeof(toggler.helpText) !== 'undefined' && toggler.helpText)
.field-toggler__help-text-wrap
.field-toggler__help-text!= toggler.helpText
// В этом файле должны быть стили для БЭМ-блока field-toggler, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.field-toggler {
$block-name: &; // #{$block-name}__element
display: block;
margin-bottom: $typo-margin-vertical;
&__title {
@include field-name;
}
&__input-wrap {
& + & {
margin-top: $typo-margin-vertical;
}
&--error {
#{$block-name}__name-text,
#{$block-name}__help-text {
color: $color-danger;
}
#{$block-name}__name-text:after {
background: $color-danger;
}
}
}
&__name {
display: inline-block;
position: relative;
padding-left: 3.2em;
margin-right: 0.6em;
font-weight: 400;
line-height: $line-height;
}
&__name-text {
display: inline-block;
@at-root input:disabled ~ & {
cursor: not-allowed;
}
&:before {
content: '';
position: absolute;
top: 3px;
left: 0;
width: 35px;
height: 18px;
background-color: #fff;
border: 1px solid $border-color;
@at-root input:focus ~ & {
@include field-focus;
}
}
@at-root input:disabled ~ &:before {
border-color: $gray-light;
background: $gray-lighter;
}
&:after {
content: '';
position: absolute;
top: 5px;
left: 2px;
width: 14px;
height: 14px;
background-color: $gray-lightest;
border: 1px solid $border-color;
transition: all 0.3s;
}
@at-root input:checked ~ &:after {
transform: translate(17px, 0);
}
@at-root input:disabled ~ &:after {
background-color: $gray-light;
}
}
&__input {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
opacity: 0;
}
&__help-text-wrap {
padding-left: 3.2em;
}
&__help-text {
@include field-help-text;
}
}
/* global document */
const closest = require('closest');
import ready from 'Utils/documentReady.js';
ready(function(){
// Для всех форм страницы
const forms = Array.from(document.querySelectorAll('form[data-check-form]'));
forms.forEach(function(form){
// Подпишемся на событие отправки
form.addEventListener('submit', function(e){
let valid = true;
// Проверим все текстовые инпуты
const fieldsText = Array.from(form.querySelectorAll('input[data-check-pattern]'));
fieldsText.forEach(function(input){
if(!checkFieldText(input)) valid = false;
});
// Проверим все чекбоксы
const fieldsCheckbox = Array.from(form.querySelectorAll('input[data-check-state]'));
fieldsCheckbox.forEach(function(input){
if(!checkFieldCheckbox(input)) valid = false;
});
// Если были ошибки, не отправляем форму
if(!valid) e.preventDefault();
});
});
// Для всех проверяемых текстовых полей
const fieldsText = Array.from(document.querySelectorAll('input[data-check-pattern]'));
fieldsText.forEach(function(input){
let hasBeenAlreadyBlured = false;
input.addEventListener('blur', function(){
checkFieldText(input);
if(!hasBeenAlreadyBlured) hasBeenAlreadyBlured = true;
});
input.addEventListener('input', function(){ if(hasBeenAlreadyBlured) checkFieldText(input); });
});
// Для всех проверяемых чекбоксов
const fieldsCheckbox = Array.from(document.querySelectorAll('input[data-check-state]'));
fieldsCheckbox.forEach(function(input){
input.addEventListener('change', function(){ checkFieldCheckbox(input); });
});
function checkFieldText(input) {
const regExp = new RegExp(input.dataset.checkPattern, 'gi');
const result = regExp.test(input.value);
const errorClass = 'field-text--error';
const parent = closest(input, '.field-text');
result ? parent.classList.remove(errorClass) : parent.classList.add(errorClass);
return result;
}
function checkFieldCheckbox(input) {
const trueVal = input.dataset.checkState == 'on' ? true : false;
const result = trueVal === input.checked
const errorClass = 'field-checkbox__input-wrap--error';
const parent = closest(input, '.field-checkbox__input-wrap');
result ? parent.classList.remove(errorClass) : parent.classList.add(errorClass);
return result;
}
});
Включается при указании на теге `form` атрибута `data-check-form`.
Для текстовых полей: содержимое поля анализируется на соответствие регулярному выражению из атрибута `data-check-pattern` по событию `blur`, а также по событию `input` при последующем фокусе.
Для чекбоксов: по событию `change` проверяется соответствия состояния и `data-check-state="on"` (`off`).
<p class="alert alert--warning">Блок не имеет классов, упоминаемых в разметке. Чтобы взять его в сборку, упомяните <code>form-validation</code> в <code>config.js#alwaysAddBlocks</code>.</p>
<p class="alert alert--warning">Pug «умно» обрабатывает строки, убирая обратный слеш (<code>\</code>), этот символ в pug нужно экранировать: не <code>\d</code>, а <code>\\d</code>. <br>См. пример кода ниже.</p>
//- Все примеси в этом файле должны начинаться c имени блока (form)
//- ВНИМАНИЕ: в файле две примеси
mixin form(mods)
//- Принимает:
//- mods {string} - список модификаторов
//- Вызов:
+form()
+fieldset('Название1')
+field-text({
helpText: 'Подсказка',
attrs: {
placeholder: 'Пример'
}
})
+field-text({
helpText: 'Подсказка',
attrs: {
placeholder: 'Пример'
}
})
-
// список модификаторов
var allMods = '';
if(typeof(mods) !== 'undefined' && mods) {
var modsList = mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' form--' + modsList[i].trim();
}
}
form.form(class=allMods)&attributes(attributes)
block
mixin fieldset(legend, mods)
//- Принимает:
//- legend {string} - название группы полей
//- mods {string} - список модификаторов
//- Вызов:
+fieldset('Название1')
+field-text({
helpText: 'Подсказка',
attrs: {
placeholder: 'Пример'
}
})
-
// список модификаторов
var allMods = '';
if(typeof(mods) !== 'undefined' && mods) {
var modsList = mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' form--' + modsList[i].trim();
}
}
fieldset.form__fieldset(class=allMods)&attributes(attributes)
legend.form__legend!= legend
block
// В этом файле должны быть стили для БЭМ-блока form, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.form {
&__fieldset {
min-width: 0;
margin: 0;
border: 0;
padding: 0;
&:not(:last-child) {
margin-bottom: $typo-margin-vertical;
}
}
&__legend {
display: block;
width: 100%;
max-width: 100%; // Привет IE
margin-top: 0;
margin-bottom: $typo-margin-vertical;
font-size: $font-size-h3;
font-family: $font-family;
font-weight: 700;
line-height: $line-height;
white-space: normal; // Привет IE
color: currentColor;
}
}
//- Все примеси в этом файле должны начинаться c имени блока (label)
mixin label(text, mods, tag)
//- Принимает:
//- text {string} - текст кнопки
//- mods {string} - список модификаторов
//- tag {string} - тег (если не span)
//- Вызов:
+label('Лейбл')
+label('Лейбл', 'danger', 'div')
-
// список модификаторов
var allMods = '';
if(typeof(mods) !== 'undefined' && mods) {
var modsList = mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' label--' + modsList[i].trim();
}
}
if(typeof(tag) !== 'undefined' && tag)
#{tag}.label(class=allMods)&attributes(attributes)!= text
block
else
span.label(class=allMods)&attributes(attributes)!= text
// В этом файле должны быть стили для БЭМ-блока label, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.label {
display: inline-block;
position: relative;
margin: 0 0.1em;
vertical-align: baseline;
line-height: 1;
padding: 0.25em 0.4em 0.2em;
background-color: $gray-lightest;
color: $text-color;
text-decoration: none;
text-transform: none;
font-weight: 700;
font-size: $font-size-sm;
white-space: nowrap;
border-radius: $border-radius;
&:hover,
&:focus {
color: $text-color;
text-decoration: none;
}
// &--main {
// background-color: $color-main;
// color: #fff;
// &:hover,
// &:focus {
// color: #fff;
// }
// }
}
//- Все примеси в этом файле должны начинаться c имени блока (loader)
mixin loader(mods)
//- Принимает:
//- mods {string} - список модификаторов
//- Вызов:
+loader()
-
// список модификаторов
var allMods = '';
if(typeof(mods) !== 'undefined' && mods) {
var modsList = mods.split(',');
for (var i = 0; i < modsList.length; i++) {
allMods = allMods + ' loader--' + modsList[i].trim();
}
}
span.loader(class=allMods)&attributes(attributes)
// В этом файле должны быть стили для БЭМ-блока loader, его элементов,
// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...
// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority
.loader {
display: inline-block;
&:before {
content: '';
display: block;
width: 3rem;
height: 3rem;
border: 3px solid $gray-lightest;
border-top: 3px solid $gray-lighter;
border-radius: 50%;
animation: loader-rotate 1s linear infinite;
}
}
@keyframes loader-rotate {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment