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 ================================================

storybook NPM version NPM downloads

# 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: ![buttons](/.readme-assets/buttons.jpg?raw=true) ![inputs](/.readme-assets/inputs.gif?raw=true) ![icons](/.readme-assets/icons.gif?raw=true) ![tables](/.readme-assets/tables.jpg?raw=true) ![datepicker](/.readme-assets/datepicker.jpg?raw=true) ![other](/.readme-assets/other.jpg?raw=true) ## 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 ================================================ ================================================ 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 ================================================ ================================================ 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 ================================================ ================================================ 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 ================================================ ================================================ FILE: src/qComponents/QDatePicker/src/basic/month-table.vue ================================================ ================================================ FILE: src/qComponents/QDatePicker/src/basic/year-table.vue ================================================ ================================================ 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 ================================================ ================================================ 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: '' } }; 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`] = ` `; ================================================ 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 ================================================ ================================================ 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 ================================================ ================================================ FILE: src/qComponents/QSelect/src/QSelectTags.vue ================================================ ================================================ 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 ================================================ ================================================ FILE: src/qComponents/QSlider/src/components/QSliderCaptions/index.vue ================================================ ================================================ 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 ================================================ ================================================ 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`] = `
Column 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 ================================================ ================================================ 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 ================================================