├── .babelrc
├── .devcontainer
├── Dockerfile
└── devcontainer.json
├── .eslintrc.js
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── dependabot.yml
└── workflows
│ ├── codeql-analysis.yml
│ └── lint.yml
├── .gitignore
├── .storybook
├── logo.png
├── main.js
├── manager.js
├── preview.js
└── theme.js
├── .stylelintrc
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── package-lock.json
├── package.json
├── postcss.config.js
├── src
├── components
│ ├── FAutocomplete
│ │ ├── FAutocomplete.spec.js
│ │ ├── FAutocomplete.vue
│ │ └── index.js
│ ├── FAvatar
│ │ ├── FAvatar.spec.js
│ │ ├── FAvatar.vue
│ │ └── index.js
│ ├── FBtn
│ │ ├── FBtn.spec.js
│ │ ├── FBtn.vue
│ │ └── index.js
│ ├── FCard
│ │ ├── FCard.spec.js
│ │ ├── FCard.vue
│ │ ├── FCardAction.vue
│ │ ├── FCardCaption.vue
│ │ ├── FCardContent.vue
│ │ ├── FCardImage.vue
│ │ ├── FCardText.vue
│ │ ├── FCardTitle.vue
│ │ └── index.js
│ ├── FCheckbox
│ │ ├── FCheckbox.spec.js
│ │ ├── FCheckbox.vue
│ │ └── index.js
│ ├── FContainer
│ │ ├── FContainer.spec.js
│ │ ├── FContainer.vue
│ │ └── index.js
│ ├── FGrid
│ │ ├── FGrid.spec.js
│ │ ├── FGrid.vue
│ │ ├── FGridItem.vue
│ │ └── index.js
│ ├── FImg
│ │ ├── FImg.spec.js
│ │ ├── FImg.vue
│ │ └── index.js
│ ├── FInput
│ │ ├── FInput.spec.js
│ │ ├── FInput.vue
│ │ └── index.js
│ ├── FList
│ │ ├── FList.spec.js
│ │ ├── FList.vue
│ │ ├── FListItem.spec.js
│ │ ├── FListItem.vue
│ │ ├── FListItemAvatar.spec.js
│ │ ├── FListItemAvatar.vue
│ │ ├── FListItemContent.spec.js
│ │ ├── FListItemContent.vue
│ │ └── index.js
│ ├── FMenu
│ │ ├── FMenu.spec.js
│ │ ├── FMenu.vue
│ │ └── index.js
│ ├── FProgressLinear
│ │ ├── FProgressLinear.spec.js
│ │ ├── FProgressLinear.vue
│ │ └── index.js
│ ├── FRadio
│ │ ├── FRadio.spec.js
│ │ ├── FRadio.vue
│ │ └── index.js
│ ├── FRadioGroup
│ │ ├── FRadioGroup.spec.js
│ │ ├── FRadioGroup.vue
│ │ └── index.js
│ ├── FSelect
│ │ ├── FSelect.spec.js
│ │ ├── FSelect.vue
│ │ └── index.js
│ ├── FSlider
│ │ ├── FSlider.spec.js
│ │ ├── FSlider.vue
│ │ └── index.js
│ ├── FSwitch
│ │ ├── FSwitch.spec.js
│ │ ├── FSwitch.vue
│ │ └── index.js
│ ├── FTextarea
│ │ ├── FTextarea.spec.js
│ │ ├── FTextarea.vue
│ │ └── index.js
│ └── index.js
├── main.js
├── mixins
│ ├── grid.js
│ └── helper.js
├── scss
│ ├── _variables.scss
│ ├── components
│ │ ├── _FAvatar.scss
│ │ ├── _FBtn.scss
│ │ ├── _FCard.scss
│ │ ├── _FCheckbox.scss
│ │ ├── _FContainer.scss
│ │ ├── _FGrid.scss
│ │ ├── _FImg.scss
│ │ ├── _FInput.scss
│ │ ├── _FList.scss
│ │ ├── _FMenu.scss
│ │ ├── _FProgressLinear.scss
│ │ ├── _FRadio.scss
│ │ ├── _FRadioGroup.scss
│ │ ├── _FSelect.scss
│ │ ├── _FSlider.scss
│ │ ├── _FSwitch.scss
│ │ └── _FTextarea.scss
│ ├── fluentify.scss
│ ├── global
│ │ ├── _base.scss
│ │ ├── _button.scss
│ │ ├── _colors.scss
│ │ ├── _grid.scss
│ │ ├── _typography.scss
│ │ └── _ul-ol.scss
│ └── utils
│ │ ├── _helpers.scss
│ │ └── _mixin.scss
├── stories
│ ├── FBtn.stories.js
│ ├── FCard.stories.js
│ ├── FCheckbox.stories.js
│ ├── FInput.stories.js
│ ├── FProgressLinear.stories.js
│ ├── FRadio.stories.js
│ ├── FRadioGroup.stories.js
│ ├── FSwitch.stories.js
│ └── FTextarea.stories.js
└── utils
│ ├── config.js
│ └── helper.js
├── tests
└── unit
│ └── button.spec.js
└── vue.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | { "presets": ["@vue/cli-plugin-babel/preset"] }
--------------------------------------------------------------------------------
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | # [Choice] Node.js version: 14, 12, 10
2 | ARG VARIANT=14
3 | FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:${VARIANT}
4 | USER root
5 | RUN apt-get update
6 |
7 | ARG USER_UID=1000
8 | ARG USER_GID=$USER_UID
9 | RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then \
10 | groupmod --gid $USER_GID node \
11 | && usermod --uid $USER_UID --gid $USER_GID node \
12 | && chmod -R $USER_UID:$USER_GID /home/node \
13 | && chmod -R $USER_UID:root /usr/local/share/nvm /usr/local/share/npm-global; \
14 | fi
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Node.js",
3 | "build": {
4 | "dockerfile": "Dockerfile",
5 | // Update 'VARIANT' to pick a Node version: 10, 12, 14
6 | "args": { "VARIANT": "14" }
7 | },
8 |
9 | // Set *default* container specific settings.json values on container create.
10 | "settings": {
11 | "terminal.integrated.shell.linux": "/bin/bash"
12 | },
13 |
14 | // Add the IDs of extensions you want installed when the container is created.
15 | "extensions": [
16 | "dbaeumer.vscode-eslint",
17 | "octref.vetur",
18 | "liuji-jim.vue",
19 | "eamodio.gitlens",
20 | "wix.vscode-import-cost",
21 | "mrmlnc.vscode-scss"
22 | ],
23 |
24 | // Use 'forwardPorts' to make a list of ports inside the container available locally.
25 | "forwardPorts": [9000],
26 |
27 | // Use 'postCreateCommand' to run commands after the container is created.
28 | // "postCreateCommand": "yarn install",
29 |
30 | // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
31 | "remoteUser": "node"
32 | }
33 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | 'env': {
4 | 'browser': true,
5 | 'commonjs': true,
6 | 'es6': true
7 | },
8 | extends: [
9 | 'eslint:recommended',
10 | 'plugin:vue/recommended'
11 | ],
12 | 'parserOptions': {
13 | 'ecmaVersion': 2015,
14 | 'sourceType': 'module'
15 | },
16 | 'rules': {
17 | 'indent': [
18 | 'error',
19 | 2
20 | ],
21 | 'quotes': [
22 | 'error',
23 | 'single'
24 | ],
25 | 'semi': [
26 | 'error',
27 | 'never'
28 | ],
29 | 'vue/no-use-v-if-with-v-for': 'off'
30 | },
31 | 'overrides': [
32 | {
33 | 'files': [
34 | '*.vue',
35 | '**/__tests__/*.{j,t}s?(x)',
36 | '**/tests/unit/**/*.spec.{j,t}s?(x)'
37 | ],
38 | env: {
39 | mocha: true
40 | },
41 | 'rules': {
42 | 'indent': 'off'
43 | }
44 | }
45 | ]
46 | }
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [alexanderyw]
4 | # patreon: # Replace with a single Patreon username
5 | # open_collective: # Replace with a single Open Collective username
6 | # ko_fi: # Replace with a single Ko-fi username
7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | # custom: # Replace with a single custom sponsorship URL
9 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | versioning-strategy: increase
9 | ignore:
10 | - dependency-name: "@storybook/addon-links"
11 | versions:
12 | - 6.1.16
13 | - 6.1.17
14 | - 6.1.19
15 | - 6.1.21
16 | - 6.2.1
17 | - 6.2.2
18 | - 6.2.3
19 | - 6.2.4
20 | - 6.2.5
21 | - 6.2.7
22 | - 6.2.8
23 | - dependency-name: "@storybook/vue"
24 | versions:
25 | - 6.1.17
26 | - 6.1.19
27 | - 6.1.21
28 | - 6.2.1
29 | - 6.2.2
30 | - 6.2.3
31 | - 6.2.4
32 | - 6.2.5
33 | - 6.2.7
34 | - 6.2.8
35 | - dependency-name: "@storybook/addon-actions"
36 | versions:
37 | - 6.1.17
38 | - 6.1.21
39 | - 6.2.1
40 | - 6.2.2
41 | - 6.2.3
42 | - 6.2.4
43 | - 6.2.5
44 | - 6.2.7
45 | - 6.2.8
46 | - dependency-name: "@babel/core"
47 | versions:
48 | - 7.12.13
49 | - 7.12.16
50 | - 7.12.17
51 | - 7.13.10
52 | - 7.13.13
53 | - 7.13.14
54 | - 7.13.15
55 | - dependency-name: y18n
56 | versions:
57 | - 4.0.1
58 | - dependency-name: eslint-plugin-vue
59 | versions:
60 | - 7.5.0
61 | - 7.6.0
62 | - 7.7.0
63 | - 7.8.0
64 | - dependency-name: chai
65 | versions:
66 | - 4.3.1
67 | - 4.3.3
68 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | pull_request:
7 | # The branches below must be a subset of the branches above
8 | branches: [master]
9 | schedule:
10 | - cron: '0 6 * * 1'
11 |
12 | jobs:
13 | analyze:
14 | name: Analyze
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | # Override automatic language detection by changing the below list
21 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
22 | language: ['javascript']
23 | # Learn more...
24 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
25 |
26 | steps:
27 | - name: Checkout repository
28 | uses: actions/checkout@v2
29 | with:
30 | # We must fetch at least the immediate parents so that if this is
31 | # a pull request then we can checkout the head.
32 | fetch-depth: 2
33 |
34 | # If this run was triggered by a pull request event, then checkout
35 | # the head of the pull request instead of the merge commit.
36 | - run: git checkout HEAD^2
37 | if: ${{ github.event_name == 'pull_request' }}
38 |
39 | # Initializes the CodeQL tools for scanning.
40 | - name: Initialize CodeQL
41 | uses: github/codeql-action/init@v1
42 | with:
43 | languages: ${{ matrix.language }}
44 |
45 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
46 | # If this step fails, then you should remove it and run the build manually (see below)
47 | - name: Autobuild
48 | uses: github/codeql-action/autobuild@v1
49 |
50 | # ℹ️ Command-line programs to run using the OS shell.
51 | # 📚 https://git.io/JvXDl
52 |
53 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
54 | # and modify them (or add more) to build your code if your project
55 | # uses a compiled language
56 |
57 | #- run: |
58 | # make bootstrap
59 | # make release
60 |
61 | - name: Perform CodeQL Analysis
62 | uses: github/codeql-action/analyze@v1
63 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Javascript and css linting
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - name: Checkout repository
19 | uses: actions/checkout@v2
20 |
21 | - name: Use Node.js ${{ matrix.node-version }}
22 | uses: actions/setup-node@v1
23 |
24 | - run: npm ci
25 |
26 | - run: npm run build
27 |
28 | lint:
29 |
30 | runs-on: ubuntu-latest
31 |
32 | steps:
33 | - name: Checkout repository
34 | uses: actions/checkout@v2
35 |
36 | - name: Use Node.js ${{ matrix.node-version }}
37 | uses: actions/setup-node@v1
38 |
39 | - run: npm ci
40 |
41 | - run: npm run lint:js
42 |
43 | - run: npm run lint:css
44 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 |
63 | /dist
64 | /reports
65 | /test/dist
66 |
67 | # storybook
68 | /storybook-static
--------------------------------------------------------------------------------
/.storybook/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FluentifyJs/fluentify/c5c24b5f7111b771dc2cc9c2ef915ca5b8e9dc89/.storybook/logo.png
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | module.exports = {
4 | addons: [
5 | '@storybook/preset-scss',
6 | '@storybook/addon-actions',
7 | '@storybook/addon-links'
8 | ],
9 | webpackFinal: async (config, { configType }) => {
10 | // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
11 | // You can change the configuration based on that.
12 | // 'PRODUCTION' is used when building the static version of storybook.
13 | config.watch = true
14 |
15 | // Make whatever fine-grained changes you need
16 | config.module.rules.push({
17 | test: /\.scss$/,
18 | use: ['style-loader', 'css-loader', 'sass-loader'],
19 | include: path.resolve(__dirname, '../'),
20 | },
21 | {
22 | test: /\.(png|svg|jpg|gif)$/,
23 | use: [
24 | 'file-loader',
25 | ],
26 | include: path.resolve(__dirname, "../")
27 | });
28 |
29 | // Return the altered config
30 | return config;
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/.storybook/manager.js:
--------------------------------------------------------------------------------
1 | import { addons } from '@storybook/addons';
2 | import yourTheme from './theme';
3 |
4 | addons.setConfig({
5 | theme: yourTheme,
6 | });
--------------------------------------------------------------------------------
/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-extraneous-dependencies */
2 | import { configure, addDecorator } from '@storybook/vue'
3 |
4 | import '!style-loader!css-loader!sass-loader!../src/scss/fluentify.scss'
5 |
6 | const req = require.context('../src/stories', true, /.stories.js$/)
7 |
8 | addDecorator(() => ({
9 | template: '
',
10 | }))
11 |
12 | function loadStories() {
13 | req.keys().forEach(filename => req(filename))
14 | }
15 |
16 | configure(loadStories, module)
17 |
--------------------------------------------------------------------------------
/.storybook/theme.js:
--------------------------------------------------------------------------------
1 | import { create } from '@storybook/theming';
2 | import logo from './logo.png';
3 |
4 | export default create({
5 | base: 'light',
6 |
7 | colorPrimary: '#41B883',
8 | colorSecondary: '#34495E',
9 |
10 | // UI
11 | appBg: 'white',
12 | appContentBg: 'white',
13 | appBorderColor: '#41B883',
14 | appBorderRadius: 4,
15 |
16 | // Typography
17 | fontBase: '"Open Sans", sans-serif',
18 | fontCode: 'monospace',
19 |
20 | // Text colors
21 | textColor: '#34495E',
22 | textInverseColor: 'rgba(255,255,255,0.9)',
23 |
24 | // Toolbar default and active colors
25 | barTextColor: 'silver',
26 | barSelectedColor: '#34495E',
27 | barBg: '#41B883',
28 |
29 | // Form colors
30 | inputBg: 'white',
31 | inputBorder: '#41B883',
32 | inputTextColor: '#34495E',
33 | inputBorderRadius: 4,
34 |
35 | brandTitle: 'Fluentify',
36 | brandUrl: 'https://github.com/FluentifyJs/fluentify',
37 | brandImage: logo,
38 | });
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "block-no-empty": null,
4 | "color-no-invalid-hex": true,
5 | "comment-empty-line-before": [ "always", {
6 | "ignore": ["stylelint-commands", "after-comment"]
7 | } ],
8 | "declaration-colon-space-after": "always",
9 | "indentation": [4],
10 | "max-empty-lines": 2,
11 | "rule-empty-line-before": [ "always", {
12 | "except": ["first-nested"],
13 | "ignore": ["after-comment"]
14 | } ],
15 | "unit-whitelist": ["em", "rem", "%", "px", "s", "ms", "deg", "fr"]
16 | }
17 | }
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Contributions are **welcome** and will be fully **credited**.
4 |
5 | We accept contributions via Pull Requests on [Github](https://github.com/AlexanderYW/fluentify).
6 |
7 |
8 | ## Pull Requests
9 |
10 | - **Keep the same style** - eslint will automatically be ran before committing
11 |
12 | - **Tip** to pass lint tests easier use the `npm run lint:fix` command
13 |
14 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests.
15 |
16 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
17 |
18 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option.
19 |
20 | - **Create feature branches** - Don't ask us to pull from your master branch.
21 |
22 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
23 |
24 | - **Send coherent history** - Make sure your commits message means something
25 |
26 |
27 | ## Running Tests
28 |
29 | Launch visual tests and watch the components at the same time
30 |
31 | ``` bash
32 | $ npm run dev
33 | ```
34 |
35 |
36 | **Happy coding**!
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Alexander Wennerstrøm
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://codebeat.co/projects/github-com-fluentifyjs-fluentify-master)
4 | 
5 | 
6 | 
7 | 
8 | 
9 |
10 | ### [View demo](https://fluent.ayw.io/)
11 |
12 | ## This Components framework is *Work in progress*, please keep that in mind that it's not ready for production yet, feel free to contribute and get it ready faster
13 |
14 | # Todo:
15 | ## Components
16 | - [x] Container
17 | - [x] Grid - Rows and Columns using css grid (Room for improvements)
18 | - [x] Button
19 | - [x] List
20 | - [x] List item
21 | - [x] List item avatar
22 | - [x] List item content (with support of single line, double line and triple line)
23 | - [ ] Card
24 | - [ ] Video player
25 | - [x] Menu component
26 | - [ ] Modal / Dialog
27 | - [ ] Date picker
28 | - [ ] Date calendar picker
29 | - [ ] Time picker
30 | - [ ] Color picker
31 | - [ ] Flyout / Snackbar
32 | - [x] Progressbar linear
33 | - [ ] Progressbar circular
34 | - [x] Input field
35 | - [x] Checkbox input
36 | - [x] Radio input
37 | - [x] Radio Group
38 | - [ ] Select (Missing arrow icon)
39 | - [ ] File picker
40 | - [ ] Textarea
41 | - [ ] Datalist
42 | - [ ] Divider
43 | - [ ] Data table
44 | - [ ] Image component
45 | - [ ] Switch
46 | - [x] Slider (horizontal/vertical)
47 | - [ ] Rating
48 | - [ ] Autocomplete / Auto-suggest input
49 | - [ ] Context menu
50 | - [ ] Navigation view (Content of view will be the list component)
51 | - [ ] Command bar
52 | - [ ] Tree view
53 | - [ ] Pivot
54 | - [ ] Icon component
55 |
56 | # Classes
57 |
58 | - [ ] Typography
59 | - [ ] Colors
60 | - [ ] Padding helper
61 | - [ ] Margin helper
62 | - [ ] Display helper (block, inline, inline-block etc)
63 | - [ ] MDL2 icon font
64 |
65 | # Theme
66 | - [ ] Custom colors support
67 | - [ ] Dark theme for all components
68 |
69 | # Website
70 | - [ ] Main page
71 | - [ ] Documentation
72 | - [ ] Theme generator
73 | - [ ] Codepen template
74 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@fluentify/fluentify",
3 | "version": "0.2.0",
4 | "description": "Fluent design system components based framework for Vue.js",
5 | "author": "Alexander Wennerstrøm ",
6 | "scripts": {
7 | "build": "vue-cli-service build",
8 | "test": "npm run coverage",
9 | "clean": "rimraf dist",
10 | "coverage": "vue-cli-service test:unit",
11 | "lint": "npm run lint:css && npm run lint:js && vue-cli-service lint",
12 | "lint:css": "stylelint src/**/*.{vue,css,scss}",
13 | "lint:js": "npx eslint --ext js --ext jsx --ext vue src src/**/*.spec.js",
14 | "lint:js:fix": "npm run lint:js -- --fix",
15 | "lint:staged": "lint-staged",
16 | "prepublish": "npm run build",
17 | "pretest": "npm run lint",
18 | "storybook:build": "build-storybook",
19 | "storybook:serve": "start-storybook -p 9000"
20 | },
21 | "dependencies": {
22 | "vue": "^2.6.12"
23 | },
24 | "devDependencies": {
25 | "@babel/core": "^7.14.0",
26 | "@storybook/preset-scss": "^1.0.3",
27 | "@storybook/addon-actions": "^6.2.9",
28 | "@storybook/addon-links": "^6.1.20",
29 | "@storybook/vue": "^6.2.9",
30 | "@vue/cli-plugin-babel": "^4.5.11",
31 | "@vue/cli-plugin-eslint": "^4.5.11",
32 | "@vue/cli-plugin-unit-mocha": "~4.5.11",
33 | "@vue/cli-service": "^4.5.11",
34 | "@vue/eslint-config-standard": "^4.0.0",
35 | "@vue/test-utils": "^1.1.3",
36 | "babel-eslint": "^10.1.0",
37 | "babel-loader": "^8.2.2",
38 | "babel-preset-vue": "^2.0.2",
39 | "chai": "^4.3.4",
40 | "eslint": "^6.8.0",
41 | "eslint-plugin-vue": "^6.2.2",
42 | "file-loader": "^6.2.0",
43 | "lint-staged": "^10.5.4",
44 | "node-sass": "^5.0.0",
45 | "pre-commit": "^1.2.2",
46 | "rimraf": "^3.0.2",
47 | "sass": "^1.32.8",
48 | "sass-loader": "^10.1.1",
49 | "stylelint": "^13.12.0",
50 | "vue-template-compiler": "^2.6.12"
51 | },
52 | "browserslist": [
53 | "last 2 versions"
54 | ],
55 | "browser": "dist/fluentify.js",
56 | "bugs": {
57 | "url": "https://github.com/FluentifyJs/fluentify/issues"
58 | },
59 | "homepage": "https://github.com/FluentifyJs/fluentify#readme",
60 | "keywords": [
61 | "vue",
62 | "vuejs",
63 | "fluent",
64 | "design",
65 | "framework",
66 | "components"
67 | ],
68 | "license": "MIT",
69 | "lint-staged": {
70 | "*.{vue,jsx,js}": [
71 | "npm run lint:js"
72 | ],
73 | "*.{vue,css}": [
74 | "npx stylefmt",
75 | "npm run lint:css"
76 | ]
77 | },
78 | "main": "dist/fluentify.common.js",
79 | "module": "dist/fluentify.esm.js",
80 | "pre-commit": "lint:staged",
81 | "repository": {
82 | "type": "git",
83 | "url": "git+https://github.com/FluentifyJs/fluentify.git"
84 | },
85 | "style": "dist/fluentify.css",
86 | "unpkg": "dist/fluentify.js"
87 | }
88 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('autoprefixer')({
4 | 'overrideBrowserslist': [
5 | '> 1%',
6 | 'last 2 versions'
7 | ]
8 | })
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/FAutocomplete/FAutocomplete.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FAutocomplete from './FAutocomplete.vue'
3 | import { createVM } from 'src/../test/helpers/utils.js'
4 | import { expect } from 'chai'
5 |
6 | describe('FAutocomplete.vue', function () {
7 | it('should render correct contents', function () {
8 | const vm = createVM(this, '',
9 | {
10 | components: {
11 | FAutocomplete
12 | },
13 | data () {
14 | return {
15 | value: 'Yes it have',
16 | items: [
17 | { id: 1, title: 'This is a title', text: 'This is a small text paragraph', avatar: 'https://via.placeholder.com/150', disabled: false },
18 | { id: 2, title: 'This is a title', text: 'This is a small text paragraph', avatar: 'https://via.placeholder.com/150', disabled: false },
19 | { id: 3, title: 'This is a title', text: 'This is a small text paragraph', avatar: 'https://via.placeholder.com/150', disabled: false }
20 | ]
21 | }
22 | }
23 | }
24 | )
25 | expect(vm.$el.querySelectorAll('.f-input input')[0].value.length).to.be.equal(0)
26 | })
27 | })
28 |
--------------------------------------------------------------------------------
/src/components/FAutocomplete/FAutocomplete.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
16 | Title: {{ resolveStringPath(itemTitle, item) }}
17 | Text: {{ resolveStringPath(itemText, item) }}
18 | value: {{ resolveStringPath(itemValue, item) }}
19 | Avatar: {{ resolveStringPath(itemAvatar, item) }}
20 | Disabled: {{ resolveStringPath(itemDisabled, item) }}
21 |
22 |
23 |
24 |
25 |
26 |
102 |
--------------------------------------------------------------------------------
/src/components/FAutocomplete/index.js:
--------------------------------------------------------------------------------
1 | import FAutocomplete from './FAutocomplete'
2 |
3 | import { use, registerComponent } from '../../utils/helper'
4 |
5 | const Plugin = {
6 | install (Vue) {
7 | registerComponent(Vue, FAutocomplete)
8 | }
9 | }
10 |
11 | use(Plugin)
12 |
13 | export default Plugin
14 |
15 | export {
16 | FAutocomplete
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/FAvatar/FAvatar.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FAvatar from './FAvatar.vue'
3 | import { createVM } from 'src/../test/helpers/utils.js'
4 | import { expect } from 'chai'
5 |
6 | describe('FAvatar.vue', function () {
7 | it('should render avatar with default settings and image', function () {
8 | const vm = createVM(this, `
9 |
10 | `,
11 | {
12 | components: {
13 | FAvatar
14 | },
15 | data () {
16 | return {
17 | src: 'https://via.placeholder.com/48'
18 | }
19 | }
20 | }
21 | )
22 | expect(vm.$el.querySelectorAll('.f-avatar img').length).to.be.equal(1)
23 | })
24 | it('should render avatar with size 150', function () {
25 | const vm = createVM(this, `
26 |
27 | `,
28 | {
29 | components: {
30 | FAvatar
31 | },
32 | data () {
33 | return {
34 | size: 150,
35 | src: 'https://via.placeholder.com/150'
36 | }
37 | }
38 | }
39 | )
40 | expect(vm.$el.querySelectorAll('.f-avatar img').length).to.be.equal(1)
41 | })
42 | it('should render avatar with with slot content', function () {
43 | const vm = createVM(this, `
44 |
45 | AW
46 |
47 | `,
48 | {
49 | components: {
50 | FAvatar
51 | }
52 | }
53 | )
54 | expect(vm.$el.querySelectorAll('.f-avatar span').length).to.be.equal(1)
55 | })
56 | })
57 |
--------------------------------------------------------------------------------
/src/components/FAvatar/FAvatar.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
![]()
13 |
14 |
15 |
16 |
17 |
56 |
--------------------------------------------------------------------------------
/src/components/FAvatar/index.js:
--------------------------------------------------------------------------------
1 | import FAvatar from './FAvatar'
2 |
3 | import { use, registerComponent } from '../../utils/helper'
4 |
5 | const Plugin = {
6 | install (Vue) {
7 | registerComponent(Vue, FAvatar)
8 | }
9 | }
10 |
11 | use(Plugin)
12 |
13 | export default Plugin
14 |
15 | export {
16 | FAvatar
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/FBtn/FBtn.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FBtn from './FBtn.vue'
3 | import { createVM } from 'src/../test/helpers/utils.js'
4 |
5 | describe('FBtn.vue', function () {
6 | it('should render correct contents', function () {
7 | const vm = createVM(this, `
8 | Nice Button
9 | Nice Button
10 | Nice Button
11 | `, { components: { FBtn }})
12 | vm.$el.querySelector('.f-btn span').textContent.should.eql('Nice Button')
13 | })
14 | })
15 |
--------------------------------------------------------------------------------
/src/components/FBtn/FBtn.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
42 |
--------------------------------------------------------------------------------
/src/components/FBtn/index.js:
--------------------------------------------------------------------------------
1 | import FBtn from './FBtn'
2 |
3 | import { use, registerComponent } from '../../utils/helper'
4 |
5 | const Plugin = {
6 | install (Vue) {
7 | registerComponent(Vue, FBtn)
8 | }
9 | }
10 |
11 | use(Plugin)
12 |
13 | export default Plugin
14 |
15 | export {
16 | FBtn
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/FCard/FCard.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FCard from './FCard.vue'
3 | import {
4 | createVM
5 | } from 'src/../test/helpers/utils.js'
6 | import {
7 | expect
8 | } from 'chai'
9 |
10 | describe('FCard.vue', function () {
11 | it('should render correct contents', function () {
12 | const vm = createVM(this, `
13 |
14 | PROFILE
15 |
16 | `, {
17 | components: {
18 | FCard
19 | },
20 | data() {
21 | return {
22 | value: 'Yes it have'
23 | }
24 | }
25 | })
26 | expect(vm.$el.querySelectorAll('.f-card').length).to.be.equal(1)
27 | })
28 | })
--------------------------------------------------------------------------------
/src/components/FCard/FCard.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
17 |
18 |
19 |
26 |
27 |
28 |
34 |
35 |
36 |
37 |
38 |
83 |
--------------------------------------------------------------------------------
/src/components/FCard/FCardAction.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/src/components/FCard/FCardCaption.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/src/components/FCard/FCardContent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/src/components/FCard/FCardImage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
12 |
13 |
14 |
15 |
29 |
30 |
--------------------------------------------------------------------------------
/src/components/FCard/FCardText.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/src/components/FCard/FCardTitle.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/src/components/FCard/index.js:
--------------------------------------------------------------------------------
1 | import FCard from './FCard'
2 | import FCardAction from './FCardAction'
3 | import FCardCaption from './FCardCaption'
4 | import FCardImage from './FCardImage'
5 | import FCardText from './FCardText'
6 | import FCardTitle from './FCardTitle'
7 | import FCardContent from './FCardContent'
8 |
9 | import { use, registerComponent } from '../../utils/helper'
10 |
11 | const Plugin = {
12 | install (Vue) {
13 | registerComponent(Vue, FCard)
14 | }
15 | }
16 |
17 | use(Plugin)
18 |
19 | export default Plugin
20 |
21 | export {
22 | FCard,
23 | FCardAction,
24 | FCardCaption,
25 | FCardImage,
26 | FCardText,
27 | FCardTitle,
28 | FCardContent
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/FCheckbox/FCheckbox.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FCheckbox from './FCheckbox.vue'
3 | import {
4 | createVM
5 | } from 'src/../test/helpers/utils.js'
6 | import {
7 | expect
8 | } from 'chai'
9 |
10 | describe('FCheckbox.vue', function () {
11 | it('should render normal unchecked', function () {
12 | const vm = createVM(this, `
13 |
14 | `, {
15 | components: {
16 | FCheckbox
17 | },
18 | data() {
19 | return {
20 | value: true
21 | }
22 | }
23 | })
24 | expect(vm.$el.querySelectorAll('.f-checkbox input')[0].checked).to.be.equal(false)
25 | })
26 | it('should render normal checked', function () {
27 | const vm = createVM(this, `
28 |
29 | `, {
30 | components: {
31 | FCheckbox
32 | },
33 | data() {
34 | return {
35 | value: true
36 | }
37 | }
38 | })
39 | expect(vm.$el.querySelectorAll('.f-checkbox input')[0].checked).to.be.equal(true)
40 | })
41 | it('should render normal indeterminate', function () {
42 | const vm = createVM(this, `
43 |
44 | `, {
45 | components: {
46 | FCheckbox
47 | },
48 | data() {
49 | return {
50 | value: true
51 | }
52 | }
53 | })
54 | expect(vm.$el.querySelectorAll('.f-checkbox input')[0].indeterminate).to.be.equal(true)
55 | })
56 | })
--------------------------------------------------------------------------------
/src/components/FCheckbox/FCheckbox.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
18 |
19 |
20 |
21 |
58 |
--------------------------------------------------------------------------------
/src/components/FCheckbox/index.js:
--------------------------------------------------------------------------------
1 | import FCheckbox from './FCheckbox'
2 |
3 | import { use, registerComponent } from '../../utils/helper'
4 |
5 | const Plugin = {
6 | install (Vue) {
7 | registerComponent(Vue, FCheckbox)
8 | }
9 | }
10 |
11 | use(Plugin)
12 |
13 | export default Plugin
14 |
15 | export {
16 | FCheckbox
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/FContainer/FContainer.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FContainer from './FContainer'
3 |
4 | import {
5 | createVM
6 | } from 'src/../test/helpers/utils.js'
7 | import {
8 | expect
9 | } from 'chai'
10 |
11 | describe('FContainer.vue', function () {
12 | it('should render container', function () {
13 | const vm = createVM(this, `
14 |
15 | Fluentify Container
16 |
17 | `, {
18 | components: {
19 | FContainer
20 | },
21 | data() {
22 | return {
23 | }
24 | }
25 | })
26 | expect(vm.$el.querySelectorAll('.f-container').length).to.be.equal(1)
27 | })
28 | })
--------------------------------------------------------------------------------
/src/components/FContainer/FContainer.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
30 |
--------------------------------------------------------------------------------
/src/components/FContainer/index.js:
--------------------------------------------------------------------------------
1 | import FContainer from './FContainer'
2 |
3 | import { use, registerComponent } from '../../utils/helper'
4 |
5 | const Plugin = {
6 | install (Vue) {
7 | registerComponent(Vue, FContainer)
8 | }
9 | }
10 |
11 | use(Plugin)
12 |
13 | export default Plugin
14 |
15 | export {
16 | FContainer
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/FGrid/FGrid.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FGrid from './FGrid'
3 | import FGridItem from './FGridItem'
4 |
5 | import {
6 | createVM
7 | } from 'src/../test/helpers/utils.js'
8 | import {
9 | expect
10 | } from 'chai'
11 |
12 | describe('FGrid.vue', function () {
13 | it('should render single line list', function () {
14 | const vm = createVM(this, `
15 |
16 |
17 | Hello
18 |
19 |
20 | `, {
21 | components: {
22 | FGrid,
23 | FGridItem
24 | },
25 | data() {
26 | return {
27 | src: 'https://via.placeholder.com/48',
28 | size: 32
29 | }
30 | }
31 | })
32 | expect(vm.$el.querySelectorAll('.f-grid').length).to.be.equal(1)
33 | })
34 | })
--------------------------------------------------------------------------------
/src/components/FGrid/FGrid.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
35 |
--------------------------------------------------------------------------------
/src/components/FGrid/FGridItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
35 |
--------------------------------------------------------------------------------
/src/components/FGrid/index.js:
--------------------------------------------------------------------------------
1 | import FGrid from './FGrid'
2 | import FGridItem from './FGridItem'
3 |
4 | import { use, registerComponent } from '../../utils/helper'
5 |
6 | const Plugin = {
7 | install (Vue) {
8 | registerComponent(Vue, FGrid),
9 | registerComponent(Vue, FGridItem)
10 | }
11 | }
12 |
13 | use(Plugin)
14 |
15 | export default Plugin
16 |
17 | export {
18 | FGrid,
19 | FGridItem
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/FImg/FImg.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FImg from './FImg'
3 |
4 | import {
5 | createVM
6 | } from 'src/../test/helpers/utils.js'
7 | import {
8 | expect
9 | } from 'chai'
10 |
11 | describe('FImg.vue', function () {
12 | it('should render default use case of component', function () {
13 | const vm = createVM(this, `
14 |
15 | `, {
16 | components: {
17 | FImg
18 | },
19 | data() {
20 | return {
21 | src: 'https://via.placeholder.com/148'
22 | }
23 | }
24 | })
25 | expect(vm.$el.querySelectorAll('.f-img').length).to.be.equal(1)
26 | })
27 | it('should render component with aspect ratio 3/4', function () {
28 | const vm = createVM(this, `
29 |
30 | `, {
31 | components: {
32 | FImg
33 | },
34 | data() {
35 | return {
36 | src: 'https://via.placeholder.com/148'
37 | }
38 | }
39 | })
40 | expect(vm.$el.querySelectorAll('.f-img').length).to.be.equal(1)
41 | })
42 | it('should render component with aspect ratio 3/4 and full width', function () {
43 | const vm = createVM(this, `
44 |
45 | `, {
46 | components: {
47 | FImg
48 | },
49 | data() {
50 | return {
51 | src: 'https://via.placeholder.com/148'
52 | }
53 | }
54 | })
55 | expect(vm.$el.querySelectorAll('.f-img').length).to.be.equal(1)
56 | })
57 | })
--------------------------------------------------------------------------------
/src/components/FImg/FImg.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
![]()
10 |
11 |
12 |
13 |
59 |
--------------------------------------------------------------------------------
/src/components/FImg/index.js:
--------------------------------------------------------------------------------
1 | import FImg from './FImg'
2 |
3 | import {
4 | use,
5 | registerComponent
6 | } from '../../utils/helper'
7 |
8 | const Plugin = {
9 | install(Vue) {
10 | registerComponent(Vue, FImg)
11 | }
12 | }
13 |
14 | use(Plugin)
15 |
16 | export default Plugin
17 |
18 | export {
19 | FImg
20 | }
--------------------------------------------------------------------------------
/src/components/FInput/FInput.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FInput from './FInput.vue'
3 | import {
4 | createVM
5 | } from 'src/../test/helpers/utils.js'
6 | import {
7 | expect
8 | } from 'chai'
9 |
10 | describe('FInput.vue', function () {
11 | it('should render correct contents', function () {
12 | const vm = createVM(this, `
13 | Empty
14 |
15 |
16 |
17 | Has value
18 |
19 |
20 |
21 | Has label
22 |
23 | `, {
24 | components: {
25 | FInput
26 | },
27 | data() {
28 | return {
29 | value: 'Yes it have'
30 | }
31 | }
32 | })
33 | expect(vm.$el.querySelectorAll('.f-input input')[0].value.length).to.be.equal(0)
34 | expect(vm.$el.querySelectorAll('.f-input input')[1].value.length).to.be.equal(11)
35 | })
36 | })
--------------------------------------------------------------------------------
/src/components/FInput/FInput.vue:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
80 |
--------------------------------------------------------------------------------
/src/components/FInput/index.js:
--------------------------------------------------------------------------------
1 | import FInput from './FInput'
2 |
3 | import { use, registerComponent } from '../../utils/helper'
4 |
5 | const Plugin = {
6 | install (Vue) {
7 | registerComponent(Vue, FInput)
8 | }
9 | }
10 |
11 | use(Plugin)
12 |
13 | export default Plugin
14 |
15 | export {
16 | FInput
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/FList/FList.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FList from './FList.vue'
3 | import FListItem from './FListItem.vue'
4 | import FListItemAvatar from './FListItemAvatar.vue'
5 | import FListItemContent from './FListItemContent.vue'
6 | import FAvatar from '../FAvatar/FAvatar.vue'
7 | import {
8 | createVM
9 | } from 'src/../test/helpers/utils.js'
10 | import {
11 | expect
12 | } from 'chai'
13 |
14 | describe('FList.vue', function () {
15 | it('should render single line list', function () {
16 | const vm = createVM(this, `
17 |
18 |
19 |
20 | Single line list item
21 |
22 |
23 |
24 | `, {
25 | components: {
26 | FList,
27 | FListItem,
28 | FListItemContent,
29 | },
30 | data() {
31 | return {
32 | src: 'https://via.placeholder.com/48',
33 | size: 32
34 | }
35 | }
36 | })
37 | expect(vm.$el.querySelectorAll('.f-list').length).to.be.equal(1)
38 | })
39 | it('should render single line with avatar', function () {
40 | const vm = createVM(this, `
41 |
42 |
43 |
44 |
45 |
46 |
47 | List item
48 |
49 |
50 |
51 | `, {
52 | components: {
53 | FList,
54 | FListItem,
55 | FListItemAvatar,
56 | FListItemContent,
57 | FAvatar
58 | },
59 | data() {
60 | return {
61 | src: 'https://via.placeholder.com/48',
62 | size: 32
63 | }
64 | }
65 | })
66 | expect(vm.$el.querySelectorAll('.f-list').length).to.be.equal(1)
67 | })
68 | it('should render two lines list item', function () {
69 | const vm = createVM(this, `
70 |
71 |
72 |
73 | Line one
74 | Line two
75 |
76 |
77 |
78 | `, {
79 | components: {
80 | FList,
81 | FListItem,
82 | FListItemAvatar,
83 | FListItemContent,
84 | FAvatar
85 | },
86 | data() {
87 | return {
88 | src: 'https://via.placeholder.com/48',
89 | size: 32
90 | }
91 | }
92 | })
93 | expect(vm.$el.querySelectorAll('.f-list').length).to.be.equal(1)
94 | })
95 | it('should render two lines list item with avatar', function () {
96 | const vm = createVM(this, `
97 |
98 |
99 |
100 |
101 |
102 |
103 | Line one
104 | Line two
105 |
106 |
107 |
108 | `, {
109 | components: {
110 | FList,
111 | FListItem,
112 | FListItemAvatar,
113 | FListItemContent,
114 | FAvatar
115 | },
116 | data() {
117 | return {
118 | src: 'https://via.placeholder.com/48',
119 | size: 32
120 | }
121 | }
122 | })
123 | expect(vm.$el.querySelectorAll('.f-list').length).to.be.equal(1)
124 | })
125 | it('should render three lines list item', function () {
126 | const vm = createVM(this, `
127 |
128 |
129 |
130 | Line one
131 | Line two
132 | Line three
133 |
134 |
135 |
136 | `, {
137 | components: {
138 | FList,
139 | FListItem,
140 | FListItemAvatar,
141 | FListItemContent,
142 | FAvatar
143 | },
144 | data() {
145 | return {
146 | src: 'https://via.placeholder.com/48',
147 | size: 32
148 | }
149 | }
150 | })
151 | expect(vm.$el.querySelectorAll('.f-list').length).to.be.equal(1)
152 | })
153 | it('should render three lines list item with avatar', function () {
154 | const vm = createVM(this, `
155 |
156 |
157 |
158 |
159 |
160 |
161 | Line one
162 | Line two
163 | Line three
164 |
165 |
166 |
167 | `, {
168 | components: {
169 | FList,
170 | FListItem,
171 | FListItemAvatar,
172 | FListItemContent,
173 | FAvatar
174 | },
175 | data() {
176 | return {
177 | src: 'https://via.placeholder.com/48',
178 | size: 32
179 | }
180 | }
181 | })
182 | expect(vm.$el.querySelectorAll('.f-list').length).to.be.equal(1)
183 | })
184 | })
--------------------------------------------------------------------------------
/src/components/FList/FList.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
27 |
--------------------------------------------------------------------------------
/src/components/FList/FListItem.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FListItem from './FListItem.vue'
3 | import FListItemContent from './FListItemContent.vue'
4 | import FListItemAvatar from './FListItemAvatar.vue'
5 | import { createVM } from 'src/../test/helpers/utils.js'
6 | import { expect } from 'chai'
7 |
8 | describe('FListItem.vue', function () {
9 | it('should render correct contents', function () {
10 | const vm = createVM(this, `
11 |
12 |
13 | H
14 |
15 |
16 | Hello
17 |
18 |
19 | `, { components: { FListItem, FListItemContent, FListItemAvatar }})
20 | expect(vm.$el.querySelectorAll('.f-list-item').length).to.be.equal(1)
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/src/components/FList/FListItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
17 |
18 |
19 |
23 |
24 |
25 |
26 |
27 |
59 |
--------------------------------------------------------------------------------
/src/components/FList/FListItemAvatar.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FListItemAvatar from './FListItemAvatar.vue'
3 | import { FAvatar } from '../FAvatar'
4 | import { createVM } from 'src/../test/helpers/utils.js'
5 | import { expect } from 'chai'
6 |
7 | describe('FListItemAvatar.vue', function () {
8 | it('should render correct contents', function () {
9 | const vm = createVM(this, `
10 |
11 |
12 |
13 | `, { components: { FListItemAvatar, FAvatar }})
14 | expect(vm.$el.querySelectorAll('.f-list-item-avatar').length).to.be.equal(1)
15 | })
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/FList/FListItemAvatar.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
27 |
--------------------------------------------------------------------------------
/src/components/FList/FListItemContent.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FListItemContent from './FListItemContent.vue'
3 | import { createVM } from 'src/../test/helpers/utils.js'
4 | import { expect } from 'chai'
5 |
6 | describe('FListItemContent.vue', function () {
7 | it('should render correct contents', function () {
8 | const vm = createVM(this, `
9 |
10 | Hello
11 |
12 | `, { components: { FListItemContent }})
13 | expect(vm.$el.querySelectorAll('.f-list-item-content').length).to.be.equal(1)
14 | })
15 | })
16 |
--------------------------------------------------------------------------------
/src/components/FList/FListItemContent.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
23 |
--------------------------------------------------------------------------------
/src/components/FList/index.js:
--------------------------------------------------------------------------------
1 | import FList from './FList'
2 | import FListItem from './FListItem'
3 | import FListItemAvatar from './FListItemAvatar'
4 | import FListItemContent from './FListItemContent'
5 |
6 | import { use, registerComponent } from '../../utils/helper'
7 |
8 | const Plugin = {
9 | install (Vue) {
10 | registerComponent(Vue, FList),
11 | registerComponent(Vue, FListItem),
12 | registerComponent(Vue, FListItemAvatar),
13 | registerComponent(Vue, FListItemContent)
14 | }
15 | }
16 |
17 | use(Plugin)
18 |
19 | export default Plugin
20 |
21 | export {
22 | FList,
23 | FListItem,
24 | FListItemAvatar,
25 | FListItemContent
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/FMenu/FMenu.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FMenu from './FMenu.vue'
3 | import FBtn from '../FBtn/FBtn.vue'
4 | import FList from '../FList/FList.vue'
5 | import FListItem from '../FList/FListItem.vue'
6 | import FListItemContent from '../FList/FListItemContent.vue'
7 | import {
8 | createVM
9 | } from 'src/../test/helpers/utils.js'
10 |
11 | describe('FMenu.vue', function () {
12 | it('should render correct contents', function () {
13 | const vm = createVM(this, `
14 |
15 | Dropdown
16 |
17 |
22 |
23 | {{ item }}
24 |
25 |
26 |
27 |
28 | `, {
29 | components: {
30 | FMenu,
31 | FBtn,
32 | FList,
33 | FListItem,
34 | FListItemContent
35 | },
36 | methods: {
37 | testMethod (index) {
38 | alert(`item: ${index}`)
39 | }
40 | }
41 | })
42 | vm.$el.querySelector('.f-menu .f-btn span').textContent.should.eql('Dropdown')
43 | })
44 | })
45 |
--------------------------------------------------------------------------------
/src/components/FMenu/FMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
25 |
100 |
--------------------------------------------------------------------------------
/src/components/FMenu/index.js:
--------------------------------------------------------------------------------
1 | import FMenu from './FMenu'
2 |
3 | import { use, registerComponent } from '../../utils/helper'
4 |
5 | const Plugin = {
6 | install (Vue) {
7 | registerComponent(Vue, FMenu)
8 | }
9 | }
10 |
11 | use(Plugin)
12 |
13 | export default Plugin
14 |
15 | export {
16 | FMenu
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/FProgressLinear/FProgressLinear.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FProgressLinear from './FProgressLinear.vue'
3 | import { createVM } from 'src/../test/helpers/utils.js'
4 | import { expect } from 'chai'
5 |
6 | describe('FProgressLinear.vue', function () {
7 | it('should render correct contents', function () {
8 | const vm = createVM(this, `
9 |
10 | `,
11 | {
12 | components: {
13 | FProgressLinear
14 | },
15 | data () {
16 | return {
17 | value: 20
18 | }
19 | }
20 | }
21 | )
22 | expect(vm.$el.querySelectorAll('.f-progress-linear').length).to.be.equal(1)
23 | })
24 | it('should render progress linear with buffered at 50%', function () {
25 | const vm = createVM(this, `
26 |
27 | `,
28 | {
29 | components: {
30 | FProgressLinear
31 | },
32 | data () {
33 | return {
34 | value: 20,
35 | buffered: 50
36 | }
37 | }
38 | }
39 | )
40 | expect(vm.$el.querySelectorAll('.f-progress-linear').length).to.be.equal(1)
41 | })
42 | it('should render progress linear as indeterminated with altered height', function () {
43 | const vm = createVM(this, `
44 |
45 | `,
46 | {
47 | components: {
48 | FProgressLinear
49 | },
50 | data () {
51 | return {
52 | value: 0
53 | }
54 | }
55 | }
56 | )
57 | expect(vm.$el.querySelectorAll('.f-progress-linear').length).to.be.equal(1)
58 | })
59 | })
60 |
--------------------------------------------------------------------------------
/src/components/FProgressLinear/FProgressLinear.vue:
--------------------------------------------------------------------------------
1 |
2 |
31 |
32 |
33 |
109 |
--------------------------------------------------------------------------------
/src/components/FProgressLinear/index.js:
--------------------------------------------------------------------------------
1 | import FProgressLinear from './FProgressLinear'
2 |
3 | import { use, registerComponent } from '../../utils/helper'
4 |
5 | const Plugin = {
6 | install (Vue) {
7 | registerComponent(Vue, FProgressLinear)
8 | }
9 | }
10 |
11 | use(Plugin)
12 |
13 | export default Plugin
14 |
15 | export {
16 | FProgressLinear
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/FRadio/FRadio.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FRadio from './FRadio.vue'
3 | import {
4 | createVM
5 | } from 'src/../test/helpers/utils.js'
6 | import {
7 | expect
8 | } from 'chai'
9 |
10 | describe('FRadio.vue', function () {
11 | it('should render normal unchecked', function () {
12 | const vm = createVM(this, `
13 |
14 |
15 |
16 | `, {
17 | components: {
18 | FRadio
19 | },
20 | data() {
21 | return {
22 | value: 1
23 | }
24 | }
25 | })
26 | expect(vm.$el.querySelectorAll('.f-radio input')[0].checked).to.be.equal(true)
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/src/components/FRadio/FRadio.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
17 |
18 |
19 |
25 |
26 |
27 |
28 |
109 |
--------------------------------------------------------------------------------
/src/components/FRadio/index.js:
--------------------------------------------------------------------------------
1 | import FRadio from './FRadio'
2 |
3 | import { use, registerComponent } from '../../utils/helper'
4 |
5 | const Plugin = {
6 | install (Vue) {
7 | registerComponent(Vue, FRadio)
8 | }
9 | }
10 |
11 | use(Plugin)
12 |
13 | export default Plugin
14 |
15 | export {
16 | FRadio
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/FRadioGroup/FRadioGroup.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FRadioGroup from './FRadioGroup.vue'
3 | import FRadio from '../FRadio/FRadio.vue'
4 | import {
5 | createVM
6 | } from 'src/../test/helpers/utils.js'
7 | import {
8 | expect
9 | } from 'chai'
10 |
11 | describe('FRadioGroup.vue', function () {
12 | it('should render second as selected', function () {
13 | const vm = createVM(this, `
14 |
15 |
16 |
22 |
26 |
27 |
28 | `, {
29 | components: {
30 | FRadioGroup,
31 | FRadio
32 | },
33 | data() {
34 | return {
35 | value: 2
36 | }
37 | }
38 | })
39 | expect(vm.$el.querySelectorAll('.f-radio input')[1].checked).to.be.equal(true)
40 | })
41 | })
--------------------------------------------------------------------------------
/src/components/FRadioGroup/FRadioGroup.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
65 |
--------------------------------------------------------------------------------
/src/components/FRadioGroup/index.js:
--------------------------------------------------------------------------------
1 | import FRadioGroup from './FRadioGroup'
2 |
3 | import { use, registerComponent } from '../../utils/helper'
4 |
5 | const Plugin = {
6 | install (Vue) {
7 | registerComponent(Vue, FRadioGroup)
8 | }
9 | }
10 |
11 | use(Plugin)
12 |
13 | export default Plugin
14 |
15 | export {
16 | FRadioGroup
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/FSelect/FSelect.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FSelect from './FSelect.vue'
3 | import { createVM } from 'src/../test/helpers/utils.js'
4 | import { expect } from 'chai'
5 |
6 | describe('FSelect.vue', function () {
7 | it('should render correct contents', function () {
8 | const vm = createVM(
9 | this,
10 | '',
11 | {
12 | components: {
13 | FSelect
14 | },
15 | data () {
16 | return {
17 | itemsArrayOfString: ['Hello', 'Nice', 'To', 'Meet', 'You']
18 | }
19 | }
20 | }
21 | )
22 |
23 | expect(vm.$el.querySelectorAll('.f-select select').length).to.be.equal(1)
24 | })
25 | })
26 |
--------------------------------------------------------------------------------
/src/components/FSelect/FSelect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 |
16 |
46 |
--------------------------------------------------------------------------------
/src/components/FSelect/index.js:
--------------------------------------------------------------------------------
1 | import FSelect from './FSelect'
2 |
3 | import { use, registerComponent } from '../../utils/helper'
4 |
5 | const Plugin = {
6 | install (Vue) {
7 | registerComponent(Vue, FSelect)
8 | }
9 | }
10 |
11 | use(Plugin)
12 |
13 | export default Plugin
14 |
15 | export {
16 | FSelect
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/FSlider/FSlider.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FSlider from './FSlider.vue'
3 | import {
4 | createVM
5 | } from 'src/../test/helpers/utils.js'
6 | import {
7 | expect
8 | } from 'chai'
9 |
10 | describe('FSlider.vue', function () {
11 | it('should render correct contents', function () {
12 | const vm = createVM(this, `
13 |
14 | `, {
15 | components: {
16 | FSlider
17 | },
18 | data() {
19 | return {
20 | value: 300
21 | }
22 | }
23 | })
24 | expect(vm.$el.querySelectorAll('.f-slider').length).to.be.equal(1)
25 | })
26 | it('should render slider as vertical', function () {
27 | const vm = createVM(this, `
28 |
29 | `, {
30 | components: {
31 | FSlider
32 | },
33 | data() {
34 | return {
35 | value: 300
36 | }
37 | }
38 | })
39 | expect(vm.$el.querySelectorAll('.f-slider').length).to.be.equal(1)
40 | })
41 | })
--------------------------------------------------------------------------------
/src/components/FSlider/FSlider.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
29 |
30 |
31 |
32 |
162 |
--------------------------------------------------------------------------------
/src/components/FSlider/index.js:
--------------------------------------------------------------------------------
1 | import FSlider from './FSlider'
2 |
3 | import { use, registerComponent } from '../../utils/helper'
4 |
5 | const Plugin = {
6 | install (Vue) {
7 | registerComponent(Vue, FSlider)
8 | }
9 | }
10 |
11 | use(Plugin)
12 |
13 | export default Plugin
14 |
15 | export {
16 | FSlider
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/FSwitch/FSwitch.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FSwitch from './FSwitch.vue'
3 | import {
4 | createVM
5 | } from 'src/../test/helpers/utils.js'
6 | import {
7 | expect
8 | } from 'chai'
9 |
10 | describe('FSwitch.vue', function () {
11 | it('should render normal unchecked', function () {
12 | const vm = createVM(this, `
13 |
14 | `, {
15 | components: {
16 | FSwitch
17 | },
18 | data() {
19 | return {
20 | value: true
21 | }
22 | }
23 | })
24 | expect(vm.$el.querySelectorAll('.f-switch input')[0].checked).to.be.equal(false)
25 | })
26 | it('should render normal checked', function () {
27 | const vm = createVM(this, `
28 |
29 | `, {
30 | components: {
31 | FSwitch
32 | },
33 | data() {
34 | return {
35 | value: true
36 | }
37 | }
38 | })
39 | expect(vm.$el.querySelectorAll('.f-switch input')[0].checked).to.be.equal(true)
40 | })
41 | it('should render normal indeterminate', function () {
42 | const vm = createVM(this, `
43 |
44 | `, {
45 | components: {
46 | FSwitch
47 | },
48 | data() {
49 | return {
50 | value: true
51 | }
52 | }
53 | })
54 | expect(vm.$el.querySelectorAll('.f-switch input')[0].indeterminate).to.be.equal(true)
55 | })
56 | })
--------------------------------------------------------------------------------
/src/components/FSwitch/FSwitch.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
24 |
25 |
26 |
27 |
93 |
--------------------------------------------------------------------------------
/src/components/FSwitch/index.js:
--------------------------------------------------------------------------------
1 | import FSwitch from './FSwitch'
2 |
3 | import { use, registerComponent } from '../../utils/helper'
4 |
5 | const Plugin = {
6 | install (Vue) {
7 | registerComponent(Vue, FSwitch)
8 | }
9 | }
10 |
11 | use(Plugin)
12 |
13 | export default Plugin
14 |
15 | export {
16 | FSwitch
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/FTextarea/FTextarea.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import FTextarea from './FTextarea.vue'
3 | import {
4 | createVM
5 | } from 'src/../test/helpers/utils.js'
6 | import {
7 | expect
8 | } from 'chai'
9 |
10 | describe('FTextarea.vue', function () {
11 | it('should render correct contents', function () {
12 | const vm = createVM(this, `
13 | Empty
14 |
15 |
16 |
17 | Has value
18 |
19 |
20 |
21 | Has label
22 |
23 | `, {
24 | components: {
25 | FTextarea
26 | },
27 | data() {
28 | return {
29 | value: 'Yes it have'
30 | }
31 | }
32 | })
33 | expect(vm.$el.querySelectorAll('.f-textarea textarea')[0].value.length).to.be.equal(0)
34 | expect(vm.$el.querySelectorAll('.f-textarea textarea')[1].value.length).to.be.equal(11)
35 | })
36 | })
--------------------------------------------------------------------------------
/src/components/FTextarea/FTextarea.vue:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
22 |
83 |
--------------------------------------------------------------------------------
/src/components/FTextarea/index.js:
--------------------------------------------------------------------------------
1 | import FTextarea from './FTextarea'
2 |
3 | import { use, registerComponent } from '../../utils/helper'
4 |
5 | const Plugin = {
6 | install (Vue) {
7 | registerComponent(Vue, FTextarea)
8 | }
9 | }
10 |
11 | use(Plugin)
12 |
13 | export default Plugin
14 |
15 | export {
16 | FTextarea
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | import FContainer from './FContainer'
2 | import FGrid from './FContainer'
3 | import FBtn from './FBtn'
4 | import FRadio from './FRadio'
5 | import FRadioGroup from './FRadioGroup'
6 | import FCheckbox from './FCheckbox'
7 | import FSlider from './FSlider'
8 | import FSelect from './FSelect'
9 | import FAvatar from './FAvatar'
10 | import FInput from './FInput'
11 | import FAutocomplete from './FAutocomplete'
12 | import FList from './FList'
13 | import FMenu from './FMenu'
14 | import FProgressLinear from './FProgressLinear'
15 | import FImg from './FImg'
16 |
17 | export {
18 | FContainer,
19 | FGrid,
20 | FBtn,
21 | FRadio,
22 | FRadioGroup,
23 | FCheckbox,
24 | FSlider,
25 | FSelect,
26 | FAvatar,
27 | FInput,
28 | FAutocomplete,
29 | FList,
30 | FMenu,
31 | FProgressLinear,
32 | FImg
33 | }
34 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import './scss/fluentify.scss'
2 |
3 | import * as components from './components'
4 |
5 | import config, {
6 | setOptions
7 | } from './utils/config'
8 | import {
9 | use,
10 | registerComponentProgrammatic
11 | } from './utils/helper'
12 |
13 | const Fluentify = {
14 | install (Vue, options = {}) {
15 | // Options
16 | setOptions(Object.assign(config, options))
17 | // Components
18 | for (const componentKey in components) {
19 | Vue.use(components[componentKey])
20 | }
21 | // Config component
22 | const FluentifyProgrammatic = {
23 | setOptions (options) {
24 | setOptions(Object.assign(config, options))
25 | }
26 | }
27 | registerComponentProgrammatic(Vue, '$fluentify', FluentifyProgrammatic)
28 | }
29 | }
30 |
31 | use(Fluentify)
32 |
33 | export default Fluentify
34 |
--------------------------------------------------------------------------------
/src/mixins/grid.js:
--------------------------------------------------------------------------------
1 | const gridItemMixin = {
2 | props: {
3 | columnStart: {
4 | type: [String, Number]
5 | },
6 | columnEnd: {
7 | type: [String, Number]
8 | },
9 | rowStart: {
10 | type: [String, Number]
11 | },
12 | rowEnd: {
13 | type: [String, Number]
14 | },
15 | },
16 | data() {
17 | return {}
18 | },
19 | created () {},
20 | methods: {},
21 | computed: {}
22 | }
23 |
24 | const gridUtilsMixin = {
25 | props: {
26 | gridFlow: {
27 | type: [String]
28 | }
29 | },
30 | data() {
31 | return {}
32 | },
33 | created () {},
34 | methods: {}
35 | }
36 |
37 | export { gridItemMixin, gridUtilsMixin }
38 |
--------------------------------------------------------------------------------
/src/mixins/helper.js:
--------------------------------------------------------------------------------
1 | var helper = {
2 | methods: {
3 | resolveStringPath (path, obj = window.self, separator = '.') {
4 | var properties = Array.isArray(path) ? path : path.split(separator)
5 | return properties.reduce((prev, curr) => prev && prev[curr], obj)
6 | }
7 | }
8 | }
9 |
10 | export default helper
11 |
--------------------------------------------------------------------------------
/src/scss/_variables.scss:
--------------------------------------------------------------------------------
1 | $container-max-widths: (
2 | sm: 540px,
3 | md: 720px,
4 | lg: 960px,
5 | xl: 1140px
6 | );
7 |
8 | $grid-columns: 12;
9 | $grid-gutter-width: 30px;
10 |
11 | $grid-breakpoints: (
12 | xs: 0,
13 | sm: 576px,
14 | md: 768px,
15 | lg: 992px,
16 | xl: 1200px
17 | );
--------------------------------------------------------------------------------
/src/scss/components/_FAvatar.scss:
--------------------------------------------------------------------------------
1 | .f-avatar {
2 | position: relative;
3 | text-align: center;
4 | vertical-align: middle;
5 | border-radius: 100%;
6 | display: inline-block;
7 | display: inline-flex;
8 | align-items: center;
9 | justify-content: center;
10 | background-color: $ChromeHigh;
11 | color: $ChromeWhite;
12 | line-height: 36px;
13 |
14 | img {
15 | border-radius: 100%;
16 | display: block;
17 | }
18 |
19 | &.f-avatar--tile, &.f-avatar--tile img {
20 | border-radius: 0;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/scss/components/_FBtn.scss:
--------------------------------------------------------------------------------
1 | .f-btn {
2 | font-size: 15px;
3 | padding: 10px 20px 10px 22px;
4 | font-weight: 600;
5 | max-width: 100%;
6 | display: inline-block;
7 | display: inline-flex;
8 | overflow: hidden;
9 | transition: all 200ms ease-in-out;
10 | box-shadow: 0 4px 8px 0 transparent;
11 | white-space: nowrap;
12 | color: $BaseHigh;
13 | text-align: center;
14 | text-decoration: none;
15 | line-height: 1.3;
16 | letter-spacing: 0;
17 | border: 2px solid transparent;
18 | position: relative;
19 | outline: 1px solid transparent;
20 | outline-offset: -3px;
21 | vertical-align: middle;
22 |
23 | &:active {
24 | -ms-transform: scale(.98);
25 | -webkit-transform: scale(.98);
26 | transform: scale(.98);
27 | }
28 |
29 | &:focus {
30 | border-color: $BaseHigh;
31 | outline: 2px solid $BaseHigh;
32 | }
33 |
34 | &:hover {
35 | span {
36 | box-shadow: 0 2px currentColor;
37 | }
38 | }
39 |
40 | span {
41 | max-width: 100%;
42 | display: inline-block;
43 | position: relative;
44 | left: 0;
45 | text-overflow: clip;
46 | overflow: hidden;
47 | transition: all 200ms ease-in-out;
48 |
49 | &:before {
50 | content: "";
51 | display: block;
52 | height: 1px;
53 | position: absolute;
54 | bottom: -1px;
55 | width: 100%;
56 | left: 0;
57 | }
58 | }
59 |
60 | &.f-btn-primary {
61 | background: $Accent;
62 | color: $ChromeWhite;
63 |
64 | &:hover, &:focus {
65 | background: $AccentDark1;
66 | }
67 |
68 | &:active {
69 | background: $AccentDark2;
70 | }
71 | }
72 |
73 | &.f-btn-secondary {
74 | background: $Secondary;
75 | color: $ChromeWhite;
76 |
77 | &:hover, &:focus {
78 | background: $SecondaryDark1;
79 | }
80 |
81 | &:active {
82 | background: $SecondaryDark2;
83 | }
84 | }
85 |
86 | &.f-btn--flat {
87 | background-color: transparent;
88 | }
89 | }
90 |
91 | [aria-expanded] {
92 | .f-btn {
93 | color: $ChromeWhite;
94 | background-color: $Accent !important;
95 | border-radius: 2px;
96 | transition: all 0.2s ease-in-out 0s;
97 | }
98 |
99 | &:focus {
100 | outline: none;
101 | }
102 | }
103 |
104 | [aria-expanded="true"] {
105 | .f-btn {
106 | text-decoration: none;
107 | outline: none;
108 | background: $SecondaryDark3 !important;
109 | }
110 | }
--------------------------------------------------------------------------------
/src/scss/components/_FCard.scss:
--------------------------------------------------------------------------------
1 | .f-card {
2 | box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 4px, rgba(0, 0, 0, 0.1) 0px 0.5px 1px;
3 | background-color: #FFFFFF;
4 | position: relative;
5 | backface-visibility: hidden;
6 | color: #000000;
7 | transition: all 0.333s ease-out 0s;
8 | border-radius: 4px;
9 | backface-visibility: hidden;
10 | display: grid;
11 | text-decoration: none;
12 | padding: 12px 24px 24px;
13 | grid-template-columns: 50%;
14 |
15 | &[href] {
16 | cursor: pointer;
17 |
18 | &:hover {
19 | transform: translate3d(0px, -1px, 0px);
20 | box-shadow: 0px 19px 43px rgba(0,0,0,0.22), 0px 4px 11px rgba(0,0,0,0.18);
21 | }
22 | }
23 |
24 | .f-card-caption {
25 | border-bottom: 1px solid rgba(115, 115, 115, 0.1);
26 | padding: 12px;
27 | letter-spacing: 0.4em;
28 | color: #505050;
29 | font-size: 11px;
30 | font-weight: 400;
31 | line-height: 16px;
32 | grid-column: 1 / 7;
33 | margin: -12px -24px 12px;
34 | @media (min-width: 540px) {
35 | padding: 16px 24px 15px;
36 | }
37 | }
38 |
39 | .f-card-title {
40 | font-size: 21px;
41 | font-weight: 600;
42 | line-height: 32px;
43 | }
44 |
45 | .f-card-text {
46 | font-size: 16px;
47 | font-weight: 400;
48 | line-height: 24px;
49 | }
50 |
51 | &>div:not(.f-card-action) {
52 | //grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
53 | // grid-template-rows: auto auto auto minmax(min-content, 1fr) auto;
54 | grid-template-rows: 40px auto auto;
55 | grid-gap: 0px 24px;
56 | grid-column: 1 / 4;
57 | grid-row: 2 / 3;
58 | display: grid;
59 |
60 | >* {
61 | grid-column: 1 / 4;
62 | }
63 | }
64 |
65 | .f-card-image {
66 | grid-column: 4 / 7;
67 | grid-row: 2 / 3;
68 | position: relative;
69 | top: -41px;
70 | margin-bottom: -41px;
71 | width: 100%;
72 | right: 0px;
73 | display: block;
74 |
75 | img {
76 | border: 0;
77 | border-radius: 4px;
78 | box-shadow: 0px 2px 4px rgba(0,0,0,0.06), 0px 0.5px 1px;
79 | display: block;
80 | margin: 0;
81 | width: 100%;
82 | max-width: 100%;
83 | height: auto;
84 | }
85 | }
86 |
87 | .f-card-action {
88 | align-self: end;
89 | }
90 |
91 | &.f-card-layout-featured {
92 | .f-card-action {
93 | grid-row: 3 / 3;
94 | grid-column: 1 / 7;
95 | padding: 12px 24px 24px;
96 | }
97 |
98 | .f-card-image {
99 | margin-top: 48px;
100 | grid-column: 1 / 7;
101 | grid-row: 3 / 3;
102 | top: 0;
103 | margin-bottom: 0;
104 | }
105 | }
106 |
107 | &.f-card-layout-fluid {
108 | padding: 0;
109 |
110 | .f-card-caption {
111 | margin: 0;
112 | }
113 |
114 | &>div:not(.f-card-action) {
115 | grid-column: 1 / 7;
116 | margin: 0;
117 | }
118 |
119 | .f-card-content {
120 | padding: 12px 24px 24px;
121 | }
122 |
123 | .f-card-title {
124 | grid-row: 1 / 1;
125 | grid-column: 1 / 3;
126 | }
127 |
128 | .f-card-text {
129 | grid-row: 1 / 1;
130 | grid-column: 3 / 7;
131 | padding: 24px 12px 0;
132 | }
133 |
134 | .f-card-action {
135 | grid-row: 3 / 3;
136 | grid-column: 1 / 7;
137 | padding: 12px 24px 24px;
138 | }
139 |
140 | .f-card-image {
141 | margin-top: 48px;
142 | grid-column: 1 / 7;
143 | grid-row: 3 / 3;
144 | top: 0;
145 | margin-bottom: 0;
146 |
147 | img {
148 | box-shadow: none;
149 | border-radius: 0;
150 | }
151 | }
152 | }
153 | }
--------------------------------------------------------------------------------
/src/scss/components/_FCheckbox.scss:
--------------------------------------------------------------------------------
1 | .f-checkbox {
2 | display: inline-block;
3 | user-select: none;
4 | -webkit-user-select: none;
5 | -ms-user-select: none;
6 | -webkit-touch-callout: none;
7 | -o-user-select: none;
8 | -moz-user-select: none;
9 |
10 | .f-checkbox--selection {
11 | position: relative;
12 | display: inline-block;
13 |
14 | input {
15 | height: 20px;
16 | width: 20px;
17 | line-height: 20px;
18 | border: 2px solid $BaseHigh;
19 | background-color: $ChromeWhite;
20 | box-sizing: border-box;
21 | font-size: 15px;
22 | font-weight: 500;
23 | -webkit-box-shadow: none;
24 | -moz-box-shadow: none;
25 | box-shadow: none;
26 | -webkit-appearance: none;
27 | opacity: 0;
28 | vertical-align: top;
29 | display: inline-block;
30 |
31 | &:focus {
32 | outline: none;
33 | }
34 |
35 | &+ span {
36 | border: 2px solid $BaseHigh;
37 | background-color: $ChromeWhite;
38 | height: 20px;
39 | width: 20px;
40 | position: absolute;
41 | top: 50%;
42 | left: 50%;
43 | transform: translate(-50%, -50%);
44 | cursor: pointer;
45 | line-height: 16px;
46 | text-align: center;
47 | }
48 |
49 | &:checked {
50 | &+ span {
51 | border-color: $Accent;
52 | background-color: $Accent;
53 | color: $ChromeWhite;
54 |
55 | &:before, &:after {
56 | position: absolute;
57 | z-index: 1;
58 | content: "";
59 | display: block;
60 | background-color: $ChromeWhite;
61 | }
62 |
63 | &:before {
64 | height: 1.5px;
65 | width: 7px;
66 | transform: rotate(45deg);
67 | top: 8px;
68 | left: 0px;
69 | }
70 |
71 | &:after {
72 | height: 1.5px;
73 | width: 13px;
74 | transform: rotate(315deg);
75 | top: 7px;
76 | left: 4px;
77 | }
78 | }
79 | }
80 |
81 | &:indeterminate {
82 | &+ span {
83 | border-color: $Accent;
84 | background-color: $Accent;
85 | color: $ChromeWhite;
86 |
87 | &:before, &:after {
88 | position: absolute;
89 | z-index: 1;
90 | content: "";
91 | display: block;
92 | background-color: $ChromeWhite;
93 | height: 1.5px;
94 | width: 14px;
95 | top: 8px;
96 | left: 1px;
97 | transform: translate(0%, -50%);
98 | }
99 | }
100 | }
101 |
102 | &:disabled {
103 | &+ span {
104 | border-color: $BaseMediumLow;
105 | background-color: $ChromeWhite;
106 | color: $BaseMediumLow;
107 |
108 | &:before, &:after {
109 | background-color: $BaseMediumLow;
110 | }
111 |
112 | &+ label {
113 | color: $BaseMediumLow;
114 | }
115 | }
116 | }
117 | }
118 |
119 | &+ label {
120 | color: $BaseHigh;
121 | display: inline-block;
122 | cursor: pointer;
123 | font-size: 15px;
124 | }
125 |
126 | &:disabled {
127 | &+ label {
128 | color: $BaseMediumLow;
129 | }
130 | }
131 | }
132 | }
--------------------------------------------------------------------------------
/src/scss/components/_FContainer.scss:
--------------------------------------------------------------------------------
1 | .f-container {
2 | width: 100%;
3 | padding-right: $grid-gutter-width / 2;
4 | padding-left: $grid-gutter-width / 2;
5 | margin-right: auto;
6 | margin-left: auto;
7 |
8 | @each $breakpoint, $container-max-width in $container-max-widths {
9 | @include media-breakpoint-up($breakpoint, $grid-breakpoints) {
10 | max-width: $container-max-width;
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/scss/components/_FGrid.scss:
--------------------------------------------------------------------------------
1 | .f-grid {
2 | display: grid;
3 | grid-template-columns: repeat($grid-columns, 1fr);
4 | grid-template-rows: auto;
5 | grid-column-gap: $grid-gutter-width / 2;
6 | margin-right: -$grid-gutter-width / 2;
7 | margin-left: -$grid-gutter-width / 2;
8 | }
9 |
10 | .f-grid-item {}
--------------------------------------------------------------------------------
/src/scss/components/_FImg.scss:
--------------------------------------------------------------------------------
1 | .f-img {
2 | display: inline-block;
3 |
4 | img {
5 | display: block;
6 | }
7 |
8 | &[style*="--aspect-ratio"]> :first-child {
9 | width: 100%;
10 | }
11 |
12 | &[style*="--aspect-ratio"]>img {
13 | height: auto;
14 | }
15 |
16 | @supports (--custom:property) {
17 | &[style*="--aspect-ratio"] {
18 | position: relative;
19 | }
20 |
21 | &[style*="--aspect-ratio"]::before {
22 | content: "";
23 | display: block;
24 | padding-bottom: calc(100% / (var(--aspect-ratio)));
25 | }
26 |
27 | &[style*="--aspect-ratio"]> :first-child {
28 | position: absolute;
29 | top: 50%;
30 | left: 50%;
31 | transform: translate(-50%, -50%)
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/scss/components/_FInput.scss:
--------------------------------------------------------------------------------
1 | .f-input {
2 | display: inline-block;
3 |
4 | span {
5 | display: block;
6 | margin-bottom: 10px;
7 | font-size: 15px;
8 | user-select: none;
9 | -webkit-user-select: none;
10 | -ms-user-select: none;
11 | -webkit-touch-callout: none;
12 | -o-user-select: none;
13 | -moz-user-select: none;
14 | }
15 |
16 | input {
17 | height: 36px;
18 | line-height: 28px;
19 | border: 2px solid $BaseMediumLow;
20 | min-width: 296px;
21 | padding: 8px 11px;
22 | box-sizing: border-box;
23 | font-size: 15px;
24 | font-weight: 500;
25 | -webkit-box-shadow: none;
26 | -moz-box-shadow: none;
27 | box-shadow: none;
28 | -webkit-appearance: none;
29 | width: 100%;
30 |
31 | &:focus {
32 | outline: none;
33 | border-color: $Accent;
34 | }
35 | }
36 |
37 | &.f-input--block {
38 | width: 100%;
39 | }
40 | }
--------------------------------------------------------------------------------
/src/scss/components/_FList.scss:
--------------------------------------------------------------------------------
1 | .f-list {
2 | border-width: initial;
3 | border-style: none;
4 | border-color: initial;
5 | border-image: initial;
6 |
7 | .f-list-item {
8 | position: relative;
9 | padding: 0;
10 | margin: 0;
11 | background-clip: padding-box;
12 | display: flex;
13 | line-height: 44px;
14 | align-items: center;
15 |
16 | .f-list-item-avatar {
17 | display: inline-flex;
18 | flex: 0 1;
19 | outline: 0;
20 | white-space: normal;
21 | padding: 5px;
22 | align-self: flex-start;
23 | }
24 |
25 | .f-list-item-content {
26 | display: inline-flex;
27 | flex: 1;
28 | outline: 0;
29 | white-space: normal;
30 | font-size: 15px;
31 | font-weight: 500;
32 | padding: 5px 10px;
33 | line-height: normal;
34 | }
35 | }
36 |
37 | a.f-list-item {
38 | cursor: pointer;
39 | background: $ListLow;
40 |
41 | &:hover {
42 | background: $ListMedium;
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/scss/components/_FMenu.scss:
--------------------------------------------------------------------------------
1 | .f-menu {
2 | .f-menu-activator {
3 | display: inline-block;
4 | }
5 |
6 | [aria-expanded="false"]+ul {
7 | display: none;
8 | }
9 |
10 | .f-menu-content {
11 | text-align: left;
12 | box-shadow: $BaseMediumLow 0px 19px 43px, $BaseLow 0px 4px 11px;
13 | transform: translateY(0px);
14 | position: absolute;
15 | border-radius: 0 2px 2px 2px;
16 | z-index: 500;
17 | color: $BaseHigh;
18 | cursor: default;
19 | min-width: 64px;
20 | max-width: 368px;
21 | background: $ChromeMediumLow;
22 | width: 240px;
23 | }
24 |
25 | &.f-menu-top {
26 | .f-menu-content {
27 | bottom: 100%;
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/scss/components/_FProgressLinear.scss:
--------------------------------------------------------------------------------
1 | .f-progress-linear {
2 | display: block;
3 | width: 100%;
4 | margin: 14px 0;
5 | position: relative;
6 |
7 | .f-progress-linear--track {
8 | background-color: $BaseMediumLow;
9 | width: 100%;
10 | position: absolute;
11 | top: 0;
12 | left: 0;
13 | bottom: 0;
14 | transition: 0.3s ease-in;
15 | overflow: hidden;
16 |
17 | span {
18 | display: inline-block;
19 | position: absolute;
20 | width: 4px;
21 | height: 4px;
22 | background-color: $Accent;
23 | border-radius: 100%;
24 | }
25 |
26 | span {
27 | -webkit-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) infinite;
28 | -moz-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) infinite;
29 | -ms-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) infinite;
30 | -o-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) infinite;
31 | animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) infinite;
32 | }
33 |
34 | span:nth-child(2) {
35 | -webkit-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 100ms infinite;
36 | -moz-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 100ms infinite;
37 | -ms-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 100ms infinite;
38 | -o-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 100ms infinite;
39 | animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 100ms infinite;
40 | }
41 |
42 | span:nth-child(3) {
43 | -webkit-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 200ms infinite;
44 | -moz-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 200ms infinite;
45 | -ms-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 200ms infinite;
46 | -o-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 200ms infinite;
47 | animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 200ms infinite;
48 | }
49 |
50 | span:nth-child(4) {
51 | -webkit-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 300ms infinite;
52 | -moz-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 300ms infinite;
53 | -ms-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 300ms infinite;
54 | -o-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 300ms infinite;
55 | animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 300ms infinite;
56 | }
57 |
58 | span:nth-child(5) {
59 | -webkit-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 400ms infinite;
60 | -moz-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 400ms infinite;
61 | -ms-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 400ms infinite;
62 | -o-animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 400ms infinite;
63 | animation: move 2.5s cubic-bezier(0.2, 0.64, 0.81, 0.23) 400ms infinite;
64 | }
65 | }
66 |
67 | .f-progress-linear--progress {
68 | background-color: $Accent;
69 | width: 0;
70 | height: inherit;
71 | transition: 0.3s ease-in;
72 | position: relative;
73 | }
74 |
75 | &.f-progress-linear--indeterminate {
76 | .f-progress-linear--track {
77 | background-color: transparent;
78 | }
79 | }
80 | }
81 |
82 | @-webkit-keyframes move {
83 | 0% {
84 | left: -4px;
85 | background-color: transparent;
86 | }
87 |
88 | 25% {
89 | background-color: $Accent;
90 | }
91 |
92 | 75% {
93 | background-color: $Accent;
94 | }
95 |
96 | 100% {
97 | left: calc(100% + 4px);
98 | background-color: transparent;
99 | }
100 | }
101 | @-moz-keyframes move {
102 | 0% {
103 | left: -4px;
104 | background-color: transparent;
105 | }
106 |
107 | 25% {
108 | background-color: $Accent;
109 | }
110 |
111 | 75% {
112 | background-color: $Accent;
113 | }
114 |
115 | 100% {
116 | left: calc(100% + 4px);
117 | background-color: transparent;
118 | }
119 | }
120 | @-ms-keyframes move {
121 | 0% {
122 | left: -4px;
123 | background-color: transparent;
124 | }
125 |
126 | 25% {
127 | background-color: $Accent;
128 | }
129 |
130 | 75% {
131 | background-color: $Accent;
132 | }
133 |
134 | 100% {
135 | left: calc(100% + 4px);
136 | background-color: transparent;
137 | }
138 | }
139 | @-o-keyframes move {
140 | 0% {
141 | left: -4px;
142 | background-color: transparent;
143 | }
144 |
145 | 25% {
146 | background-color: $Accent;
147 | }
148 |
149 | 75% {
150 | background-color: $Accent;
151 | }
152 |
153 | 100% {
154 | left: calc(100% + 4px);
155 | background-color: transparent;
156 | }
157 | }
158 | @keyframes move {
159 | 0% {
160 | left: -4px;
161 | background-color: transparent;
162 | }
163 |
164 | 25% {
165 | background-color: $Accent;
166 | }
167 |
168 | 75% {
169 | background-color: $Accent;
170 | }
171 |
172 | 100% {
173 | left: 100%;
174 | background-color: transparent;
175 | }
176 | }
--------------------------------------------------------------------------------
/src/scss/components/_FRadio.scss:
--------------------------------------------------------------------------------
1 | .f-radio {
2 | display: inline-block;
3 | user-select: none;
4 | -webkit-user-select: none;
5 | -ms-user-select: none;
6 | -webkit-touch-callout: none;
7 | -o-user-select: none;
8 | -moz-user-select: none;
9 | margin-right: 10px;
10 | line-height: normal;
11 | vertical-align: top;
12 |
13 | .f-radio--selection {
14 | position: relative;
15 | display: table-cell;
16 | vertical-align: top;
17 |
18 | input {
19 | height: 20px;
20 | width: 20px;
21 | line-height: 20px;
22 | box-sizing: border-box;
23 | font-size: 15px;
24 | font-weight: 500;
25 | -webkit-box-shadow: none;
26 | -moz-box-shadow: none;
27 | box-shadow: none;
28 | -webkit-appearance: none;
29 | opacity: 0;
30 | vertical-align: top;
31 | display: inline-block;
32 |
33 | &:focus {
34 | outline: none;
35 | }
36 |
37 | &+ span {
38 | border: 2px solid $BaseHigh;
39 | background-color: transparent;
40 | height: 20px;
41 | width: 20px;
42 | position: absolute;
43 | top: 0;
44 | left: 50%;
45 | transform: translate(-50%, 0);
46 | cursor: pointer;
47 | line-height: 16px;
48 | text-align: center;
49 | border-radius: 100%
50 | }
51 |
52 | &:checked {
53 | &+ span {
54 | border-color: $Accent;
55 | background-color: transparent;
56 | color: $ChromeWhite;
57 |
58 | &:after {
59 | position: absolute;
60 | z-index: 1;
61 | content: "";
62 | display: block;
63 | background-color: $BaseHigh;
64 | width: 10px;
65 | height: 10px;
66 | top: 3px;
67 | left: 3px;
68 | border-radius: 100%
69 | }
70 | }
71 | }
72 |
73 | &:disabled {
74 | &+ span {
75 | border-color: $BaseMediumLow;
76 | background-color: $ChromeWhite;
77 | color: $BaseMediumLow;
78 |
79 | &:before, &:after {
80 | background-color: $BaseMediumLow;
81 | }
82 |
83 | &+ label {
84 | color: $BaseMediumLow;
85 | }
86 | }
87 | }
88 | }
89 |
90 | &+ label {
91 | color: $BaseHigh;
92 | display: table-cell;
93 | cursor: pointer;
94 | vertical-align: top;
95 | line-height: 18px;
96 | padding-left: 8px;
97 | font-size: 15px;
98 | }
99 | }
100 |
101 | &.f-radio--disabled {
102 | .f-radio--selection {
103 | &+ label {
104 | color: $BaseMediumLow;
105 | }
106 | }
107 | }
108 | }
--------------------------------------------------------------------------------
/src/scss/components/_FRadioGroup.scss:
--------------------------------------------------------------------------------
1 | .f-radio-group {
2 | &>label {
3 | margin-bottom: 20px;
4 | display: inline-block;
5 | font-size: 15px;
6 | }
7 |
8 | .f-radio-group--wrap {
9 | &.f-radio-group--wrap__vertical {
10 | .f-radio {
11 | display: block;
12 | margin-bottom: 24px;
13 | margin-right: 0;
14 | }
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/scss/components/_FSelect.scss:
--------------------------------------------------------------------------------
1 | .f-select {
2 | display: inline-block;
3 |
4 | span {
5 | display: block;
6 | margin-bottom: 10px;
7 | }
8 |
9 | select {
10 | height: 36px;
11 | line-height: 28px;
12 | border: 2px solid $BaseMediumLow;
13 | min-width: 296px;
14 | padding: 8px 11px;
15 | box-sizing: border-box;
16 | font-size: 15px;
17 | font-weight: 500;
18 | -webkit-box-shadow: none;
19 | -moz-box-shadow: none;
20 | box-shadow: none;
21 | -webkit-appearance: none;
22 |
23 | &:focus {
24 | outline: none;
25 | border-color: $Accent;
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/src/scss/components/_FSlider.scss:
--------------------------------------------------------------------------------
1 | .f-slider {
2 | margin: 10px 0;
3 | user-select: none;
4 | -webkit-user-select: none;
5 | -ms-user-select: none;
6 | -webkit-touch-callout: none;
7 | -o-user-select: none;
8 | -moz-user-select: none;
9 |
10 | label {
11 | display: block;
12 | margin-bottom: 10px;
13 | font-size: 15px;
14 | }
15 |
16 | .f-slider-wrap {
17 | width: auto;
18 | padding: 18px 0;
19 |
20 | .f-slider-bar {
21 | height: 2px;
22 | position: relative;
23 | display: block;
24 | border-radius: 15px;
25 | background-color: $BaseMediumLow;
26 |
27 | .f-slider-bar__handle {
28 | width: 8px;
29 | height: 24px;
30 | top: 50%;
31 | position: absolute;
32 | border-radius: 12px;
33 | background-color: $Accent;
34 | transition: all 0s;
35 | will-change: transform;
36 | cursor: pointer;
37 | z-index: 3;
38 | transition-duration: 0s;
39 | left: 0px;
40 | transform: translate(-50%, -50%);
41 | }
42 |
43 | .f-slider-bar__process {
44 | background-color: $Accent;
45 | width: 0px;
46 | height: 2px;
47 | }
48 | }
49 | }
50 |
51 | &.f-slider--vertical {
52 | height: 300px;
53 | width: 38px;
54 | margin: 0 10px;
55 |
56 | .f-slider-wrap {
57 | height: 100%;
58 | padding: 0 18px;
59 |
60 | .f-slider-bar {
61 | width: 2px;
62 | height: 100%;
63 | position: relative;
64 | display: block;
65 | border-radius: 15px;
66 | background-color: $BaseMediumLow;
67 |
68 | .f-slider-bar__handle {
69 | height: 8px;
70 | width: 24px;
71 | left: 50%;
72 | position: absolute;
73 | border-radius: 12px;
74 | background-color: $Accent;
75 | transition: all 0s;
76 | will-change: transform;
77 | cursor: pointer;
78 | z-index: 3;
79 | transition-duration: 0s;
80 | bottom: 0px;
81 | top: initial;
82 | transform: translate(-50%, 50%);
83 | }
84 |
85 | .f-slider-bar__process {
86 | background-color: $Accent;
87 | height: 0px;
88 | width: 2px;
89 | position: absolute;
90 | bottom: 0;
91 | }
92 | }
93 | }
94 | }
95 | }
--------------------------------------------------------------------------------
/src/scss/components/_FSwitch.scss:
--------------------------------------------------------------------------------
1 | $Width: 44px;
2 | $Border: 2px;
3 | $ThumbWidth: 10px;
4 | $ThumbMargin: 3px;
5 |
6 | .f-switch {
7 | display: inline-block;
8 | user-select: none;
9 | -webkit-user-select: none;
10 | -ms-user-select: none;
11 | -webkit-touch-callout: none;
12 | -o-user-select: none;
13 | -moz-user-select: none;
14 |
15 | .f-switch--control {
16 | position: relative;
17 | display: inline-block;
18 |
19 | input {
20 | position: absolute;
21 | width: 100%;
22 | height: 100%;
23 | opacity: 0;
24 | cursor: pointer;
25 | z-index: 1;
26 |
27 | &+.f-switch--input {
28 | border: 2px solid $BaseHigh;
29 | background-color: $ChromeWhite;
30 | height: 20px;
31 | width: 44px;
32 | cursor: pointer;
33 | line-height: 16px;
34 | text-align: left;
35 | transition: all 0.2s ease-in-out;
36 | border-radius: 10px;
37 | position: relative;
38 |
39 | .f-switch--thumb {
40 | border-radius: 100%;
41 | display: inline-block;
42 | margin: 3px;
43 | width: 10px;
44 | height: 10px;
45 | background-color: $BaseHigh;
46 | transition: all 0.2s ease-in-out;
47 | }
48 | }
49 |
50 | &:checked {
51 | &+.f-switch--input {
52 | border-color: $Accent;
53 | background-color: $Accent;
54 | color: $ChromeWhite;
55 |
56 | .f-switch--thumb {
57 | background-color: $ChromeWhite;
58 | transform: translateX($Width - $ThumbWidth - ($ThumbMargin * 2) - ($Border * 2));
59 | }
60 | }
61 | }
62 |
63 | &:focus {
64 | outline: none;
65 | }
66 |
67 | &:active {
68 | &+.f-switch--input {
69 | border-color: transparent;
70 | background-color: $BaseMedium;
71 | color: $BaseMedium;
72 | transition: all 0.05s ease-in-out;
73 |
74 | .f-switch--thumb {
75 | background-color: $ChromeWhite;
76 | transition: all 0.05s ease-in-out;
77 | }
78 |
79 | &:before,
80 | &:after {
81 | background-color: $BaseMedium;
82 | }
83 |
84 | &+label {
85 | color: $BaseMedium;
86 | }
87 | }
88 | }
89 |
90 | &:disabled {
91 | &+.f-switch--input {
92 | border-color: $BaseMediumLow;
93 | background-color: $ChromeWhite;
94 | color: $BaseMediumLow;
95 |
96 | .f-switch--thumb {
97 | background-color: $BaseMediumLow;
98 | }
99 |
100 | &:before,
101 | &:after {
102 | background-color: $BaseMediumLow;
103 | }
104 |
105 | &+label {
106 | color: $BaseMediumLow;
107 | }
108 | }
109 |
110 | &:checked {
111 | &+.f-switch--input {
112 | border-color: transparent;
113 | background-color: $BaseLow;
114 |
115 | .f-switch--thumb {
116 | background-color: $BaseLow;
117 | }
118 | }
119 | }
120 | }
121 | }
122 |
123 | &+label {
124 | color: $BaseHigh;
125 | display: inline-block;
126 | cursor: pointer;
127 | font-size: 15px;
128 | vertical-align: top;
129 | }
130 |
131 | &:disabled {
132 | &+label {
133 | color: $BaseMediumLow;
134 | }
135 | }
136 | }
137 | }
--------------------------------------------------------------------------------
/src/scss/components/_FTextarea.scss:
--------------------------------------------------------------------------------
1 | .f-textarea {
2 | display: inline-block;
3 |
4 | span {
5 | display: block;
6 | margin-bottom: 10px;
7 | font-size: 15px;
8 | user-select: none;
9 | -webkit-user-select: none;
10 | -ms-user-select: none;
11 | -webkit-touch-callout: none;
12 | -o-user-select: none;
13 | -moz-user-select: none;
14 | }
15 |
16 | textarea {
17 | line-height: 18px;
18 | border: 2px solid $BaseMediumLow;
19 | min-width: 296px;
20 | min-height: 82px;
21 | padding: 8px 11px;
22 | box-sizing: border-box;
23 | font-size: 15px;
24 | font-weight: 500;
25 | -webkit-box-shadow: none;
26 | -moz-box-shadow: none;
27 | box-shadow: none;
28 | -webkit-appearance: none;
29 | width: 100%;
30 |
31 | &:focus {
32 | outline: none;
33 | border-color: $Accent;
34 | }
35 | }
36 |
37 | &.f-textarea--block {
38 | width: 100%;
39 | }
40 | }
--------------------------------------------------------------------------------
/src/scss/fluentify.scss:
--------------------------------------------------------------------------------
1 | @import "variables";
2 |
3 | @import "utils/mixin";
4 | @import "utils/helpers";
5 |
6 | // Global
7 | @import "global/colors";
8 | @import "global/base";
9 | @import "global/button";
10 | @import "global/typography";
11 | @import "global/ul-ol";
12 | @import "global/grid";
13 |
14 | // Components
15 | @import "components/FContainer";
16 | @import "components/FGrid";
17 | @import "components/FBtn";
18 | @import "components/FAvatar";
19 | @import "components/FMenu";
20 | @import "components/FInput";
21 | @import "components/FTextarea";
22 | @import "components/FCheckbox";
23 | @import "components/FSwitch";
24 | @import "components/FRadio";
25 | @import "components/FRadioGroup";
26 | @import "components/FList";
27 | @import "components/FProgressLinear";
28 | @import "components/FSelect";
29 | @import "components/FCard";
30 | @import "components/FSlider";
31 | @import "components/FImg";
32 |
--------------------------------------------------------------------------------
/src/scss/global/_base.scss:
--------------------------------------------------------------------------------
1 | * {
2 | padding: 0;
3 | margin: 0;
4 | }
5 |
6 | *, *:before, *:after {
7 | -webkit-box-sizing: inherit;
8 | box-sizing: inherit;
9 | }
10 |
11 | html {
12 | -webkit-box-sizing: border-box;
13 | box-sizing: border-box;
14 | -ms-text-size-adjust: 100%;
15 | -webkit-text-size-adjust: 100%;
16 | }
17 |
18 | body {
19 | margin: 0;
20 | background-color: $ChromeWhite;
21 | }
22 |
23 | button, input, select, textarea {
24 | color: inherit;
25 | font: inherit;
26 | margin: 0;
27 | }
--------------------------------------------------------------------------------
/src/scss/global/_button.scss:
--------------------------------------------------------------------------------
1 | button, input[type="reset"], input[type="submit"] {
2 | -webkit-appearance: button;
3 | cursor: pointer;
4 | }
--------------------------------------------------------------------------------
/src/scss/global/_colors.scss:
--------------------------------------------------------------------------------
1 | $Accent: #0078D7;
2 | $AccentLight3: #A6D8FF;
3 | $AccentLight2: #76B9ED;
4 | $AccentLight1: #429CE3;
5 | $AccentDark1: #005A9E;
6 | $AccentDark2: #004275;
7 | $AccentDark3: #002642;
8 |
9 | $Secondary: #0078D7;
10 | $SecondaryLight3: #A6D8FF;
11 | $SecondaryLight2: #76B9ED;
12 | $SecondaryLight1: #429CE3;
13 | $SecondaryDark1: #005A9E;
14 | $SecondaryDark2: #004275;
15 | $SecondaryDark3: #002642;
16 |
17 | $Base: #000000;
18 | $BaseLow: rgba($Base, 0.2);
19 | $BaseMediumLow: rgba($Base, 0.4);
20 | $BaseMedium: rgba($Base, 0.6);
21 | $BaseMediumHigh: rgba($Base, 0.8);
22 | $BaseHigh: rgba($Base, 1);
23 |
24 | $AltBase: #ffffff;
25 | $AltLow: rgba($AltBase, 0.2);
26 | $AltMediumLow: rgba($AltBase, 0.4);
27 | $AltMedium: rgba($AltBase, 0.6);
28 | $AltMediumHigh: rgba($AltBase, 0.8);
29 | $AltHigh: rgba($AltBase, 1);
30 |
31 | $ListLow: rgba(#000000, 0.1);
32 | $ListMedium: rgba(#000000, 0.2);
33 | $ListAccentLow: rgba(#0078D7, 0.4);
34 | $ListAccentMedium: rgba(#0078D7, 0.6);
35 | $ListAccentHigh: rgba(#0078D7, 0.7);
36 |
37 | $ChromeLow: #F2F2F2;
38 | $ChromeMediumLow: #F2F2F2;
39 | $ChromeMedium: #E6E6E6;
40 | $ChromeHigh: #CCCCCC;
41 |
42 | $ChromeAltLow: #171717;
43 |
44 | $ChromeDisabledLow: #7A7A7A;
45 | $ChromeDisabledHigh: #CCCCCC;
46 |
47 | $ChromeBlackLow: rgba(#000000, 0.2);
48 | $ChromeBlackMediumLow: rgba(#000000, 0.4);
49 | $ChromeBlackMedium: rgba(#000000, 0.8);
50 | $ChromeBlackHigh: rgba(#000000, 1);
51 |
52 | $ChromeWhite: #FFFFFF;
--------------------------------------------------------------------------------
/src/scss/global/_grid.scss:
--------------------------------------------------------------------------------
1 | [grid-flow*="row"] {
2 | grid-auto-flow: row;
3 |
4 | &[grid-flow*="dense"] {
5 | grid-auto-flow: row dense;
6 | }
7 | }
8 |
9 | [grid-flow*="column"] {
10 | grid-auto-flow: column;
11 |
12 | [grid-flow*="dense"] {
13 | grid-auto-flow: column dense;
14 | }
15 | }
16 |
17 | @each $breakpoint in map-keys($grid-breakpoints) {
18 | @include media-breakpoint-up($breakpoint, $grid-breakpoints) {
19 | @for $i from 1 through $grid-columns {
20 | [column-start*="#{$breakpoint}-#{$i}"] {
21 | grid-column-start: #{$i};
22 | }
23 |
24 | [column-end*="#{$breakpoint}-#{$i}"] {
25 | grid-column-end: #{$i+1};
26 | }
27 |
28 | [row-start*="#{$breakpoint}-#{$i}"] {
29 | grid-row-start: #{$i};
30 | }
31 |
32 | [row-end*="#{$breakpoint}-#{$i}"] {
33 | grid-row-end: #{$i};
34 | }
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/src/scss/global/_typography.scss:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Segoe UI font faces
4 | *
5 | */
6 | @font-face {
7 | font-family: "Segoe UI";
8 | font-weight: 200;
9 | src: local("Segoe UI Light");
10 | }
11 |
12 | @font-face {
13 | font-family: "Segoe UI";
14 | font-weight: 300;
15 | src: local("Segoe UI Semilight"), local("Segoe UI");
16 | }
17 |
18 | @font-face {
19 | font-family: "Segoe UI";
20 | font-weight: 400;
21 | src: local("Segoe UI");
22 | }
23 |
24 | @font-face {
25 | font-family: "Segoe UI";
26 | font-weight: 600;
27 | src: local("Segoe UI Semibold");
28 | }
29 |
30 | @font-face {
31 | font-family: "Segoe UI";
32 | font-weight: 700;
33 | src: local("Segoe UI Bold");
34 | }
35 |
36 | @font-face {
37 | font-family: "Segoe UI";
38 | font-style: italic;
39 | font-weight: 400;
40 | src: local("Segoe UI Italic");
41 | }
42 |
43 | @font-face {
44 | font-family: "Segoe UI";
45 | font-style: italic;
46 | font-weight: 700;
47 | src: local("Segoe UI Bold Italic");
48 | }
49 |
50 | /**
51 | *
52 | * Base typography
53 | *
54 | */
55 | body {
56 | font-family: "Segoe UI", "SegoeUI", "Helvetica Neue", Helvetica, Arial, sans-serif;
57 | font-size: 15px;
58 | font-weight: 400;
59 | color: $BaseHigh;
60 | caret-color: $BaseHigh;
61 | }
62 |
63 | /**
64 | *
65 | * Typography classes
66 | *
67 | */
68 | .header {
69 | font-size: 46px;
70 | font-weight: 200;
71 | }
72 |
73 | .subheader {
74 | font-size: 34px;
75 | font-weight: 200;
76 | }
77 |
78 | .title {
79 | font-size: 24px;
80 | font-weight: 300;
81 | }
82 |
83 | .subtitle {
84 | font-size: 20px;
85 | font-weight: 400;
86 | }
87 |
88 | .subtitlealt {
89 | font-size: 18px;
90 | font-weight: 400;
91 | }
92 |
93 | .captionalt {
94 | font-size: 13px;
95 | font-weight: 400;
96 | letter-spacing: 0.4em;
97 | }
98 |
99 | .caption {
100 | font-size: 12px;
101 | font-weight: 400;
102 | letter-spacing: 0.4em;
103 | }
104 |
105 | /**
106 | *
107 | * Font weight classes
108 | *
109 | */
110 | .font-weight-light {
111 | font-weight: 200;
112 | }
113 |
114 | .font-weight-semilight {
115 | font-weight: 300;
116 | }
117 |
118 | .font-weight-regular {
119 | font-weight: 400;
120 | }
121 |
122 | .font-weight-semibold {
123 | font-weight: 600;
124 | }
125 |
126 | .font-weight-bold {
127 | font-weight: 700;
128 | }
129 |
130 | /**
131 | *
132 | * Font style classes
133 | *
134 | */
135 | .font-style-italic {
136 | font-style: italic;
137 | }
138 |
139 | .font-style-oblique {
140 | font-style: oblique;
141 | }
142 |
143 | .font-style-normal {
144 | font-style: normal;
145 | }
--------------------------------------------------------------------------------
/src/scss/global/_ul-ol.scss:
--------------------------------------------------------------------------------
1 | ol, ul {
2 | margin-top: 0;
3 | margin-bottom: 0;
4 | padding: 0;
5 | list-style: none;
6 | }
--------------------------------------------------------------------------------
/src/scss/utils/_helpers.scss:
--------------------------------------------------------------------------------
1 | .hide-focus {
2 | outline-color: transparent !important;
3 | }
4 |
5 | .d-grid {
6 | display: grid;
7 | }
8 |
9 | .d-inline-grid {
10 | display: inline-grid;
11 | }
12 |
13 | .d-flex {
14 | display: flex;
15 | }
16 |
17 | .d-block {
18 | display: block;
19 | }
20 |
21 | .d-inline {
22 | display: inline;
23 | }
24 |
25 | .d-inline-block {
26 | display: inline-block;
27 | }
--------------------------------------------------------------------------------
/src/scss/utils/_mixin.scss:
--------------------------------------------------------------------------------
1 | @mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
2 | $min: breakpoint-min($name, $breakpoints);
3 |
4 | @if $min {
5 | @media (min-width: $min) {
6 | @content;
7 | }
8 | }
9 |
10 | @else {
11 | @content;
12 | }
13 | }
14 |
15 | @function breakpoint-min($name, $breakpoints: $grid-breakpoints) {
16 | $min: map-get($breakpoints, $name);
17 | @return if($min !=0, $min, null);
18 | }
--------------------------------------------------------------------------------
/src/stories/FBtn.stories.js:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/vue'
2 | import { action } from '@storybook/addon-actions'
3 |
4 | // import * as components from '../components'
5 | import FBtn from '../components/FBtn/FBtn.vue'
6 |
7 | storiesOf('FBtn', module)
8 | .add('with text', () => ({
9 | components: { FBtn },
10 | template: 'Hello Button',
11 | methods: { action: action('clicked') }
12 | }))
13 | .add('with some emoji', () => ({
14 | components: { FBtn },
15 | template: '😀 😎 👍 💯',
16 | methods: { action: action('clicked') }
17 | }))
18 | .add('disabled', () => ({
19 | components: { FBtn },
20 | template: '😀 😎 👍 💯',
21 | methods: { action: action('clicked') }
22 | }))
23 |
--------------------------------------------------------------------------------
/src/stories/FCard.stories.js:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/vue'
2 |
3 | // import * as components from '../components'
4 | import { FCard, FCardAction, FCardCaption, FCardImage, FCardText, FCardTitle, FCardContent } from '../components/FCard'
5 | import { FBtn } from '../components/FBtn'
6 |
7 | storiesOf('FCard', module)
8 | .add('Default', () => ({
9 | components: {
10 | FCard,
11 | FCardAction,
12 | FCardCaption,
13 | FCardImage,
14 | FCardText,
15 | FCardTitle,
16 | FCardContent,
17 | FBtn
18 | },
19 | template: `
20 | Fluent
21 |
22 | Test title
23 | Test text
24 |
25 | button
26 |
27 |
28 |
29 | `,
30 | }))
31 | .add('Click', () => ({
32 | components: {
33 | FCard,
34 | FCardAction,
35 | FCardCaption,
36 | FCardImage,
37 | FCardText,
38 | FCardTitle,
39 | FCardContent,
40 | FBtn
41 | },
42 | template: `
43 | Fluent
44 |
45 | Test title
46 | Test text
47 |
48 | button
49 |
50 |
51 |
52 | `,
53 | }))
54 | .add('Featured', () => ({
55 | components: {
56 | FCard,
57 | FCardAction,
58 | FCardCaption,
59 | FCardImage,
60 | FCardText,
61 | FCardTitle,
62 | FCardContent,
63 | FBtn
64 | },
65 | template: `
66 | Fluent
67 |
68 | Test title
69 | Test text
70 |
71 |
72 |
73 | button
74 |
75 | `,
76 | }))
77 | .add('Fluid', () => ({
78 | components: {
79 | FCard,
80 | FCardAction,
81 | FCardCaption,
82 | FCardImage,
83 | FCardText,
84 | FCardTitle,
85 | FCardContent,
86 | FBtn
87 | },
88 | template: `
89 | Fluent
90 |
91 | Test title
92 | Test text
93 |
94 |
95 |
96 | Github
97 | NPM
98 |
99 | `,
100 | }))
--------------------------------------------------------------------------------
/src/stories/FCheckbox.stories.js:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/vue'
2 |
3 | // import * as components from '../components'
4 | import FCheckbox from '../components/FCheckbox/FCheckbox.vue'
5 |
6 | storiesOf('FCheckbox', module)
7 | .add('Standard', () => ({
8 | components: { FCheckbox },
9 | template: '',
10 |
11 | }))
12 | .add('Indeterminate', () => ({
13 | components: { FCheckbox },
14 | template: '',
15 |
16 | }))
17 | .add('Disabled', () => ({
18 | components: { FCheckbox },
19 | template: '',
20 |
21 | }))
22 |
--------------------------------------------------------------------------------
/src/stories/FInput.stories.js:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/vue'
2 |
3 | // import * as components from '../components'
4 | import FInput from '../components/FInput/FInput.vue'
5 |
6 | storiesOf('FInput', module)
7 | .add('text', () => ({
8 | components: { FInput },
9 | template: ' {{text}}
',
10 | data: () => {
11 | return {
12 | text: 'text',
13 | }
14 | }
15 | }))
16 | .add('text with label', () => ({
17 | components: { FInput },
18 | template: '',
19 |
20 | }))
21 | .add('text disabled', () => ({
22 | components: { FInput },
23 | template: '',
24 |
25 | }))
26 | .add('text required', () => ({
27 | components: { FInput },
28 | template: '',
29 |
30 | }))
31 | .add('text block', () => ({
32 | components: { FInput },
33 | template: '',
34 |
35 | }))
36 | .add('password', () => ({
37 | components: { FInput },
38 | template: '',
39 |
40 | }))
41 | .add('email', () => ({
42 | components: { FInput },
43 | template: '',
44 |
45 | }))
46 | .add('number', () => ({
47 | components: { FInput },
48 | template: '',
49 |
50 | }))
51 |
--------------------------------------------------------------------------------
/src/stories/FProgressLinear.stories.js:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/vue'
2 |
3 | // import * as components from '../components'
4 | import FProgressLinear from '../components/FProgressLinear/FProgressLinear.vue'
5 |
6 | storiesOf('FProgressLinear', module)
7 | .add('50%', () => ({
8 | components: { FProgressLinear },
9 | template: '',
10 |
11 | }))
12 | .add('50% with buffered value at 75%', () => ({
13 | components: { FProgressLinear },
14 | template: '',
15 |
16 | }))
17 | .add('custom height', () => ({
18 | components: { FProgressLinear },
19 | template: '',
20 |
21 | }))
22 | .add('Indeterminate', () => ({
23 | components: { FProgressLinear },
24 | template: '',
25 |
26 | }))
--------------------------------------------------------------------------------
/src/stories/FRadio.stories.js:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/vue'
2 |
3 | // import * as components from '../components'
4 | import FRadio from '../components/FRadio/FRadio.vue'
5 |
6 | storiesOf('FRadio', module)
7 | .add('Standard', () => ({
8 | components: { FRadio },
9 | template: `
10 |
11 |
12 | {{ radio }}
13 |
`,
14 | data: () => {
15 | return {
16 | radio: null
17 | }
18 | }
19 | }))
20 | .add('Disabled', () => ({
21 | components: { FRadio },
22 | template: '',
23 |
24 | }))
25 |
--------------------------------------------------------------------------------
/src/stories/FRadioGroup.stories.js:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/vue'
2 |
3 | // import * as components from '../components'
4 | import FRadio from '../components/FRadio/FRadio.vue'
5 | import FRadioGroup from '../components/FRadioGroup/FRadioGroup.vue'
6 |
7 | storiesOf('FRadioGroup', module)
8 | .add('Standard', () => ({
9 | components: { FRadio, FRadioGroup },
10 | template: `
11 |
12 |
18 |
19 |
20 | {{ radio }}
21 |
`,
22 | data: () => {
23 | return {
24 | radio: null
25 | }
26 | }
27 | }))
28 | .add('Disabled', () => ({
29 | components: { FRadioGroup },
30 | template: '',
31 |
32 | }))
33 |
--------------------------------------------------------------------------------
/src/stories/FSwitch.stories.js:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/vue'
2 |
3 | // import * as components from '../components'
4 | import FSwitch from '../components/FSwitch/FSwitch.vue'
5 |
6 | storiesOf('FSwitch', module)
7 | .add('Boolean', () => ({
8 | components: { FSwitch },
9 | template: `
10 | {{switch1}}
11 | {{switch2}}
12 |
`,
13 | data: () => {
14 | return {
15 | switch1: false,
16 | switch2: true
17 | }
18 | }
19 | }))
20 | .add('Array', () => ({
21 | components: { FSwitch },
22 | template: `
23 |
24 |
25 |
26 | {{fSwitch}}
27 |
`,
28 | data: () => {
29 | return {
30 | fSwitch: ['1']
31 | }
32 | }
33 | }))
34 | .add('Disabled', () => ({
35 | components: { FSwitch },
36 | template: `
37 |
38 |
39 |
40 |
`,
41 | data: () => {
42 | return {
43 | fSwitch: true
44 | }
45 | }
46 | }))
47 |
--------------------------------------------------------------------------------
/src/stories/FTextarea.stories.js:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/vue'
2 |
3 | // import * as components from '../components'
4 | import FTextarea from '../components/FTextarea/FTextarea.vue'
5 |
6 | storiesOf('FTextarea', module)
7 | .add('text', () => ({
8 | components: { FTextarea },
9 | template: '',
10 | data: () => {
11 | return {
12 | text: 'text'
13 | }
14 | }
15 | }))
16 | .add('text with label', () => ({
17 | components: { FTextarea },
18 | template: '',
19 | }))
20 | .add('text disabled', () => ({
21 | components: { FTextarea },
22 | template: '',
23 | }))
24 | .add('text required', () => ({
25 | components: { FTextarea },
26 | template: '',
27 | }))
28 | .add('text block', () => ({
29 | components: { FTextarea },
30 | template: '',
31 | }))
--------------------------------------------------------------------------------
/src/utils/config.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | }
3 |
4 | export default config
5 |
6 | export const setOptions = (options) => { config = options }
7 |
--------------------------------------------------------------------------------
/src/utils/helper.js:
--------------------------------------------------------------------------------
1 | export const use = (plugin) => {
2 | if (typeof window !== 'undefined' && window.Vue) {
3 | window.Vue.use(plugin)
4 | }
5 | }
6 |
7 | export const registerComponent = (Vue, component) => {
8 | Vue.component(component.name, component)
9 | }
10 |
11 | export const registerComponentProgrammatic = (Vue, property, component) => {
12 | Vue.prototype[property] = component
13 | }
14 |
--------------------------------------------------------------------------------
/tests/unit/button.spec.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai'
2 | import { shallowMount } from '@vue/test-utils'
3 | import FBtn from '@/components/FBtn/FBtn.vue'
4 |
5 | describe('FBtn.vue', () => {
6 | it('renders label in default slot when passed', () => {
7 | const label = 'new message'
8 | const wrapper = shallowMount(FBtn, {
9 | // propsData: { label }
10 | slots: {
11 | default: label
12 | }
13 | })
14 | expect(wrapper.text()).to.include(label)
15 | })
16 | })
17 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | css: {
3 | extract: true
4 | }
5 | }
--------------------------------------------------------------------------------