Repository: Qvant-lab/qui
Branch: master
Commit: 3ead95280f7e
Files: 322
Total size: 590.1 KB
Directory structure:
gitextract_kt2b5v6x/
├── .eslintrc
├── .github/
│ └── workflows/
│ ├── codeql-analysis.yml
│ └── deploy.yml
├── .gitignore
├── .lintstagedrc
├── .npmignore
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── .storybook/
│ ├── locales/
│ │ ├── en.js
│ │ ├── index.js
│ │ └── ru.js
│ ├── main.js
│ ├── manager.js
│ ├── preview.js
│ └── theme.js
├── .stylelintignore
├── .stylelintrc
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── babel.config.js
├── jest.config.js
├── package.json
├── scripts/
│ └── badges.js
├── src/
│ ├── components.scss
│ ├── fonts/
│ │ └── index.scss
│ ├── icons/
│ │ └── index.scss
│ ├── main.scss
│ ├── normalize.scss
│ ├── onDemand.js
│ ├── qComponents/
│ │ ├── QBreadcrumbs/
│ │ │ ├── QBreadcrumbs.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QBreadcrumbs.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QBreadcrumbs.vue
│ │ │ └── q-breadcrumbs.scss
│ │ ├── QButton/
│ │ │ ├── QButton.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QButton.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QButton.vue
│ │ │ └── q-button.scss
│ │ ├── QCascader/
│ │ │ ├── QCascader.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QCascader.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QCascader.vue
│ │ │ ├── QCascaderMenu.vue
│ │ │ ├── QCascaderPanel.vue
│ │ │ ├── q-cascader-menu.scss
│ │ │ └── q-cascader.scss
│ │ ├── QCheckbox/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QCheckbox.vue
│ │ │ └── q-checkbox.scss
│ │ ├── QCheckboxGroup/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QCheckboxGroup.vue
│ │ │ └── q-checkbox-group.scss
│ │ ├── QCol/
│ │ │ ├── QCol.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QCol.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QCol.vue
│ │ │ └── q-col.scss
│ │ ├── QCollapse/
│ │ │ ├── QCollapse.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QCollapse.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QCollapse.vue
│ │ │ └── q-collapse.scss
│ │ ├── QCollapseItem/
│ │ │ ├── QCollapseItem.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QCollapseItem.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QCollapseItem.vue
│ │ │ └── q-collapse-item.scss
│ │ ├── QColorPicker/
│ │ │ ├── QColorAlphaSlider.test.js
│ │ │ ├── QColorHueSlider.test.js
│ │ │ ├── QColorPicker.test.js
│ │ │ ├── QColorSvpanel.test.js
│ │ │ ├── QPickerDropdown.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ ├── QColorAlphaSlider.test.js.snap
│ │ │ │ ├── QColorHueSlider.test.js.snap
│ │ │ │ ├── QColorPicker.test.js.snap
│ │ │ │ ├── QColorSvpanel.test.js.snap
│ │ │ │ └── QPickerDropdown.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QColorAlphaSlider.vue
│ │ │ ├── QColorHueSlider.vue
│ │ │ ├── QColorPicker.vue
│ │ │ ├── QColorSvpanel.vue
│ │ │ ├── QPickerDropdown.vue
│ │ │ ├── draggable.js
│ │ │ ├── q-color-alpha-slider.scss
│ │ │ ├── q-color-hue-slider.scss
│ │ │ ├── q-color-picker.scss
│ │ │ ├── q-color-svpanel.scss
│ │ │ └── q-picker-dropdown.scss
│ │ ├── QContextMenu/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QContextMenu.vue
│ │ │ └── q-context-menu.scss
│ │ ├── QDatePicker/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QDatePicker.vue
│ │ │ ├── basic/
│ │ │ │ ├── date-table.vue
│ │ │ │ ├── month-table.vue
│ │ │ │ └── year-table.vue
│ │ │ ├── helpers.js
│ │ │ ├── panel/
│ │ │ │ ├── date-range.vue
│ │ │ │ ├── date.vue
│ │ │ │ ├── focus-mixin.js
│ │ │ │ ├── focus-time-mixin.js
│ │ │ │ ├── month-range.vue
│ │ │ │ ├── range-mixin.js
│ │ │ │ └── year-range.vue
│ │ │ ├── q-date-picker.scss
│ │ │ └── styles/
│ │ │ ├── date-table.scss
│ │ │ ├── month-table.scss
│ │ │ ├── picker-panel.scss
│ │ │ ├── picker.scss
│ │ │ └── year-table.scss
│ │ ├── QDialog/
│ │ │ ├── QDialog.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QDialog.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QDialog.js
│ │ │ ├── QDialog.vue
│ │ │ └── q-dialog.scss
│ │ ├── QDrawer/
│ │ │ ├── QDrawer.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QDrawer.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QDrawer.vue
│ │ │ └── q-drawer.scss
│ │ ├── QForm/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QForm.vue
│ │ │ └── q-form.scss
│ │ ├── QFormItem/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QFormItem.vue
│ │ │ └── q-form-item.scss
│ │ ├── QInput/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QInput.vue
│ │ │ └── q-input.scss
│ │ ├── QInputNumber/
│ │ │ ├── QInputNumber.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QInputNumber.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QInputNumber.vue
│ │ │ └── q-input-number.scss
│ │ ├── QMessageBox/
│ │ │ ├── QMessageBox.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QMessageBox.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QMessageBox.js
│ │ │ ├── QMessageBox.vue
│ │ │ └── q-message-box.scss
│ │ ├── QNotification/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QNotification.js
│ │ │ ├── QNotification.vue
│ │ │ └── q-notification.scss
│ │ ├── QOption/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QOption.vue
│ │ │ └── q-option.scss
│ │ ├── QPagination/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QPagination.vue
│ │ │ └── q-pagination.scss
│ │ ├── QPopover/
│ │ │ ├── QPopover.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QPopover.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QPopover.vue
│ │ │ └── q-popover.scss
│ │ ├── QRadio/
│ │ │ ├── QRadio.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QRadio.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QRadio.vue
│ │ │ └── q-radio.scss
│ │ ├── QRadioGroup/
│ │ │ ├── QRadioGroup.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QRadioGroup.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QRadioGroup.vue
│ │ │ └── q-radio-group.scss
│ │ ├── QRow/
│ │ │ ├── QRow.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QRow.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QRow.vue
│ │ │ └── q-row.scss
│ │ ├── QScrollbar/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QBar.vue
│ │ │ ├── QScrollbar.vue
│ │ │ ├── q-scrollbar.scss
│ │ │ └── util.js
│ │ ├── QSelect/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QSelect.vue
│ │ │ ├── QSelectDropdown.vue
│ │ │ ├── QSelectTags.vue
│ │ │ ├── q-select-dropdown.scss
│ │ │ ├── q-select-tags.scss
│ │ │ └── q-select.scss
│ │ ├── QSlider/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QSlider.vue
│ │ │ ├── components/
│ │ │ │ ├── QSliderBar/
│ │ │ │ │ └── index.vue
│ │ │ │ ├── QSliderButton/
│ │ │ │ │ └── index.vue
│ │ │ │ ├── QSliderCaptions/
│ │ │ │ │ └── index.vue
│ │ │ │ └── QSliderSteps/
│ │ │ │ └── index.vue
│ │ │ ├── q-slider.scss
│ │ │ └── styles/
│ │ │ ├── q-slider-bar.scss
│ │ │ ├── q-slider-button.scss
│ │ │ ├── q-slider-captions.scss
│ │ │ ├── q-slider-steps.scss
│ │ │ └── q-slider.scss
│ │ ├── QTabPane/
│ │ │ ├── QTabPane.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QTabPane.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QTabPane.vue
│ │ │ └── q-tab-pane.scss
│ │ ├── QTable/
│ │ │ ├── QTable.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QTable.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QTable.vue
│ │ │ ├── components/
│ │ │ │ ├── DragElements/
│ │ │ │ │ └── index.vue
│ │ │ │ └── QTableRow/
│ │ │ │ └── index.vue
│ │ │ ├── hocs/
│ │ │ │ └── withQTableRow/
│ │ │ │ └── index.js
│ │ │ └── q-table.scss
│ │ ├── QTabs/
│ │ │ ├── QTabs.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QTabs.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QTabs.vue
│ │ │ └── q-tabs.scss
│ │ ├── QTag/
│ │ │ ├── QTag.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── QTag.test.js.snap
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QTag.vue
│ │ │ └── q-tag.scss
│ │ ├── QTextarea/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QTextarea.vue
│ │ │ ├── calcTextareaHeight.js
│ │ │ └── q-textarea.scss
│ │ ├── QTimePicker/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QTimePicker.vue
│ │ │ ├── components/
│ │ │ │ ├── panel.vue
│ │ │ │ └── time-panel.scss
│ │ │ └── q-time-picker.scss
│ │ ├── QUpload/
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── QUpload.vue
│ │ │ ├── QUploadDropZone.vue
│ │ │ ├── QUploadFileMultiple.vue
│ │ │ ├── QUploadFileSingle.vue
│ │ │ ├── q-upload-drop-zone.scss
│ │ │ ├── q-upload-file-multiple.scss
│ │ │ ├── q-upload-file-single.scss
│ │ │ └── q-upload.scss
│ │ ├── constants/
│ │ │ ├── locales/
│ │ │ │ ├── en.js
│ │ │ │ ├── index.js
│ │ │ │ └── ru.js
│ │ │ └── popperPlacements.js
│ │ ├── helpers/
│ │ │ ├── collapse-transition.js
│ │ │ ├── dateHelpers.js
│ │ │ └── index.js
│ │ ├── index.js
│ │ └── mixins/
│ │ ├── emitter.js
│ │ ├── inputs.js
│ │ └── pickers.js
│ ├── transition.scss
│ └── vars.scss
├── stories/
│ ├── components/
│ │ ├── Layout/
│ │ │ ├── Layout.stories.js
│ │ │ ├── QCol.stories.js
│ │ │ ├── QRow.stories.js
│ │ │ └── layout.scss
│ │ ├── QBreadcrumbs.stories.js
│ │ ├── QButton.stories.mdx
│ │ ├── QCascader.stories.js
│ │ ├── QCheckbox.stories.js
│ │ ├── QCheckboxGroup.stories.js
│ │ ├── QCollapse.stories.js
│ │ ├── QColorPicker.stories.js
│ │ ├── QContextMenu.stories.js
│ │ ├── QDatePicker/
│ │ │ ├── DateRange.js
│ │ │ ├── DateTime.js
│ │ │ ├── DateTimeRange.js
│ │ │ ├── Default.js
│ │ │ ├── Month.js
│ │ │ ├── MonthRange.js
│ │ │ ├── QDatePicker.stories.js
│ │ │ ├── Year.js
│ │ │ └── YearRange.js
│ │ ├── QDialog/
│ │ │ ├── DialogFormTest.vue
│ │ │ └── QDialog.stories.js
│ │ ├── QDrawer.stories.js
│ │ ├── QForm.stories.js
│ │ ├── QInput.stories.js
│ │ ├── QInputNumber.stories.js
│ │ ├── QMessageBox/
│ │ │ ├── MessageBoxFormTest.vue
│ │ │ └── QMessageBox.stories.js
│ │ ├── QNotification.stories.js
│ │ ├── QPagination.stories.js
│ │ ├── QPopover.stories.js
│ │ ├── QRadio.stories.js
│ │ ├── QRadioGroup.stories.js
│ │ ├── QScrollbar/
│ │ │ ├── QScrollbar.stories.js
│ │ │ └── q-scrollbar.scss
│ │ ├── QSelect/
│ │ │ ├── Default.js
│ │ │ ├── Multiple.js
│ │ │ └── QSelect.stories.js
│ │ ├── QSlider/
│ │ │ ├── Breakpoints.js
│ │ │ ├── Captions.js
│ │ │ ├── Default.js
│ │ │ ├── Disabled.js
│ │ │ ├── QSlider.stories.js
│ │ │ ├── Range.js
│ │ │ ├── Vertical.js
│ │ │ └── WithoutTooltip.js
│ │ ├── QTabPane.stories.js
│ │ ├── QTable/
│ │ │ ├── CustomRows.js
│ │ │ ├── CustomWidth.js
│ │ │ ├── Default.js
│ │ │ ├── Draggable.js
│ │ │ ├── Groups.js
│ │ │ ├── QTable.stories.js
│ │ │ ├── Selectable.js
│ │ │ ├── StickyColumn.js
│ │ │ └── Total.js
│ │ ├── QTabs.stories.js
│ │ ├── QTag.stories.js
│ │ ├── QTextarea.stories.js
│ │ ├── QTimePicker.stories.js
│ │ └── QUpload/
│ │ ├── Default.js
│ │ ├── Multiple.js
│ │ └── QUpload.stories.js
│ ├── core/
│ │ ├── colors.js
│ │ ├── colors.stories.mdx
│ │ └── icons.stories.mdx
│ └── intro.stories.mdx
└── tests/
└── unit/
└── setup.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintrc
================================================
{
"root": false,
"env": {
"browser": true
},
"parserOptions": {
"parser": "babel-eslint",
"sourceType": "module"
},
"extends": [
"eslint:recommended",
"airbnb-base",
"plugin:vue/recommended",
"prettier",
"prettier/vue"
],
"rules": {
"func-names": 0,
"no-unused-expressions": [
"error",
{
"allowShortCircuit": true,
"allowTernary": false,
"allowTaggedTemplates": false
}
],
"prefer-destructuring": [
"error",
{
"array": false
}
],
"import/extensions": "off",
"import/no-unresolved": "off",
"vue/component-name-in-template-casing": ["error", "kebab-case"],
"vue/max-attributes-per-line": "warn",
"vue/no-v-html": 0,
"vue/html-closing-bracket-newline": "warn",
"vue/html-indent": "warn",
"no-console": 0,
"no-debugger": 0
},
"overrides": [
{
"files": ["**/*.test.js"],
"env": { "jest": true },
"globals": {
"mount": "readonly",
"shallowMount": "readonly"
}
}
]
}
================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: 'CodeQL'
on:
push:
branches: [master]
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
schedule:
- cron: '21 8 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: ['javascript']
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
================================================
FILE: .github/workflows/deploy.yml
================================================
name: github pages
on:
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Setup Node
uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Cache dependencies
uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- run: yarn --frozen-lockfile
- run: yarn build-storybook
- run: yarn deploy-storybook -- --ci
env:
GH_TOKEN: Qvant-lab:${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .gitignore
================================================
.DS_Store
node_modules
coverage
/dist
.out
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
================================================
FILE: .lintstagedrc
================================================
{
"*.js": ["prettier --write", "git add"],
"*.scss": ["stylelint --fix", "prettier --write", "git add"],
"*.vue": [
"stylelint --fix",
"prettier --parser vue --write",
"eslint --fix --no-ignore",
"git add"
],
"{*.json,.*rc}": ["prettier --parser json --write", "git add"],
"*.{yaml,yml}": ["prettier --parser yaml --write", "git add"]
}
================================================
FILE: .npmignore
================================================
yarn-error.log
/.storybook
/.github
/.readme-assets
================================================
FILE: .nvmrc
================================================
12.22.6
================================================
FILE: .prettierignore
================================================
.nvmrc
/dist
================================================
FILE: .prettierrc
================================================
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"quoteProps": "as-needed",
"trailingComma": "none",
"bracketSpacing": true,
"arrowParens": "avoid",
"endOfLine": "lf"
}
================================================
FILE: .storybook/locales/en.js
================================================
export default {
qBreadcrumbsStories: {
routeA: 'Route A',
routeB: 'Route B',
routeC: 'Route C',
routeD: 'Route D'
}
};
================================================
FILE: .storybook/locales/index.js
================================================
import en from './en';
import ru from './ru';
import {
en as qMessagesEn,
ru as qMessagesRu
} from '../../src/qComponents/constants/locales';
export default {
en: {
...en,
...qMessagesEn
},
ru: {
...ru,
...qMessagesRu
}
};
================================================
FILE: .storybook/locales/ru.js
================================================
export default {
qBreadcrumbsStories: {
routeA: 'Роут А',
routeB: 'Очень длинный маршрут Б',
routeC: 'Роут С',
routeD: 'Роут D'
}
};
================================================
FILE: .storybook/main.js
================================================
module.exports = {
stories: ['../stories/**/**/*.stories.@(ts|js|mdx)'],
logLevel: 'debug',
addons: [
'@storybook/addon-docs',
'@storybook/addon-controls',
'@storybook/addon-storysource',
'@storybook/preset-scss',
'@storybook/addon-toolbars'
]
};
================================================
FILE: .storybook/manager.js
================================================
import addons from '@storybook/addons';
import theme from './theme';
addons.setConfig({
theme
});
================================================
FILE: .storybook/preview.js
================================================
import Vue from 'vue';
import Qui from '../src/qComponents';
import VueI18n from 'vue-i18n';
import messages from './locales';
Vue.use(VueI18n);
Vue.use(Qui, {
localization: {
locale: 'en'
}
});
export const parameters = {
layout: 'centered',
controls: { expanded: true },
docs: {
inlineStories: true
}
};
export const globalTypes = {
locale: {
name: 'Locale',
description: 'Internationalization locale',
defaultValue: 'en',
toolbar: {
icon: 'globe',
items: [
{ value: 'en', right: '🇺🇸', title: 'English' },
{ value: 'ru', right: '🇷🇺', title: 'Русский' }
]
}
}
};
const i18n = new VueI18n({
locale: 'en',
messages,
silentTranslationWarn: true,
silentFallbackWarn: true,
fallbackRoot: true
});
export const decorators = [
(args, { globals: { locale } }) => ({
i18n,
beforeCreate: function() {
this.$Q.locale = locale;
i18n.locale = locale;
this.$root._i18n = i18n;
},
template: ' '
})
];
================================================
FILE: .storybook/theme.js
================================================
import { create } from '@storybook/theming/create';
import logo from '../.readme-assets/qui-logo.svg';
export default create({
name: 'Theme',
base: 'light',
brandTitle: 'QUI',
brandUrl: 'https://qvant-lab.github.io/qui/',
brandImage: logo
});
================================================
FILE: .stylelintignore
================================================
/src/normalize.scss
================================================
FILE: .stylelintrc
================================================
{
"extends": ["stylelint-config-prettier"],
"plugins": ["stylelint-order"],
"rules": {
"color-hex-length": "short",
"color-named": "never",
"font-weight-notation": "numeric",
"function-url-quotes": ["always", { "except": ["empty"] }],
"length-zero-no-unit": true,
"rule-empty-line-before": [
"always-multi-line",
{ "ignore": ["after-comment", "first-nested"] }
],
"selector-pseudo-element-colon-notation": "double",
"order/properties-order": [
"content",
"position",
"top",
"right",
"bottom",
"left",
"z-index",
"display",
"flex",
"flex-grow",
"flex-shrink",
"flex-basis",
"flex-flow",
"flex-direction",
"flex-wrap",
"justify-content",
"align-content",
"align-items",
"order",
"align-self",
"float",
"clear",
"box-sizing",
"width",
"min-width",
"max-width",
"height",
"min-height",
"max-height",
"margin",
"margin-top",
"margin-right",
"margin-bottom",
"margin-left",
"padding",
"padding-top",
"padding-right",
"padding-bottom",
"padding-left",
"overflow",
"overflow-x",
"overflow-y",
"list-style",
"list-style-position",
"list-style-type",
"list-style-image",
"border-collapse",
"border-spacing",
"table-layout",
"empty-cells",
"caption-side",
"font",
"font-style",
"font-variant",
"font-weight",
"font-size",
"line-height",
"font-family",
"vertical-align",
"text-align",
"direction",
"color",
"text-transform",
"text-decoration",
"font-size-adjust",
"font-stretch",
"font-effect",
"font-emphasize",
"font-emphasize-position",
"font-emphasize-style",
"font-smooth",
"text-align-last",
"letter-spacing",
"word-spacing",
"white-space",
"text-emphasis",
"text-emphasis-color",
"text-emphasis-style",
"text-emphasis-position",
"text-indent",
"text-justify",
"-ms-writing-mode",
"text-outline",
"text-wrap",
"text-overflow",
"text-overflow-ellipsis",
"text-overflow-mode",
"text-orientation",
"word-wrap",
"word-break",
"tab-size",
"hyphens",
"unicode-bidi",
"columns",
"column-count",
"column-fill",
"column-gap",
"column-rule",
"column-rule-color",
"column-rule-style",
"column-rule-width",
"column-span",
"column-width",
"text-shadow",
"page-break-after",
"page-break-before",
"page-break-inside",
"background",
"background-color",
"background-image",
"linear-gradient",
"background-repeat",
"background-position",
"background-position-x",
"background-position-y",
"background-size",
"background-clip",
"background-origin",
"background-attachment",
"box-decoration-break",
"background-blend-mode",
"border",
"border-width",
"border-style",
"border-color",
"border-top",
"border-top-width",
"border-top-style",
"border-top-color",
"border-right",
"border-right-width",
"border-right-style",
"border-right-color",
"border-bottom",
"border-bottom-width",
"border-bottom-style",
"border-bottom-color",
"border-left",
"border-left-width",
"border-left-style",
"border-left-color",
"border-radius",
"border-top-left-radius",
"border-top-right-radius",
"border-bottom-right-radius",
"border-bottom-left-radius",
"border-image",
"border-image-source",
"border-image-slice",
"border-image-width",
"border-image-outset",
"border-image-repeat",
"outline",
"outline-width",
"outline-style",
"outline-color",
"outline-offset",
"box-shadow",
"transform",
"transform-origin",
"backface-visibility",
"perspective",
"perspective-origin",
"transform-style",
"visibility",
"cursor",
"opacity",
"filter",
"transition",
"transition-delay",
"transition-timing-function",
"transition-duration",
"transition-property",
"animation",
"animation-name",
"animation-duration",
"animation-play-state",
"animation-timing-function",
"animation-delay",
"animation-iteration-count",
"animation-direction",
"quotes",
"counter-reset",
"counter-increment",
"resize",
"user-select",
"nav-index",
"nav-up",
"nav-right",
"nav-down",
"nav-left",
"pointer-events",
"will-change",
"clip",
"clip-path",
"zoom"
]
}
}
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at . All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Qvant
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# A Vue.js Neumorphism Design System for Web
Responsive, user-friendly and lightweight library helping us build great products for our customers. This library for Vue 2.x
‼️ Currently we are working on [Qui Max](https://github.com/Qvant-lab/qui-max) for Vue 3.x, it is more complex and modern version of Qui - we recommend use it instead of this package. Qui remains for Vue 2.x, but we can't claim it will be supported well.
[Storybook (live demo)](https://qvant-lab.github.io/qui/)
What is it?
- 🔩 30+ Vue components
- 📦 icons pack
- 🏳️🌈 colors & grid
- 🥷 neumorphism styles
- 📚 storybook sandbox
Some examples below:






## Install
CDN:
```html
```
Npm | Yarn:
```bash
npm install @qvant/qui -S
yarn add @qvant/qui
```
You can import Qui entirely, or just import what you need. Let's start with fully import.
## Quick setup
In main.js:
```js
import Vue from 'vue';
import Qui from '@qvant/qui';
import '@qvant/qui/dist/qui.css';
// Setup all components
Vue.use(Qui);
// that's it! All components will be imported with styles
```
in YourComponent.vue: (Example)
```vue
```
...or configure quick setup
In main.js:
```js
import Vue from 'vue';
import Qui from '@qvant/qui';
import '@qvant/qui/dist/qui.css';
Vue.use(Qui, {
localization: {
locale: 'en', // Russian language by default, you can set `en` for English
customI18nMessages: {
// rewrite default texts, see the source: src/qComponents/constants/locales
en: {
QDatepicker: {
placeholder: 'Pick your birthday!'
}
}
},
zIndexCounter: 3000, // zIndexCounter is being used by some components, (e.g QPopover, QSelect, QDialog ...etc), 2000 by default
prefix: 'yo' // you can change component's prefix, e.g. must be used instead of
}
});
```
in YourComponent.vue: (Example)
```vue
```
Now you have implemented Vue and Qui to your project, and it's time to write your code.
Please refer to each component's [Stories](https://qvant-lab.github.io/qui/) to learn how to use them.
## Not quick setup
If you have a module bundler (e.g webpack), you can import components separately and take care about your bundle size
In main.js:
```js
// import the main plugin from another place (it ensures Qui will be installed without any components, but instance will set required properties and directives)
import Qui from '@qvant/qui/src/onDemand';
// import the component you want
import QButton from '@qvant/qui/src/qComponents/QButton';
// ...or in async way
Vue.component('q-button', () =>
import(/* webpackChunkName: "qui" */ '@qvant/qui/src/qComponents/QButton')
);
// init
Vue.use(Qui);
Vue.use(QButton);
```
In main.scss:
```scss
// need to set the path for files with statics
$--base-path: '~@qvant/qui/src';
// set main styles
@import '~@qvant/qui/src/main.scss';
// notice that you must use `fonts` and `icons` styles for some of components:
@import '~@qvant/qui/src/fonts/index.scss';
@import '~@qvant/qui/src/icons/index.scss';
```
import all styles:
```scss
@import '~@qvant/qui/src/components.scss';
```
...or components separately:
```scss
@import '~@qvant/qui/src/qComponents/QBreadcrumbs/src/q-breadcrumbs.scss';
@import '~@qvant/qui/src/qComponents/QButton/src/q-button.scss';
// ...etc
```
## Optional
- if you want use modals inside your components as property of 'this':
```js
import { QMessageBox, QDialog, QNotification } from '@qvant/qui';
// or import separately
import QMessageBox from '@qvant/qui/src/qComponents/QMessageBox';
import QDialog from '@qvant/qui/src/qComponents/QDialog';
import QNotification from '@qvant/qui/src/qComponents/QNotification';
Vue.prototype.$message = QMessageBox;
Vue.prototype.$dialog = QDialog;
Vue.prototype.$notify = options =>
QNotification({
duration: 3000, // - ms
...options
});
```
- if you use VueI18n, you need to merge messages:
```js
import VueI18n from 'vue-i18n';
import { en, ru } from '@qvant/qui/src/qComponents/constants/locales';
Vue.use(VueI18n);
const messages = {
en: {
message: {
hello: 'hello world'
},
...en
},
ru: {
message: {
hello: 'привет, мир'
},
...ru
}
};
const i18n = new VueI18n({
locale: 'en',
messages
});
new Vue({
i18n
}).$mount('#your-app');
```
## Supported languages
- Russian ✅
- English ✅
- Also you can use any language by setting texts for components via 'customI18nMessages' property in the Qui instance. See the example above.
## Browser Support
Modern browsers are recomended
- safari: >11
- chrome: >=61
- firefox: >=58
- opera: >=62
- edge: >=16
- yandex: >=18
- ie: ? (we don't know :) and will not support it)
## Development
Clone repository and run storybook
```bash
yarn storybook
npm run storybook
```
## LICENSE
MIT
================================================
FILE: babel.config.js
================================================
module.exports = {
presets: ['@vue/cli-plugin-babel/preset']
};
================================================
FILE: jest.config.js
================================================
module.exports = {
moduleFileExtensions: ['js', 'vue', 'json'],
transform: {
'^.+\\.vue$': 'vue-jest',
'^.+\\.js$': 'babel-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
'jest-transform-stub'
},
transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\](?!lodash-es/).+\\.js$'],
setupFiles: ['/tests/unit/setup.js'],
testMatch: ['/src/**/*.test.(ts|tsx|js)'],
testURL: 'http://localhost/',
coverageReporters: ['text-summary', 'html', 'lcov', 'clover'],
coverageDirectory: '/tests/unit/coverage',
coverageThreshold: {
global: {
branches: 7,
functions: 14,
lines: 17
}
}
};
================================================
FILE: package.json
================================================
{
"name": "@qvant/qui",
"version": "1.4.5",
"private": false,
"description": "A Vue.js Design system for Web.",
"author": {
"name": "Qvant Frontend team"
},
"scripts": {
"build": "npx vue-cli-service build --target lib --name qui --entry ./src/qComponents/index.js",
"build-storybook": "build-storybook -c .storybook -o .out",
"create-badges": "node scripts/badges.js",
"deploy-storybook": "storybook-to-ghpages",
"eslint": "eslint --fix --no-ignore .storybook/**/**/*.{vue,js}",
"prettier": "prettier --write '**/*.{js,vue,scss,json,yml}'",
"storybook": "start-storybook -s ./public -p 6006",
"stylelint:fix": "stylelint --fix '**/*.{scss,vue}'",
"test:unit": "jest"
},
"main": "./dist/qui.common.js",
"dependencies": {
"@popperjs/core": "^2.4.4",
"async-validator": "^3.4.0",
"color": "^3.1.2",
"date-fns": "^2.15.0",
"focus-visible": "^5.2.0",
"jest-transform-stub": "^2.0.0",
"lodash-es": "^4.17.15",
"resize-observer-polyfill": "^1.5.0",
"v-click-outside": "^3.1.2",
"vue": "^2.6.11",
"vue-i18n": "^8.22.2"
},
"devDependencies": {
"@babel/core": "^7.10.4",
"@storybook/addon-controls": "^6.1.10",
"@storybook/addon-docs": "^6.1.10",
"@storybook/addon-storysource": "^6.1.10",
"@storybook/addon-toolbars": "^6.1.11",
"@storybook/preset-scss": "^1.0.3",
"@storybook/storybook-deployer": "^2.8.7",
"@storybook/theming": "^6.1.10",
"@storybook/vue": "^6.1.10",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/test-utils": "^1.1.1",
"babel-eslint": "^10.1.0",
"babel-jest": "^26.6.3",
"babel-loader": "^8.1.0",
"babel-preset-vue": "^2.0.2",
"badges": "^4.24.0",
"css-loader": "^4.3.0",
"eslint": "^7.9.0",
"eslint-config-airbnb-base": "^14.1.0",
"eslint-config-prettier": "^6.7.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-vue": "^6.0.1",
"husky": "^4.2.5",
"jest": "^26.6.3",
"lint-staged": "^10.1.7",
"node-sass": "^4.13.0",
"sass-loader": "^10.0.2",
"style-loader": "^1.2.1",
"stylelint": "^13.7.1",
"stylelint-config-prettier": "^8.0.1",
"stylelint-order": "^4.0.0",
"vue-jest": "^3.0.7",
"vue-loader": "^15.9.3",
"vue-template-compiler": "^2.6.11"
},
"_id": "@qvant/qui@1.4.5",
"bugs": {
"url": "https://github.com/Qvant-lab/qui/issues"
},
"contributors": [
{
"name": "Viktor Zheleztsov",
"url": "https://github.com/ViZhe"
},
{
"name": "Maksim Novikov",
"url": "https://github.com/Esvalirion"
},
{
"name": "Shamil Alisultanov",
"url": "https://github.com/shamilfrontend"
}
],
"demo": "https://qvant-lab.github.io/qui/",
"homepage": "https://github.com/Qvant-lab/qui#readme",
"husky": {
"hooks": {
"pre-commit": "lint-staged && yarn test:unit --coverage"
}
},
"keywords": [
"design-system",
"components",
"library",
"ui",
"vue",
"sass",
"neumorphism"
],
"license": "MIT",
"maintainers": [
{
"name": "Tim Bochkarev",
"url": "https://github.com/Tim152"
}
],
"repository": {
"type": "git",
"url": "git+https://github.com/Qvant-lab/qui.git"
}
}
================================================
FILE: scripts/badges.js
================================================
// edit `list` & run `node scripts/badges.js` for creating new badges
const { renderBadges } = require('badges');
const fs = require('fs');
// Listing of badges to output
const list = [
[
'shields',
{
left: 'storybook',
right: 'yes',
color: 'green',
alt: 'storybook',
url: 'https://qvant-lab.github.io/qui',
title: 'storybook'
}
],
[
'shields',
{
left: 'responsive',
right: 'yes',
color: 'green',
title: 'responsive'
}
],
'npmversion',
'npmdownloads',
'daviddm',
'daviddmdev'
];
// Configuration for the badges
const config = {
npmPackageName: '@qvant/qui',
saucelabsUsername: 'Qui',
githubSlug: 'Qvant-lab/qui',
nodeicoQueryString: { downloads: true, compact: true, height: 2 },
homepage: 'https://qvant-lab.github.io/qui/'
};
// Options for rendering the badges
const options = {
// Filter Category
// When set to a string, will only render badges from the list that of the specified category
// Values can be 'development', 'testing', 'funding', or 'social'
// E.g. to render only funding badges, set to 'funding'
filterCategory: false,
// Filter Scripts
// When true, do not render any badges from the list that are scripts
filterScripts: false
};
// Render the badges to a string
const badges = renderBadges(list, config, options);
fs.readFile('README.md', 'utf8', function(err, data) {
if (err) {
return console.log(err);
}
const result = data.replace(
/((.|\n)*)/g,
`${badges}`
);
fs.writeFile('README.md', result, 'utf8', function(error) {
if (error) return console.log(error);
});
});
================================================
FILE: src/components.scss
================================================
@import '../qComponents/QBreadcrumbs/src/q-breadcrumbs.scss';
@import '../qComponents/QButton/src/q-button.scss';
@import '../qComponents/QCascader/src/q-cascader.scss';
@import '../qComponents/QCheckbox/src/q-checkbox.scss';
@import '../qComponents/QCheckboxGroup/src/q-checkbox-group.scss';
@import '../qComponents/QCol/src/q-col.scss';
@import '../qComponents/QCollapseItem/src/q-collapse-item.scss';
@import '../qComponents/QColorPicker/src/q-color-picker.scss';
@import '../qComponents/QContextMenu/src/q-context-menu.scss'; //
@import '../qComponents/QDatePicker/src/q-date-picker.scss';
@import '../qComponents/QDialog/src/q-dialog.scss';
@import '../qComponents/QDrawer/src/q-drawer.scss';
@import '../qComponents/QFormItem/src/q-form-item.scss';
@import '../qComponents/QInput/src/q-input.scss';
@import '../qComponents/QInputNumber/src/q-input-number.scss';
@import '../qComponents/QMessageBox/src/q-message-box.scss';
@import '../qComponents/QNotification/src/q-notification.scss';
@import '../qComponents/QOption/src/q-option.scss';
@import '../qComponents/QPagination/src/q-pagination.scss';
@import '../qComponents/QPopover/src/q-popover.scss';
@import '../qComponents/QRadio/src/q-radio.scss';
@import '../qComponents/QRadioGroup/src/q-radio-group.scss';
@import '../qComponents/QRow/src/q-row.scss';
@import '../qComponents/QScrollbar/src/q-scrollbar.scss';
@import '../qComponents/QSelect/src/q-select.scss';
@import '../qComponents/QSlider/src/q-slider.scss';
@import '../qComponents/QTable/src/q-table.scss';
@import '../qComponents/QTabPane/src/q-tab-pane.scss';
@import '../qComponents/QTabs/src/q-tabs.scss';
@import '../qComponents/QTag/src/q-tag.scss';
@import '../qComponents/QTextarea/src/q-textarea.scss';
@import '../qComponents/QTimePicker/src/q-time-picker.scss';
@import '../qComponents/QUpload/src/q-upload.scss';
================================================
FILE: src/fonts/index.scss
================================================
$--base-path: '..' !default;
@font-face {
font-display: swap;
font-style: normal;
font-weight: 800;
font-family: 'Gilroy';
src: local('Gilroy ExtraBold'), local('Gilroy-ExtraBold'),
url('#{$--base-path}/fonts/Gilroy-ExtraBold.woff?29042020') format('woff');
}
@font-face {
font-display: swap;
font-style: normal;
font-weight: 600;
font-family: 'Gilroy';
src: local('Gilroy Bold'), local('Gilroy-Bold'),
url('#{$--base-path}/fonts/Gilroy-Bold.woff?29042020') format('woff');
}
@font-face {
font-display: swap;
font-style: normal;
font-weight: 500;
font-family: 'Gilroy';
src: local('Gilroy Medium'), local('Gilroy-Medium'),
url('#{$--base-path}/fonts/Gilroy-Medium.woff?29042020') format('woff');
}
@font-face {
font-display: swap;
font-style: normal;
font-weight: 400;
font-family: 'Gilroy';
src: local('Gilroy Regular'), local('Gilroy-Regular'),
url('#{$--base-path}/fonts/Gilroy-Regular.woff?29042020') format('woff');
}
================================================
FILE: src/icons/index.scss
================================================
$--base-path: '..' !default;
@font-face {
font-style: normal;
font-weight: 400;
font-family: 'qicon';
src: url('#{$--base-path}/icons/qicon.woff?07092021') format('woff');
font-display: block;
}
[class^='q-icon-'],
[class*=' q-icon-'] {
display: inline-block;
font-style: normal;
font-variant: normal;
font-weight: 400;
line-height: 1;
// use !important to prevent issues with browser extensions that change fonts
// stylelint-disable-next-line font-family-no-missing-generic-family-keyword
font-family: 'qicon' !important;
vertical-align: baseline;
text-transform: none;
letter-spacing: 0;
speak: none;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.q-icon-y-4::before {
content: '\e900';
}
.q-icon-y-3::before {
content: '\e901';
}
.q-icon-y-2::before {
content: '\e902';
}
.q-icon-y-1::before {
content: '\e903';
}
.q-icon-withdraw-fill::before {
content: '\e904';
}
.q-icon-wifi::before {
content: '\e905';
}
.q-icon-wallet::before {
content: '\e906';
}
.q-icon-wallet-stroke::before {
content: '\e907';
}
.q-icon-view-list::before {
content: '\e908';
}
.q-icon-triangle-up::before {
content: '\e909';
}
.q-icon-triangle-right::before {
content: '\e90a';
}
.q-icon-triangle-left::before {
content: '\e90b';
}
.q-icon-triangle-down::before {
content: '\e90c';
}
.q-icon-trash-bin::before {
content: '\e90d';
}
.q-icon-trash-bin-stroke::before {
content: '\e90e';
}
.q-icon-target::before {
content: '\e90f';
}
.q-icon-stop-2::before {
content: '\e910';
}
.q-icon-stop-1::before {
content: '\e911';
}
.q-icon-star::before {
content: '\e912';
}
.q-icon-star-fill::before {
content: '\e913';
}
.q-icon-settings-vertical::before {
content: '\e914';
}
.q-icon-settings-horizontal::before {
content: '\e915';
}
.q-icon-search::before {
content: '\e916';
}
.q-icon-save::before {
content: '\e917';
}
.q-icon-rubles::before {
content: '\e918';
}
.q-icon-rubles-circle::before {
content: '\e919';
}
.q-icon-router::before {
content: '\e91a';
}
.q-icon-router-arrow-down::before {
content: '\e91b';
}
.q-icon-reverse::before {
content: '\e91c';
}
.q-icon-question::before {
content: '\e91d';
}
.q-icon-question-mark::before {
content: '\e91e';
}
.q-icon-proceed-2::before {
content: '\e91f';
}
.q-icon-proceed-1::before {
content: '\e920';
}
.q-icon-plus::before {
content: '\e921';
}
.q-icon-play::before {
content: '\e922';
}
.q-icon-piggy-bank-fill::before {
content: '\e923';
}
.q-icon-pic::before {
content: '\e924';
}
.q-icon-phone-settings-stroke::before {
content: '\e925';
}
.q-icon-percent::before {
content: '\e926';
}
.q-icon-pencil-square-stroke::before {
content: '\e927';
}
.q-icon-pencil-list::before {
content: '\e928';
}
.q-icon-pause::before {
content: '\e929';
}
.q-icon-multiply-fill::before {
content: '\e92a';
}
.q-icon-minus::before {
content: '\e92b';
}
.q-icon-menu::before {
content: '\e92c';
}
.q-icon-menu-2-fill::before {
content: '\e92d';
}
.q-icon-logout::before {
content: '\e92e';
}
.q-icon-login::before {
content: '\e92f';
}
.q-icon-lock::before {
content: '\e930';
}
.q-icon-lock-fill::before {
content: '\e931';
}
.q-icon-key::before {
content: '\e932';
}
.q-icon-info::before {
content: '\e933';
}
.q-icon-info-stroke::before {
content: '\e934';
}
.q-icon-info-fill::before {
content: '\e935';
}
.q-icon-house-stroke::before {
content: '\e936';
}
.q-icon-graph-gisto::before {
content: '\e937';
}
.q-icon-finish-fill::before {
content: '\e938';
}
.q-icon-filter-stroke::before {
content: '\e939';
}
.q-icon-filter-fill::before {
content: '\e93a';
}
.q-icon-file::before {
content: '\e93b';
}
.q-icon-eye::before {
content: '\e93c';
}
.q-icon-eye-fill::before {
content: '\e93d';
}
.q-icon-eye-close::before {
content: '\e93e';
}
.q-icon-envelope-edit::before {
content: '\e93f';
}
.q-icon-earth::before {
content: '\e940';
}
.q-icon-drag-linear::before {
content: '\e941';
}
.q-icon-drag-vertical-fill::before {
content: '\e942';
}
.q-icon-double-triangle-right::before {
content: '\e943';
}
.q-icon-double-triangle-left::before {
content: '\e944';
}
.q-icon-dots-3-horizontal::before {
content: '\e945';
}
.q-icon-diagram-round::before {
content: '\e946';
}
.q-icon-diagram-round-stroke::before {
content: '\e947';
}
.q-icon-database-arrow-down::before {
content: '\e948';
}
.q-icon-credit-card::before {
content: '\e949';
}
.q-icon-comment::before {
content: '\e94a';
}
.q-icon-cog-stroke::before {
content: '\e94b';
}
.q-icon-cloud-upload::before {
content: '\e94c';
}
.q-icon-cog-fill::before {
content: '\e94d';
}
.q-icon-close::before {
content: '\e94e';
}
.q-icon-clock-stroke::before {
content: '\e94f';
}
.q-icon-checkbox-square-multiply-non::before {
content: '\e950';
}
.q-icon-check::before {
content: '\e951';
}
.q-icon-change-list::before {
content: '\e952';
}
.q-icon-chain::before {
content: '\e953';
}
.q-icon-cart::before {
content: '\e954';
}
.q-icon-calendar::before {
content: '\e955';
}
.q-icon-calendar-refresh::before {
content: '\e956';
}
.q-icon-calendar-clock::before {
content: '\e957';
}
.q-icon-bell::before {
content: '\e958';
}
.q-icon-bell-ring::before {
content: '\e959';
}
.q-icon-attention-mark::before {
content: '\e95a';
}
.q-icon-attach-fill::before {
content: '\e95b';
}
.q-icon-arrow-up::before {
content: '\e95c';
}
.q-icon-arrow-right::before {
content: '\e95d';
}
.q-icon-arrow-left::before {
content: '\e95e';
}
.q-icon-arrow-down::before {
content: '\e95f';
}
.q-icon-alert-stroke::before {
content: '\e960';
}
.q-icon-archive-arrow-down::before {
content: '\e961';
}
.q-icon-alert-fill::before {
content: '\e962';
}
.q-icon-account::before {
content: '\e963';
}
.q-icon-account-settings::before {
content: '\e964';
}
.q-icon-account-group::before {
content: '\e965';
}
.q-icon-account-group-web::before {
content: '\e966';
}
.q-icon-account-couple-fill::before {
content: '\e967';
}
.q-icon-account-check-fill::before {
content: '\e968';
}
================================================
FILE: src/main.scss
================================================
@import './normalize.scss';
@import './vars.scss';
@import './transition.scss';
*,
*::before,
*::after {
box-sizing: border-box;
}
html,
body,
#app {
height: 100%;
}
body {
margin: 0;
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
line-height: var(--line-height-base);
font-family: 'Gilroy', sans-serif;
color: rgba(var(--color-rgb-gray), 0.64);
letter-spacing: var(--letter-spacing-base);
background-color: var(--color-tertiary-gray-lighter);
}
svg {
vertical-align: middle;
}
button {
cursor: pointer;
&:focus {
outline: none;
}
}
a {
color: var(--color-primary-blue);
text-decoration: none;
&:focus {
outline: none;
}
}
/*
:focus-visible polyfill.
This will hide the focus indicator if the element receives focus via the mouse,
but it will still show up on keyboard focus.
*/
.js-focus-visible :focus:not(.focus-visible) {
outline: none;
}
================================================
FILE: src/normalize.scss
================================================
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
margin: 0.67em 0;
font-size: 2em;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-size: 1em; /* 2 */
font-family: monospace, monospace; /* 1 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
border-bottom: none; /* 1 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-size: 1em; /* 2 */
font-family: monospace, monospace; /* 1 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
margin: 0; /* 2 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
font-family: inherit; /* 1 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input {
/* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select {
/* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type='button'],
[type='reset'],
[type='submit'] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type='button']::-moz-focus-inner,
[type='reset']::-moz-focus-inner,
[type='submit']::-moz-focus-inner {
padding: 0;
border-style: none;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
display: table; /* 1 */
box-sizing: border-box; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
color: inherit; /* 2 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type='checkbox'],
[type='radio'] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type='number']::-webkit-inner-spin-button,
[type='number']::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type='search'] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type='search']::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}
================================================
FILE: src/onDemand.js
================================================
/* eslint-disable global-require */
/* eslint-disable no-param-reassign */
import vClickOutside from 'v-click-outside';
import 'focus-visible';
import { version } from '../package.json';
import { installI18n } from './qComponents/constants/locales';
const install = (
Vue,
{
localization: { locale = 'ru', customI18nMessages = {} } = {},
zIndexCounter = 2000
} = {}
) => {
Vue.prototype.$Q = {};
// define plugins
Object.defineProperties(Vue.prototype.$Q, {
zIndex: {
get() {
zIndexCounter += 1;
return zIndexCounter;
}
},
locale: {
value: locale
}
});
Vue.use(vClickOutside);
installI18n({ locale, customI18nMessages });
};
const Qui = {
version,
install
};
export default Qui;
================================================
FILE: src/qComponents/QBreadcrumbs/QBreadcrumbs.test.js
================================================
import Component from './src/QBreadcrumbs';
describe('QBreadcrumbs', () => {
let instance;
let options;
beforeEach(() => {
options = {
mocks: {
$route: { matched: [{ meta: 'one' }] }
}
};
instance = shallowMount(Component, options);
});
it('should match snapshot', () => {
expect(instance.element).toMatchSnapshot();
});
describe('computed', () => {
it('lastCrumb should return custom last crumb if it has been passed', () => {
const expected = 'one';
instance.setProps({
last: expected
});
expect(instance.vm.lastCrumb).toEqual(expected);
});
});
describe('pushTo', () => {
it(`should return the router's 'name' if it has been passed`, () => {
const route = { name: 'R_MAIN', path: '/main' };
const expected = { name: route.name };
expect(instance.vm.pushTo(route)).toEqual(expected);
});
it(`should return the router's 'path' if the 'name' hasn't been passed`, () => {
const route = { path: '/main' };
const expected = route.path;
expect(instance.vm.pushTo(route)).toEqual(expected);
});
});
});
================================================
FILE: src/qComponents/QBreadcrumbs/__snapshots__/QBreadcrumbs.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QBreadcrumbs should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QBreadcrumbs/index.js
================================================
import QBreadcrumbs from './src/QBreadcrumbs.vue';
/* istanbul ignore next */
QBreadcrumbs.install = function(Vue) {
Vue.component(QBreadcrumbs.name, QBreadcrumbs);
};
export default QBreadcrumbs;
================================================
FILE: src/qComponents/QBreadcrumbs/src/QBreadcrumbs.vue
================================================
{{ crumb.meta.breadcrumb }}
{{ lastCrumb }}
================================================
FILE: src/qComponents/QBreadcrumbs/src/q-breadcrumbs.scss
================================================
.q-breadcrumbs {
display: inline-grid;
grid-auto-flow: column;
align-items: baseline;
max-width: 100%;
&__divider {
position: relative;
top: 5px;
margin-right: 8px;
margin-left: 8px;
font-size: 24px;
color: rgba(var(--color-rgb-gray), 0.32);
}
&__crumb {
overflow: hidden;
font-weight: 800;
font-size: 24px;
line-height: 31px;
color: var(--color-primary-blue);
white-space: nowrap;
text-overflow: ellipsis;
&:focus {
text-decoration: underline;
}
&_last:first-child {
color: var(--color-primary-black);
}
&_exact-active:not(:first-child),
&_last:not(:first-child) {
font-weight: var(--font-weight-bold);
font-size: 16px;
line-height: var(--line-height-base);
color: var(--color-primary-black);
cursor: default;
}
}
}
================================================
FILE: src/qComponents/QButton/QButton.test.js
================================================
import Component from './src/QButton';
describe('QButton', () => {
it('should match snapshot', () => {
const { element } = shallowMount(Component);
expect(element).toMatchSnapshot();
});
it(`should have class 'q-button_type_icon' if type is icon`, () => {
const instance = shallowMount(Component);
const expected = 'q-button_type_icon';
instance.setProps({
type: 'icon'
});
expect(instance.vm.classes).toContain(expected);
});
it(`should have class 'q-button_theme_link' if theme is link`, () => {
const instance = shallowMount(Component);
const expected = 'q-button_theme_link';
instance.setProps({
theme: 'link'
});
expect(instance.vm.classes).toContain(expected);
});
describe('handleClick', () => {
it('should emit click', () => {
const instance = shallowMount(Component);
instance.vm.handleClick();
expect(instance.emitted().click).toBeTruthy();
});
});
});
================================================
FILE: src/qComponents/QButton/__snapshots__/QButton.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QButton should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QButton/index.js
================================================
import QButton from './src/QButton.vue';
/* istanbul ignore next */
QButton.install = function(Vue) {
Vue.component(QButton.name, QButton);
};
export default QButton;
================================================
FILE: src/qComponents/QButton/src/QButton.vue
================================================
================================================
FILE: src/qComponents/QButton/src/q-button.scss
================================================
.q-button {
display: inline-block;
box-sizing: border-box;
max-height: 40px;
padding: 12px 40px;
font-weight: var(--font-weight-bold);
font-size: 12px;
line-height: var(--line-height-button);
vertical-align: middle;
text-align: center;
color: var(--color-tertiary-white);
text-transform: uppercase;
letter-spacing: var(--letter-spacing-base);
white-space: nowrap;
background-color: var(--color-primary);
background-image: var(--gradient-primary);
border: none;
border-radius: var(--border-radius-base);
outline: none;
box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
4px 4px 8px rgba(var(--color-rgb-blue), 0.4),
-4px -4px 12px var(--color-tertiary-white);
cursor: pointer;
transition: background-color 0.1s;
appearance: none;
& + & {
margin-left: 16px;
}
&:focus {
outline: none;
}
&:hover {
background-color: var(--color-primary);
background-image: none;
box-shadow: -1px -1px 4px rgba(var(--color-rgb-white), 0.25),
1px 1px 4px rgba(var(--color-rgb-blue), 0.4),
4px 4px 8px rgba(var(--color-rgb-blue), 0.4),
-4px -4px 8px rgba(var(--color-rgb-white), 0.8);
}
&:active {
background-image: none;
box-shadow: var(--box-shadow-pressed);
}
&:visited {
background-image: var(--gradient-primary);
box-shadow: var(--box-shadow-primary);
}
&::-moz-focus-inner {
border: 0;
}
&_type {
&_icon {
display: inline-flex;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
padding: 0;
font-size: 26px;
}
}
&_theme {
&_primary {
&.focus-visible {
background-color: var(--color-primary-darker);
background-image: none;
}
}
&_secondary {
color: var(--color-primary-blue);
background-color: var(--color-tertiary-gray);
background-image: none;
box-shadow: var(--box-shadow-primary);
&:hover {
color: var(--color-tertiary-white);
background-color: var(--color-primary);
box-shadow: var(--box-shadow-hover);
}
&:active {
outline: none;
box-shadow: var(--box-shadow-pressed);
}
&.focus-visible {
color: var(--color-tertiary-white);
background-color: var(--color-primary-darker);
}
}
&_link {
padding: 0;
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
color: var(--color-primary-blue);
text-transform: initial;
background-color: transparent;
background-image: none;
box-shadow: none;
&.focus-visible {
text-decoration: underline;
}
&:hover {
color: var(--color-primary-black);
background-color: transparent;
box-shadow: none;
}
&:active {
outline: none;
box-shadow: none;
}
}
}
&_size {
&_small {
&.q-button_type_icon {
width: 24px;
height: 24px;
padding: 0;
font-size: 16px;
line-height: 1;
}
&.q-button_theme_link {
font-size: 12px;
}
}
}
&_full-width {
width: 100%;
}
&_disabled {
--color: rgba(var(--color-rgb-gray), 0.64);
--background-color: var(--color-tertiary-gray);
--box-shadow: 1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
-1px -1px 3px rgba(var(--color-rgb-white), 0.25);
color: var(--color);
background-color: var(--background-color);
background-image: none;
box-shadow: var(--box-shadow);
cursor: not-allowed;
&:hover,
&:active {
color: var(--color);
background-color: var(--background-color);
box-shadow: var(--box-shadow);
}
&.q-button_theme_link {
--box-shadow: none;
--background-color: transparent;
}
}
&_loading {
position: relative;
pointer-events: none;
.q-button__inner {
margin-left: 0;
visibility: hidden;
}
.q-icon-reverse {
--icon-size: 24px;
position: absolute;
top: calc(50% - var(--icon-size) / 2);
left: calc(50% - var(--icon-size) / 2);
font-size: var(--icon-size);
transform-origin: calc(var(--icon-size) / 2);
animation: rotating 2s linear infinite;
}
&.q-button_size_small {
.q-icon-reverse {
--icon-size: 16px;
}
}
&::before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border-radius: inherit;
pointer-events: none;
}
}
&_circle {
border-radius: 50%;
}
}
@keyframes rotating {
0% {
transform: rotateZ(0deg);
}
100% {
transform: rotateZ(360deg);
}
}
================================================
FILE: src/qComponents/QCascader/QCascader.test.js
================================================
import Component from './src/QCascader';
const module = require('../helpers');
module.randId = jest.fn();
describe('QCascader', () => {
let instance;
let options;
beforeEach(() => {
options = {
mocks: { $t: () => {} },
propsData: {
value: 'resource',
placeholder: 'placeholder',
options: [
{
value: 'guide',
label: 'Guide',
children: [
{
value: 'child',
label: 'Child',
children: [{ value: 'next child', label: 'Next child' }]
}
]
},
{ value: 'resource', label: 'Resource' }
]
}
};
instance = mount(Component, options);
});
it('should match snapshot', () => {
const spy = prefix => `${prefix}000`;
module.randId.mockImplementation(spy);
instance = mount(Component, options);
expect(instance.element).toMatchSnapshot();
});
it('data should match snapshot', () => {
expect(Component.data()).toMatchSnapshot();
});
describe('computed', () => {
describe('model', () => {
it('should set inputValue if has been changed', () => {
const expected = 'guide';
instance.vm.model = expected;
expect(instance.vm.inputValue).toEqual(expected);
});
});
describe('isClearBtnVisible', () => {
it('should return false if showClose is false', async () => {
await instance.setData({ showClose: false });
expect(instance.vm.isClearBtnVisible).toBeFalsy();
});
it('should return true if showClose is true', async () => {
await instance.setData({ showClose: true });
expect(instance.vm.isClearBtnVisible).toBeTruthy();
});
});
});
describe('watch', () => {
describe('value', () => {
it('should set checkedValues if value is array', async () => {
const expected = ['guide', 'child'];
await instance.setProps({ value: expected });
expect(instance.vm.checkedValues).toEqual(expected);
});
});
});
});
================================================
FILE: src/qComponents/QCascader/__snapshots__/QCascader.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QCascader data should match snapshot 1`] = `
Object {
"areTagsHovered": false,
"checkedNodes": Array [],
"checkedValues": Array [],
"focus": false,
"id": "q-cascader-000",
"inputInitialHeight": 0,
"inputValue": null,
"popper": null,
"presentText": null,
"showClose": false,
}
`;
exports[`QCascader should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QCascader/index.js
================================================
import QCascader from './src/QCascader';
/* istanbul ignore next */
QCascader.install = function(Vue) {
Vue.component(QCascader.name, QCascader);
};
export default QCascader;
================================================
FILE: src/qComponents/QCascader/src/QCascader.vue
================================================
{{
tag.fullPathLabel
}}
{{ tag.label }}
+{{ checkedNodes.length - 1 }}
{{
tag.fullPathLabel
}}
{{ tag.label }}
================================================
FILE: src/qComponents/QCascader/src/QCascaderMenu.vue
================================================
================================================
FILE: src/qComponents/QCascader/src/QCascaderPanel.vue
================================================
================================================
FILE: src/qComponents/QCascader/src/q-cascader-menu.scss
================================================
.q-cascader-menu {
min-width: 200px;
height: auto;
max-height: 304px;
margin-left: 1px;
&__wrap {
overflow: hidden;
color: var(--color-primary-blue);
background-color: var(--color-tertiary-gray-light);
border-radius: var(--border-radius-base);
box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
-1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
4px 4px 8px rgba(var(--color-rgb-blue), 0.4),
-4px -4px 12px var(--color-tertiary-white);
&_no-right-borders {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&_no-left-bottom-border {
border-bottom-left-radius: 0;
}
&_no-left-top-border {
border-top-left-radius: 0;
}
&_no-right-top-border {
border-top-right-radius: 0;
}
&_with-right-borders {
border-bottom-right-radius: var(--border-radius-base);
}
}
&__scrollbar {
max-height: 300px;
}
&__empty-text {
margin: 9px 0;
text-align: center;
color: var(--color-controls-black);
}
&:last-child {
border-right: none;
.q-cascader-node {
padding-right: 20px;
}
}
&__list {
min-height: 100%;
margin: 0;
padding: 0;
list-style: none;
}
&__hover-zone {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.q-cascader-node {
position: relative;
z-index: 1;
display: flex;
align-items: center;
height: 40px;
margin-top: 1px;
padding: 4px 8px;
background-color: var(--color-tertiary-gray-light);
outline: none;
box-shadow: var(--box-shadow-pressed);
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
&:not(.q-cascader-node_disabled) {
cursor: pointer;
&:hover {
position: relative;
z-index: 1;
color: var(--color-primary-black);
background: var(--color-tertiary-gray);
}
}
&:focus,
&_active {
position: relative;
color: var(--color-primary-black);
box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4);
.q-cascader-node__postfix::before {
color: var(--color-primary-blue);
}
}
&_active {
background-color: var(--color-tertiary-gray-ultra-light);
}
&:focus {
background-color: var(--color-tertiary-gray);
}
&_disabled {
color: var(--color-opacity-gray-dark);
cursor: not-allowed;
.q-cascader-node__postfix {
&::before {
font-size: 24px;
}
}
}
&__prefix {
display: none;
}
&__postfix {
position: absolute;
right: 10px;
&::before {
font-size: 22px;
color: rgba(var(--color-rgb-gray), 0.64);
}
}
&__label {
flex: 1;
padding: 0 25px 0 10px;
overflow: hidden;
text-align: left;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
================================================
FILE: src/qComponents/QCascader/src/q-cascader.scss
================================================
.q-cascader {
position: relative;
display: inline-block;
width: 100%;
font-size: var(--font-size-base);
&:not(.q-cascader_disabled):hover {
.q-input__inner {
cursor: pointer;
}
}
.q-input {
cursor: pointer;
&__inner {
text-overflow: ellipsis;
}
&__icon {
&::before {
font-size: 24px;
}
&:hover {
color: var(--color-primary-blue);
background-color: unset;
}
}
.q-icon-triangle-down {
transition: transform 0.3s;
&_reverse {
transform: rotateZ(180deg);
}
}
&_hover {
.q-input__inner {
background-color: var(--field-background-color-hover);
}
}
&_focus {
.q-input__inner {
background-color: var(--color-tertiary-gray-ultra-light);
box-shadow: var(--box-shadow-focus);
}
}
}
&__dropdown {
position: relative;
z-index: 100;
}
&__tags {
position: absolute;
top: 50%;
right: 30px;
left: 8px;
display: flex;
flex-wrap: wrap;
box-sizing: border-box;
line-height: normal;
text-align: left;
transform: translateY(-50%);
.q-tag {
max-width: calc(100% - 15px);
margin: 2px 0 2px 8px;
line-height: var(--line-height-base);
color: var(--color-opacity-gray-dark);
text-overflow: ellipsis;
background: var(--color-tertiary-gray-darker);
border: none;
border-radius: 2px;
.q-cascader__tag-text {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.q-icon-close {
position: relative;
top: 0;
flex: none;
border-radius: unset;
&:hover {
color: var(--color-primary-blue);
background-color: unset;
}
&::before {
font-size: 16px;
}
}
}
}
&__search-input {
flex: 1;
box-sizing: border-box;
min-width: 60px;
height: 24px;
margin: 2px 0 2px 15px;
padding: 0;
border: none;
outline: none;
}
}
.q-cascader-panel {
display: none;
&[data-show] {
display: flex;
border-radius: var(--border-radius-base);
}
}
@import './q-cascader-menu.scss';
================================================
FILE: src/qComponents/QCheckbox/index.js
================================================
import QCheckbox from './src/QCheckbox.vue';
/* istanbul ignore next */
QCheckbox.install = function(Vue) {
Vue.component(QCheckbox.name, QCheckbox);
};
export default QCheckbox;
================================================
FILE: src/qComponents/QCheckbox/src/QCheckbox.vue
================================================
{{ label }}
================================================
FILE: src/qComponents/QCheckbox/src/q-checkbox.scss
================================================
.q-checkbox {
--checkbox-color-base: var(--color-primary-black);
--checkbox-color-disabled: rgba(var(--color-rgb-gray), 0.64);
--checkbox-background-color-base: var(--color-tertiary-gray-ultra-light);
--checkbox-background-color-hover: var(--color-tertiary-gray);
--checkbox-background-color-focus: var(--color-tertiary-gray-ultra-light);
--checkbox-background-color-checked: var(--color-tertiary-gray-ultra-light);
--checkbox-background-color-disabled: var(--color-tertiary-gray);
--checkbox-mark-color-base: var(--color-primary-blue);
--checkbox-mark-color-disabled: rgba(var(--color-rgb-gray), 0.64);
--checkbox-box-shadow-base: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
inset -1px -1px 1px rgba(var(--color-rgb-white), 0.7),
inset 1px 1px 2px rgba(var(--color-rgb-blue), 0.2);
--checkbox-box-shadow-focus: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
4px 4px 10px rgba(var(--color-rgb-blue), 0.4),
-4px -4px 10px rgba(var(--color-rgb-white), 0.25),
inset -1px -1px 1px rgba(var(--color-rgb-white), 0.7),
inset 1px 1px 2px rgba(var(--color-rgb-blue), 0.2);
position: relative;
display: inline-flex;
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
line-height: 1;
vertical-align: middle;
color: var(--checkbox-color-base);
white-space: nowrap;
outline: none;
cursor: pointer;
user-select: none;
&__label {
margin-top: 3px;
padding-left: 16px;
font-size: var(--font-size-base);
line-height: 18px;
white-space: normal;
word-break: break-word;
.q-checkbox_disabled & {
color: var(--checkbox-color-disabled);
cursor: not-allowed;
}
}
&__input {
position: relative;
line-height: 0;
white-space: nowrap;
border: none;
outline: none;
cursor: pointer;
}
&__inner {
position: relative;
z-index: 1;
display: inline-block;
box-sizing: border-box;
width: 24px;
height: 24px;
overflow: hidden;
background-color: var(--checkbox-background-color-base);
border: none;
border-radius: var(--border-radius-base);
box-shadow: var(--checkbox-box-shadow-base);
&-icon {
position: absolute;
top: 4px;
left: 4px;
box-sizing: content-box;
width: 16px;
font-size: 16px;
text-align: center;
color: var(--checkbox-mark-color-base);
}
.q-checkbox_checked &,
.q-checkbox__input_indeterminate & {
background-color: var(--checkbox-background-color-checked);
&::after {
opacity: 1;
}
}
.q-checkbox__input_focus & {
background-color: var(--checkbox-background-color-focus);
box-shadow: var(--checkbox-box-shadow-focus);
}
&,
.q-checkbox__input_focus &,
.q-checkbox_checked &,
.q-checkbox__input_indeterminate & {
&:hover {
background-color: var(--checkbox-background-color-hover);
}
}
.q-checkbox_disabled & {
background-color: var(--checkbox-background-color-disabled);
cursor: not-allowed;
&-icon {
color: var(--checkbox-mark-color-disabled);
cursor: not-allowed;
}
}
}
&__original {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
margin: 0;
outline: none;
opacity: 0;
}
}
================================================
FILE: src/qComponents/QCheckboxGroup/index.js
================================================
import QCheckboxGroup from './src/QCheckboxGroup.vue';
/* istanbul ignore next */
QCheckboxGroup.install = function(Vue) {
Vue.component(QCheckboxGroup.name, QCheckboxGroup);
};
export default QCheckboxGroup;
================================================
FILE: src/qComponents/QCheckboxGroup/src/QCheckboxGroup.vue
================================================
================================================
FILE: src/qComponents/QCheckboxGroup/src/q-checkbox-group.scss
================================================
.q-checkbox-group {
display: flex;
align-items: flex-start;
font-size: 0;
line-height: 1;
&_vertical {
flex-direction: column;
.q-checkbox:not(:last-child) {
margin-bottom: 16px;
}
}
&_horizontal {
.q-checkbox:not(:last-child) {
margin-right: 32px;
}
}
}
================================================
FILE: src/qComponents/QCol/QCol.test.js
================================================
import Component from './src/QCol';
describe('QCol', () => {
it('should match snapshot', async () => {
const { element } = shallowMount(Component);
expect(element).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QCol/__snapshots__/QCol.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QCol should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QCol/index.js
================================================
import QCol from './src/QCol';
QCol.install = Vue => {
Vue.component(QCol.name, QCol);
};
export default QCol;
================================================
FILE: src/qComponents/QCol/src/QCol.vue
================================================
================================================
FILE: src/qComponents/QCol/src/q-col.scss
================================================
.q-col {
position: relative;
flex: 1 0 0;
width: 100%;
max-width: 100%;
padding-right: calc(var(--layout-gutter) / 2);
padding-left: calc(var(--layout-gutter) / 2);
&_size_auto {
flex: 0 0 auto;
width: auto;
max-width: 100%;
}
@for $i from 1 through 12 {
&_size_#{$i} {
flex: 0 0 percentage($i / 12);
max-width: percentage($i / 12);
}
}
@for $i from 0 through 11 {
&_offset_#{$i} {
margin-left: percentage($i / 12);
}
}
}
================================================
FILE: src/qComponents/QCollapse/QCollapse.test.js
================================================
import Component from './src/QCollapse';
describe('QCollapse', () => {
it('should match snapshot', () => {
const { element } = mount(Component);
expect(element).toMatchSnapshot();
});
it('data should match snapshot', () => {
expect(Component.data()).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QCollapse/__snapshots__/QCollapse.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QCollapse data should match snapshot 1`] = `
Object {
"activeNames": Array [],
"uniqueId": [Function],
}
`;
exports[`QCollapse should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QCollapse/index.js
================================================
import QCollapse from './src/QCollapse.vue';
/* istanbul ignore next */
QCollapse.install = function(Vue) {
Vue.component(QCollapse.name, QCollapse);
};
export default QCollapse;
================================================
FILE: src/qComponents/QCollapse/src/QCollapse.vue
================================================
================================================
FILE: src/qComponents/QCollapse/src/q-collapse.scss
================================================
================================================
FILE: src/qComponents/QCollapseItem/QCollapseItem.test.js
================================================
import Component from './src/QCollapseItem';
describe('QCollapseItem', () => {
it('should match snapshot', () => {
const { element } = mount(Component);
expect(element).toMatchSnapshot();
});
it('data should match snapshot', () => {
expect(Component.data()).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QCollapseItem/__snapshots__/QCollapseItem.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QCollapseItem data should match snapshot 1`] = `
Object {
"activeNames": Array [],
}
`;
exports[`QCollapseItem should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QCollapseItem/index.js
================================================
import QCollapseItem from './src/QCollapseItem.vue';
/* istanbul ignore next */
QCollapseItem.install = function(Vue) {
Vue.component(QCollapseItem.name, QCollapseItem);
};
export default QCollapseItem;
================================================
FILE: src/qComponents/QCollapseItem/src/QCollapseItem.vue
================================================
================================================
FILE: src/qComponents/QCollapseItem/src/q-collapse-item.scss
================================================
.q-collapse-item {
&:not(:last-child) {
margin-bottom: 16px;
}
&__header {
display: flex;
align-items: center;
width: 100%;
text-align: left;
background-color: var(--color-tertiary-gray-light);
border: none;
border-radius: var(--border-radius-base);
outline: none;
box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4);
cursor: pointer;
transition: background-color 0.1s;
.q-collapse-item_active & {
background-color: var(--color-tertiary-gray-ultra-light);
border-radius: var(--border-radius-base) var(--border-radius-base) 0 0;
}
&:focus,
&:hover {
background-color: var(--color-tertiary-gray);
}
}
&__title {
flex: 1;
padding: 12px 0 12px 24px;
font-weight: 600;
font-size: 16px;
line-height: 20px;
color: rgba(var(--color-rgb-gray), 0.64);
.q-collapse-item_active > .q-collapse-item__header:not(:hover) &,
.q-collapse-item_active > .q-collapse-item__header:not(:focus) & {
color: var(--color-primary-black);
}
}
&__icon {
flex: 0 64px;
padding: 20px 16px 20px 24px;
font-size: 24px;
color: var(--color-primary-blue);
}
&__body {
margin-top: 1px;
background-color: var(--color-tertiary-gray-light);
border-radius: 0 0 var(--border-radius-base) var(--border-radius-base);
box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4);
}
&__content {
padding: 24px;
}
}
================================================
FILE: src/qComponents/QColorPicker/QColorAlphaSlider.test.js
================================================
import Component from './src/QColorAlphaSlider';
describe('QColorAlphaSlider', () => {
it('should match snapshot', async () => {
const instance = shallowMount(Component, {
propsData: {
color: 'rgba(255,155,155,.5)',
alpha: 55
}
});
expect(instance.element).toMatchSnapshot();
});
it('data should match snapshot', () => {
expect(Component.data()).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QColorPicker/QColorHueSlider.test.js
================================================
import Component from './src/QColorHueSlider';
describe('QColorHueSlider', () => {
it('should match snapshot', async () => {
const instance = shallowMount(Component, {
propsData: {
hue: 155
}
});
expect(instance.element).toMatchSnapshot();
});
it('data should match snapshot', () => {
expect(Component.data()).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QColorPicker/QColorPicker.test.js
================================================
import Component from './src/QColorPicker';
describe('QColorPicker', () => {
it('should match snapshot', async () => {
const instance = shallowMount(Component);
expect(instance.element).toMatchSnapshot();
});
it('data should match snapshot', () => {
expect(Component.data()).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QColorPicker/QColorSvpanel.test.js
================================================
import Component from './src/QColorSvpanel';
describe('QColorSvpanel', () => {
it('should match snapshot', async () => {
const instance = shallowMount(Component, {
propsData: {
hue: 100,
saturation: 50,
value: 25
}
});
expect(instance.element).toMatchSnapshot();
});
it('data should match snapshot', () => {
expect(Component.data()).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QColorPicker/QPickerDropdown.test.js
================================================
import Component from './src/QPickerDropdown';
describe('QPickerDropdown', () => {
it('should match snapshot', async () => {
const instance = shallowMount(Component);
expect(instance.element).toMatchSnapshot();
});
it('data should match snapshot', () => {
expect(Component.data()).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QColorPicker/__snapshots__/QColorAlphaSlider.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QColorAlphaSlider data should match snapshot 1`] = `
Object {
"thumbLeft": 0,
}
`;
exports[`QColorAlphaSlider should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QColorPicker/__snapshots__/QColorHueSlider.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QColorHueSlider data should match snapshot 1`] = `
Object {
"thumbTop": 0,
}
`;
exports[`QColorHueSlider should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QColorPicker/__snapshots__/QColorPicker.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QColorPicker data should match snapshot 1`] = `
Object {
"isClickIgnored": false,
"isPickerShown": false,
"popperJS": null,
"zIndex": null,
}
`;
exports[`QColorPicker should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QColorPicker/__snapshots__/QColorSvpanel.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QColorSvpanel data should match snapshot 1`] = `
Object {
"cursorLeft": 0,
"cursorTop": 0,
}
`;
exports[`QColorSvpanel should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QColorPicker/__snapshots__/QPickerDropdown.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QPickerDropdown data should match snapshot 1`] = `
Object {
"alpha": 100,
"elementToFocusAfterClosing": null,
"hue": 0,
"saturation": 100,
"tempColor": "",
"value": 100,
}
`;
exports[`QPickerDropdown should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QColorPicker/index.js
================================================
import QColorPicker from './src/QColorPicker';
QColorPicker.install = Vue => {
Vue.component(QColorPicker.name, QColorPicker);
};
export default QColorPicker;
================================================
FILE: src/qComponents/QColorPicker/src/QColorAlphaSlider.vue
================================================
================================================
FILE: src/qComponents/QColorPicker/src/QColorHueSlider.vue
================================================
================================================
FILE: src/qComponents/QColorPicker/src/QColorPicker.vue
================================================
================================================
FILE: src/qComponents/QColorPicker/src/QColorSvpanel.vue
================================================
================================================
FILE: src/qComponents/QColorPicker/src/QPickerDropdown.vue
================================================
================================================
FILE: src/qComponents/QColorPicker/src/draggable.js
================================================
let isDragging = false;
export default function(element, options) {
const moveFn = event => {
if (options.drag) options.drag(event);
};
const upFn = event => {
document.removeEventListener('mousemove', moveFn);
document.removeEventListener('mouseup', upFn);
document.onselectstart = null;
document.ondragstart = null;
isDragging = false;
if (options.end) options.end(event);
};
element.addEventListener('mousedown', event => {
if (isDragging) return;
document.onselectstart = () => false;
document.ondragstart = () => false;
document.addEventListener('mousemove', moveFn);
document.addEventListener('mouseup', upFn);
isDragging = true;
if (options.start) options.start(event);
});
}
================================================
FILE: src/qComponents/QColorPicker/src/q-color-alpha-slider.scss
================================================
.q-color-alpha-slider {
position: relative;
width: 464px;
height: 16px;
margin-top: 8px;
overflow: hidden;
background-image: linear-gradient(
45deg,
var(--color-tertiary-gray-ultra-dark) 25%,
transparent 25%
),
linear-gradient(
-45deg,
var(--color-tertiary-gray-ultra-dark) 25%,
transparent 25%
),
linear-gradient(
45deg,
transparent 75%,
var(--color-tertiary-gray-ultra-dark) 75%
),
linear-gradient(
-45deg,
transparent 75%,
var(--color-tertiary-gray-ultra-dark) 75%
);
background-position: 8px 16px, -8px 8px, 16px 8px, -16px 0;
background-size: 16px 16px;
border-radius: var(--border-radius-base);
box-shadow: inset 2px 2px 4px rgba(var(--color-rgb-gray), 0.32);
cursor: pointer;
&__bar {
width: 100%;
height: 100%;
}
&__thumb {
position: absolute;
top: 0;
left: 0;
z-index: 1;
width: 8px;
height: 12px;
margin: 2px;
background-color: var(--color-tertiary-white);
border-radius: 2px;
box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4);
}
}
================================================
FILE: src/qComponents/QColorPicker/src/q-color-hue-slider.scss
================================================
.q-color-hue-slider {
position: relative;
width: 16px;
height: 208px;
margin-left: 16px;
cursor: pointer;
&__bar {
width: 100%;
height: 100%;
background-image: linear-gradient(
180deg,
#f00 0,
#ff0 17%,
#0f0 33%,
#0ff 50%,
#00f 67%,
#f0f 83%,
#f00
);
border-radius: var(--border-radius-base);
box-shadow: inset 2px 2px 4px rgba(var(--color-rgb-gray), 0.32);
}
&__thumb {
position: absolute;
top: 0;
left: 0;
z-index: 1;
width: 12px;
height: 8px;
margin: 2px;
background-color: var(--color-tertiary-white);
border-radius: 2px;
box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4);
}
}
================================================
FILE: src/qComponents/QColorPicker/src/q-color-picker.scss
================================================
@import './q-picker-dropdown';
@import './q-color-svpanel';
@import './q-color-hue-slider';
@import './q-color-alpha-slider';
.q-color-picker {
&-trigger {
display: inline-block;
cursor: pointer;
&__default {
position: relative;
display: block;
width: 40px;
height: 40px;
padding: 0;
font-size: 24px;
border: none;
border-radius: var(--border-radius-base);
box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
4px 4px 8px rgba(var(--color-rgb-blue), 0.4),
-4px -4px 12px var(--color-tertiary-white);
cursor: inherit;
&:hover {
box-shadow: -1px -1px 4px rgba(var(--color-rgb-white), 0.25),
1px 1px 4px rgba(var(--color-rgb-blue), 0.4),
4px 4px 8px rgba(var(--color-rgb-blue), 0.4),
-4px -4px 8px rgba(var(--color-rgb-white), 0.8);
}
&:focus {
outline: none;
box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4);
}
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
display: block;
width: 100%;
height: 100%;
background-image: linear-gradient(
45deg,
var(--color-tertiary-gray-ultra-dark) 25%,
transparent 25%
),
linear-gradient(
-45deg,
var(--color-tertiary-gray-ultra-dark) 25%,
transparent 25%
),
linear-gradient(
45deg,
transparent 75%,
var(--color-tertiary-gray-ultra-dark) 75%
),
linear-gradient(
-45deg,
transparent 75%,
var(--color-tertiary-gray-ultra-dark) 75%
);
background-position: 8px 16px, -8px 8px, 16px 8px, -16px 0;
background-size: 16px 16px;
border-radius: var(--border-radius-base);
}
.q-color-picker-trigger_is-disabled & {
background-color: var(--color-tertiary-gray);
box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4);
cursor: not-allowed;
&::before {
display: none;
}
}
}
&__color {
position: absolute;
top: 0;
left: 0;
display: block;
width: 100%;
height: 100%;
overflow: hidden;
background-color: var(--color-tertiary-gray-light);
border-radius: var(--border-radius-base);
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity ease 0.25s;
}
.q-color-picker-trigger__default:hover & {
&::before {
background-color: rgba(var(--color-rgb-gray), 0.1);
opacity: 1;
}
.q-color-picker-trigger_is-disabled & {
&::before {
opacity: 0;
}
}
}
.q-color-picker-trigger__default:focus & {
&::before {
background-color: rgba(var(--color-rgb-white), 0.1);
opacity: 1;
}
}
}
&__icon {
position: relative;
z-index: 1;
line-height: 40px;
color: var(--color-primary-black);
transition: transform 0.25s ease, color 0.25s ease;
.q-color-picker-trigger_is-opened & {
transform: rotate(-180deg);
}
.q-color-picker-trigger_is-disabled & {
color: rgba(var(--color-rgb-gray), 0.64);
}
&_light {
color: var(--color-tertiary-white);
.q-color-picker-trigger_is-disabled & {
color: rgba(var(--color-rgb-white), 0.64);
}
}
}
}
}
================================================
FILE: src/qComponents/QColorPicker/src/q-color-svpanel.scss
================================================
.q-color-svpanel {
position: relative;
width: 464px;
height: 208px;
cursor: pointer;
&::before,
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
&::before {
background-image: linear-gradient(
90deg,
var(--color-tertiary-white),
transparent
);
}
&::after {
background-image: linear-gradient(0, #000, transparent);
}
&__cursor {
position: absolute;
z-index: 1;
width: 8px;
height: 8px;
background-color: var(--color-tertiary-gray-lighter);
border-radius: 50%;
box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
4px 4px 8px rgba(var(--color-rgb-blue), 0.4),
-4px -4px 12px var(--color-tertiary-white);
transform: translate(-4px, -4px);
cursor: grab;
}
}
================================================
FILE: src/qComponents/QColorPicker/src/q-picker-dropdown.scss
================================================
.q-picker-dropdown {
padding: 16px;
background-color: var(--color-tertiary-gray-ultra-light);
border-radius: var(--border-radius-base);
outline: none;
box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4);
&__base {
display: flex;
}
&__footer {
display: flex;
margin-top: 16px;
}
&__input {
width: 223px;
margin-right: auto;
}
}
================================================
FILE: src/qComponents/QContextMenu/index.js
================================================
import QContextMenu from './src/QContextMenu.vue';
/* istanbul ignore next */
QContextMenu.install = function(Vue) {
Vue.component(QContextMenu.name, QContextMenu);
};
export default QContextMenu;
================================================
FILE: src/qComponents/QContextMenu/src/QContextMenu.vue
================================================
================================================
FILE: src/qComponents/QContextMenu/src/q-context-menu.scss
================================================
.q-context {
&-menu {
min-width: 150px;
overflow: hidden;
background-color: var(--color-tertiary-gray-light);
border-radius: var(--border-radius-base);
box-shadow: var(--box-shadow-primary);
&__icon {
margin-right: 16px;
font-size: 24px;
}
&__item {
position: relative;
display: flex;
width: 100%;
padding: 12px 24px;
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
line-height: 24px;
color: var(--color-primary-blue);
text-decoration: none;
white-space: nowrap;
background-color: var(--color-tertiary-gray-light);
border: none;
cursor: pointer;
&_with-icon {
padding-right: 50px;
padding-left: 24px;
}
&:not(:first-child) {
margin-top: 1px;
}
&:hover,
&:focus {
position: relative;
z-index: 1;
color: var(--color-primary-black);
background-color: var(--color-tertiary-gray);
outline: none;
}
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4);
}
}
}
&-trigger {
display: inline-block;
cursor: pointer;
&__button {
display: block;
width: 40px;
height: 40px;
padding: 0;
font-size: 24px;
color: var(--color-primary-blue);
background-color: transparent;
border: none;
outline: none;
&:hover,
&[data-focus-visible-added] {
color: var(--color-primary-black);
background-color: rgba(var(--color-rgb-gray), 0.16);
border-radius: 100%;
}
}
}
}
================================================
FILE: src/qComponents/QDatePicker/index.js
================================================
import QDatePicker from './src/QDatePicker.vue';
/* istanbul ignore next */
QDatePicker.install = function install(Vue) {
Vue.component(QDatePicker.name, QDatePicker);
};
export default QDatePicker;
================================================
FILE: src/qComponents/QDatePicker/src/QDatePicker.vue
================================================
================================================
FILE: src/qComponents/QDatePicker/src/basic/date-table.vue
================================================
{{ day }}
{{ cell.text }}
================================================
FILE: src/qComponents/QDatePicker/src/basic/month-table.vue
================================================
{{ getMonthName(cell.text) }}
================================================
FILE: src/qComponents/QDatePicker/src/basic/year-table.vue
================================================
handleMouseMove(event, cell)"
@click="event => handleYearTableClick(event, cell)"
>
{{ cell.year.getFullYear() }}
================================================
FILE: src/qComponents/QDatePicker/src/helpers.js
================================================
import { format, isDate, parseISO } from 'date-fns';
import { ru, enGB as en } from 'date-fns/locale';
const locales = { ru, en };
const formatLocalDate = (value, dateFnsFormat, dateFnsLocale = 'ru') => {
let parsedValue = value;
if (!isDate(parsedValue)) {
parsedValue = parseISO(parsedValue);
}
if (isDate(parsedValue)) {
return format(parsedValue, dateFnsFormat ?? 'dd MMM yyyy', {
locale: locales[dateFnsLocale]
});
}
return parsedValue;
};
const setTimeToDate = (date, type, value) => {
let newDate = date;
if (isDate(date)) {
newDate = new Date(date);
switch (type) {
case 'hours':
newDate.setHours(Number(value));
break;
case 'minutes':
newDate.setMinutes(Number(value));
break;
case 'seconds':
newDate.setSeconds(Number(value));
break;
default:
break;
}
}
return newDate;
};
const clearTime = function(date) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
};
const clearMilliseconds = function(date) {
return new Date(
date.getFullYear(),
date.getMonth(),
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds(),
0
);
};
export { clearTime, clearMilliseconds, formatLocalDate, setTimeToDate };
================================================
FILE: src/qComponents/QDatePicker/src/panel/date-range.vue
================================================
================================================
FILE: src/qComponents/QDatePicker/src/panel/date.vue
================================================
================================================
FILE: src/qComponents/QDatePicker/src/panel/focus-mixin.js
================================================
const DATE_CELLS_COUNT = 42;
const DATE_CELLS_IN_ROW_COUNT = 7;
const PERIOD_CELLS_IN_ROW_COUNT = 4;
const LEFT_MONTH_PANEL_START_INDEX = 0;
const RIGHT_MONTH_PANEL_START_INDEX = 12;
const RIGHT_YEAR_PANEL_START_INDEX = 10;
export default {
data() {
return {
panelInFocus: null,
lastFocusedCellIndex: null
};
},
mounted() {
this.dateCells = this.$el.querySelectorAll('.q-date-table .cell');
this.monthCells = this.$el.querySelectorAll('.q-month-table .cell');
this.yearCells = this.$el.querySelectorAll('.q-year-table .cell');
},
methods: {
setPanelFocus() {
if (this.isRanged) {
if (this.$refs.leftPanel?.contains(document.activeElement)) {
this.panelInFocus = 'left';
} else if (this.$refs.rightPanel?.contains(document.activeElement)) {
this.panelInFocus = 'right';
} else if (
this.$refs.leftTimePanel?.$el.contains(document.activeElement)
) {
this.panelInFocus = 'timeLeft';
} else if (
this.$refs.rightTimePanel?.$el.contains(document.activeElement)
) {
this.panelInFocus = 'timeRight';
}
} else if (this.$refs.datePanel?.contains(document.activeElement)) {
this.panelInFocus = 'date';
} else if (this.$refs.timePanel?.$el.contains(document.activeElement)) {
this.panelInFocus = 'time';
} else {
this.panelInFocus = null;
}
},
navigateDropdown(e) {
if (e.key !== 'Tab') {
if (e.target.classList.contains('cell_time')) {
this.moveWithinTime(e);
} else if (e.target.classList.contains('cell_date')) {
this.moveWithinDates(e);
} else if (e.target.classList.contains('cell_month')) {
this.moveWithinPeriod({ period: 'month', e });
} else if (e.target.classList.contains('cell_year')) {
this.moveWithinPeriod({ period: 'year', e });
} else if (['monthrange', 'month'].includes(this.currentView)) {
this.monthCells[0]?.focus();
} else if (['yearrange', 'year'].includes(this.currentView)) {
this.yearCells[0]?.focus();
} else {
this.dateCells[0]?.focus();
}
}
this.setPanelFocus();
},
moveWithinPeriod({ period, e }) {
let currentNodeIndex;
let nextNodeIndex;
const periodCells = period === 'month' ? this.monthCells : this.yearCells;
const rightPanelStartIndex =
period === 'month'
? RIGHT_MONTH_PANEL_START_INDEX
: RIGHT_YEAR_PANEL_START_INDEX;
Array.from(periodCells).some((element, index) => {
if (document.activeElement === element) {
currentNodeIndex = index;
return true;
}
return false;
});
switch (e.key) {
case 'ArrowUp': {
nextNodeIndex = currentNodeIndex - PERIOD_CELLS_IN_ROW_COUNT;
break;
}
case 'ArrowRight':
if (
this.isRanged &&
this.panelInFocus === 'left' &&
(currentNodeIndex + 1) % PERIOD_CELLS_IN_ROW_COUNT === 0
) {
nextNodeIndex = rightPanelStartIndex;
} else {
nextNodeIndex = currentNodeIndex + 1;
}
break;
case 'ArrowLeft':
if (
this.isRanged &&
this.panelInFocus === 'right' &&
currentNodeIndex % PERIOD_CELLS_IN_ROW_COUNT === 0
) {
nextNodeIndex = LEFT_MONTH_PANEL_START_INDEX + 3;
} else {
nextNodeIndex = currentNodeIndex - 1;
}
break;
case 'ArrowDown': {
nextNodeIndex = currentNodeIndex + PERIOD_CELLS_IN_ROW_COUNT;
break;
}
default:
break;
}
const node = periodCells[nextNodeIndex];
const newIndex = nextNodeIndex % PERIOD_CELLS_IN_ROW_COUNT;
if (node) {
node.focus();
this.lastFocusedCellIndex = nextNodeIndex;
} else if (nextNodeIndex > this.lastFocusedCellIndex) {
if (this.isRanged) {
this.handleRightNextYearClick();
this.handleLeftNextYearClick();
} else {
this.handleNextYearClick();
}
periodCells[newIndex]?.focus();
} else if (nextNodeIndex < this.lastFocusedCellIndex) {
if (this.isRanged) {
this.handleLeftPrevYearClick();
this.handleRightPrevYearClick();
} else {
this.handlePrevYearClick();
}
}
},
moveWithinDates(e) {
this.timePanelInFocus = 'left';
let currentNodeIndex;
let nextNodeIndex;
Array.from(this.dateCells).some((element, index) => {
const isItActiveElement = document.activeElement === element;
if (isItActiveElement) currentNodeIndex = index;
return isItActiveElement;
});
switch (e.key) {
case 'ArrowUp': {
nextNodeIndex = currentNodeIndex - DATE_CELLS_IN_ROW_COUNT;
break;
}
case 'ArrowRight':
if (this.isRanged) {
if (
this.panelInFocus === 'left' &&
(currentNodeIndex + 1) % DATE_CELLS_IN_ROW_COUNT === 0
) {
nextNodeIndex = DATE_CELLS_COUNT;
this.setPanelFocus();
break;
} else if (
this.showTime &&
this.panelInFocus === 'right' &&
(currentNodeIndex + 1) % DATE_CELLS_IN_ROW_COUNT === 0
) {
this.moveWithinTime();
return;
}
nextNodeIndex = currentNodeIndex + 1;
} else if (
this.showTime &&
(currentNodeIndex + 1) % DATE_CELLS_IN_ROW_COUNT === 0
) {
this.moveWithinTime();
} else {
nextNodeIndex = currentNodeIndex + 1;
}
break;
case 'ArrowLeft':
if (
this.panelInFocus === 'right' &&
(currentNodeIndex - DATE_CELLS_IN_ROW_COUNT) %
DATE_CELLS_IN_ROW_COUNT ===
0
) {
nextNodeIndex = DATE_CELLS_IN_ROW_COUNT - 1;
this.setPanelFocus();
} else {
nextNodeIndex = currentNodeIndex - 1;
}
break;
case 'ArrowDown': {
nextNodeIndex = currentNodeIndex + DATE_CELLS_IN_ROW_COUNT;
break;
}
default:
break;
}
const node = this.dateCells[nextNodeIndex];
const newIndex = nextNodeIndex % DATE_CELLS_IN_ROW_COUNT;
if (node) {
node.focus();
this.lastFocusedCellIndex = nextNodeIndex;
} else if (nextNodeIndex > this.lastFocusedCellIndex) {
if (this.isRanged) {
this.handleRightNextMonthClick();
this.handleLeftNextMonthClick();
} else {
this.handleNextMonthClick();
}
this.dateCells[newIndex]?.focus();
} else if (nextNodeIndex < this.lastFocusedCellIndex) {
if (this.isRanged) {
this.handleLeftPrevMonthClick();
this.handleRightPrevMonthClick();
} else {
this.handlePrevMonthClick();
}
this.dateCells[DATE_CELLS_COUNT + newIndex]?.focus();
}
}
}
};
================================================
FILE: src/qComponents/QDatePicker/src/panel/focus-time-mixin.js
================================================
const DATE_CELLS_IN_ROW_COUNT = 7;
const RIGHT_DATE_PANEL_START_INDEX = 42;
const LEFT_TIME_PANEL_HOURS_INDEX = 0;
const LEFT_TIME_PANEL_MINUTES_INDEX = 24;
const LEFT_TIME_PANEL_SECONDS_INDEX = 84;
const RIGHT_TIME_PANEL_HOURS_INDEX = 144;
const RIGHT_TIME_PANEL_MINUTES_INDEX = 168;
const RIGHT_TIME_PANEL_SECONDS_INDEX = 228;
export default {
mounted() {
this.timeCells = this.$el.querySelectorAll('.time-panel__pickers .cell');
},
methods: {
moveWithinTime(e) {
// switch from date
if (!e) {
if (this.timeCells[0]?.disabled) return;
this.timeCells[0].focus();
this.setPanelFocus();
// true keyup
} else {
let currentNodeIndex;
let nextNodeIndex;
this.timeCells.forEach((element, index) => {
if (document.activeElement === element) {
currentNodeIndex = index;
}
});
const classList = e.target.classList;
switch (e.key) {
case 'ArrowUp': {
nextNodeIndex = currentNodeIndex - 1;
break;
}
case 'ArrowRight':
if (this.isRanged) {
if (this.panelInFocus === 'timeLeft') {
switch (true) {
case classList.contains('cell_hours'):
nextNodeIndex = LEFT_TIME_PANEL_MINUTES_INDEX;
break;
case classList.contains('cell_minutes'):
nextNodeIndex = LEFT_TIME_PANEL_SECONDS_INDEX;
break;
case classList.contains('cell_seconds'):
nextNodeIndex = RIGHT_TIME_PANEL_HOURS_INDEX;
break;
default:
break;
}
} else if (this.panelInFocus === 'timeRight') {
switch (true) {
case classList.contains('cell_hours'):
nextNodeIndex = RIGHT_TIME_PANEL_MINUTES_INDEX;
break;
case classList.contains('cell_minutes'):
nextNodeIndex = RIGHT_TIME_PANEL_SECONDS_INDEX;
break;
default:
break;
}
}
break;
}
if (classList.contains('cell_hours')) {
nextNodeIndex = LEFT_TIME_PANEL_MINUTES_INDEX;
} else if (classList.contains('cell_minutes')) {
nextNodeIndex = LEFT_TIME_PANEL_SECONDS_INDEX;
}
break;
case 'ArrowLeft':
if (this.isRanged) {
if (this.panelInFocus === 'timeRight') {
switch (true) {
case classList.contains('cell_seconds'):
nextNodeIndex = RIGHT_TIME_PANEL_MINUTES_INDEX;
break;
case classList.contains('cell_minutes'):
nextNodeIndex = RIGHT_TIME_PANEL_HOURS_INDEX;
break;
case classList.contains('cell_hours'):
nextNodeIndex = LEFT_TIME_PANEL_SECONDS_INDEX;
break;
default:
break;
}
} else if (this.panelInFocus === 'timeLeft') {
switch (true) {
case classList.contains('cell_seconds'):
nextNodeIndex = LEFT_TIME_PANEL_MINUTES_INDEX;
break;
case classList.contains('cell_minutes'):
nextNodeIndex = LEFT_TIME_PANEL_HOURS_INDEX;
break;
case classList.contains('cell_hours'):
this.dateCells[RIGHT_DATE_PANEL_START_INDEX + 6]?.focus();
break;
default:
break;
}
break;
}
if (
this.panelInFocus === 'timeRight' &&
classList.contains('cell_seconds')
) {
nextNodeIndex = RIGHT_TIME_PANEL_MINUTES_INDEX;
} else if (
this.panelInFocus === 'timeRight' &&
classList.contains('cell_minutes')
) {
nextNodeIndex = RIGHT_TIME_PANEL_HOURS_INDEX;
} else if (
this.panelInFocus === 'timeRight' &&
classList.contains('cell_hours')
) {
nextNodeIndex = LEFT_TIME_PANEL_SECONDS_INDEX;
}
} else if (classList.contains('cell_seconds')) {
nextNodeIndex = LEFT_TIME_PANEL_MINUTES_INDEX;
} else if (classList.contains('cell_minutes')) {
nextNodeIndex = LEFT_TIME_PANEL_HOURS_INDEX;
} else {
// switch to dates
this.dateCells[DATE_CELLS_IN_ROW_COUNT - 1]?.focus();
}
break;
case 'ArrowDown': {
nextNodeIndex = currentNodeIndex + 1;
break;
}
default:
break;
}
this.timeCells[nextNodeIndex]?.focus();
this.setPanelFocus();
}
}
}
};
================================================
FILE: src/qComponents/QDatePicker/src/panel/month-range.vue
================================================
================================================
FILE: src/qComponents/QDatePicker/src/panel/range-mixin.js
================================================
import {
subYears,
addYears,
isDate,
startOfDecade,
endOfDecade,
isSameMonth,
addMonths,
getDecade,
endOfDay
} from 'date-fns';
import { isTimeValueValid } from '../../../helpers/dateHelpers';
export default {
inject: {
picker: {
default: null
}
},
computed: {
btnDisabled() {
return !(
this.minDate &&
this.maxDate &&
!this.selecting &&
this.isValidValue([this.minDate, this.maxDate])
);
},
leftLabel() {
if (this.$parent.type === 'yearrange') {
return `${startOfDecade(this.leftDate).getFullYear()} - ${endOfDecade(
this.leftDate
).getFullYear()}`;
}
const formatter = new Intl.DateTimeFormat(this.$Q.locale, {
month: 'short'
});
return `${formatter.format(
this.leftDate
)} ${this.leftDate.getFullYear()}`;
},
rightLabel() {
if (this.$parent.type === 'yearrange') {
return `${startOfDecade(this.rightDate).getFullYear()} - ${endOfDecade(
this.rightDate
).getFullYear()}`;
}
const formatter = new Intl.DateTimeFormat(this.$Q.locale, {
month: 'short'
});
return `${formatter.format(
this.rightDate
)} ${this.rightDate.getFullYear()}`;
},
leftYear() {
if (isDate(this.leftDate)) {
return this.leftDate.getFullYear();
}
return new Date().getFullYear();
},
leftMonth() {
if (isDate(this.leftDate)) {
return this.leftDate.getMonth();
}
return new Date().getMonth();
},
rightMonth() {
if (isDate(this.rightDate)) {
return this.rightDate.getMonth();
}
return new Date().getMonth() + 1;
}
},
watch: {
value: {
handler(newVal) {
if (!newVal || !newVal?.length) {
this.handleClear();
} else {
this.minDate = newVal[0];
this.maxDate = newVal[1];
switch (this.$parent.type) {
case 'yearrange': {
if (getDecade(this.minDate) === getDecade(this.maxDate)) {
this.leftDate = this.minDate;
this.rightDate = addYears(this.minDate, 10);
}
break;
}
default: {
if (isSameMonth(this.minDate, this.maxDate)) {
this.leftDate = this.minDate;
this.rightDate = addMonths(this.minDate, 1);
} else {
this.leftDate = this.minDate;
this.rightDate = this.maxDate;
}
}
}
}
},
immediate: true
}
},
methods: {
handleLeftPrevYearClick() {
this.leftDate = subYears(this.leftDate, 1);
},
handleRightNextYearClick() {
this.rightDate = addYears(this.rightDate, 1);
},
handleLeftNextYearClick() {
this.leftDate = addYears(this.leftDate, 1);
},
handleRightPrevYearClick() {
this.rightDate = subYears(this.rightDate, 1);
},
handleShortcutClick(shortcut) {
if (shortcut.onClick) {
shortcut.onClick(this);
}
},
handleRangePick(val, close = true) {
if (this.maxDate === val.maxDate && this.minDate === val.minDate) {
return;
}
if (isDate(val.maxDate)) {
// eslint-disable-next-line no-param-reassign
val.maxDate = endOfDay(val.maxDate);
}
if (val.rangeState) {
this.rangeState = val.rangeState;
}
this.maxDate = val.maxDate;
this.minDate = val.minDate;
const to = this.disabledValues?.time?.to;
if (this.maxDate && to && isTimeValueValid(to)) {
const [hours, minutes, seconds] = to.split(':');
this.minDate.setHours(Number(hours));
this.minDate.setMinutes(Number(minutes));
this.minDate.setSeconds(Number(seconds));
this.maxDate.setHours(Number(hours));
this.maxDate.setMinutes(Number(minutes));
this.maxDate.setSeconds(Number(seconds));
}
// emit QDatepicker intermediate value
this.picker?.$emit('rangepick', val);
if (!close) return;
if (this.isValidValue([this.minDate, this.maxDate])) {
this.$emit('pick', [this.minDate, this.maxDate], {
hidePicker: !this.showTime
});
}
},
handleChangeRange(val) {
this.minDate = val.minDate;
this.maxDate = val.maxDate;
this.rangeState = val.rangeState;
},
isValidValue(value) {
return (
Array.isArray(value) &&
value &&
value[0] &&
value[1] &&
isDate(value[0]) &&
isDate(value[1]) &&
value[0].getTime() <= value[1].getTime()
);
}
}
};
================================================
FILE: src/qComponents/QDatePicker/src/panel/year-range.vue
================================================
================================================
FILE: src/qComponents/QDatePicker/src/q-date-picker.scss
================================================
@import './styles/date-table.scss';
@import './styles/month-table.scss';
@import './styles/year-table.scss';
@import './styles/picker.scss';
@import './styles/picker-panel.scss';
@import '../../QTimePicker/src/components/time-panel.scss';
================================================
FILE: src/qComponents/QDatePicker/src/styles/date-table.scss
================================================
.q-date-table {
width: 100%;
table-layout: fixed;
font-size: 10px;
user-select: none;
&_days {
line-height: 1;
}
&__cell-wrapper {
position: relative;
width: 20px;
height: 20px;
padding: 0;
text-align: center;
cursor: pointer;
.cell {
position: relative;
box-sizing: border-box;
width: 20px;
height: 20px;
margin: 0 auto;
padding: 0;
font-weight: var(--font-weight-base);
color: var(--color-primary-blue);
background-color: var(--color-tertiary-gray-light);
border: none;
border-radius: var(--border-radius-base);
box-shadow: var(--box-shadow-secondary);
&:hover {
&:not(.cell_disabled):not(.cell_current):not(.cell_in-range) {
color: var(--color-primary-black);
background-color: var(--color-tertiary-gray);
}
}
&[data-focus-visible-added] {
&:not(.cell_disabled) {
color: var(--color-tertiary-white);
background-color: var(--color-primary-blue);
box-shadow: var(--box-shadow-pressed);
transform: scale(1.2);
transition: transform 0.2s;
}
}
&_disabled {
color: rgba(var(--color-primary-black), 0.64);
background-color: var(--color-tertiary-gray);
cursor: not-allowed;
opacity: 1;
}
&_selected {
color: var(--color-tertiary-white);
background-color: var(--color-primary-blue);
box-shadow: var(--box-shadow-primary);
}
&_today:not(.cell_disabled) {
color: var(--color-primary);
}
&_today {
&::before {
content: '';
position: absolute;
top: -4px;
left: 9px;
display: block;
width: 2px;
height: 2px;
background-color: var(--color-primary);
border-radius: 50%;
}
}
&_current,
&_in-range {
color: var(--color-tertiary-white);
background-color: var(--color-primary-blue);
&::after {
content: '';
position: absolute;
top: 0;
right: 0;
left: 0;
z-index: -1;
width: 20px;
height: 20px;
margin: auto;
border-radius: 4px;
box-shadow: var(--box-shadow-primary);
}
&:hover {
color: #fff;
}
}
&_today {
&.cell_current,
&.cell_in-range:not(.cell_prev-month),
&.cell_in-range:not(.cell_next-month) {
color: #fff;
}
}
&_next-month,
&_prev-month {
color: rgba(var(--color-rgb-gray), 0.32);
&.cell_in-range {
color: var(--color-primary-black);
background-color: var(--color-tertiary-gray);
}
}
}
}
th {
font-weight: var(--font-weight-base);
color: rgba(var(--color-rgb-gray), 0.64);
}
}
================================================
FILE: src/qComponents/QDatePicker/src/styles/month-table.scss
================================================
.q-month-table {
width: 100%;
padding: 10px 0 20px;
table-layout: fixed;
font-size: 12px;
&__cell-wrapper {
text-align: center;
cursor: pointer;
.cell {
position: relative;
display: block;
width: 40px;
height: 40px;
font-weight: var(--font-weight-base);
color: var(--color-primary-blue);
border: none;
border-radius: var(--border-radius-base);
box-shadow: var(--box-shadow-secondary);
&:hover:not([disabled]):not(.cell_in-range):not(.cell_current) {
color: var(--color-primary-black);
background-color: var(--color-tertiary-gray);
}
&[data-focus-visible-added] {
&:not(.cell_disabled) {
color: var(--color-tertiary-white);
background-color: var(--color-primary-blue);
box-shadow: var(--box-shadow-pressed);
transform: scale(1.1);
transition: transform 0.2s;
}
}
&_today {
color: var(--color-primary);
&.cell_current {
color: var(--color-tertiary-white);
}
&::before {
content: '';
position: absolute;
top: -5px;
left: 19px;
display: block;
width: 2px;
height: 2px;
background-color: var(--color-primary);
border-radius: 50%;
}
}
&[disabled] {
color: var(--color-tertiary-gray-ultra-dark);
background-color: var(--color-tertiary-gray-lighter);
cursor: not-allowed;
&:hover {
color: var(--color-tertiary-gray-ultra-dark);
}
}
&_in-range {
color: #fff;
background-color: var(--color-primary-blue);
box-shadow: var(--box-shadow-pressed);
}
&_current:not(.disabled) {
color: #fff;
background-color: var(--color-primary-blue);
box-shadow: var(--box-shadow-pressed);
}
}
}
}
================================================
FILE: src/qComponents/QDatePicker/src/styles/picker-panel.scss
================================================
.q-picker-panel {
z-index: 1;
display: none;
border-radius: 4px;
&_shown {
display: block;
}
&__sidebar {
flex-shrink: 0;
width: 114px;
padding: 16px 8px;
overflow: auto;
background: var(--color-tertiary-gray-light);
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
box-shadow: var(--box-shadow-pressed);
& + .q-picker-panel__content {
margin-left: 1px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
&__body,
&__body-wrapper {
display: flex;
flex-shrink: 0;
height: 100%;
background: var(--color-tertiary-gray-light);
border-radius: 4px;
box-shadow: var(--box-shadow-secondary);
&::after {
content: '';
display: table;
clear: both;
}
}
&__header {
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 8px 0;
text-align: center;
text-transform: uppercase;
&-sign {
display: flex;
flex-basis: 72px;
justify-content: center;
margin-right: 12px;
margin-left: 12px;
font-weight: 600;
font-size: 12px;
line-height: 15px;
color: var(--color-primary-black);
white-space: nowrap;
}
}
&__header-label {
cursor: pointer;
user-select: none;
&:nth-child(2) {
margin-left: 5px;
}
&:hover {
color: var(--color-primary-blue);
}
}
&__content {
position: relative;
flex-shrink: 0;
box-sizing: border-box;
width: 220px;
background: var(--color-tertiary-gray-light);
border-radius: 4px;
box-shadow: var(--box-shadow-pressed);
&_no-right-borders {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&_no-left-borders {
margin-left: 1px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
&_focused {
z-index: 2;
border-radius: 4px;
outline: none;
box-shadow: var(--box-shadow-secondary);
transform: scale(1.03);
transition: transform 0.2s;
}
}
&__shortcut {
display: block;
width: 100%;
padding-bottom: 8px;
font-size: 10px;
line-height: 12px;
text-align: left;
color: rgba(var(--color-rgb-gray), 0.64);
background-color: transparent;
border: 0;
outline: none;
cursor: pointer;
&:hover {
color: var(--color-primary-blue);
}
&[data-focus-visible-added] {
color: var(--color-primary-blue);
text-decoration: underline;
}
}
&__icon-btn {
width: 20px;
min-width: 20px;
height: 20px;
padding: 0;
font-size: 20px;
line-height: 21px;
color: var(--color-primary-blue);
background: var(--color-tertiary-gray-light);
border: 0;
border-radius: 20px;
outline: none;
box-shadow: var(--box-shadow-primary);
cursor: pointer;
&:hover {
color: var(--color-primary-black);
}
&[data-focus-visible-added] {
color: var(--color-primary-black);
background-color: var(--color-tertiary-gray);
}
&_disabled {
color: var(--color-tertiary-gray-ultra-darker);
&:hover {
color: var(--color-tertiary-gray-ultra-darker);
cursor: not-allowed;
}
}
}
&__link-btn {
vertical-align: middle;
}
&__timepickers {
display: inline-flex;
.time-panel {
display: block;
height: 100%;
box-shadow: var(--box-shadow-pressed);
&_focused {
box-shadow: var(--box-shadow-secondary);
}
}
}
}
================================================
FILE: src/qComponents/QDatePicker/src/styles/picker.scss
================================================
.q-date-editor {
--field-box-shadow-focus: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
inset -1px -1px 1px rgba(var(--color-rgb-white), 0.7),
inset 1px 1px 2px rgba(var(--color-rgb-blue), 0.2);
position: relative;
display: inline-block;
text-align: left;
.q-input__icon.q-icon-close {
color: var(--color-primary-blue);
cursor: pointer;
&:hover {
color: var(--color-primary-black);
}
}
.q-range-input {
display: inline-block;
width: 42%;
height: 100%;
margin: 0;
padding: 0;
font-weight: var(--font-weight-base);
font-size: inherit;
line-height: 40px;
color: var(--color-primary-black);
white-space: nowrap;
text-overflow: ellipsis;
border: none;
outline: none;
appearance: none;
&::placeholder {
color: rgba(var(--color-rgb-gray), 0.32);
opacity: 1;
}
}
.q-range-separator {
display: inline-block;
width: 5%;
height: 100%;
margin: 0;
padding: 0 2px;
font-size: 14px;
line-height: 32px;
text-align: center;
color: var(--color-primary-black);
}
}
.q-range-editor {
display: inline-flex;
align-items: center;
width: 100%;
padding-left: 5px;
background-color: var(--color-tertiary-gray-light);
border: none;
border-radius: var(--border-radius-base);
box-shadow: var(--box-shadow-primary);
cursor: pointer;
&:hover:not(.q-range-editor_disabled) {
background-color: var(--color-tertiary-gray);
box-shadow: var(--box-shadow-hover);
}
.q-range-input {
line-height: 1;
text-align: center;
background: none;
}
&_focused {
background-color: var(--color-tertiary-gray-ultra-light);
outline: none;
box-shadow: var(--field-box-shadow-focus);
}
&_disabled {
background-color: var(--color-tertiary-gray);
box-shadow: var(--box-shadow-pressed);
cursor: not-allowed;
.q-range-separator,
.q-range-input {
color: var(--field-color-disabled);
cursor: not-allowed;
}
}
.q-form-item_is-error & {
border: var(--border-error);
}
}
================================================
FILE: src/qComponents/QDatePicker/src/styles/year-table.scss
================================================
.q-year-table {
width: 100%;
padding: 10px 0 20px;
table-layout: fixed;
font-size: 12px;
&__cell-wrapper {
text-align: center;
cursor: pointer;
.cell {
position: relative;
display: block;
width: 40px;
height: 40px;
margin: 0 auto;
font-weight: 500;
line-height: 40px;
color: var(--color-primary-blue);
border: none;
border-radius: var(--border-radius-base);
box-shadow: var(--box-shadow-secondary);
&:hover:not([disabled]):hover:not(.cell_in-range):not(.cell_current) {
color: var(--color-primary-black);
background-color: var(--color-tertiary-gray);
}
&[data-focus-visible-added] {
&:not(.cell_disabled) {
color: var(--color-tertiary-white);
background-color: var(--color-primary-blue);
box-shadow: var(--box-shadow-pressed);
transform: scale(1.1);
transition: transform 0.2s;
}
}
&_current:not(.cell_disabled) {
color: var(--color-tertiary-white);
background-color: var(--color-primary-blue);
box-shadow: var(--box-shadow-pressed);
}
&_today {
color: var(--color-primary);
&::before {
content: '';
position: absolute;
top: -5px;
left: 19px;
display: block;
width: 2px;
height: 2px;
background-color: var(--color-primary);
border-radius: 50%;
}
}
&[disabled] {
color: var(--color-tertiary-gray-ultra-dark);
background-color: var(--color-tertiary-gray-lighter);
cursor: not-allowed;
&:hover {
color: var(--color-tertiary-gray-ultra-dark);
}
}
&_in-range {
color: var(--color-tertiary-white);
background-color: var(--color-primary-blue);
box-shadow: var(--box-shadow-pressed);
}
}
}
}
================================================
FILE: src/qComponents/QDialog/QDialog.test.js
================================================
import Component from './src/QDialog.vue';
describe('QDialog', () => {
it('should match snapshot', async () => {
const instance = shallowMount(Component, {
stubs: ['dialog-content']
});
await instance.setData({
isShown: true
});
expect(instance.element).toMatchSnapshot();
});
it('data should match snapshot', () => {
expect(Component.data()).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QDialog/__snapshots__/QDialog.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QDialog data should match snapshot 1`] = `
Object {
"callback": null,
"elementToFocusAfterClosing": null,
"isShown": false,
"top": null,
}
`;
exports[`QDialog should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QDialog/index.js
================================================
import QDialog from './src/QDialog.js';
export default QDialog;
================================================
FILE: src/qComponents/QDialog/src/QDialog.js
================================================
import Vue from 'vue';
import QDialog from './QDialog.vue';
let currentPromise;
const defaultCallback = ({ payload }) => {
currentPromise.resolve({ payload });
};
const Dialog = function(config = {}) {
const DialogComponent = Vue.extend({
components: {
DialogContent: config.component
},
...QDialog
});
const instance = new DialogComponent();
Object.entries(config).forEach(([key, value]) => {
instance[key] = value;
});
if (!instance.zIndex) {
instance.zIndex = Vue.prototype.$Q?.zIndex ?? 2000;
}
instance.callback = defaultCallback;
let parent = document.body;
if (config.rootSelector) {
parent = document.querySelector(config.rootSelector);
}
parent.appendChild(instance.$mount().$el);
Vue.nextTick(() => {
instance.isShown = true;
});
return {
instance,
answer: new Promise((resolve, reject) => {
currentPromise = {
resolve,
reject
};
})
};
};
export default Dialog;
================================================
FILE: src/qComponents/QDialog/src/QDialog.vue
================================================
================================================
FILE: src/qComponents/QDialog/src/q-dialog.scss
================================================
.q-dialog {
position: fixed;
top: 0;
right: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
&__container {
width: 100%;
height: 100%;
overflow: auto;
background-color: var(--color-tertiary-gray-ultra-light);
}
&__view {
display: flex;
}
&__inner {
position: relative;
display: flex;
flex-direction: column;
width: 100%;
padding: 36px 32px 32px;
}
&__close {
position: absolute;
top: 24px;
right: 32px;
}
&__title {
padding-right: 104px;
font-weight: 800;
font-size: 24px;
line-height: 31px;
color: var(--color-primary-black);
word-break: break-word;
}
&__content {
flex: 1;
margin-top: 32px;
}
}
.q-dialog-fade-enter-active {
animation: q-dialog-fade-in 0.3s;
}
.q-dialog-fade-leave-active {
animation: q-dialog-fade-out 0.3s;
}
@keyframes q-dialog-fade-in {
0% {
transform: translate3d(0, -20px, 0);
opacity: 0;
}
100% {
transform: translate3d(0, 0, 0);
opacity: 1;
}
}
@keyframes q-dialog-fade-out {
0% {
transform: translate3d(0, 0, 0);
opacity: 1;
}
100% {
transform: translate3d(0, -20px, 0);
opacity: 0;
}
}
================================================
FILE: src/qComponents/QDrawer/QDrawer.test.js
================================================
import Component from './src/QDrawer';
describe('QDrawer', () => {
it('should match snapshot', async () => {
const instance = shallowMount(Component);
await instance.setProps({
visible: true
});
expect(instance.element).toMatchSnapshot();
});
it('data should match snapshot', () => {
expect(Component.data()).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QDrawer/__snapshots__/QDrawer.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QDrawer data should match snapshot 1`] = `
Object {
"elementToFocusAfterClosing": null,
"isRendered": false,
"zIndex": null,
}
`;
exports[`QDrawer should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QDrawer/index.js
================================================
import QDrawer from './src/QDrawer';
QDrawer.install = Vue => {
Vue.component(QDrawer.name, QDrawer);
};
export default QDrawer;
================================================
FILE: src/qComponents/QDrawer/src/QDrawer.vue
================================================
================================================
FILE: src/qComponents/QDrawer/src/q-drawer.scss
================================================
@keyframes left-drawer-in {
0% {
transform: translate(-100%, 0);
}
100% {
transform: translate(0, 0);
}
}
@keyframes left-drawer-out {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(-100%, 0);
}
}
@keyframes right-drawer-in {
0% {
transform: translate(100%, 0);
}
100% {
transform: translate(0, 0);
}
}
@keyframes right-drawer-out {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(100%, 0);
}
}
@mixin animation-in($position) {
.q-drawer-wrapper_#{$position} {
animation: #{$position}-drawer-in 0.3s 1ms;
}
}
@mixin animation-out($position) {
.q-drawer-wrapper_#{$position} {
animation: #{$position}-drawer-out 0.3s;
}
}
$positions: right, left;
@keyframes q-drawer-fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes q-drawer-slide-in {
0% {
transform: translate(-100%, 0);
}
100% {
transform: translate(0, 0);
}
}
.q-drawer {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
width: 100%;
height: 100%;
background-color: rgba(var(--color-rgb-white), 0.72);
&-fade-enter-active {
animation: q-drawer-fade-in 0.3s;
}
&-fade-leave-active {
animation: q-drawer-fade-in 0.3s reverse;
}
@each $position in $positions {
@include animation-out($position);
@include animation-in($position);
}
&-wrapper {
position: absolute;
top: 0;
z-index: 1;
display: flex;
flex-direction: column;
min-width: 100px;
height: 100%;
padding: 24px;
padding-right: 0;
text-align: left;
background-color: var(--color-tertiary-gray-ultra-light);
outline: none;
box-shadow: -1px -1px 3px rgba(var(--color-rgb-blue), 0.4),
1px 1px 3px rgba(var(--color-rgb-white), 0.25);
&_left {
left: 0;
box-shadow: 1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
-1px -1px 3px rgba(var(--color-rgb-white), 0.25);
}
&_right {
right: 0;
}
}
&__header {
padding-right: 56px;
padding-bottom: 32px;
}
&__close {
position: absolute;
top: 16px;
right: 16px;
padding: 0;
font-size: 32px;
color: var(--color-primary-blue);
background-color: transparent;
border: none;
&:hover,
&:focus {
color: var(--color-primary-black);
outline: none;
}
}
&__title {
font-weight: 800;
font-size: 20px;
line-height: 25px;
color: var(--color-primary-black);
}
&__content {
padding-right: 24px;
}
}
================================================
FILE: src/qComponents/QForm/index.js
================================================
import QForm from './src/QForm';
QForm.install = Vue => {
Vue.component(QForm.name, QForm);
};
export default QForm;
================================================
FILE: src/qComponents/QForm/src/QForm.vue
================================================
================================================
FILE: src/qComponents/QForm/src/q-form.scss
================================================
================================================
FILE: src/qComponents/QFormItem/index.js
================================================
import QFormItem from './src/QFormItem';
QFormItem.install = Vue => {
Vue.component(QFormItem.name, QFormItem);
};
export default QFormItem;
================================================
FILE: src/qComponents/QFormItem/src/QFormItem.vue
================================================
================================================
FILE: src/qComponents/QFormItem/src/q-form-item.scss
================================================
.q-form-item {
position: relative;
&__header {
position: relative;
z-index: 1;
display: flex;
align-items: baseline;
margin-bottom: 8px;
}
&__label {
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
line-height: 18px;
color: var(--color-primary-black);
.q-form-item_is-required:not(.q-form-item_is-no-asterisk) &::after {
content: '*';
margin-left: 4px;
color: var(--color-secondary-orange);
}
}
&__sublabel {
margin-left: auto;
font-size: 10px;
line-height: 12px;
color: rgba(var(--color-rgb-gray), 0.64);
}
&__body {
position: relative;
padding-bottom: 24px;
}
&__error {
position: absolute;
top: calc(100% - 16px);
right: 0;
left: 0;
font-size: 10px;
line-height: 12px;
color: var(--color-secondary-orange);
}
}
================================================
FILE: src/qComponents/QInput/index.js
================================================
import QInput from './src/QInput.vue';
/* istanbul ignore next */
QInput.install = function(Vue) {
Vue.component(QInput.name, QInput);
};
export default QInput;
================================================
FILE: src/qComponents/QInput/src/QInput.vue
================================================
================================================
FILE: src/qComponents/QInput/src/q-input.scss
================================================
.q-input {
--field-color-base: var(--color-primary-black);
--field-color-placeholder: rgba(var(--color-rgb-gray), 0.32);
--field-color-disabled: rgba(var(--color-rgb-gray), 0.64);
--field-icon-color-base: var(--color-primary-blue);
--field-icon-color-hover: var(--color-primary-black);
--field-icon-color-inactive: rgba(var(--color-rgb-gray), 0.64);
--field-background-color-base: var(--color-tertiary-gray-light);
--field-background-color-hover: var(--color-tertiary-gray);
--field-background-color-focus: var(--color-tertiary-gray-ultra-light);
--field-background-color-disabled: var(--color-tertiary-gray);
--field-box-shadow-base: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
4px 4px 8px rgba(var(--color-rgb-blue), 0.4),
-4px -4px 12px var(--color-tertiary-white);
--field-box-shadow-hover: -1px -1px 4px rgba(var(--color-rgb-white), 0.25),
1px 1px 4px rgba(var(--color-rgb-blue), 0.4),
4px 4px 8px rgba(var(--color-rgb-blue), 0.4),
-4px -4px 8px rgba(var(--color-rgb-white), 0.8);
--field-box-shadow-focus: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
inset -1px -1px 1px rgba(var(--color-rgb-white), 0.7),
inset 1px 1px 2px rgba(var(--color-rgb-blue), 0.2);
--field-box-shadow-disabled: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4);
position: relative;
display: inline-block;
width: 100%;
&__inner {
display: inline-block;
width: 100%;
height: 40px;
padding: 0 16px;
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
line-height: var(--line-height-base);
color: var(--field-color-base);
background-color: var(--field-background-color-base);
border: none;
border-radius: var(--border-radius-base);
box-shadow: var(--field-box-shadow-base);
appearance: textfield;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
appearance: none;
margin: 0;
}
&::placeholder,
&:placeholder-shown {
color: var(--field-color-placeholder);
text-overflow: ellipsis;
opacity: 1;
}
/** disable default clear on IE */
&::-ms-reveal,
&::-ms-clear {
display: none;
width: 0;
height: 0;
}
&:focus {
background-color: var(--field-background-color-focus);
outline: none;
box-shadow: var(--field-box-shadow-focus);
}
&:hover {
background-color: var(--field-background-color-hover);
box-shadow: var(--field-box-shadow-hover);
}
.q-form-item_is-error & {
&,
&:focus {
padding-left: 15px;
border: var(--border-error);
}
}
}
&_focus {
.q-input__inner {
background-color: var(--field-background-color-focus);
outline: none;
box-shadow: var(--field-box-shadow-focus);
}
}
&_disabled {
&::after {
content: '\e930';
position: absolute;
top: 0;
right: 8px;
font-size: 24px;
line-height: 40px;
font-family: 'qicon';
cursor: not-allowed;
}
.q-input__inner {
padding-right: 40px;
color: var(--field-color-disabled);
background-color: var(--field-background-color-disabled);
box-shadow: var(--field-box-shadow-disabled);
cursor: not-allowed;
}
}
&_suffix {
.q-input__inner {
padding-right: 40px;
}
}
&__suffix {
position: absolute;
top: 0;
right: 0;
width: 40px;
height: 100%;
text-align: center;
transition: all 0.3s;
pointer-events: none;
.q-input.q-input_disabled & {
display: none;
}
}
&__suffix-inner {
display: block;
pointer-events: all;
}
&__icon {
width: 40px;
text-align: center;
color: var(--field-icon-color-inactive);
&_reverse {
color: var(--field-icon-color-base);
}
&:not(:last-child) {
display: none;
}
&::before {
font-size: 24px;
line-height: 40px;
}
&.q-icon-close,
&.q-icon-eye-close,
&.q-icon-eye {
color: var(--field-icon-color-base);
}
&.q-icon-close,
&.q-icon-eye,
&.q-icon-eye-close {
cursor: pointer;
&::before {
font-family: 'qicon';
}
&:focus,
&:hover {
color: var(--field-icon-color-hover);
}
}
}
&__count {
position: absolute;
top: -24px;
right: 0;
margin-bottom: 2px;
font-size: 10px;
text-align: right;
}
}
================================================
FILE: src/qComponents/QInputNumber/QInputNumber.test.js
================================================
import Component from './src/QInputNumber';
describe('QInputNumber', () => {
it('should match snapshot', () => {
const { element } = shallowMount(Component);
expect(element).toMatchSnapshot();
});
it('should match snapshot without controls', () => {
const { element } = shallowMount(Component, {
propsData: {
controls: false
}
});
expect(element).toMatchSnapshot();
});
it('data should match snapshot', () => {
expect(Component.data()).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QInputNumber/__snapshots__/QInputNumber.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QInputNumber data should match snapshot 1`] = `
Object {
"number": null,
"prevNumber": null,
"userNumber": null,
}
`;
exports[`QInputNumber should match snapshot 1`] = `
`;
exports[`QInputNumber should match snapshot without controls 1`] = `
`;
================================================
FILE: src/qComponents/QInputNumber/index.js
================================================
import QInputNumber from './src/QInputNumber.vue';
/* istanbul ignore next */
QInputNumber.install = function(Vue) {
Vue.component(QInputNumber.name, QInputNumber);
};
export default QInputNumber;
================================================
FILE: src/qComponents/QInputNumber/src/QInputNumber.vue
================================================
================================================
FILE: src/qComponents/QInputNumber/src/q-input-number.scss
================================================
.q-input-number {
position: relative;
display: inline-block;
width: 100%;
line-height: 40px;
&_with-controls {
width: 160px;
& .q-input__inner {
box-sizing: border-box;
padding: 0 34px;
text-align: center;
}
}
&__button {
position: absolute;
top: 0;
right: 0;
z-index: 1;
width: 32px;
height: 40px;
font-size: 16px;
color: rgba(var(--color-rgb-gray), 0.64);
background-color: transparent;
border: none;
border-radius: var(--border-radius-base);
outline: none;
cursor: pointer;
&_decrease {
right: auto;
left: 0;
}
&:enabled:not(.q-input-number__button_is-disabled) {
&:hover::before,
&:focus::before {
background-color: rgba(var(--color-rgb-gray), 0.16);
border-radius: 16px;
outline: none;
}
&:focus::before {
color: var(--color-primary-black);
}
}
&[disabled]:hover {
cursor: not-allowed;
}
&_is-disabled {
color: rgba(var(--color-rgb-gray), 0.16);
cursor: not-allowed;
}
}
&__input {
&.q-input_disabled::after {
display: none;
}
}
}
================================================
FILE: src/qComponents/QMessageBox/QMessageBox.test.js
================================================
import Component from './src/QMessageBox.vue';
describe('QMessageBox', () => {
it('should match snapshot', async () => {
const instance = shallowMount(Component, {
stubs: ['message-box-content']
});
await instance.setData({
isShown: true
});
expect(instance.element).toMatchSnapshot();
});
it('data should match snapshot', () => {
expect(Component.data()).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QMessageBox/__snapshots__/QMessageBox.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QMessageBox data should match snapshot 1`] = `
Object {
"callback": null,
"elementToFocusAfterClosing": null,
"isCancelBtnLoading": false,
"isComponentUsed": false,
"isConfirmBtnLoading": false,
"isShown": false,
}
`;
exports[`QMessageBox should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QMessageBox/index.js
================================================
import QMessageBox from './src/QMessageBox.js';
export default QMessageBox;
================================================
FILE: src/qComponents/QMessageBox/src/QMessageBox.js
================================================
import Vue from 'vue';
import QMessageBox from './QMessageBox.vue';
let currentPromise;
const defaultCallback = ({ action, payload }) => {
if (action === 'confirm') {
currentPromise.resolve({ action, payload });
} else if (action === 'cancel' || action === 'close') {
currentPromise.reject({ action });
}
};
const Message = function(config = {}) {
const MessageBox = Vue.extend({
components: {
MessageBoxContent: config.component
},
...QMessageBox
});
const instance = new MessageBox();
Object.entries(config).forEach(([key, value]) => {
instance[key] = value;
});
if (!instance.zIndex) {
instance.zIndex = Vue.prototype.$Q?.zIndex ?? 2000;
}
instance.callback = defaultCallback;
instance.isComponentUsed = Boolean(config.component);
if (
config.message &&
!instance.isComponentUsed &&
Object.prototype.hasOwnProperty.call(config.message, 'componentOptions')
) {
instance.$slots.default = [config.message];
instance.message = null;
} else {
delete instance.$slots.default;
}
document.body.appendChild(instance.$mount().$el);
Vue.nextTick(() => {
instance.isShown = true;
});
return new Promise((resolve, reject) => {
currentPromise = {
resolve,
reject
};
});
};
export default Message;
================================================
FILE: src/qComponents/QMessageBox/src/QMessageBox.vue
================================================
{{ title }}
{{ message }}
{{ submessage }}
{{ confirmButtonText }}
{{ cancelButtonText }}
================================================
FILE: src/qComponents/QMessageBox/src/q-message-box.scss
================================================
.q-message-box {
--message-box-indent: 32px;
--message-box-container-width: 488px;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
&__scrollbar {
width: 100%;
}
&__view {
position: relative;
display: flex;
justify-content: center;
align-items: center;
.q-message-box__scrollbar:not(.q-scrollbar_has-vertical-bar) & {
align-items: center;
}
}
&__shadow,
&__clickable-shadow {
position: absolute;
top: 0;
left: 0;
}
&__shadow {
width: calc(100% + 20px);
height: calc(100% + 20px);
background-color: rgba(var(--color-rgb-white), 0.7);
}
&__clickable-shadow {
width: 100%;
height: 100%;
}
&__container {
position: relative;
display: flex;
flex-direction: column;
width: var(--message-box-container-width);
margin-top: 80px;
margin-bottom: 80px;
background-color: var(--color-tertiary-gray-ultra-light);
border-radius: var(--border-radius-base);
box-shadow: 0 100px 384px rgba(var(--color-rgb-gray), 0.07),
0 41px 162px rgba(var(--color-rgb-gray), 0.05),
0 22px 85px rgba(var(--color-rgb-gray), 0.04),
0 12px 48px rgba(var(--color-rgb-gray), 0.03),
0 7px 25px rgba(var(--color-rgb-gray), 0.03),
0 3px 10px rgba(var(--color-rgb-gray), 0.02);
}
&__close {
position: absolute;
top: 16px;
right: 16px;
padding: 0;
font-size: 32px;
color: var(--color-primary-blue);
background-color: transparent;
border: none;
&:hover,
&:focus {
color: var(--color-primary-black);
outline: none;
}
}
&__title {
margin: var(--message-box-indent) calc(var(--message-box-indent) * 2)
calc(var(--message-box-indent) / 2) var(--message-box-indent);
font-weight: var(--font-weight-bold);
font-size: 16px;
line-height: var(--line-height-base);
color: var(--color-primary-black);
word-break: break-word;
}
&__content {
padding-right: var(--message-box-indent);
padding-left: var(--message-box-indent);
&:last-child {
margin-bottom: 32px;
}
}
&__message,
&__submessage {
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
line-height: var(--line-height-base);
word-break: break-word;
}
&__message {
color: var(--color-primary-black);
}
&__submessage {
margin-top: 24px;
color: rgba(var(--color-rgb-gray), 0.64);
}
&__actions {
display: flex;
margin-top: 32px;
margin-bottom: 24px;
padding-left: var(--message-box-indent);
}
}
.q-msgbox-fade-enter-active {
animation: q-msgbox-fade-in 0.3s;
}
.q-msgbox-fade-leave-active {
animation: q-msgbox-fade-out 0.3s;
}
@keyframes q-msgbox-fade-in {
0% {
transform: translate3d(0, -20px, 0);
opacity: 0;
}
100% {
transform: translate3d(0, 0, 0);
opacity: 1;
}
}
@keyframes q-msgbox-fade-out {
0% {
transform: translate3d(0, 0, 0);
opacity: 1;
}
100% {
transform: translate3d(0, -20px, 0);
opacity: 0;
}
}
================================================
FILE: src/qComponents/QNotification/index.js
================================================
import QNotification from './src/QNotification.js';
export default QNotification;
================================================
FILE: src/qComponents/QNotification/src/QNotification.js
================================================
import Vue from 'vue';
import QNotification from './QNotification.vue';
const instances = [];
let instancesCounter = 1;
const parent = document.createElement('div');
parent.className = 'q-notification-box';
const Notification = (config = {}) => {
instancesCounter += 1;
const id = `notification_${instancesCounter}`;
const NotificationConstructor = Vue.extend(QNotification);
const instance = new NotificationConstructor();
Object.entries(config).forEach(([key, value]) => {
instance[key] = value;
});
instance.id = id;
instance.onClose = () => {
Notification.close(id, config.onClose);
};
if (
config.message &&
Object.prototype.hasOwnProperty.call(config.message, 'componentOptions')
) {
instance.$slots.default = [config.message];
instance.message = null;
} else {
delete instance.$slots.default;
}
parent.style.zIndex = Vue.prototype.$Q?.zIndex + 1 ?? 2000;
document.body.appendChild(parent);
parent.appendChild(instance.$mount().$el);
Vue.nextTick(() => {
instance.isShown = true;
});
instances.push(instance);
return instance;
};
Notification.close = (instanceId, onClose) => {
if (typeof onClose === 'function') onClose();
const foundInstanceIndex = instances.findIndex(({ id }) => id === instanceId);
instances.splice(foundInstanceIndex, 1);
};
Notification.closeAll = () => {
for (let i = instances.length - 1; i >= 0; i -= 1) {
instances[i].close();
}
};
export default Notification;
================================================
FILE: src/qComponents/QNotification/src/QNotification.vue
================================================
================================================
FILE: src/qComponents/QNotification/src/q-notification.scss
================================================
.q-notification-box {
position: fixed;
top: 16px;
right: 16px;
}
.q-notification {
position: relative;
display: flex;
align-items: center;
width: 552px;
min-height: 88px;
padding-top: 24px;
padding-bottom: 24px;
overflow: hidden;
background-color: var(--color-tertiary-gray-ultra-light);
border-radius: var(--border-radius-base);
box-shadow: var(--box-shadow-pressed);
transition: opacity 0.3s, transform 0.3s;
&:not(:first-child) {
margin-top: 16px;
}
&__icon {
position: absolute;
top: 28px;
left: 24px;
font-size: 32px;
.q-notification_type_success & {
color: var(--color-secondary-green);
}
.q-notification_type_warning & {
color: var(--color-secondary-yellow);
}
.q-notification_type_info & {
color: rgba(var(--color-rgb-gray), 32);
}
.q-notification_type_error & {
color: var(--color-secondary-orange);
}
}
&__content {
padding-right: 56px;
padding-left: 80px;
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
line-height: var(--line-height-base);
color: var(--color-primary-black);
letter-spacing: var(--letter-spacing-base);
}
&__close {
position: absolute;
top: 32px;
right: 16px;
padding: 0;
font-size: 24px;
color: var(--color-primary-blue);
background-color: transparent;
border: none;
&:hover,
&:focus {
color: var(--color-primary-black);
outline: none;
}
}
}
.q-notification-fade-enter {
transform: translateX(100%);
opacity: 0;
}
.q-notification-fade-leave-active {
animation: q-notification-fade-out 0.3s;
}
@keyframes q-notification-fade-out {
0% {
transform: translateX(0);
opacity: 1;
}
100% {
transform: translateX(100%);
opacity: 0;
}
}
================================================
FILE: src/qComponents/QOption/index.js
================================================
import QOption from './src/QOption';
QOption.install = function(Vue) {
Vue.component(QOption.name, QOption);
};
export default QOption;
================================================
FILE: src/qComponents/QOption/src/QOption.vue
================================================
================================================
FILE: src/qComponents/QOption/src/q-option.scss
================================================
.q-option {
--option-background-color-base: var(--color-tertiary-gray-light);
--option-background-color-hover: var(--color-tertiary-gray);
--option-background-color-focus: var(--color-tertiary-gray);
--option-background-color-selected: var(--color-tertiary-gray-ultra-light);
--option-background-color-disabled: var(--color-tertiary-gray-light);
--option-color-base: var(--color-primary-blue);
--option-color-hover: var(--color-primary-black);
--option-color-selected: var(--color-primary-black);
--option-color-disabled: rgba(var(--color-rgb-gray), 0.64);
position: relative;
display: flex;
align-items: flex-end;
height: 40px;
padding: 8px 16px;
background-color: var(--option-background-color-base);
outline: none;
box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4);
cursor: pointer;
user-select: none;
&:not(:first-child) {
margin-top: 1px;
}
&_selected {
background-color: var(--option-background-color-selected);
}
&:hover:not(.q-option_disabled) {
background-color: var(--option-background-color-hover);
}
&:focus {
outline: none;
}
&.focus-visible {
background-color: var(--option-background-color-focus);
}
&_all ~ .q-option {
padding-left: 24px;
}
&_disabled {
padding-right: 40px;
background-color: var(--option-background-color-disabled);
cursor: not-allowed;
}
&_with-checkbox {
padding-left: 8px;
.q-option__label {
margin-left: 8px;
}
}
&__label {
overflow: hidden;
font-size: var(--font-size-base);
line-height: var(--line-height-base);
text-align: left;
color: var(--option-color-base);
white-space: nowrap;
text-overflow: ellipsis;
.q-option_selected & {
color: var(--option-color-selected);
}
.q-option:hover:not(.q-option_disabled) & {
color: var(--option-color-hover);
}
.q-option_disabled & {
color: var(--option-color-disabled);
}
}
&__lock {
position: absolute;
top: 0;
right: 0;
width: 40px;
font-size: 24px;
line-height: 40px;
text-align: center;
}
}
================================================
FILE: src/qComponents/QPagination/index.js
================================================
import QPagination from './src/QPagination.vue';
QPagination.install = Vue => {
Vue.component(QPagination.name, QPagination);
};
export default QPagination;
================================================
FILE: src/qComponents/QPagination/src/QPagination.vue
================================================
================================================
FILE: src/qComponents/QPagination/src/q-pagination.scss
================================================
.q-pagination {
--pagination-color-base: var(--color-primary-blue);
--pagination-color-hover: var(--color-primary-black);
--pagination-color-selected: var(--color-primary-black);
--pagination-color-disabled: rgba(var(--color-rgb-gray), 0.64);
--pagination-background-color-base: var(--color-tertiary-gray-light);
--pagination-background-color-hover: var(--color-tertiary-gray);
--pagination-background-color-selected: var(
--color-tertiary-gray-ultra-light
);
--pagination-background-color-disabled: var(--color-tertiary-gray);
--pagination-box-shadow-primary: -1px -1px 4px var(--color-tertiary-white),
1px 1px 4px rgba(var(--color-rgb-blue), 0.4),
4px 4px 8px rgba(var(--color-rgb-blue), 0.4),
-4px -4px 8px rgba(var(--color-rgb-white), 0.25);
--pagination-box-shadow-secondary: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4);
display: inline-grid;
margin-top: 24px;
white-space: nowrap;
grid-gap: 8px;
&__btn {
box-sizing: border-box;
min-width: 40px;
height: 40px;
margin: 0;
padding: 0;
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
line-height: 40px;
vertical-align: top;
text-align: center;
color: var(--pagination-color-base);
background-color: var(--pagination-background-color-base);
border: none;
border-radius: var(--border-radius-base);
box-shadow: var(--pagination-box-shadow-primary);
cursor: pointer;
grid-row: 1;
&:hover,
&:focus {
color: var(--pagination-color-hover);
background-color: var(--pagination-background-color-hover);
outline: none;
}
&_quick {
font-size: 24px;
&::before {
line-height: 40px;
}
.q-pagination_disabled &,
&:not(:focus):not(:hover) {
&::before {
content: '...';
font-size: var(--font-size-base);
}
}
}
&_icon {
font-size: 24px;
}
&_active:not(:hover),
&_active:not(:focus) {
color: var(--pagination-color-selected);
background-color: var(--pagination-background-color-selected);
box-shadow: var(--pagination-box-shadow-secondary);
cursor: default;
}
&.q-pagination__btn_disabled,
.q-pagination_disabled & {
color: var(--pagination-color-disabled);
background-color: var(--pagination-background-color-disabled);
box-shadow: var(--pagination-box-shadow-secondary);
cursor: not-allowed;
}
}
}
================================================
FILE: src/qComponents/QPopover/QPopover.test.js
================================================
import Component from './index';
describe('QPopover', () => {
const options = {
slots: {
reference: 'Open '
}
};
it('should match snapshot', () => {
const { element } = shallowMount(Component, options);
expect(element).toMatchSnapshot();
});
it('data should match snapshot', () => {
expect(Component.data()).toMatchSnapshot();
});
describe('computed', () => {
describe('popoverClasses', () => {
it('should return expected value if icon does not exist', () => {
const instance = shallowMount(Component, options);
instance.setProps({
icon: ''
});
const expected = {
'q-popover_without-icon': true
};
expect(instance.vm.popoverClasses).toEqual(expected);
});
it('should return expected value if icon exists', () => {
const instance = shallowMount(Component, options);
instance.setProps({
icon: 'icon-name'
});
const expected = {
'q-popover_without-icon': false
};
expect(instance.vm.popoverClasses).toEqual(expected);
});
});
});
describe('methods', () => {
describe('togglePopover', () => {
it('should set isPopoverShown to false', () => {
const instance = shallowMount(Component, options);
instance.setData({
isPopoverShown: true
});
instance.vm.togglePopover();
expect(instance.vm.isPopoverShown).toBeFalsy();
});
it('should set isPopoverShown to true', () => {
const instance = shallowMount(Component, options);
instance.setData({
isPopoverShown: false
});
instance.vm.togglePopover();
expect(instance.vm.isPopoverShown).toBeTruthy();
});
});
describe('destroy', () => {
it('should set isPopoverShown to false', () => {
const instance = shallowMount(Component, options);
instance.setData({
isPopoverShown: true
});
instance.vm.destroy();
expect(instance.vm.isPopoverShown).toBeFalsy();
});
it('should call destroy', () => {
const instance = shallowMount(Component, options);
const spy = jest.fn(() => {});
instance.setData({
popperJS: {
destroy: spy
}
});
instance.vm.destroy();
expect(spy).toBeCalled();
});
it('should set popperJS to null', () => {
const instance = shallowMount(Component, options);
instance.setData({
popperJS: {
destroy: () => {}
}
});
instance.vm.destroy();
expect(instance.vm.popperJS).toBeNull();
});
});
});
});
================================================
FILE: src/qComponents/QPopover/__snapshots__/QPopover.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QPopover data should match snapshot 1`] = `
Object {
"isPopoverShown": false,
"popperJS": null,
"referenceElement": null,
"zIndex": null,
}
`;
exports[`QPopover should match snapshot 1`] = `
Open
`;
================================================
FILE: src/qComponents/QPopover/index.js
================================================
import QPopover from './src/QPopover.vue';
/* istanbul ignore next */
QPopover.install = function(Vue) {
Vue.component(QPopover.name, QPopover);
};
export default QPopover;
================================================
FILE: src/qComponents/QPopover/src/QPopover.vue
================================================
================================================
FILE: src/qComponents/QPopover/src/q-popover.scss
================================================
.q-popover {
display: flex;
align-items: flex-start;
min-width: 320px;
max-width: 552px;
padding: 24px;
text-align: left;
color: var(--color-primary-black);
letter-spacing: 0.2px;
background-color: var(--color-tertiary-white);
border-radius: var(--border-radius-base);
box-shadow: var(--box-shadow-primary);
&__icon {
margin-right: 16px;
text-align: center;
color: var(--color-tertiary-white);
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
&::before {
font-size: 40px;
line-height: 40px;
}
}
&__inner {
max-height: 646px;
padding-top: 8px;
}
&__title {
min-width: 216px;
font-weight: 600;
font-size: 16px;
line-height: 22px;
& + .q-popover__content {
margin-top: 16px;
}
}
&__content {
font-style: normal;
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
line-height: 20px;
color: var(--color-primary-black);
}
&_without-icon {
padding: 16px;
}
}
================================================
FILE: src/qComponents/QRadio/QRadio.test.js
================================================
import Component from './src/QRadio';
describe('QRadio', () => {
it('should match snapshot', () => {
const { element } = shallowMount(Component);
expect(element).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QRadio/__snapshots__/QRadio.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QRadio should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QRadio/index.js
================================================
import QRadio from './src/QRadio';
QRadio.install = Vue => {
Vue.component(QRadio.name, QRadio);
};
export default QRadio;
================================================
FILE: src/qComponents/QRadio/src/QRadio.vue
================================================
{{ label }}
================================================
FILE: src/qComponents/QRadio/src/q-radio.scss
================================================
.q-radio {
--radio-color-base: var(--color-primary-black);
--radio-color-disabled: rgba(var(--color-rgb-gray), 0.64);
--radio-background-color-base: var(--color-tertiary-gray-ultra-light);
--radio-background-color-hover: var(--color-tertiary-gray);
--radio-background-color-focus: var(--color-tertiary-gray-ultra-light);
--radio-background-color-checked: var(--color-tertiary-gray-ultra-light);
--radio-background-color-disabled: var(--color-tertiary-gray);
--radio-dot-color-base: var(--color-primary-blue);
--radio-dot-color-disabled: rgba(var(--color-rgb-gray), 0.64);
--radio-box-shadow-base: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
inset -1px -1px 1px rgba(var(--color-rgb-white), 0.7),
inset 1px 1px 2px rgba(var(--color-rgb-blue), 0.2);
--radio-box-shadow-focus: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
4px 4px 10px rgba(var(--color-rgb-blue), 0.4),
-4px -4px 10px rgba(var(--color-rgb-white), 0.25),
inset -1px -1px 1px rgba(var(--color-rgb-white), 0.7),
inset 1px 1px 2px rgba(var(--color-rgb-blue), 0.2);
position: relative;
display: inline-flex;
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
line-height: 1;
color: var(--radio-color-base);
white-space: nowrap;
outline: none;
cursor: pointer;
user-select: none;
&_disabled {
cursor: not-allowed;
}
&__label {
margin-top: 3px;
padding-left: 16px;
font-size: var(--font-size-base);
line-height: 18px;
white-space: normal;
word-break: break-word;
.q-radio_disabled & {
color: var(--radio-color-disabled);
}
}
&__input {
position: relative;
line-height: 0;
white-space: nowrap;
border: none;
outline: none;
}
&__inner {
position: relative;
display: inline-block;
box-sizing: border-box;
width: 24px;
height: 24px;
background-color: var(--radio-background-color-base);
border-radius: 100%;
outline: none;
box-shadow: var(--radio-box-shadow-base);
&::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 12px;
height: 12px;
background-color: var(--radio-dot-color-base);
border-radius: 100%;
transform: translate(-50%, -50%) scale(0);
transition: transform 0.15s ease-in;
}
.q-radio_checked & {
background-color: var(--radio-background-color-checked);
&::after {
transform: translate(-50%, -50%) scale(1);
}
}
.q-radio:not(.q-radio_disabled):focus & {
background-color: var(--radio-background-color-focus);
box-shadow: var(--radio-box-shadow-focus);
}
&,
.q-radio:not(.q-radio_disabled):focus &,
.q-radio_checked & {
&:hover {
background-color: var(--radio-background-color-hover);
}
}
.q-radio_disabled & {
background-color: var(--radio-background-color-disabled);
&::after {
background-color: var(--radio-dot-color-disabled);
}
}
}
&__original {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
margin: 0;
outline: none;
opacity: 0;
}
}
================================================
FILE: src/qComponents/QRadioGroup/QRadioGroup.test.js
================================================
import Component from './src/QRadioGroup';
describe('QRadioGroup', () => {
it('should match snapshot', () => {
const { element } = shallowMount(Component);
expect(element).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QRadioGroup/__snapshots__/QRadioGroup.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QRadioGroup should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QRadioGroup/index.js
================================================
import QRadioGroup from './src/QRadioGroup';
QRadioGroup.install = Vue => {
Vue.component(QRadioGroup.name, QRadioGroup);
};
export default QRadioGroup;
================================================
FILE: src/qComponents/QRadioGroup/src/QRadioGroup.vue
================================================
================================================
FILE: src/qComponents/QRadioGroup/src/q-radio-group.scss
================================================
.q-radio-group {
display: flex;
align-items: flex-start;
font-size: 0;
line-height: 1;
&_vertical {
flex-direction: column;
.q-radio:not(:last-child) {
margin-bottom: 16px;
}
}
&_horizontal {
.q-radio:not(:last-child) {
margin-right: 32px;
}
}
}
================================================
FILE: src/qComponents/QRow/QRow.test.js
================================================
import Component from './src/QRow';
describe('QRow', () => {
it('should match snapshot', async () => {
const { element } = shallowMount(Component);
expect(element).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QRow/__snapshots__/QRow.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QRow should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QRow/index.js
================================================
import QRow from './src/QRow';
QRow.install = Vue => {
Vue.component(QRow.name, QRow);
};
export default QRow;
================================================
FILE: src/qComponents/QRow/src/QRow.vue
================================================
================================================
FILE: src/qComponents/QRow/src/q-row.scss
================================================
$align-h: (
start: flex-start,
end: flex-end,
center: center,
between: space-between,
around: space-around
);
$align-v: (
start: flex-start,
end: flex-end,
center: center,
baseline: baseline,
stretch: stretch
);
.q-row {
display: flex;
flex-wrap: wrap;
margin-right: calc(var(--layout-gutter) / -2);
margin-left: calc(var(--layout-gutter) / -2);
@each $key, $value in $align-h {
&_align-h_#{$key} {
justify-content: $value;
}
}
@each $key, $value in $align-v {
&_align-v_#{$key} {
align-items: $value;
}
}
}
================================================
FILE: src/qComponents/QScrollbar/index.js
================================================
import QScrollbar from './src/QScrollbar';
/* istanbul ignore next */
QScrollbar.install = function(Vue) {
Vue.component(QScrollbar.name, QScrollbar);
};
export default QScrollbar;
================================================
FILE: src/qComponents/QScrollbar/src/QBar.vue
================================================
================================================
FILE: src/qComponents/QScrollbar/src/QScrollbar.vue
================================================
================================================
FILE: src/qComponents/QScrollbar/src/q-scrollbar.scss
================================================
.q-scrollbar {
position: relative;
height: 100%;
overflow: hidden;
&_visible,
&:hover,
&:active,
&:focus {
> .q-scrollbar__bar {
opacity: 1;
transition: opacity 340ms ease-out;
}
}
&__wrap {
height: 100%;
overflow: scroll;
&_hidden-default {
scrollbar-width: none;
&::-webkit-scrollbar {
width: 0;
height: 0;
}
}
}
&__view {
min-height: 100%;
}
&__thumb {
position: relative;
display: block;
width: 0;
height: 0;
background-color: var(--color-primary-blue);
border-radius: var(--border-radius-base);
cursor: pointer;
transition: 0.3s background-color;
&:hover {
background-color: var(--color-primary-blue);
}
&_secondary {
background-color: rgba(var(--color-rgb-gray), 0.16);
&:hover,
&:active {
background-color: rgba(var(--color-rgb-gray), 0.32);
}
}
}
&__bar {
position: absolute;
right: 4px;
bottom: 4px;
z-index: 1;
border-radius: var(--border-radius-base);
opacity: 0;
transition: opacity 120ms ease-out;
&_vertical {
top: 6px;
width: 2px;
> .q-scrollbar__thumb {
width: 100%;
}
}
&_horizontal {
left: 6px;
height: 2px;
> .q-scrollbar__thumb {
height: 100%;
}
}
&_secondary.q-scrollbar__bar_vertical {
width: 8px;
background-color: var(--color-tertiary-gray);
}
&_secondary.q-scrollbar__bar_horizontal {
height: 8px;
background-color: var(--color-tertiary-gray);
}
}
}
================================================
FILE: src/qComponents/QScrollbar/src/util.js
================================================
export const BAR_MAP = {
vertical: {
offset: 'offsetHeight',
scroll: 'scrollTop',
scrollSize: 'scrollHeight',
size: 'height',
key: 'vertical',
axis: 'Y',
client: 'clientY',
direction: 'top'
},
horizontal: {
offset: 'offsetWidth',
scroll: 'scrollLeft',
scrollSize: 'scrollWidth',
size: 'width',
key: 'horizontal',
axis: 'X',
client: 'clientX',
direction: 'left'
}
};
export function renderThumbStyle({ move, size, bar }) {
const style = {};
const translate = `translate${bar.axis}(${move}%)`;
style[bar.size] = size;
style.transform = translate;
style.msTransform = translate;
style.webkitTransform = translate;
return style;
}
================================================
FILE: src/qComponents/QSelect/index.js
================================================
import QSelect from './src/QSelect.vue';
QSelect.install = function(Vue) {
Vue.component(QSelect.name, QSelect);
};
export default QSelect;
================================================
FILE: src/qComponents/QSelect/src/QSelect.vue
================================================
================================================
FILE: src/qComponents/QSelect/src/QSelectDropdown.vue
================================================
{{ emptyText }}
{{ loadMoreText }}
================================================
FILE: src/qComponents/QSelect/src/QSelectTags.vue
================================================
{{ selected[0].preparedLabel }}
+ {{ selected.length - 1 }}
{{ option.preparedLabel }}
================================================
FILE: src/qComponents/QSelect/src/q-select-dropdown.scss
================================================
.q-select-dropdown {
--dropdown-max-height: 204px;
--dropdown-box-shadow-base: var(--box-shadow-primary);
--dropdown-background-color-base: var(--color-tertiary-gray-light);
position: absolute;
z-index: 1001;
display: none;
width: 100%;
overflow: hidden;
background-color: var(--dropdown-background-color-base);
border-radius: var(--border-radius-base);
box-shadow: var(--dropdown-box-shadow-base);
&_shown {
display: block;
}
&__empty {
margin: 0;
padding: 10px 16px 8px;
color: rgba(var(--color-rgb-gray), 0.64);
}
&__wrap {
max-height: var(--dropdown-max-height);
}
}
================================================
FILE: src/qComponents/QSelect/src/q-select-tags.scss
================================================
.q-select-tags {
--field-color-base: var(--color-primary-black);
display: flex;
flex-wrap: wrap;
align-items: center;
width: calc(100% - 40px);
padding: 8px 0 8px 8px;
white-space: normal;
pointer-events: none;
&_filterable {
&:not(.q-select-tags_collapse-tags) {
.q-tag:last-of-type {
max-width: 65%;
}
}
}
&.q-select-tags_collapse-tags {
flex-wrap: nowrap;
.q-tag + .q-tag {
flex-shrink: 0;
}
}
&__input {
z-index: 1;
flex: 1;
min-width: 24px;
height: 20px;
padding: 0;
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
line-height: var(--line-height-base);
color: var(--field-color-base);
background-color: transparent;
border: none;
outline: none;
appearance: none;
&:first-child {
margin-left: 8px;
}
}
}
================================================
FILE: src/qComponents/QSelect/src/q-select.scss
================================================
@import './q-select-tags';
@import './q-select-dropdown';
.q-select {
position: relative;
display: inline-flex;
width: 100%;
min-height: 40px;
vertical-align: middle;
&__input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
.q-input__inner {
height: 100%;
user-select: none;
}
}
}
================================================
FILE: src/qComponents/QSlider/index.js
================================================
import QSlider from './src/QSlider.vue';
QSlider.install = function(Vue) {
Vue.component(QSlider.name, QSlider);
};
export default QSlider;
================================================
FILE: src/qComponents/QSlider/src/QSlider.vue
================================================
================================================
FILE: src/qComponents/QSlider/src/components/QSliderBar/index.vue
================================================
================================================
FILE: src/qComponents/QSlider/src/components/QSliderButton/index.vue
================================================
{{ formattedValue }}
================================================
FILE: src/qComponents/QSlider/src/components/QSliderCaptions/index.vue
================================================
{{ caption.label }}
================================================
FILE: src/qComponents/QSlider/src/components/QSliderSteps/index.vue
================================================
================================================
FILE: src/qComponents/QSlider/src/q-slider.scss
================================================
@import './styles/q-slider.scss';
@import './styles/q-slider-bar.scss';
@import './styles/q-slider-button.scss';
@import './styles/q-slider-steps.scss';
@import './styles/q-slider-captions.scss';
================================================
FILE: src/qComponents/QSlider/src/styles/q-slider-bar.scss
================================================
.q-slider-bar {
position: absolute;
z-index: 1;
height: 8px;
background-color: rgba(var(--color-rgb-red), 0.32);
border-radius: 3px;
}
================================================
FILE: src/qComponents/QSlider/src/styles/q-slider-button.scss
================================================
.q-slider-button {
position: absolute;
top: 50%;
z-index: 2;
display: flex;
justify-content: center;
align-items: center;
padding: 0;
line-height: normal;
text-align: center;
background-color: transparent;
border: none;
transform: translate(-50%, -50%);
user-select: none;
&__target {
width: 16px;
height: 16px;
background-color: var(--color-tertiary-gray-light);
border-radius: 50%;
box-shadow: -4px -4px 12px var(--color-tertiary-white),
4px 4px 8px rgba(var(--color-rgb-blue), 0.4),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
-1px -1px 3px rgba(var(--color-rgb-white), 0.25);
user-select: none;
}
&__tooltip {
position: absolute;
bottom: calc(100% + 8px);
display: none;
min-width: 24px;
padding: 5px 4px;
font-weight: var(--font-weight-base);
text-align: center;
color: var(--color-primary-black);
white-space: nowrap;
background-color: var(--color-tertiary-gray-lighter);
border-radius: var(--border-radius-base);
box-shadow: var(--box-shadow-secondary);
}
&:hover,
&:focus,
&_show-tooltip {
z-index: 3;
outline: none;
.q-slider-button__target {
background-color: var(--color-primary);
box-shadow: none;
}
.q-slider-button__tooltip {
z-index: 3;
display: block;
}
}
}
================================================
FILE: src/qComponents/QSlider/src/styles/q-slider-captions.scss
================================================
.q-slider-captions {
position: relative;
width: 100%;
height: 20px;
margin-top: 8px;
&__btn {
position: absolute;
top: 0;
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
line-height: var(--line-height-base);
color: rgba(var(--color-rgb-gray), 0.32);
background-color: transparent;
border: none;
transform: translateX(-50%);
&:hover,
&:focus,
&_selected {
color: var(--color-primary-black);
}
}
}
================================================
FILE: src/qComponents/QSlider/src/styles/q-slider-steps.scss
================================================
.q-slider-steps {
position: relative;
z-index: 1;
width: 100%;
height: 100%;
overflow: hidden;
&__step {
position: absolute;
top: 0;
width: 4px;
height: 8px;
background-color: var(--color-tertiary-gray-light);
box-shadow: 1px 0 1px rgba(174, 174, 192, 0.2),
-1px 0 1px rgba(255, 255, 255, 0.7);
transform: translateX(-50%);
&:first-child,
&:last-child {
display: none;
}
}
}
================================================
FILE: src/qComponents/QSlider/src/styles/q-slider.scss
================================================
.q-slider {
--box-shadow-line: inset 1px 1px 2px rgba(var(--color-rgb-blue), 0.2),
inset -1px -1px 1px rgba(var(--color-rgb-white), 0.7);
position: relative;
width: 100%;
&__path {
position: relative;
width: 100%;
height: 8px;
background-color: var(--color-tertiary-gray);
border-radius: 4px;
box-shadow: var(--box-shadow-line);
cursor: pointer;
}
&_is-disabled {
.q-slider {
&__path {
cursor: default;
}
}
.q-slider-button {
&__target {
background-color: var(--color-tertiary-gray-light);
&:hover {
background-color: var(--color-tertiary-gray-light);
}
}
&:focus {
.q-slider-button__target {
background-color: var(--color-tertiary-gray-light);
box-shadow: var(--box-shadow-hover);
}
}
&:hover {
cursor: not-allowed;
.q-slider-button__target {
background-color: var(--color-tertiary-gray-light);
box-shadow: var(--box-shadow-hover);
}
}
}
.q-slider-bar {
background-color: rgba(var(--color-rgb-gray), 0.32);
}
.q-slider-captions {
&__btn {
cursor: default;
}
}
}
&_is-vertical {
width: auto;
height: 196px;
.q-slider {
&__path {
width: 8px;
height: 100%;
}
}
.q-slider-button {
top: auto;
left: 50%;
transform: translate(-50%, 50%);
}
.q-slider-bar {
width: 8px;
height: auto;
}
.q-slider-steps {
&__step {
top: auto;
width: 8px;
height: 4px;
transform: translateY(50%);
}
}
.q-slider-captions {
position: absolute;
top: 0;
left: 12px;
width: 20px;
height: 100%;
&__btn {
top: auto;
left: 0;
transform: translateY(50%);
}
}
}
}
================================================
FILE: src/qComponents/QTabPane/QTabPane.test.js
================================================
import Component from './src/QTabPane';
describe('QTabPane', () => {
it('should match snapshot', async () => {
const instance = shallowMount(Component, {
propsData: {
name: 'pane_name',
title: 'Pane Title'
}
});
expect(instance.element).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QTabPane/__snapshots__/QTabPane.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QTabPane should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QTabPane/index.js
================================================
import QTabPane from './src/QTabPane';
QTabPane.install = Vue => {
Vue.component(QTabPane.name, QTabPane);
};
export default QTabPane;
================================================
FILE: src/qComponents/QTabPane/src/QTabPane.vue
================================================
{{ title }}
{{ description }}
================================================
FILE: src/qComponents/QTabPane/src/q-tab-pane.scss
================================================
.q-tab-pane {
--tab-pane-width: 200px;
&:not(:last-child) {
margin-right: 8px;
}
&__inner {
display: flex;
align-items: center;
}
&__btn {
width: var(--tab-pane-width);
padding: 11px 0;
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
line-height: 18px;
text-align: center;
color: var(--color-primary-blue);
letter-spacing: 0.2px;
background-color: var(--color-tertiary-gray-light);
border: none;
border-radius: var(--border-radius-base);
outline: none;
box-shadow: var(--box-shadow-primary);
cursor: pointer;
&:hover {
color: var(--color-primary-black);
background-color: var(--color-tertiary-gray);
}
&:focus {
color: var(--color-primary-black);
background-color: var(--color-tertiary-gray-dark);
outline: none;
}
&_active {
color: var(--color-primary-black);
background-color: var(--color-tertiary-gray-ultra-light);
}
&_active {
&:hover {
color: var(--color-primary-black);
background-color: var(--color-tertiary-gray-ultra-light);
}
}
&_disabled {
color: rgba(var(--color-rgb-gray), 0.64);
background-color: var(--color-tertiary-gray);
cursor: not-allowed;
&:focus,
&:hover {
color: rgba(var(--color-rgb-gray), 0.64);
background-color: var(--color-tertiary-gray);
}
}
}
&__description {
width: var(--tab-pane-width);
margin-top: 8px;
font-weight: var(--font-weight-base);
font-size: 10px;
line-height: 12px;
color: rgba(var(--color-rgb-gray), 0.32);
}
}
================================================
FILE: src/qComponents/QTable/QTable.test.js
================================================
import Component from './index';
describe('QTable', () => {
const options = {
propsData: {
groupsOfColumns: [
{
columns: [
{
key: 'col1',
value: 'Column 1'
}
]
}
],
rows: [
{
col1: 'Columns row 1'
}
]
}
};
it('QTable should match snapshot', () => {
const { element } = shallowMount(Component, options);
expect(element).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QTable/__snapshots__/QTable.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QTable QTable should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QTable/index.js
================================================
import QTable from './src/QTable.vue';
QTable.install = function(Vue) {
Vue.component(QTable.name, QTable);
};
export default QTable;
================================================
FILE: src/qComponents/QTable/src/QTable.vue
================================================
{{ total[column.key] }}
{{ rowData.value }}
{{ emptyText || $t('QTable.noData') }}
================================================
FILE: src/qComponents/QTable/src/components/DragElements/index.vue
================================================
================================================
FILE: src/qComponents/QTable/src/components/QTableRow/index.vue
================================================
================================================
FILE: src/qComponents/QTable/src/hocs/withQTableRow/index.js
================================================
const renderedChilds = ({
renderContext,
childrenKey,
listeners,
component,
children,
props,
scopedSlots
}) => {
return children.reduce((acc, row) => {
const renderingArray = [];
renderingArray.push(
renderContext(component, {
props: {
...props,
indent: row.indent,
row
},
on: { ...listeners },
scopedSlots
})
);
if (row[childrenKey]) {
const renderedData = {
renderContext,
childrenKey,
component,
listeners,
props,
scopedSlots,
children: row.data[childrenKey]
};
renderingArray.push(renderedChilds(renderedData));
}
return [...acc, ...renderingArray];
}, []);
};
const withQTableRow = QTableRow => ({
functional: true,
name: 'withQTableRow',
render(renderContext, context) {
const { props, listeners, scopedSlots, data } = context;
const { childrenKey, row } = props;
const renderingArray = [];
if (row.data[childrenKey]) {
const renderedData = {
renderContext,
childrenKey,
component: QTableRow,
listeners,
children: row.data[childrenKey],
props,
scopedSlots
};
if (row.isTreeOpened) renderingArray.push(renderedChilds(renderedData));
}
return [renderContext(QTableRow, { ...data }), ...renderingArray];
}
});
export default withQTableRow;
================================================
FILE: src/qComponents/QTable/src/q-table.scss
================================================
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.q-table {
--row-custom-border-color: transparent;
--custom-box-shadow: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
2px 1px 3px rgba(var(--color-rgb-blue), 0.4);
--custom-box-shadow-reversed: 1px -1px 3px rgba(var(--color-rgb-white), 0.25),
-2px 1px 3px rgba(var(--color-rgb-blue), 0.4);
&__fixed {
table-layout: fixed;
}
&__expand-arrow {
display: inline-block;
font-size: 23px;
vertical-align: middle;
&_opened {
transform: rotate(90deg);
}
}
&__drag-elements {
display: inline-block;
width: auto;
cursor: grab;
}
.dnd-separator,
.dnd-handler {
position: absolute;
display: none;
pointer-events: none;
}
.dnd-separator {
left: -100%;
z-index: 9;
width: 1px;
height: 100%;
background-color: var(--color-primary-blue);
box-shadow: 0 0 8px rgba(65, 98, 240, 0.24);
}
.dnd-handler {
z-index: 999;
height: 100%;
background-color: rgba(var(--color-rgb-gray), 0.16);
}
.drop-handler {
position: absolute;
top: 0;
left: -1px;
z-index: -1;
width: 50%;
height: 100%;
&.dnd-after {
right: 0;
left: auto;
}
}
.dragInit {
cursor: grabbing;
user-select: none;
.drop-handler {
z-index: 99;
}
}
&__wrapper {
position: relative;
height: 100%;
padding-right: 3px;
&_scrollable {
.q-table__table {
padding-bottom: 11px;
}
}
& .q-checkbox__inner {
z-index: 0;
}
}
&__loading-wrapper {
height: 100%;
overflow: hidden;
}
&__loader {
position: absolute;
top: 0;
left: 0;
z-index: 10;
display: flex;
justify-content: center;
width: 100%;
height: 100%;
min-height: 115px;
padding-top: 120px;
background-color: var(--color-tertiary-gray-light);
opacity: 0;
transition: height 0.2s, background-color 0.5s, opacity 0.2s 0.6s;
&-circle {
width: 64px;
height: 64px;
border-radius: 100%;
animation: spin 4s linear infinite;
&_quarter {
position: absolute;
display: block;
box-sizing: border-box;
width: 64px;
height: 64px;
border: 8px solid #fff;
border-color: var(--color-tertiary-gray-darker) transparent transparent
transparent;
border-radius: 50%;
animation: spin 1s cubic-bezier(0.5, 0, 0.5, 1) infinite;
&:nth-child(1) {
animation-delay: -0.3s;
}
&:nth-child(2) {
animation-delay: -0.2s;
}
&:nth-child(3) {
animation-delay: -0.1s;
}
}
}
&_is-loading {
z-index: 10;
opacity: 1;
transition: opacity 0.2s 0.1s;
}
}
&__table {
width: 100%;
margin-right: 3px;
border-spacing: 0 1px;
font-size: var(--font-size-base);
text-align: left;
}
&__grid {
border-spacing: 1px;
}
&__header-cell {
position: relative;
padding: 12px 24px;
overflow: hidden;
font-weight: var(--font-weight-bold);
line-height: var(--line-height-base);
vertical-align: bottom;
color: var(--color-primary-black);
&:not(.q-table__header-cell__checkbox) [class*='q-icon'] {
font-size: 24px;
vertical-align: middle;
}
&-wrapper {
display: flex;
align-items: flex-end;
}
&_align-right .q-table__header-cell-wrapper {
flex-direction: row-reverse;
}
&-content {
display: flex;
white-space: nowrap;
.q-table__cell-sortable & {
cursor: pointer;
}
&_original {
display: -webkit-box;
align-items: flex-end;
width: auto;
overflow: hidden;
white-space: initial;
word-break: break-word;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
}
.q-context-wrapper {
margin: -12px 0;
}
.drag-n-drop-icon {
color: var(--color-tertiary-gray-ultra-dark);
}
.q-table__separated & {
background-color: var(--color-tertiary-gray-light);
border-top-width: 4px;
border-top-style: solid;
box-shadow: var(--custom-box-shadow);
&.q-table__header-cell__checkbox {
border-top: none;
}
&:first-child {
border-top-left-radius: var(--border-radius-base);
}
&:last-child {
border-top-right-radius: var(--border-radius-base);
}
}
&:first-child {
padding-left: 16px;
}
&.q-table__sticked-cell {
background-color: var(--color-tertiary-gray-light);
}
&.q-table__cell-sortable {
& .q-table__sort-arrow {
vertical-align: middle;
color: inherit;
visibility: hidden;
cursor: pointer;
}
&_ascending,
&_descending {
cursor: pointer;
.q-table__sort-arrow {
color: var(--color-primary-blue);
visibility: visible;
}
}
&:hover .q-table__sort-arrow {
visibility: visible;
}
}
}
&__cell {
height: 64px;
padding: 12px 24px;
overflow: hidden;
font-weight: var(--font-weight-base);
line-height: 20px;
background-color: var(--color-tertiary-gray-light);
box-shadow: var(--custom-box-shadow);
&:first-child {
padding-left: 16px;
}
tr:first-child & {
&:first-child {
border-top-left-radius: var(--border-radius-base);
}
&:last-child {
border-top-right-radius: var(--border-radius-base);
}
}
tr:last-child & {
&:first-child {
border-bottom-left-radius: var(--border-radius-base);
}
&:last-child {
border-bottom-right-radius: var(--border-radius-base);
}
}
}
&__total-cell {
position: relative;
padding: 16px 24px;
overflow: hidden;
font-weight: 400;
text-align: left;
color: rgba(var(--color-rgb-gray), 0.64);
background-color: var(--color-tertiary-gray-ultra-light);
box-shadow: var(--custom-box-shadow);
&_selectable {
padding-left: 16px;
}
}
&__cell {
position: relative;
}
&__sticked-cell {
position: sticky;
left: 0;
z-index: 2;
box-shadow: var(--custom-box-shadow);
&_is-reversed {
box-shadow: var(--custom-box-shadow-reversed);
}
}
&__row {
&_is-interactive {
cursor: pointer;
&:hover .q-table__cell {
background-color: var(--color-tertiary-gray);
}
}
}
tr {
&.green-row,
&.red-row,
&.grey-row,
&.dark-row,
&.yellow-row,
&.custom-border {
& td:first-child {
padding-left: 12px;
border-left: 4px solid;
}
}
&.green-row td:first-child {
border-left-color: var(--color-secondary-green);
}
&.red-row td:first-child {
border-left-color: var(--color-secondary-orange);
}
&.grey-row td:first-child {
border-left-color: rgba(var(--color-rgb-gray), 0.32);
}
&.dark-row td:first-child {
border-left-color: rgba(var(--color-rgb-gray), 0.64);
}
&.yellow-row td:first-child {
border-left-color: var(--color-secondary-yellow);
}
&.custom-border td:first-child {
border-left-color: var(--row-custom-border-color);
}
}
&__empty {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 228px;
background-color: var(--color-tertiary-gray-light);
border-radius: var(--border-radius-base);
box-shadow: var(--custom-box-shadow);
&-image {
width: 148px;
height: 148px;
margin-bottom: 8px;
background-color: var(--color-tertiary-gray-ultra-light);
border-radius: 50%;
$--base-path: '../../..' !default;
&::before {
content: '';
display: block;
width: 160px;
height: 160px;
margin-top: -6px;
margin-left: -6px;
background-image: url('#{$--base-path}/assets/empty-table-v2.svg');
background-size: 100%;
}
}
}
}
================================================
FILE: src/qComponents/QTabs/QTabs.test.js
================================================
import Component from './src/QTabs';
describe('QTabs', () => {
it('should match snapshot', () => {
const instance = shallowMount(Component);
expect(instance.element).toMatchSnapshot();
});
it('data should match snapshot', () => {
expect(Component.data()).toMatchSnapshot();
});
});
================================================
FILE: src/qComponents/QTabs/__snapshots__/QTabs.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QTabs data should match snapshot 1`] = `
Object {
"currentName": "",
}
`;
exports[`QTabs should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QTabs/index.js
================================================
import QTabs from './src/QTabs';
QTabs.install = Vue => {
Vue.component(QTabs.name, QTabs);
};
export default QTabs;
================================================
FILE: src/qComponents/QTabs/src/QTabs.vue
================================================
================================================
FILE: src/qComponents/QTabs/src/q-tabs.scss
================================================
.q-tabs {
display: flex;
}
================================================
FILE: src/qComponents/QTag/QTag.test.js
================================================
import Component from './src/QTag';
describe('QTag', () => {
it('should match snapshot', async () => {
const { element } = shallowMount(Component);
expect(element).toMatchSnapshot();
});
describe('methods', () => {
describe('handleClose', () => {
it('should emit close', () => {
const instance = shallowMount(Component);
instance.vm.handleClose();
expect(instance.emitted().close).toBeTruthy();
});
});
});
});
================================================
FILE: src/qComponents/QTag/__snapshots__/QTag.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QTag should match snapshot 1`] = `
`;
================================================
FILE: src/qComponents/QTag/index.js
================================================
import QTag from './src/QTag';
QTag.install = Vue => {
Vue.component(QTag.name, QTag);
};
export default QTag;
================================================
FILE: src/qComponents/QTag/src/QTag.vue
================================================
================================================
FILE: src/qComponents/QTag/src/q-tag.scss
================================================
.q-tag {
position: relative;
display: inline-flex;
align-items: center;
box-sizing: border-box;
max-width: 100%;
margin-top: 2px;
margin-right: 4px;
margin-bottom: 2px;
padding-right: 8px;
padding-left: 8px;
line-height: 20px;
color: rgba(var(--color-rgb-gray), 0.64);
background-color: var(--color-tertiary-gray-darker);
border-radius: 2px;
&_closable {
padding-right: 2px;
}
.q-cascader &:first-child {
max-width: 65%;
}
&__text {
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
word-break: break-all;
}
&__close {
width: 16px;
height: 16px;
margin-left: 4px;
padding: 0;
font-size: 16px;
line-height: 1;
text-align: center;
color: rgba(var(--color-rgb-gray), 0.64);
background-color: transparent;
border: none;
cursor: pointer;
pointer-events: auto;
&:focus,
&:hover {
color: var(--color-primary-black);
}
}
}
================================================
FILE: src/qComponents/QTextarea/index.js
================================================
import QTextarea from './src/QTextarea.vue';
/* istanbul ignore next */
QTextarea.install = function(Vue) {
Vue.component(QTextarea.name, QTextarea);
};
export default QTextarea;
================================================
FILE: src/qComponents/QTextarea/src/QTextarea.vue
================================================
{{ $t('QTextarea.charNumber') }}: {{ textLength }}/{{ upperLimit }}
================================================
FILE: src/qComponents/QTextarea/src/calcTextareaHeight.js
================================================
let hiddenTextarea;
const HIDDEN_STYLE = `
height:0 !important;
visibility:hidden !important;
overflow:hidden !important;
position:absolute !important;
z-index:-1000 !important;
top:0 !important;
right:0 !important
`;
const CONTEXT_STYLE = [
'letter-spacing',
'line-height',
'padding-top',
'padding-bottom',
'font-family',
'font-weight',
'font-size',
'text-rendering',
'text-transform',
'width',
'text-indent',
'padding-left',
'padding-right',
'border-width',
'box-sizing'
];
function calculateNodeStyling(targetElement) {
const style = window.getComputedStyle(targetElement);
const boxSizing = style.getPropertyValue('box-sizing');
const paddingSize =
parseFloat(style.getPropertyValue('padding-bottom')) +
parseFloat(style.getPropertyValue('padding-top'));
const borderSize =
parseFloat(style.getPropertyValue('border-bottom-width')) +
parseFloat(style.getPropertyValue('border-top-width'));
const contextStyle = CONTEXT_STYLE.map(
name => `${name}:${style.getPropertyValue(name)}`
).join(';');
return { contextStyle, paddingSize, borderSize, boxSizing };
}
export default function calcTextareaHeight(
targetElement,
minRows = 1,
maxRows = null
) {
if (!hiddenTextarea) {
hiddenTextarea = document.createElement('textarea');
document.body.appendChild(hiddenTextarea);
}
const {
paddingSize,
borderSize,
boxSizing,
contextStyle
} = calculateNodeStyling(targetElement);
hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`);
hiddenTextarea.value = targetElement.value || targetElement.placeholder || '';
let height = hiddenTextarea.scrollHeight;
const result = {};
if (boxSizing === 'border-box') {
height += borderSize;
} else if (boxSizing === 'content-box') {
height -= paddingSize;
}
hiddenTextarea.value = '';
const singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
if (minRows !== null) {
let minHeight = singleRowHeight * minRows;
if (boxSizing === 'border-box') {
minHeight = minHeight + paddingSize + borderSize;
}
height = Math.max(minHeight, height);
result.minHeight = `${minHeight}px`;
}
if (maxRows !== null) {
let maxHeight = singleRowHeight * maxRows;
if (boxSizing === 'border-box') {
maxHeight = maxHeight + paddingSize + borderSize;
}
height = Math.min(maxHeight, height);
}
result.height = `${height}px`;
hiddenTextarea.parentNode &&
hiddenTextarea.parentNode.removeChild(hiddenTextarea);
hiddenTextarea = null;
return result;
}
================================================
FILE: src/qComponents/QTextarea/src/q-textarea.scss
================================================
.q-textarea {
--field-color-base: var(--color-primary-black);
--field-color-placeholder: rgba(var(--color-rgb-gray), 0.32);
--field-color-disabled: rgba(var(--color-rgb-gray), 0.64);
--field-icon-color-base: var(--color-primary-blue);
--field-icon-color-hover: var(--color-primary-black);
--field-icon-color-inactive: rgba(var(--color-rgb-gray), 0.64);
--field-background-color-base: var(--color-tertiary-gray-light);
--field-background-color-hover: var(--color-tertiary-gray);
--field-background-color-focus: var(--color-tertiary-gray-ultra-light);
--field-background-color-disabled: var(--color-tertiary-gray);
--field-box-shadow-base: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
4px 4px 8px rgba(var(--color-rgb-blue), 0.4),
-4px -4px 12px var(--color-tertiary-white);
--field-box-shadow-hover: -1px -1px 4px rgba(var(--color-rgb-white), 0.25),
1px 1px 4px rgba(var(--color-rgb-blue), 0.4),
4px 4px 8px rgba(var(--color-rgb-blue), 0.4),
-4px -4px 8px rgba(var(--color-rgb-white), 0.8);
--field-box-shadow-focus: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
inset -1px -1px 1px rgba(var(--color-rgb-white), 0.7),
inset 1px 1px 2px rgba(var(--color-rgb-blue), 0.2);
--field-box-shadow-disabled: -1px -1px 3px rgba(var(--color-rgb-white), 0.25),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4);
position: relative;
display: inline-block;
width: 100%;
vertical-align: middle;
&__inner {
display: block;
width: 100%;
height: 40px;
padding: 10px 16px;
font-weight: var(--font-weight-base);
font-size: var(--font-size-base);
line-height: var(--line-height-base);
color: var(--field-color-base);
text-overflow: ellipsis;
background-color: var(--field-background-color-base);
border: none;
border-radius: var(--border-radius-base);
box-shadow: var(--field-box-shadow-base);
resize: vertical;
&::placeholder {
color: var(--field-color-placeholder);
text-overflow: ellipsis;
opacity: 1;
}
&:hover {
background-color: var(--field-background-color-hover);
box-shadow: var(--field-box-shadow-hover);
}
&:focus {
background-color: var(--field-background-color-focus);
outline: none;
box-shadow: var(--field-box-shadow-focus);
}
&::-webkit-scrollbar {
width: 2px;
}
&::-webkit-scrollbar:hover {
width: 6px;
cursor: default;
}
&::-webkit-scrollbar-thumb {
background-color: var(--color-primary-blue);
border-radius: var(--border-radius-base);
}
.q-form-item_is-error & {
padding: 9px 15px;
border: var(--border-error);
}
}
&_disabled {
&::after {
content: '\e930';
position: absolute;
top: 0;
right: 0;
width: 40px;
font-size: 24px;
line-height: 40px;
font-family: 'qicon';
cursor: not-allowed;
pointer-events: none;
}
.q-textarea__inner {
padding-right: 40px;
color: var(--field-color-disabled);
background-color: var(--field-background-color-disabled);
box-shadow: var(--field-box-shadow-disabled);
cursor: not-allowed;
}
}
&__count {
margin-bottom: 2px;
font-size: 10px;
text-align: right;
}
}
================================================
FILE: src/qComponents/QTimePicker/index.js
================================================
import QTimePicker from './src/QTimePicker';
QTimePicker.install = Vue => {
Vue.component(QTimePicker.name, QTimePicker);
};
export default QTimePicker;
================================================
FILE: src/qComponents/QTimePicker/src/QTimePicker.vue
================================================
================================================
FILE: src/qComponents/QTimePicker/src/components/panel.vue
================================================
{{ fullTime }}
{{ $t('QTimePicker.hour') }}
{{ hour }}
================================================
FILE: src/qComponents/QTimePicker/src/components/time-panel.scss
================================================
.time-panel {
position: relative;
z-index: 1;
display: none;
width: 124px;
height: 256px;
padding: 5px 0;
font-size: 10px;
background: var(--color-tertiary-gray-light);
border-radius: var(--border-radius-base);
box-shadow: var(--box-shadow-secondary);
&[data-show] {
display: block;
}
&_no-right-borders {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&_no-left-borders {
margin-left: 1px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
&_focused {
z-index: 2;
border-radius: 4px;
outline: none;
box-shadow: var(--box-shadow-secondary);
transform: scale(1.03);
transition: transform 0.2s;
}
&__sign {
height: 19px;
font-weight: 500;
text-align: center;
&::after {
content: '';
position: relative;
top: -4px;
z-index: 1;
display: block;
height: 6px;
background: linear-gradient(to bottom, #f0f0f3, transparent);
}
}
&__common {
padding: 10px 0 11px;
font-weight: 400;
line-height: 10px;
text-align: center;
color: var(--color-primary-black);
}
&__pickers {
display: flex;
justify-content: center;
height: 201px;
}
&__scrollbar {
width: 35px;
height: 201px;
}
.cell {
position: relative;
display: block;
box-sizing: border-box;
width: 20px;
height: 20px;
margin: 12px auto;
padding: 0;
font-weight: var(--font-weight-base);
line-height: 20px;
color: var(--color-primary-blue);
background-color: var(--color-tertiary-gray-light);
border: none;
border-radius: var(--border-radius-base);
box-shadow: 36px 2px 45px #fff, 4px 4px 8px rgba(var(--color-rgb-blue), 0.4),
1px 1px 3px rgba(var(--color-rgb-blue), 0.4),
-1px -1px 3px rgba(var(--color-rgb-white), 0.25);
cursor: pointer;
&:first-child {
margin-top: 10px;
}
&:last-child {
margin-bottom: 5px;
}
&[data-focus-visible-added] {
&:not(.cell_disabled) {
color: var(--color-tertiary-white);
background-color: var(--color-primary-blue);
box-shadow: var(--box-shadow-pressed);
transform: scale(1.2);
transition: transform 0.2s;
}
}
&:hover {
&:not(.cell_current) {
color: var(--color-primary-black);
background-color: var(--color-tertiary-gray);
}
}
&[disabled] {
color: rgba(var(--color-primary-black), 0.64);
background-color: var(--color-tertiary-gray);
cursor: not-allowed;
opacity: 1;
&:hover {
color: rgba(var(--color-primary-black), 0.64);
}
}
&_current {
color: #fff;
background-color: var(--color-primary-blue);
&:hover {
color: #fff;
}
}
}
}
================================================
FILE: src/qComponents/QTimePicker/src/q-time-picker.scss
================================================
@import '../src/components/time-panel.scss';
.q-time-picker {
&__input {
position: relative;
}
}
================================================
FILE: src/qComponents/QUpload/index.js
================================================
import QUpload from './src/QUpload.vue';
QUpload.install = function(Vue) {
Vue.component(QUpload.name, QUpload);
};
export default QUpload;
================================================
FILE: src/qComponents/QUpload/src/QUpload.vue
================================================
================================================
FILE: src/qComponents/QUpload/src/QUploadDropZone.vue
================================================
================================================
FILE: src/qComponents/QUpload/src/QUploadFileMultiple.vue
================================================
================================================
FILE: src/qComponents/QUpload/src/QUploadFileSingle.vue
================================================
================================================
FILE: src/qComponents/QUpload/src/q-upload-drop-zone.scss
================================================
.q-upload-drop-zone {
--background-color: var(--color-tertiary-gray-light);
--border-color: rgba(var(--color-rgb-gray), 0.16);
--text-color: var(--color-primary-blue);
--box-shadow: var(--box-shadow-primary);
position: relative;
z-index: 10;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: var(--q-upload-size);
height: var(--q-upload-size);
overflow: hidden;
text-align: center;
background-color: var(--background-color);
border-radius: 6px;
outline: none;
box-shadow: var(--box-shadow);
cursor: pointer;
&:hover,
&[data-focus-visible-added] {
--background-color: var(--color-tertiary-gray);
--box-shadow: var(--box-shadow-hover);
}
&::before {
content: '';
position: absolute;
top: 8px;
right: 8px;
bottom: 8px;
left: 8px;
border: 1px dashed var(--border-color);
}
&__icon {
font-size: 40px;
line-height: 1;
color: var(--text-color);
}
&__text {
margin-top: 8px;
font-weight: var(--font-weight-bold);
font-size: 12px;
line-height: 15px;
color: var(--text-color);
text-transform: uppercase;
}
&_is-dragover {
--border-color: var(--color-primary-blue);
--background-color: var(--color-tertiary-gray);
}
&_is-loading {
--background-color: var(--color-tertiary-gray-ultra-light);
--text-color: rgba(var(--color-rgb-gray), 0.64);
--box-shadow: var(--box-shadow-pressed);
&:hover {
--background-color: var(--color-tertiary-gray-ultra-light);
--box-shadow: var(--box-shadow-pressed);
}
.q-upload-drop-zone__icon {
animation: rotating 2s linear infinite;
}
}
&_is-disabled {
--border-color: rgba(var(--color-rgb-gray), 0.64);
--text-color: rgba(var(--color-rgb-gray), 0.64);
--background-color: var(--color-tertiary-gray);
--box-shadow: var(--box-shadow-pressed);
cursor: not-allowed;
&:hover {
--box-shadow: var(--box-shadow-pressed);
}
}
.q-form-item_is-error & {
--border-color: var(--color-secondary-orange);
}
}
================================================
FILE: src/qComponents/QUpload/src/q-upload-file-multiple.scss
================================================
.q-upload-file-multiple {
--text-color: rgba(var(--color-rgb-gray), 0.64);
position: absolute;
right: 0;
bottom: 0;
z-index: 5;
display: flex;
flex-direction: column;
width: var(--q-upload-size);
height: var(--q-upload-size);
background-color: var(--color-tertiary-gray-light);
border-radius: var(--border-radius-base);
box-shadow: var(--box-shadow-secondary);
visibility: hidden;
opacity: 0;
transition: visibility ease 0.5s, opacity ease 0.25s;
&_shown {
visibility: visible;
opacity: 1;
transition: visibility ease 0.5s, opacity ease 0.25s 0.25s;
}
&__title {
display: flex;
align-items: center;
margin-top: 16px;
margin-bottom: 16px;
margin-left: 16px;
font-weight: var(--font-weight-bold);
font-size: 12px;
line-height: 24px;
color: var(--color-primary-black);
text-transform: uppercase;
letter-spacing: var(--letter-spacing-base);
}
&__clear-all {
margin-left: 8px;
}
&__inner {
width: 100%;
padding: 0 16px 16px;
}
&__line {
display: flex;
justify-content: space-between;
align-items: center;
& + & {
margin-top: 11px;
}
}
&__icon {
width: 24px;
margin-right: 8px;
font-size: 24px;
color: var(--text-color);
&.q-icon-reverse {
animation: rotating 2s linear infinite;
}
}
&__name {
position: relative;
display: block;
width: 168px;
margin-right: auto;
overflow: hidden;
font-weight: var(--font-weight-base);
font-size: 14px;
line-height: 22px;
text-align: left;
color: var(--text-color);
white-space: nowrap;
text-overflow: ellipsis;
}
&__action {
width: 24px;
margin-left: 8px;
padding: 0;
font-size: 24px;
color: var(--color-primary-blue);
background-color: transparent;
border: none;
outline: none;
cursor: pointer;
&:hover,
&.focus-visible {
color: var(--color-primary-black);
}
&.q-icon-lock {
color: var(--text-color);
}
}
&__loader {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 2px;
background-color: rgba(var(--color-rgb-gray), 0.16);
}
&__bar {
width: 0;
height: 100%;
background-color: var(--color-primary-blue);
transition: width 0.1s ease;
}
}
================================================
FILE: src/qComponents/QUpload/src/q-upload-file-single.scss
================================================
.q-upload-file-single {
--text-color: rgba(var(--color-rgb-gray), 0.64);
position: absolute;
right: 24px;
bottom: 24px;
left: 24px;
z-index: 15;
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px;
overflow: hidden;
background-color: var(--color-tertiary-gray);
border-radius: var(--border-radius-base);
box-shadow: var(--box-shadow-pressed);
&__icon {
width: 24px;
margin-right: 8px;
font-size: 24px;
color: var(--text-color);
}
&__name {
width: 136px;
margin-right: auto;
font-weight: var(--font-weight-base);
font-size: 10px;
line-height: 12px;
text-align: left;
color: var(--text-color);
}
&__btn {
width: 24px;
margin-left: 8px;
padding: 0;
font-size: 24px;
color: var(--color-primary-blue);
background-color: transparent;
border: none;
outline: none;
cursor: pointer;
&:hover,
&.focus-visible {
color: var(--color-primary-black);
}
}
&__loader {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 2px;
background-color: rgba(var(--color-rgb-gray), 0.16);
}
&__bar {
width: 0;
height: 100%;
background-color: var(--color-primary-blue);
transition: width 0.1s ease;
}
}
================================================
FILE: src/qComponents/QUpload/src/q-upload.scss
================================================
@import './q-upload-drop-zone.scss';
@import './q-upload-file-single.scss';
@import './q-upload-file-multiple.scss';
.q-upload {
--q-upload-size: 264px;
position: relative;
display: inline-block;
&_multiple {
width: var(--q-upload-size);
height: var(--q-upload-size);
transition: width ease 0.5s, height ease 0.5s;
&_open {
&-right {
width: 544px;
}
&-bottom {
height: 544px;
}
}
}
&__input {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
margin: 0;
outline: none;
opacity: 0;
}
}
@keyframes rotating {
0% {
transform: rotateZ(0deg);
}
100% {
transform: rotateZ(360deg);
}
}
================================================
FILE: src/qComponents/constants/locales/en.js
================================================
export default {
QInput: {
charNumber: 'Number of characters'
},
QSelect: {
loading: 'Loading...',
more: 'Not all results are shown, refine your query',
noMatch: 'No matching data',
noData: 'No data',
selectAll: 'Select all'
},
QTextarea: {
charNumber: 'Number of characters'
},
QDatePicker: {
placeholder: 'Pick a date',
startPlaceholder: 'Start',
endPlaceholder: 'End',
prevYear: 'Previous Year',
nextYear: 'Next Year',
prevMonth: 'Previous Month',
nextMonth: 'Next Month',
timeFrom: 'from',
timeTo: 'to'
},
QCascader: {
placeholder: 'Pick',
noDataText: 'No data'
},
QColorPicker: {
clear: 'Clear',
confirm: 'Apply'
},
QTable: {
noData: 'No data'
},
QTimePicker: {
hour: 'hour',
minute: 'min',
second: 'sec'
},
QUpload: {
uploadFile: 'upload file',
replaceFile: 'replace file',
loading: 'loading',
uploadedFiles: 'Uploaded files'
}
};
================================================
FILE: src/qComponents/constants/locales/index.js
================================================
/* eslint-disable no-underscore-dangle */
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import { merge } from 'lodash-es';
import ru from './ru';
import en from './en';
const installI18n = ({ locale, customI18nMessages }) => {
const isI18nExist = Boolean(Vue.prototype._i18n);
if (isI18nExist) {
console.warn(
`Error: Qui can't setup VueI18n, it has detected in the app, please merge i18n messages, see the docs`
);
return;
}
Vue.use(VueI18n);
const i18n = new VueI18n({
locale,
messages: merge({ en, ru }, customI18nMessages)
});
Vue.prototype._i18n = i18n;
};
export default {
en,
ru
};
export { ru, en, installI18n };
================================================
FILE: src/qComponents/constants/locales/ru.js
================================================
export default {
QInput: {
charNumber: 'Количество символов'
},
QSelect: {
loading: 'Загрузка...',
more: 'Показаны не все результаты, уточните поиск',
noMatch: 'Нет совпадений',
noData: 'Нет данных',
selectAll: 'Выбрать всё'
},
QTextarea: {
charNumber: 'Количество символов'
},
QDatePicker: {
placeholder: 'Выберите дату',
startPlaceholder: 'Дата начала',
endPlaceholder: 'Дата окончания',
prevYear: 'Предыдущий год',
nextYear: 'Следующий год',
prevMonth: 'Предыдущий месяц',
nextMonth: 'Следующий месяц',
timeFrom: 'с',
timeTo: 'по'
},
QCascader: {
placeholder: 'Выберите',
noDataText: 'Нет данных'
},
QColorPicker: {
clear: 'Очистить',
confirm: 'Применить'
},
QTable: {
noData: 'Нет данных'
},
QTimePicker: {
hour: 'час',
minute: 'мин',
second: 'сек'
},
QUpload: {
uploadFile: 'загрузить файл',
replaceFile: 'заменить файл',
loading: 'загрузка',
uploadedFiles: 'Загруженные файлы'
}
};
================================================
FILE: src/qComponents/constants/popperPlacements.js
================================================
export default [
'top',
'top-start',
'top-end',
'right',
'right-start',
'right-end',
'bottom',
'bottom-start',
'bottom-end',
'left',
'left-start',
'left-end'
];
================================================
FILE: src/qComponents/helpers/collapse-transition.js
================================================
/* eslint-disable no-param-reassign */
export default {
name: 'QCollapseTransition',
functional: true,
render(h, { children }) {
const data = {
on: {
beforeEnter(el) {
el.classList.add('collapse-transition');
if (!el.dataset) el.dataset = {};
el.dataset.oldPaddingTop = el.style.paddingTop;
el.dataset.oldPaddingBottom = el.style.paddingBottom;
el.style.height = '0';
el.style.paddingTop = 0;
el.style.paddingBottom = 0;
},
enter(el) {
el.dataset.oldOverflow = el.style.overflow;
if (el.scrollHeight !== 0) {
el.style.height = `${el.scrollHeight}px`;
el.style.paddingTop = el.dataset.oldPaddingTop;
el.style.paddingBottom = el.dataset.oldPaddingBottom;
} else {
el.style.height = '';
el.style.paddingTop = el.dataset.oldPaddingTop;
el.style.paddingBottom = el.dataset.oldPaddingBottom;
}
el.style.overflow = 'hidden';
},
afterEnter(el) {
el.classList.remove('collapse-transition');
// for safari: remove class then reset height is necessary
el.style.height = '';
el.style.overflow = el.dataset.oldOverflow;
},
beforeLeave(el) {
if (!el.dataset) el.dataset = {};
el.dataset.oldPaddingTop = el.style.paddingTop;
el.dataset.oldPaddingBottom = el.style.paddingBottom;
el.dataset.oldOverflow = el.style.overflow;
el.style.height = `${el.scrollHeight}px`;
el.style.overflow = 'hidden';
},
leave(el) {
if (el.scrollHeight !== 0) {
el.classList.add('collapse-transition');
// for safari: add class after set height, or it will jump to zero height suddenly, weired
el.style.height = 0;
el.style.paddingTop = 0;
el.style.paddingBottom = 0;
}
},
afterLeave(el) {
el.classList.remove('collapse-transition');
el.style.height = '';
el.style.overflow = el.dataset.oldOverflow;
el.style.paddingTop = el.dataset.oldPaddingTop;
el.style.paddingBottom = el.dataset.oldPaddingBottom;
}
}
};
return h('transition', data, children);
}
};
================================================
FILE: src/qComponents/helpers/dateHelpers.js
================================================
import { isDate } from 'date-fns';
const addZero = value => {
return String(value).padStart(2, '0');
};
const isTimeValueValid = value => {
let isValid = false;
if (typeof value === 'string') {
const array = value.split(':');
isValid =
array.every((datePart, index) => {
let condition = false;
if (index === 0) {
// hours
condition = Number(datePart) >= 0 && Number(datePart) < 24;
} else if ([1, 2].includes(index)) {
// minutes / seconds
condition = Number(datePart) >= 0 && Number(datePart) < 60;
}
return condition && datePart.length === 2;
}) && array.length === 3;
} else if (isDate(value) || value === null) {
isValid = true;
}
return isValid;
};
export { addZero, isTimeValueValid };
================================================
FILE: src/qComponents/helpers/index.js
================================================
/* eslint-disable no-underscore-dangle */
import ResizeObserver from 'resize-observer-polyfill';
export const on = (function() {
if (document.addEventListener) {
return function(element, event, handler) {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
};
}
return function(element, event, handler) {
if (element && event && handler) {
element.attachEvent(`on${event}`, handler);
}
};
})();
/* istanbul ignore next */
export const off = (function() {
if (document.removeEventListener) {
return function(element, event, handler) {
if (element && event) {
element.removeEventListener(event, handler, false);
}
};
}
return function(element, event, handler) {
if (element && event) {
element.detachEvent(`on${event}`, handler);
}
};
})();
const resizeHandler = entries => {
Object.values(entries).forEach(entry => {
const listeners = entry.target.__resizeListeners__ ?? [];
if (listeners.length) {
listeners.forEach(fn => {
fn();
});
}
});
};
export const addResizeListener = (element, fn) => {
if (!element) return;
if (!element.__resizeListeners__) {
// eslint-disable-next-line no-param-reassign
element.__resizeListeners__ = [];
// eslint-disable-next-line no-param-reassign
element.__ro__ = new ResizeObserver(resizeHandler);
element.__ro__.observe(element);
}
element.__resizeListeners__.push(fn);
};
export const removeResizeListener = (element, fn) => {
if (!element || !element.__resizeListeners__) return;
element.__resizeListeners__.splice(
element.__resizeListeners__.indexOf(fn),
1
);
if (!element.__resizeListeners__.length) {
element.__ro__.disconnect();
}
};
export const randId = prefix =>
Math.random()
.toString(36)
.replace('0.', prefix ?? '');
================================================
FILE: src/qComponents/index.js
================================================
/* eslint-disable no-underscore-dangle, global-require, no-param-reassign */
import 'focus-visible';
import { kebabCase, isString } from 'lodash-es';
import vClickOutside from 'v-click-outside';
import { version } from '../../package.json';
import { installI18n } from './constants/locales';
import QButton from './QButton';
import QBreadcrumbs from './QBreadcrumbs';
import QCascader from './QCascader';
import QDatePicker from './QDatePicker';
import QTimePicker from './QTimePicker';
import QCheckbox from './QCheckbox';
import QCheckboxGroup from './QCheckboxGroup';
import QRadio from './QRadio';
import QRadioGroup from './QRadioGroup';
import QRow from './QRow';
import QCol from './QCol';
import QCollapse from './QCollapse';
import QCollapseItem from './QCollapseItem';
import QColorPicker from './QColorPicker';
import QContextMenu from './QContextMenu';
import QForm from './QForm';
import QFormItem from './QFormItem';
import QInput from './QInput';
import QInputNumber from './QInputNumber';
import QPagination from './QPagination';
import QPopover from './QPopover';
import QScrollbar from './QScrollbar';
import QSelect from './QSelect';
import QSlider from './QSlider';
import QOption from './QOption';
import QTextarea from './QTextarea';
import QTabs from './QTabs';
import QTabPane from './QTabPane';
import QTag from './QTag';
import QDrawer from './QDrawer';
import QTable from './QTable';
import QUpload from './QUpload';
// modals
import QNotification from './QNotification';
import QDialog from './QDialog';
import QMessageBox from './QMessageBox';
const Components = {
QBreadcrumbs,
QButton,
QCascader,
QCheckbox,
QCheckboxGroup,
QCol,
QCollapse,
QCollapseItem,
QColorPicker,
QContextMenu,
QDatePicker,
QDialog,
QDrawer,
QForm,
QFormItem,
QInput,
QInputNumber,
QMessageBox,
QNotification,
QOption,
QPagination,
QPopover,
QRadio,
QRadioGroup,
QRow,
QScrollbar,
QSelect,
QSlider,
QTabPane,
QTable,
QTabs,
QTag,
QTextarea,
QTimePicker,
QUpload
};
const allComponents = Object.keys(Components);
const allComponentsExceptModals = allComponents.filter(
name => !['QNotification', 'QMessageBox', 'QDialog'].includes(name)
);
// import styles
require('../fonts/index.scss');
require('../icons/index.scss');
require('../main.scss');
allComponents.forEach(component => {
const kebabCaseComponent = kebabCase(component);
try {
// eslint-disable-next-line import/no-dynamic-require
require(`../qComponents/${component}/src/${kebabCaseComponent}.scss`);
} catch (err) {
console.warn(err);
}
});
// install
const install = (
Vue,
{
localization: { locale = 'ru', customI18nMessages = {} } = {},
zIndexCounter = 2000,
prefix = ''
} = {}
) => {
Vue.prototype.$Q = {};
// define plugins
Object.defineProperties(Vue.prototype.$Q, {
zIndex: {
get() {
zIndexCounter += 1;
return zIndexCounter;
}
},
locale: {
get: () => locale,
set(newLocale) {
locale = newLocale;
}
}
});
Vue.use(vClickOutside);
installI18n({ locale, customI18nMessages });
// setup modals
if (!Vue.prototype.$notify) {
Vue.prototype.$notify = options =>
QNotification({
duration: 3000, // - ms
...options
});
} else if (process.env.NODE_ENV !== 'production') {
console.warn(`$notify hasn't been registered, it has existed before`);
}
if (!Vue.prototype.$message) {
Vue.prototype.$message = QMessageBox;
} else if (process.env.NODE_ENV !== 'production') {
console.warn(`$message hasn't been registered, it has existed before`);
}
if (!Vue.prototype.$dialog) {
Vue.prototype.$dialog = QDialog;
} else if (process.env.NODE_ENV !== 'production') {
console.warn(`$dialog hasn't been registered, it has existed before`);
}
allComponentsExceptModals.forEach(name => {
const newName =
prefix && isString(prefix) ? name.replace(/^Q/, prefix) : name;
Vue.component(newName, Components[name]);
});
};
const Qui = {
version,
install
};
// Auto-install when vue is found (eg. in browser via
================================================
FILE: stories/components/QDialog/QDialog.stories.js
================================================
import Vue from 'vue';
import QDialog from '../../../src/qComponents/QDialog';
import QDialogComponent from '../../../src/qComponents/QDialog/src/QDialog.vue';
export default {
title: 'Components/QDialog',
component: QDialogComponent
};
export const QDialogStory = (_, { argTypes }) => ({
props: Object.keys(argTypes),
data() {
return { dialogInstance: null };
},
beforeDestroy() {
// Use it to destroy dialog in handmode from outside
if (this.dialogInstance) this.dialogInstance.$destroy();
},
methods: {
async handleClick() {
console.log(Object.keys(argTypes));
const { answer, instance } = QDialog.bind(Vue)({
component: () => import('./DialogFormTest'),
...this.$props
});
this.dialogInstance = instance;
console.log('instance', instance);
console.log('answer', await answer);
}
},
template: 'Click to open '
});
QDialogStory.storyName = 'Default';
QDialogStory.args = {
title: 'Morbi massa libero, vehicula nec consequat sed, porta a sem.'
};
================================================
FILE: stories/components/QDrawer.stories.js
================================================
import QDrawer from '../../src/qComponents/QDrawer';
export default {
title: 'Components/QDrawer',
component: QDrawer,
argTypes: {
visible: { control: { type: 'none' } },
width: { control: { type: 'number' } },
position: {
control: { type: 'inline-radio', options: ['left', 'right'] }
}
}
};
export const QNotificationStory = (_, { argTypes }) => ({
props: Object.keys(argTypes),
data() {
return {
drawer: false
};
},
template: `
open
I'm drawer's slot
`
});
QNotificationStory.storyName = 'Default';
QNotificationStory.args = {
visible: false,
appendToBody: false,
title: 'What is Lorem Ipsum?',
width: 350
};
================================================
FILE: stories/components/QForm.stories.js
================================================
import QForm from '../../src/qComponents/QForm';
import QFormItem from '../../src/qComponents/QFormItem';
export default {
title: 'Components/QForm',
component: QForm,
subcomponents: { QFormItem },
argTypes: {
rules: { control: { type: 'object' } },
model: { control: { type: 'none' } }
}
};
export const QFormStory = (_, { argTypes }) => ({
props: Object.keys(argTypes).filter(arg => arg !== 'model'),
data() {
return {
formModel: {
name: '',
intro: '',
date: null
}
};
},
methods: {
async handleSubmitClick() {
const { isValid, invalidFields } = await this.$refs.form.validate();
console.log('QForm | validate', isValid, invalidFields);
if (isValid) {
// eslint-disable-next-line no-alert
alert('Success');
}
},
handleResetClick() {
this.$refs.form.resetFields();
}
},
template: `
Create
Reset
`
});
QFormStory.storyName = 'Default';
QFormStory.args = {
rules: {
name: [
{
required: true,
message: 'Please input name',
trigger: 'blur'
},
{
min: 3,
max: 5,
message: 'Length should be 3 to 10',
trigger: 'blur'
}
],
intro: {
required: true,
message: 'Please input introtext',
trigger: 'change'
},
date: {
required: true
}
}
};
================================================
FILE: stories/components/QInput.stories.js
================================================
import QInput from '../../src/qComponents/QInput';
import { iconsList } from '../core/icons.stories.mdx';
export default {
title: 'Components/QInput',
component: QInput,
argTypes: {
placeholder: {
control: {
type: 'text'
}
},
maxlength: {
control: {
type: 'number'
}
},
value: {
control: {
type: 'text'
}
},
type: {
control: {
type: 'select',
options: ['text', 'password', 'number', 'email', 'hidden', 'tel', 'url']
}
},
suffixIcon: {
control: {
type: 'select',
options: iconsList
}
},
autocomplete: {
control: {
type: 'select',
options: ['on', 'off']
}
}
}
};
export const QInputStory = (_, { argTypes }) => ({
props: Object.keys(argTypes).filter(arg => arg !== 'value'),
data() {
return {
value: ''
};
},
template: ' '
});
QInputStory.storyName = 'Default';
================================================
FILE: stories/components/QInputNumber.stories.js
================================================
import QInputNumber from '../../src/qComponents/QInputNumber';
export default {
title: 'Components/QInputNumber',
component: QInputNumber,
argTypes: {
value: {
control: {
type: 'none'
}
}
}
};
export const Default = (_, { argTypes }) => ({
props: Object.keys(argTypes).filter(val => val !== 'value'),
data() {
return {
value: 2
};
},
methods: {
handleEmit(value, type) {
console.log(value, type);
}
},
template: `
`
});
Default.storyName = 'Default';
================================================
FILE: stories/components/QMessageBox/MessageBoxFormTest.vue
================================================
Send
Cancel
================================================
FILE: stories/components/QMessageBox/QMessageBox.stories.js
================================================
import Vue from 'vue';
import QMessageBox from '../../../src/qComponents/QMessageBox';
import QMessageBoxComponent from '../../../src/qComponents/QMessageBox/src/QMessageBox.vue';
export default {
title: 'Components/QMessageBox',
component: QMessageBoxComponent
};
export const QMessageBoxStory = (_, { argTypes }) => ({
props: Object.keys(argTypes).filter(val => val !== 'beforeClose'),
methods: {
async beforeClose({ action, ctx }) {
if (action !== 'confirm') return true;
ctx.isConfirmBtnLoading = true;
const promise = () =>
new Promise(resolve => {
setTimeout(() => resolve('готово!'), 1000);
});
try {
await promise();
ctx.isConfirmBtnLoading = false;
return true;
} catch (error) {
ctx.isConfirmBtnLoading = false;
return false;
}
},
async handleClick() {
try {
const response = await QMessageBox.bind(Vue)({
...this.$props,
beforeClose: this.beforeClose
});
console.log(response);
} catch (error) {
console.log(error);
}
}
},
template: 'Click to open '
});
QMessageBoxStory.storyName = 'Default';
QMessageBoxStory.args = {
title: 'Morbi massa libero, vehicula nec consequat sed, porta a sem.',
message:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vehicula volutpat nulla et egestas. Mauris mollis, lorem vel aliquam gravida.',
submessage:
'Sed sit amet nibh consequat, pellentesque arcu ut, congue lorem.',
confirmButtonText: 'Fusce blandit',
cancelButtonText: 'Integer non'
};
export const QMessageBoxComponentStory = (_, { argTypes }) => ({
props: Object.keys(argTypes),
methods: {
async handleClick() {
try {
const response = await QMessageBox.bind(Vue)({
component: () => import('./MessageBoxFormTest')
});
console.log(response);
} catch (error) {
console.log(error);
}
}
},
template: 'Click to open '
});
QMessageBoxComponentStory.storyName = 'Component';
QMessageBoxComponentStory.args = {
title: 'Morbi massa libero, vehicula nec consequat sed, porta a sem.'
};
export const QMessageBoxHTMLStory = (_, { argTypes }) => ({
props: Object.keys(argTypes),
methods: {
async handleClick() {
try {
const response = await QMessageBox.bind(Vue)({
...this.$props
});
console.log(response);
} catch (error) {
console.log(error);
}
}
},
template: 'Click to open '
});
QMessageBoxHTMLStory.storyName = 'HTML';
QMessageBoxHTMLStory.args = {
title: 'Morbi massa libero, vehicula nec consequat sed, porta a sem.',
message:
'This is HTML string ',
dangerouslyUseHTMLString: true,
submessage:
'Sed sit amet nibh consequat, pellentesque arcu ut, congue lorem.',
confirmButtonText: 'Fusce blandit'
};
export const QMessageBoxVNodeStory = (_, { argTypes }) => ({
props: Object.keys(argTypes),
methods: {
async handleClick() {
try {
const h = this.$createElement;
const response = await QMessageBox.bind(Vue)({
...this.$props,
message: h('p', null, [
h('span', null, 'Message can be '),
h('i', { style: 'color: teal' }, 'VNode')
])
});
console.log(response);
} catch (error) {
console.log(error);
}
}
},
template: 'Click to open '
});
QMessageBoxVNodeStory.storyName = 'VNode';
QMessageBoxVNodeStory.agrs = {
title: 'Morbi massa libero, vehicula nec consequat sed, porta a sem.',
confirmButtonText: 'Fusce blandit'
};
================================================
FILE: stories/components/QNotification.stories.js
================================================
import QNotification from '../../src/qComponents/QNotification';
import QNotificationComponent from '../../src/qComponents/QNotification/src/QNotification.vue';
import { iconsList } from '../core/icons.stories.mdx';
export default {
title: 'Components/QNotification',
component: QNotificationComponent,
argTypes: {
type: {
control: {
type: 'select',
options: [null, 'success', 'warning', 'info', 'error']
}
},
icon: {
control: {
type: 'select',
options: iconsList
}
}
}
};
export const QNotificationStory = (_, { argTypes }) => ({
props: Object.keys(argTypes),
methods: {
handleClick() {
QNotification({
message: this.message,
type: this.type,
dangerouslyUseHTMLString: this.dangerouslyUseHTMLString,
duration: this.duration,
icon: this.icon
});
},
handleCloseAllClick() {
QNotification.closeAll();
}
},
template: `
Click to open
close all
`
});
QNotificationStory.storyName = 'Default';
QNotificationStory.args = {
message: 'Morbi massa libero, vehicula nec consequat sed, porta a sem.'
};
================================================
FILE: stories/components/QPagination.stories.js
================================================
import QPagination from '../../src/qComponents/QPagination';
export default {
title: 'Components/QPagination',
component: QPagination
};
export const QPaginationStory = (_, { argTypes }) => ({
props: Object.keys(argTypes),
methods: {
handleCurrentChange(val) {
this.currentPage = val;
}
},
template: `
`
});
QPaginationStory.storyName = 'Default';
QPaginationStory.args = {
pageCount: 30,
currentPage: 1,
total: 300,
pageSize: 10
};
================================================
FILE: stories/components/QPopover.stories.js
================================================
import QPopover from '../../src/qComponents/QPopover';
import { iconsList } from '../core/icons.stories.mdx';
import PLACEMENTS from '../../src/qComponents/constants/popperPlacements';
export default {
title: 'Components/QPopover',
component: QPopover,
argTypes: {
trigger: {
control: {
type: 'inline-radio',
options: ['click', 'hover']
}
},
placement: {
control: {
type: 'select',
options: PLACEMENTS
}
},
icon: {
control: {
type: 'select',
options: iconsList
}
}
}
};
export const Default = (_, { argTypes }) => ({
props: Object.keys(argTypes),
data() {
return {
componentKey: 0
};
},
watch: {
trigger() {
// trigger is being setting up in mounted, we need to update render
this.componentKey += 1;
}
},
template: `
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
`
});
Default.storyName = 'Default';
Default.args = {
title: 'What is Lorem Ipsum?',
icon: 'q-icon-question'
};
================================================
FILE: stories/components/QRadio.stories.js
================================================
import QRadio from '../../src/qComponents/QRadio';
export default {
title: 'Components/QRadio/QRadio',
component: QRadio,
argTypes: {
'v-model': { control: { type: 'none' } },
value: { control: { type: 'none' } },
label: { control: 'text' }
}
};
export const QRadioStory = (_, { argTypes }) => ({
props: Object.keys(argTypes),
data() {
return {
radio1: '1',
radio2: '1'
};
},
template: `
`
});
QRadioStory.storyName = 'Default';
QRadioStory.args = {
label: 'Option A'
};
================================================
FILE: stories/components/QRadioGroup.stories.js
================================================
import QRadioGroup from '../../src/qComponents/QRadioGroup';
import QRadio from '../../src/qComponents/QRadio';
export default {
title: 'Components/QRadio/QRadioGroup',
component: QRadioGroup,
subcomponents: { QRadio },
argTypes: {
'v-model': { control: { type: 'none' } },
direction: {
control: { type: 'inline-radio', options: ['vertical', 'horizontal'] }
}
}
};
export const QRadioGroupStory = (_, { argTypes }) => ({
props: Object.keys(argTypes),
data() {
return { value: 3 };
},
template: `
`
});
QRadioGroupStory.storyName = 'Default';
================================================
FILE: stories/components/QScrollbar/QScrollbar.stories.js
================================================
import QScrollbar from '../../../src/qComponents/QScrollbar';
import './q-scrollbar.scss';
export default {
title: 'Components/QScrollbar',
component: QScrollbar,
argTypes: {
theme: {
control: { type: 'inline-radio', options: ['primary', 'secondary'] }
}
}
};
export const QScrollbarStory = (_, { argTypes }) => ({
props: Object.keys(argTypes),
template: `
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
`
});
QScrollbarStory.storyName = 'Default';
QScrollbarStory.args = {
wrapClass: 'scrollbar__wrap'
};
================================================
FILE: stories/components/QScrollbar/q-scrollbar.scss
================================================
.scrollbar__wrap {
width: 500px;
height: 200px;
background: #fff;
}
.french-bread {
padding: 0 20px;
p:first-child {
margin-top: 0;
}
}
================================================
FILE: stories/components/QSelect/Default.js
================================================
const options = [
{
value: { value: 'value1' },
label: 'Option 1'
},
{
value: { value: 'value2' },
label: 'Option 2'
},
{
value: { value: 'value3' },
label:
'Lorem Ipsum is simply dummy text of the printing and typesetting industry.'
},
{
value: { value: 'value4' },
label: 'Option 4',
disabled: true
},
{
value: { value: 'value5' },
label: 'Sed sit amet nibh consequat, pellentesque arcu ut, congue lorem.',
disabled: true
},
{
value: { value: 'value6' },
label: 'Option 6'
}
];
export default (_, { argTypes }) => ({
props: Object.keys(argTypes).filter(arg => arg !== 'value'),
data() {
return {
remoteLoading: false,
value: null,
options
};
},
watch: {
multiple() {
this.value = null;
},
remote(value) {
if (!value) this.options = options;
}
},
methods: {
handleSearch(query) {
if (!this.remote) return;
this.remoteLoading = true;
setTimeout(() => {
this.remoteLoading = false;
if (query !== '') {
this.options = options.filter(item => {
return item.label.toLowerCase().includes(query.toLowerCase());
});
} else {
this.options = options;
}
}, 2000);
}
},
template: `
`
});
================================================
FILE: stories/components/QSelect/Multiple.js
================================================
import Template from './Default';
const Multiple = Template.bind({});
Multiple.args = {
appendToBody: false,
multiple: true,
filterable: true,
allowCreate: false,
collapseTags: false,
clearable: true
};
export default Multiple;
================================================
FILE: stories/components/QSelect/QSelect.stories.js
================================================
import QSelect from '../../../src/qComponents/QSelect';
import QOption from '../../../src/qComponents/QOption';
import Default from './Default';
import Multiple from './Multiple';
export default {
title: 'Components/QSelect',
component: QSelect,
subcomponents: { QOption },
argTypes: {
value: { control: { type: 'none' } }
}
};
export { Default, Multiple };
================================================
FILE: stories/components/QSlider/Breakpoints.js
================================================
const BreakpointsStory = (_, { argTypes }) => ({
props: Object.keys(argTypes).filter(val => val !== 'value'),
data() {
return {
value: null
};
},
template: `
`
});
BreakpointsStory.args = {
showSteps: true,
step: 10
};
export default BreakpointsStory;
================================================
FILE: stories/components/QSlider/Captions.js
================================================
const CaptionsStory = (_, { argTypes }) => ({
props: Object.keys(argTypes).filter(val => val !== 'value'),
data() {
return {
value: 20
};
},
template: `
`
});
CaptionsStory.args = {
showSteps: true,
step: 20,
captions: {
0: '0',
20: '20%',
40: '40%',
60: '60%',
80: '80%',
100: '100%'
}
};
export default CaptionsStory;
================================================
FILE: stories/components/QSlider/Default.js
================================================
export default (_, { argTypes }) => ({
props: Object.keys(argTypes).filter(val => val !== 'value'),
data() {
return {
value: null
};
},
template: `
`
});
================================================
FILE: stories/components/QSlider/Disabled.js
================================================
const DisabledStory = (_, { argTypes }) => ({
props: Object.keys(argTypes).filter(val => val !== 'value'),
data() {
return {
value: 50
};
},
template: `
`
});
DisabledStory.args = {
disabled: true
};
export default DisabledStory;
================================================
FILE: stories/components/QSlider/QSlider.stories.js
================================================
import QSlider from '../../../src/qComponents/QSlider';
import Default from './Default';
import WithoutTooltip from './WithoutTooltip';
import Breakpoints from './Breakpoints';
import Captions from './Captions';
import Range from './Range';
import Disabled from './Disabled';
import Vertical from './Vertical';
export default {
title: 'Components/QSlider',
component: QSlider,
decorators: [
() => ({
template: '
'
})
]
};
export {
Default,
WithoutTooltip,
Breakpoints,
Captions,
Range,
Disabled,
Vertical
};
================================================
FILE: stories/components/QSlider/Range.js
================================================
const RangeStory = (_, { argTypes }) => ({
props: Object.keys(argTypes).filter(val => val !== 'value'),
data() {
return {
value: [20, 80]
};
},
template: `
`
});
RangeStory.args = {
range: true
};
export default RangeStory;
================================================
FILE: stories/components/QSlider/Vertical.js
================================================
const VerticalStory = (_, { argTypes }) => ({
props: Object.keys(argTypes).filter(val => val !== 'value'),
data() {
return {
value: 30
};
},
template: `
`
});
VerticalStory.args = {
vertical: true
};
export default VerticalStory;
================================================
FILE: stories/components/QSlider/WithoutTooltip.js
================================================
const WithoutTooltipStory = (_, { argTypes }) => ({
props: Object.keys(argTypes).filter(val => val !== 'value'),
data() {
return {
value: null
};
},
template: `
`
});
WithoutTooltipStory.args = {
showTooltip: false
};
export default WithoutTooltipStory;
================================================
FILE: stories/components/QTabPane.stories.js
================================================
import QTabPane from '../../src/qComponents/QTabPane';
import QTabs from '../../src/qComponents/QTabs';
export default {
title: 'Components/QTabs/QTabPane',
component: QTabPane,
subcomponents: { QTabs },
argTypes: { width: { control: { type: 'number' } } }
};
export const QTabPaneStory = (_, { argTypes }) => ({
props: Object.keys(argTypes),
data() {
return {
activeTab: 'first_tab'
};
},
template: `
`
});
QTabPaneStory.storyName = 'Default';
QTabPaneStory.args = {
name: 'first_tab',
title: 'First tab',
description:
'Lorem Ipsum is simply dummy text of the printing and typesetting industry.'
};
================================================
FILE: stories/components/QTable/CustomRows.js
================================================
import Template from './Default';
const CustomRows = Template.bind({});
CustomRows.args = {
customRowClass: ({ rowIndex }) => {
if (rowIndex === 0) {
return 'red-row';
}
if (rowIndex === 1) {
return 'green-row';
}
return '';
},
customRowStyle: ({ rowIndex }) => {
if (rowIndex === 2) {
return { '--row-custom-border-color': '#ff0' };
}
return '';
}
};
export default CustomRows;
================================================
FILE: stories/components/QTable/CustomWidth.js
================================================
import Template from './Default';
const CustomWidth = Template.bind({});
CustomWidth.args = {
fixedLayout: true
};
export default CustomWidth;
================================================
FILE: stories/components/QTable/Default.js
================================================
export default (_, { argTypes }) => ({
props: Object.keys(argTypes),
methods: {
handleRowClick(row) {
console.log(row);
},
changeColumnsOrder(groupsOfColumns) {
this.groupsOfColumns = groupsOfColumns;
}
},
template: `
{{ value }} custom
{{ value }} custom
`
});
================================================
FILE: stories/components/QTable/Draggable.js
================================================
import Template from './Default';
const Draggable = Template.bind({});
Draggable.args = {
fixedLayout: true,
groupsOfColumns: [
{
key: 'one',
color: '#de4b7a',
draggable: true,
columns: [
{
key: 'col1',
value: 'Column 1',
formatter: val => `formatted_${val}`
},
{
key: 'col2',
value: 'Column 2'
},
{
key: 'col3',
value: 'Column 3',
sortable: true
},
{
key: 'col4',
value: 'Column 4',
sortable: true,
slots: {
header: 'customHeader',
row: 'customRow'
}
},
{
key: 'col5',
value: 'Column 5'
},
{
key: 'col6',
value: 'Column 6'
}
]
}
]
};
export default Draggable;
================================================
FILE: stories/components/QTable/Groups.js
================================================
import Template from './Default';
const Groups = Template.bind({});
Groups.args = {
fixedLayout: true,
groupsOfColumns: [
{
key: 'one',
color: '#de4b7a',
columns: [
{
key: 'col1',
value: 'Column 1',
formatter: val => `formatted_${val}`
},
{
key: 'col2',
value: 'Column 2'
},
{
key: 'col3',
value: 'Column 3',
sortable: true
}
]
},
{
color: '#74aff8',
align: 'right',
key: 'two',
columns: [
{
key: 'col4',
value: 'Column 4',
sortable: true,
slots: {
header: 'customHeader',
row: 'customRow'
}
},
{
key: 'col5',
value: 'Column 6'
},
{
key: 'col6',
value: 'Column 6'
}
]
}
]
};
export default Groups;
================================================
FILE: stories/components/QTable/QTable.stories.js
================================================
import QTable from '../../../src/qComponents/QTable';
import Default from './Default';
import CustomWidth from './CustomWidth';
import Selectable from './Selectable';
import Total from './Total';
import StickyColumn from './StickyColumn';
import CustomRows from './CustomRows';
import Groups from './Groups';
import Draggable from './Draggable';
export default {
title: 'Components/QTable',
component: QTable,
decorators: [
() => ({ template: '
' })
],
argTypes: {
total: {
control: { type1: 'object' }
},
childrenKey: {
table: { disable: true }
},
expandable: {
table: { disable: true }
},
indentSize: {
table: { disable: true }
},
uniqueKey: {
table: { disable: true }
},
pagesInExpand: {
table: { disable: true }
}
},
args: {
defaultSort: {
key: 'col1',
direction: 'descending'
},
fixedLayout: false,
groupsOfColumns: [
{
key: 'one',
columns: [
{
key: 'col1',
value: 'Column 1',
formatter: val => `formatted_${val}`
},
{
key: 'col2',
value: 'Column 2'
},
{
key: 'col3',
value: 'Column 3'
},
{
key: 'col4',
value: 'Column 4',
sortable: true,
slots: {
header: 'customHeader',
row: 'customRow'
}
},
{
key: 'col5',
value: 'Column with very long title'
},
{
key: 'col6',
value: 'Column 6'
}
]
}
],
rows: [
{
col1: 'Lorem ipsum dolor sit amet',
col2: 'consectetur adipiscing elit',
col3: 'sed do eiusmod tempor incididunt',
col4: 'ut labore et dolore magna aliqua.',
col5: 'Ut enim ad minim veniam',
col6: 'quis nostrud exercitation ullamco laboris',
col8: 'quis nostrud exercitation ullamco laboris',
col7: 'quis nostrud exercitation ullamco laboris'
},
{
col1: 'nisi ut aliquip ex ea commodo consequat',
col2: 'Duis aute irure dolor in reprehenderit',
col3: 'in voluptate velit esse cillum dolore eu fugiat nulla pariatur',
col4: 'Excepteur sint occaecat cupidatat non proident',
col5: 'sunt in culpa qui officia deserunt mollit anim id est laborum',
col6: 'sunt in culpa qui officia deserunt mollit anim id est laborum',
col7: 'sunt in culpa qui officia deserunt mollit anim id est laborum',
col8: 'sunt in culpa qui officia deserunt mollit anim id est laborum'
},
{
col1:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua',
col2:
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
col3:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua',
col4:
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
col5:
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
col6:
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
col7:
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
col8:
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.'
}
],
changeSort(sort) {
console.log('sort', sort);
}
}
};
export {
Default,
CustomWidth,
Selectable,
Total,
StickyColumn,
CustomRows,
Groups,
Draggable
};
================================================
FILE: stories/components/QTable/Selectable.js
================================================
import Template from './Default';
const Selectable = Template.bind({});
Selectable.args = {
selectableColumn: {
totalCheckboxPosition: 'header'
}
};
export default Selectable;
================================================
FILE: stories/components/QTable/StickyColumn.js
================================================
import Template from './Default';
const StickyColumn = Template.bind({});
StickyColumn.args = {
groupsOfColumns: [
{
key: 'one',
columns: [
{
key: 'col1',
value: 'Column 1',
formatter: val => `formatted_${val}`,
width: '300px'
},
{
key: 'col2',
value: 'Column 2 sticky',
sticky: {
position: 'left'
}
},
{
key: 'col3',
value: 'Column 3',
width: '150px',
sortable: true
},
{
key: 'col4',
value: 'Column 4',
sortable: true,
slots: {
header: 'customHeader',
row: 'customRow'
}
},
{
key: 'col5',
value: 'Column 5 sticky',
sticky: {
position: 'right'
}
},
{
key: 'col6',
value: 'Column 6'
},
{
key: 'col7',
value: 'Column 7 sticky',
width: '157px',
sticky: {
position: 'right'
}
},
{
key: 'col8',
value: 'Column 8',
width: '1000px'
}
]
}
],
selectableColumn: {
sticky: true,
totalCheckboxPosition: 'total'
},
fixedLayout: true,
grid: true,
total: {
col1: 'Total 100',
col2: 'Total 200',
col3: 'Total 300',
col4: 'Total 400',
col5: 'Total 500',
col6: 'Total 600',
col7: 'Total 700',
col8: 'Total 800'
}
};
export default StickyColumn;
================================================
FILE: stories/components/QTable/Total.js
================================================
import Template from './Default';
const Total = Template.bind({});
Total.args = {
total: {
col1: 'Total 100',
col2: 'Total 200',
col3: 'Total 300',
col4: 'Total 400',
col5: 'Total 500',
col6: 'Total 600'
}
};
export default Total;
================================================
FILE: stories/components/QTabs.stories.js
================================================
import QTabs from '../../src/qComponents/QTabs';
import QTabPane from '../../src/qComponents/QTabPane';
export default {
title: 'Components/QTabs/QTabs',
component: QTabs,
subcomponents: { QTabPane },
argTypes: {
'v-model': { control: { type: 'none' } },
tabWidth: { control: { type: 'number' } }
}
};
export const QTabsStory = (_, { argTypes }) => ({
props: Object.keys(argTypes),
data() {
return {
activeTab: 'first_tab'
};
},
template: `
`
});
QTabsStory.storyName = 'Default';
================================================
FILE: stories/components/QTag.stories.js
================================================
import QTag from '../../src/qComponents/QTag';
export default {
title: 'Components/QTag',
component: QTag
};
export const QTagStory = (_, { argTypes }) => ({
props: Object.keys(argTypes),
data() {
return {
tags: ['Tag 1', 'Tag 2', 'Tag 3', 'Tag 4', 'Tag 5']
};
},
methods: {
handleCloseClick(clickedTag) {
console.log('Close tag clicked');
this.tags = this.tags.filter(tag => tag !== clickedTag);
}
},
template: `
{{ tag }}
`
});
QTagStory.storyName = 'Default';
================================================
FILE: stories/components/QTextarea.stories.js
================================================
import QTextarea from '../../src/qComponents/QTextarea';
export default {
title: 'Components/QTextarea',
component: QTextarea,
argTypes: {
placeholder: {
control: { type: 'text' }
},
maxlength: {
control: {
type: 'number'
}
},
value: {
control: {
type: 'none'
}
},
resize: {
control: {
type: 'select',
options: ['vertical', 'horizontal', 'both', 'none']
}
}
}
};
export const Default = (_, { argTypes }) => ({
props: Object.keys(argTypes).filter(arg => arg !== 'input'),
data() {
return {
input: ''
};
},
template: `
`
});
Default.storyName = 'Default';
================================================
FILE: stories/components/QTimePicker.stories.js
================================================
import QTimePicker from '../../src/qComponents/QTimePicker';
export default {
title: 'Components/QTimePicker',
component: QTimePicker,
argTypes: {
value: { control: { type: 'none' } },
disabledValues: {
control: {
type: 'select',
options: [
null,
{
to: '03:03:03'
}
]
}
}
}
};
export const Default = (_, { argTypes }) => ({
props: Object.keys(argTypes).filter(arg => arg !== 'value'),
components: { QTimePicker },
data() {
return {
value: null
};
},
methods: {
handleTimeChange(val) {
this.value = val;
}
},
template: `
`
});
Default.storyName = 'Default';
================================================
FILE: stories/components/QUpload/Default.js
================================================
const QUploadStory = (_, { argTypes }) => ({
props: Object.keys(argTypes),
data() {
return {
formModel: {
file: null
}
};
},
methods: {
async handleFileSelect(sourceFile) {
this.formModel.file = {
sourceFile,
name: sourceFile.name,
loading: 0
};
const promise = () =>
new Promise(resolve => {
const interval = setInterval(() => {
this.formModel.file.loading += 10;
}, 100);
setTimeout(() => {
clearInterval(interval);
this.formModel.file.loading = null;
resolve();
}, 1000);
});
await promise();
},
handleAbort() {
console.log('abort uploading');
},
handleClear() {
this.formModel.file = null;
}
},
template: `
`
});
QUploadStory.storyName = 'Default';
QUploadStory.args = {
accept: ['image/*', '.pdf']
};
export default QUploadStory;
================================================
FILE: stories/components/QUpload/Multiple.js
================================================
const QUploadStoryMultiple = (_, { argTypes }) => ({
props: Object.keys(argTypes),
data() {
return {
formModel: {
files: []
}
};
},
methods: {
async handleFileSelect(sourceFile, fileId) {
this.formModel.files.push({
id: fileId,
sourceFile,
name: sourceFile.name,
loading: 0
});
const currentFile = this.formModel.files.find(({ id }) => id === fileId);
const promise = () =>
new Promise(resolve => {
const interval = setInterval(() => {
currentFile.loading += 10;
}, 100);
setTimeout(() => {
clearInterval(interval);
currentFile.loading = null;
resolve();
}, 1000);
});
await promise();
},
handleAbort(fileId) {
console.log('abort uploading for: ', fileId);
},
handleClear(fileId) {
this.formModel.files = this.formModel.files.filter(
({ id }) => id !== fileId
);
},
handleClearAll() {
this.formModel.files = [];
}
},
template: `
`
});
QUploadStoryMultiple.storyName = 'Multiple';
QUploadStoryMultiple.args = {
accept: ['image/*', '.pdf']
};
export default QUploadStoryMultiple;
================================================
FILE: stories/components/QUpload/QUpload.stories.js
================================================
import QUpload from '../../../src/qComponents/QUpload';
import Default from './Default';
import Multiple from './Multiple';
export default {
title: 'Components/QUpload',
component: QUpload,
argTypes: {
'v-model': { control: { type: 'none' } },
multiple: { control: { type: 'none' } },
direction: {
control: { type: 'select', options: ['right', 'bottom'] }
}
}
};
export { Default, Multiple };
================================================
FILE: stories/core/colors.js
================================================
export const primaryColors = {
'--color-primary': '#de4b7a',
'--color-primary-purple': '#d048b4',
'--color-primary-blue': '#4162f0',
'--color-primary-blue-light': '#74aff8',
'--color-primary-blue-aqua': '#5ecae7',
'--color-primary-black': '#1d1c1a',
'--color-primary-darker': '#b83e65'
};
export const secondaryColors = {
'--color-secondary-orange': '#f95e2d',
'--color-secondary-green': '#40ba3e',
'--color-secondary-yellow': '#f2d22b'
};
export const tertiaryColors = {
'--color-tertiary-white': '#fff',
'--color-tertiary-gray-ultra-light': '#fafafa',
'--color-tertiary-gray-lighter': '#f4f4f6',
'--color-tertiary-gray-light': '#f0f0f3',
'--color-tertiary-gray': '#eaeaea',
'--color-tertiary-gray-dark': '#e2e2e2',
'--color-tertiary-gray-darker': '#dadada',
'--color-tertiary-gray-ultra-dark': '#c4c4c4',
'--color-tertiary-gray-ultra-darker': '#adacae'
};
export const gradients = {
'--gradient-primary':
'linear-gradient(101.12deg, var(--color-primary) 0.76%, var(--color-primary-purple) 100%)',
'--gradient-secondary':
'linear-gradient(180deg, var(--color-primary) -27.44%, var(--color-primary-blue) 100.54%)'
};
================================================
FILE: stories/core/colors.stories.mdx
================================================
import { Meta, ColorPalette, ColorItem } from '@storybook/addon-docs/blocks';
import '../../src/vars.scss';
import {
primaryColors,
secondaryColors,
tertiaryColors,
gradients
} from './colors';
================================================
FILE: stories/core/icons.stories.mdx
================================================
import {
Meta,
Preview,
Story,
ArgsTable,
IconGallery,
IconItem
} from '@storybook/addon-docs/blocks';
import '../../src/icons/index.scss';
export const iconsList = [
'q-icon-bell',
'q-icon-bell-ring',
'q-icon-triangle-down',
'q-icon-triangle-up',
'q-icon-triangle-left',
'q-icon-triangle-right',
'q-icon-double-triangle-left',
'q-icon-double-triangle-right',
'q-icon-account',
'q-icon-account-settings',
'q-icon-account-couple-fill',
'q-icon-settings-vertical',
'q-icon-arrow-down',
'q-icon-arrow-up',
'q-icon-arrow-right',
'q-icon-arrow-left',
'q-icon-attention-mark',
'q-icon-alert-stroke',
'q-icon-alert-fill',
'q-icon-info',
'q-icon-question-mark',
'q-icon-question',
'q-icon-rubles-circle',
'q-icon-proceed-1',
'q-icon-proceed-2',
'q-icon-close',
'q-icon-check',
'q-icon-minus',
'q-icon-pause',
'q-icon-reverse',
'q-icon-plus',
'q-icon-router',
'q-icon-router-arrow-down',
'q-icon-calendar',
'q-icon-calendar-refresh',
'q-icon-pic',
'q-icon-checkbox-square-multiply-non',
'q-icon-file',
'q-icon-menu',
'q-icon-stop-1',
'q-icon-stop-2',
'q-icon-view-list',
'q-icon-graph-gisto',
'q-icon-eye',
'q-icon-eye-close',
'q-icon-search',
'q-icon-trash-bin',
'q-icon-envelope-edit',
'q-icon-cloud-upload',
'q-icon-diagram-round',
'q-icon-wallet',
'q-icon-lock-fill',
'q-icon-target',
'q-icon-database-arrow-down',
'q-icon-dots-3-horizontal',
'q-icon-wifi',
'q-icon-logout',
'q-icon-lock',
'q-icon-comment',
'q-icon-credit-card',
'q-icon-login',
'q-icon-piggy-bank-fill',
'q-icon-rubles',
'q-icon-multiply-fill',
'q-icon-account-group',
'q-icon-menu-2-fill',
'q-icon-eye-fill',
'q-icon-cog-stroke',
'q-icon-pencil-square-stroke',
'q-icon-play',
'q-icon-finish-fill',
'q-icon-archive-arrow-down',
'q-icon-wallet-stroke',
'q-icon-change-list',
'q-icon-withdraw-fill',
'q-icon-info-stroke',
'q-icon-trash-bin-stroke',
'q-icon-star',
'q-icon-save',
'q-icon-percent',
'q-icon-drag-vertical-fill',
'q-icon-diagram-round-stroke',
'q-icon-attach-fill',
'q-icon-account-check-fill',
'q-icon-calendar-clock',
'q-icon-chain',
'q-icon-clock-stroke',
'q-icon-cog-fill',
'q-icon-earth',
'q-icon-filter-fill',
'q-icon-filter-stroke',
'q-icon-pencil-list',
'q-icon-settings-horizontal',
'q-icon-star-fill',
'q-icon-account-group-web',
'q-icon-drag-linear',
'q-icon-y-1',
'q-icon-y-2',
'q-icon-y-3',
'q-icon-y-4',
'q-icon-key',
'q-icon-cart',
'q-icon-house-stroke',
'q-icon-info-fill',
'q-icon-phone-settings-stroke'
];
# Icon
Use a class to apply an icon to an element
{iconsList.map((iconName, index) => (
))}
## Playground
export const Template = (args, { argTypes }) => ({
props: Object.keys(argTypes),
template: `
`
});
{Template.bind({})}
================================================
FILE: stories/intro.stories.mdx
================================================
import { Meta, Story } from '@storybook/addon-docs/blocks';
# A Vue.js Design System for Web
Responsive, user-friendly and lightweight library helping us build great products for our customers.
This library for Vue 2.x, Qui for Vue 3 in our roadmap.
What is it?
- 🔩 30+ Vue components
- 📦 icons pack
- 🏳️🌈 colors & grid
- 🥷 neumorphism styles
- 📚 storybook sandbox
Some examples below:







## Install
```bash
npm install @qvant/qui -S
yarn add @qvant/qui
```
You can import Qui entirely, or just import what you need. Let's start with fully import.
## Quick setup
In main.js:
```js
import Vue from 'vue';
import Qui from '@qvant/qui';
import '@qvant/qui/dist/qui.css';
// Setup all components
Vue.use(Qui);
// that's it! All components will be imported with styles
```
in YourComponent.vue: (Example)
```vue
```
...or configure quick setup
In main.js:
```js
import Vue from 'vue';
import Qui from '@qvant/qui';
import '@qvant/qui/dist/qui.css';
Vue.use(Qui, {
localization: {
locale: 'en', // Russian language by default, you can set `en` for English
customI18nMessages: {
// rewrite default texts, see the source: src/qComponents/constants/locales
en: {
QDatepicker: {
placeholder: 'Pick your birthday!'
}
}
},
zIndexCounter: 3000, // zIndexCounter is being used by some components, (e.g QPopover, QSelect, QDialog ...etc), 2000 by default
prefix: 'yo' // you can change component's prefix, e.g. must be used instead of
}
});
```
in YourComponent.vue: (Example)
```vue
```
Now you have implemented Vue and Qui to your project, and it's time to write your code.
Please refer to each component's [Stories](https://qvant-lab.github.io/qui/) to learn how to use them.
## Not quick setup
If you have a module bundler (e.g webpack), you can import components separately and take care about your bundle size
In main.js:
```js
// import the main plugin from another place (it ensures Qui will be installed without any components, but instance will set required properties and directives)
import Qui from '@qvant/qui/src/onDemand';
// import the component you want
import QButton from '@qvant/qui/src/qComponents/QButton';
// ...or in async way
Vue.component('q-button', () =>
import(/* webpackChunkName: "qui" */ '@qvant/qui/src/qComponents/QButton')
);
// init
Vue.use(Qui);
Vue.use(QButton);
```
In main.scss:
```scss
// need to set the path for files with statics
$--base-path: '~@qvant/qui/src';
// set main styles
@import '~@qvant/qui/src/main.scss';
// notice that you must use `fonts` and `icons` styles for some of components:
@import '~@qvant/qui/src/fonts/index.scss';
@import '~@qvant/qui/src/icons/index.scss';
```
Import all styles:
```scss
@import '~@qvant/qui/src/components.scss';
```
...or components separately:
```scss
@import '~@qvant/qui/src/qComponents/QBreadcrumbs/src/q-breadcrumbs.scss';
@import '~@qvant/qui/src/qComponents/QButton/src/q-button.scss';
// ...etc
```
## Optional
- if you want use modals inside your components as property of 'this':
```js
import { QMessageBox, QDialog, QNotification } from '@qvant/qui';
// or import separately
import QMessageBox from '@qvant/qui/src/qComponents/QMessageBox';
import QDialog from '@qvant/qui/src/qComponents/QDialog';
import QNotification from '@qvant/qui/src/qComponents/QNotification';
Vue.prototype.$message = QMessageBox;
Vue.prototype.$dialog = QDialog;
Vue.prototype.$notify = options =>
QNotification({
duration: 3000, // - ms
...options
});
```
- if you use VueI18n, you need to merge messages:
```js
import VueI18n from 'vue-i18n';
import { en, ru } from '@qvant/qui/src/qComponents/constants/locales';
Vue.use(VueI18n);
const messages = {
en: {
message: {
hello: 'hello world'
},
...en
},
ru: {
message: {
hello: 'привет, мир'
},
...ru
}
};
const i18n = new VueI18n({
locale: 'en',
messages
});
new Vue({
i18n
}).$mount('#your-app');
```
## Supported languages
- Russian ✅
- English ✅
- Also you can use any language by setting texts for components via 'customI18nMessages' property in the Qui instance. See the example above.
## Browser Support
Modern browsers are recomended
- safari: >11
- chrome: >=61
- firefox: >=58
- opera: >=62
- edge: >=16
- yandex: >=18
- ie: ? (we don't know :) and will not support it)
## Development
Clone repository and run storybook
```bash
yarn storybook
npm run storybook
```
## LICENSE
MIT
================================================
FILE: tests/unit/setup.js
================================================
import Vue from 'vue';
import { mount, shallowMount } from '@vue/test-utils';
import Qui from '../../src/qComponents';
Vue.use(Qui);
global.mount = mount;
global.shallowMount = shallowMount;