├── .browserslistrc ├── .eslintrc.js ├── .gitignore ├── .prettierignore ├── README.md ├── babel.config.js ├── index.js ├── index.ts ├── jest.config.js ├── package-lock.json ├── package.json ├── prettier.config.js ├── public ├── favicon.ico └── index.html ├── src ├── App.tsx ├── App.vue ├── assets │ ├── bootstrap copy.css │ ├── bootstrap.css │ └── logo.png ├── components │ ├── accordion │ │ ├── SBAccordion.tsx │ │ └── example.tsx │ ├── alert │ │ ├── SBAlert.tsx │ │ └── SBAlertExample.vue │ ├── badge │ │ └── SBBadge.tsx │ ├── breadcrumb │ │ └── SBBreadcrumb.tsx │ ├── button │ │ ├── SBButton.tsx │ │ └── __tests__ │ │ │ └── SBButton.spec.ts │ ├── buttonGroup │ │ ├── SBButtonGroup.tsx │ │ └── exmaple.tsx │ ├── card │ │ ├── Example.vue │ │ ├── SBCard.tsx │ │ ├── SBCardHeader.tsx │ │ ├── SBCardImg.tsx │ │ ├── SBCardTitle.tsx │ │ ├── SBListgroup.tsx │ │ └── SBListgroupItem.tsx │ ├── closeButton │ │ └── SBCloseButton.tsx │ ├── dropDown │ │ ├── SBDropdown.tsx │ │ ├── SBDropdownItem.tsx │ │ └── example.vue │ ├── form │ │ ├── SBForm.tsx │ │ ├── SBFormGroup.tsx │ │ ├── SBFormInput.tsx │ │ ├── SBFormRadios.tsx │ │ ├── SBFormRadiosGroup.tsx │ │ ├── SBFormSelect.tsx │ │ └── example-SBFormSelect.vue │ ├── layoutAndGridSystem │ │ ├── SBCol.tsx │ │ ├── SBContainer.tsx │ │ └── SBRow.tsx │ ├── nav │ │ ├── SBNav.tsx │ │ └── SBNavLink.tsx │ ├── pagination │ │ ├── Pagination.tsx │ │ └── pagination.css │ ├── progress │ │ ├── SBProgress.tsx │ │ ├── SBProgressBar.tsx │ │ └── example.vue │ ├── spinners │ │ └── SBSpinners.tsx │ ├── table │ │ ├── SBTable.tsx │ │ ├── example.vue │ │ └── table.css │ └── toasts │ │ └── SBToasts.tsx ├── index.js ├── index.ts ├── main.ts ├── router.ts └── shims-vue.d.ts ├── tests └── unit │ └── example.spec.ts └── tsconfig.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/typescript/recommended' 10 | ], 11 | parserOptions: { 12 | ecmaVersion: 2020 13 | }, 14 | rules: { 15 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 17 | '@typescript-eslint/member-delimiter-style': 'off', 18 | '@typescript-eslint/interface-name-prefix': 'off', 19 | 'prefer-const': 'off', 20 | '@typescript-eslint/no-empty-function': 'off', 21 | 'vue/require-render-return': 'warn', 22 | 'vue/no-unused-components': 'warn' 23 | }, 24 | overrides: [ 25 | { 26 | files: [ 27 | '**/__tests__/*.{j,t}s?(x)', 28 | '**/tests/unit/**/*.spec.{j,t}s?(x)' 29 | ], 30 | env: { 31 | jest: true 32 | } 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | src/assets -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SuperBVue 2 | 3 | 4 | 5 | **SuperBVue** Bootstrap v5 UI component and grid system available for Vue v3 6 | 7 | ## Looking for the documentation? 8 | Head over here ==> [SuperBVue](https://superbvue.netlify.app/) 9 | 10 | ## Prerequisite 11 | - Vue 3 is required 12 | 13 | ## Features 14 | 15 | - **Ease of Styling:** SuperBVue using boostrap v5 UI 16 | - Fully support Vue 3 17 | - Typescript support 18 | - No JQuery dependencies 19 | 20 | ## Installation 21 | 22 | ```bash 23 | yarn add superbvue 24 | ``` 25 | or 26 | ```bash 27 | npm install superbvue 28 | ``` 29 | 30 | ## Usage 31 | 32 | **1. Import the SuperBVue UI in your `main.js` file.** 33 | ```js 34 | import { createApp } from 'vue' 35 | import { SBButton } from 'superbvue' 36 | import App from './App.vue' 37 | 38 | const app = createApp(App) 39 | 40 | app.component('SBButton', SBButton) 41 | 42 | app.mount('#app') 43 | ``` 44 | Or in component level: 45 | ```js 46 | import { defineComponent } from 'vue' 47 | import { SBButton } from 'superbvue' 48 | 49 | export default defineComponent({ 50 | components: { 51 | SBButton 52 | } 53 | }) 54 | ``` 55 | 56 | **2. In your component, you can global use it in template.** 57 | ```html 58 | 61 | 62 | 71 | ``` 72 | 73 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import SBAccordion from './src/components/accordion/SBAccordion' 2 | import SBAlert from './src/components/alert/SBAlert' 3 | import SBBadge from './src/components/badge/SBBadge' 4 | import SBBreadcrumb from './src/components/breadcrumb/SBBreadcrumb' 5 | import SBButton from './src/components/button/SBButton' 6 | import SBButtonGroup from './src/components/buttonGroup/SBButtonGroup' 7 | import SBCard from './src/components/card/SBCard' 8 | import SBCardHeader from './src/components/card/SBCardHeader' 9 | import SBListgroup from './src/components/card/SBListgroup' 10 | import SBListgroupItem from './src/components/card/SBListgroupItem' 11 | import SBCardTitle from './src/components/card/SBCardTitle' 12 | import SBCloseButton from './src/components/closeButton/SBCloseButton' 13 | import SBContainer from './src/components/layoutAndGridSystem/SBContainer' 14 | import SBCol from './src/components/layoutAndGridSystem/SBCol' 15 | import SBFormSelect from './src/components/form/SBFormSelect' 16 | import SBFormInput from './src/components/form/SBFormInput' 17 | import SBNav from './src/components/nav/SBNav' 18 | import SBNavLink from './src/components/nav/SBNavLink' 19 | import SBProgress from './src/components/progress/SBProgress' 20 | import SBProgressBar from './src/components/progress/SBProgressBar' 21 | import SBPagination from './src/components/pagination/Pagination' 22 | import SBRow from './src/components/layoutAndGridSystem/SBRow' 23 | import SBTable from './src/components/table/SBTable' 24 | 25 | export { 26 | SBAccordion, 27 | SBAlert, 28 | SBBadge, 29 | SBBreadcrumb, 30 | SBButton, 31 | SBButtonGroup, 32 | SBCard, 33 | SBCardHeader, 34 | SBListgroup, 35 | SBListgroupItem, 36 | SBCardTitle, 37 | SBCloseButton, 38 | SBContainer, 39 | SBCol, 40 | SBFormSelect, 41 | SBFormInput, 42 | SBNav, 43 | SBNavLink, 44 | SBProgress, 45 | SBProgressBar, 46 | SBPagination, 47 | SBRow, 48 | SBTable 49 | } 50 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import SBAccordion from './src/components/accordion/SBAccordion' 2 | import SBAlert from './src/components/alert/SBAlert' 3 | import SBBadge from './src/components/badge/SBBadge' 4 | import SBBreadcrumb from './src/components/breadcrumb/SBBreadcrumb' 5 | import SBButton from './src/components/button/SBButton' 6 | import SBButtonGroup from './src/components/buttonGroup/SBButtonGroup' 7 | import SBCard from './src/components/card/SBCard' 8 | import SBCardHeader from './src/components/card/SBCardHeader' 9 | import SBListgroup from './src/components/card/SBListgroup' 10 | import SBListgroupItem from './src/components/card/SBListgroupItem' 11 | import SBCardTitle from './src/components/card/SBCardTitle' 12 | import SBCloseButton from './src/components/closeButton/SBCloseButton' 13 | import SBContainer from './src/components/layoutAndGridSystem/SBContainer' 14 | import SBCol from './src/components/layoutAndGridSystem/SBCol' 15 | import SBFormSelect from './src/components/form/SBFormSelect' 16 | import SBFormInput from './src/components/form/SBFormInput' 17 | import SBNav from './src/components/nav/SBNav' 18 | import SBNavLink from './src/components/nav/SBNavLink' 19 | import SBProgress from './src/components/progress/SBProgress' 20 | import SBProgressBar from './src/components/progress/SBProgressBar' 21 | import SBPagination from './src/components/pagination/Pagination' 22 | import SBRow from './src/components/layoutAndGridSystem/SBRow' 23 | import SBTable from './src/components/table/SBTable' 24 | 25 | export { 26 | SBAccordion, 27 | SBAlert, 28 | SBBadge, 29 | SBBreadcrumb, 30 | SBButton, 31 | SBButtonGroup, 32 | SBCard, 33 | SBCardHeader, 34 | SBListgroup, 35 | SBListgroupItem, 36 | SBCardTitle, 37 | SBCloseButton, 38 | SBContainer, 39 | SBCol, 40 | SBFormSelect, 41 | SBFormInput, 42 | SBNav, 43 | SBNavLink, 44 | SBProgress, 45 | SBProgressBar, 46 | SBPagination, 47 | SBRow, 48 | SBTable 49 | } 50 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel', 3 | transform: { 4 | '^.+\\.vue$': 'vue-jest' 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "superbvue", 3 | "version": "0.1.10", 4 | "private": false, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "test:unit": "vue-cli-service test:unit", 9 | "test:unit:watch": "vue-cli-service test:unit --watch", 10 | "lint": "vue-cli-service lint", 11 | "prettier:src": "npx prettier -w src/**" 12 | }, 13 | "homepage": "", 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/superbvue/SuperBVue" 17 | }, 18 | "keywords": [ 19 | "supervue", 20 | "vue", 21 | "ui framework", 22 | "component framework", 23 | "ui library", 24 | "component library", 25 | "bootstrap" 26 | ], 27 | "dependencies": { 28 | "core-js": "^3.6.5", 29 | "prettier": "^2.2.1", 30 | "vue": "^3.0.0", 31 | "vue-router": "^4.0.0-0" 32 | }, 33 | "devDependencies": { 34 | "@types/jest": "^24.0.19", 35 | "@typescript-eslint/eslint-plugin": "^2.33.0", 36 | "@typescript-eslint/parser": "^2.33.0", 37 | "@vue/cli-plugin-babel": "~4.5.0", 38 | "@vue/cli-plugin-router": "~4.5.0", 39 | "@vue/cli-plugin-eslint": "~4.5.0", 40 | "@vue/cli-plugin-typescript": "~4.5.0", 41 | "@vue/cli-plugin-unit-jest": "~4.5.0", 42 | "@vue/cli-service": "~4.5.0", 43 | "@vue/compiler-sfc": "^3.0.0", 44 | "@vue/eslint-config-typescript": "^5.0.2", 45 | "@vue/test-utils": "^2.0.0-0", 46 | "eslint": "^6.7.2", 47 | "eslint-plugin-vue": "^7.0.0-0", 48 | "typescript": "~3.9.3", 49 | "vue-jest": "^5.0.0-0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | tabWidth: 2, 3 | semi: false, 4 | printWidth: 120, 5 | bracketSpacing: true, 6 | jsxBracketSameLine: true, 7 | trailingComma: 'none', 8 | arrowParens: 'always', 9 | singleQuote: true 10 | } 11 | 12 | module.exports = config 13 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superbvue/SuperBVue/62c88ca9257baede5c43d0c072028ab3350621df/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue' 2 | import SBProgress from './components/progress/SBProgress' 3 | import SBAccordion from './components/accordion/SBAccordion' 4 | import SBBadge from './components/badge/SBBadge' 5 | import SBBreadcrumb from './components/breadcrumb/SBBreadcrumb' 6 | import SBButton from './components/button/SBButton' 7 | import SBCard from './components/card/SBCard' 8 | import SBCardImg from './components/card/SBCardImg' 9 | import SBCloseButton from './components/closeButton/SBCloseButton' 10 | import SBFormInput from './components/form/SBFormInput' 11 | import SBFormSelect from './components/form/SBFormSelect' 12 | import SBNavLink from './components/nav/SBNavLink' 13 | import SBSpinners from './components/spinners/SBSpinners' 14 | import SBToasts from './components/toasts/SBToasts' 15 | import SBTable from './components/table/SBTable' 16 | import SBPagination from './components/pagination/Pagination' 17 | import SBCardTitle from './components/card/SBCardTitle' 18 | import SBListgroup from './components/card/SBListgroup' 19 | import SBListgroupItem from './components/card/SBListgroupItem' 20 | 21 | const App = defineComponent({ 22 | name: 'App', 23 | data() { 24 | return { 25 | state: { 26 | items: [ 27 | { isActive: true, age: 1, fistName: 'Dickerson', lastName: 'Macdonald' }, 28 | { isActive: false, age: 2, fistName: 'Larsen', rowVariant: 'primary', lastName: 'Shaw' }, 29 | { isActive: false, age: 3, fistName: 'Geneva', lastName: 'Wilson' }, 30 | { isActive: true, age: 4, fistName: 'Nicholas', lastName: 'Hoffman' }, 31 | { isActive: false, age: 5, fistName: 'Fintan', rowVariant: 'Florin', lastName: 'Shaw' }, 32 | { isActive: false, age: 6, fistName: 'Hans', lastName: 'Wilson' }, 33 | { isActive: true, age: 7, fistName: 'Andrew', lastName: 'El' }, 34 | { isActive: false, age: 8, fistName: 'Tremble', rowVariant: 'primary', lastName: 'Spanns' }, 35 | { isActive: false, age: 9, fistName: 'Erik', lastName: 'Karianinen' } 36 | ], 37 | field: [ 38 | // { key: 'Agess', sortable: true }, 39 | // { key: 'First', sortable: false }, 40 | // { key: 'Last', sortable: true } 41 | 'Ages', 'First', 'Last' 42 | ], 43 | perPage: 3, 44 | currentPage: 1, 45 | } 46 | } 47 | }, 48 | methods: { 49 | handleSetCurrentPage(event: any) { 50 | this.state.currentPage = event.target.value 51 | } 52 | }, 53 | computed: { 54 | row(): number { 55 | return this.state.items.length 56 | } 57 | }, 58 | render() { 59 | return ( 60 |
61 | App tsx component 62 | {/* */} 63 | {/* */} 64 | {/* */} 65 | 66 | {/* 67 | Some quick example text to build on the card title and make up the bulk of the card's content. 68 | Go somewhere 69 | */} 70 | 71 | {/* 72 | 73 | Cras justo odio 74 | 75 | */} 76 | 77 | 78 | Some quick example text to build on the card title and make up the bulk of the card's content. 79 | 80 |
81 | ) 82 | } 83 | }) 84 | 85 | export default App 86 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 122 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superbvue/SuperBVue/62c88ca9257baede5c43d0c072028ab3350621df/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/accordion/SBAccordion.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | import SBButton from '../button/SBButton' 3 | 4 | interface ISBAccordionProps { 5 | disabled?: boolean 6 | onClick?: () => void 7 | class?: string 8 | } 9 | 10 | // TODO: NOT DONE.. NEED MORE WORK ON... 11 | const SBAccordion = defineComponent({ 12 | name: 'SBAccordion', 13 | props: { 14 | disabled: { 15 | type: Boolean, 16 | required: false 17 | }, 18 | onClick: { 19 | type: Function as PropType<(event: MouseEvent) => void>, 20 | required: false, 21 | default: () => {} 22 | }, 23 | class: { 24 | type: String, 25 | required: false 26 | } 27 | }, 28 | data() { 29 | return { 30 | state: { 31 | listed: [{ id: 0, isShow: false, class: '', isCollapse: false }] 32 | } 33 | } 34 | }, 35 | methods: { 36 | emitToParent(event: MouseEvent) { 37 | this.onClick(event) 38 | }, 39 | handleShowCollapse(id: string) { 40 | console.log({ id }) 41 | } 42 | }, 43 | computed: {}, 44 | render() { 45 | let computeClass = (props: ISBAccordionProps) => { 46 | return [ 47 | 'btn-close', 48 | { 49 | disabled: props.disabled 50 | }, 51 | props.class 52 | ] 53 | } 54 | let renderMyVersion = null 55 | 56 | if ((this as any).$slots.default) { 57 | // for (let i = 0; i < (this as any).$slots.default().length; i++) { 58 | // console.log('object', (this as any).$slots.default()[i]) 59 | // renderMyVersion = 60 | // } 61 | 62 | renderMyVersion = (this as any).$slots.default().map((element: any, index: any) => { 63 | let nestChildred = null 64 | 65 | // nestChildred = element.map(()) 66 | console.log('element', element.children[1]) 67 | // let buttonElement = element.children[0].children[0] 68 | // console.log('buttonElement', buttonElement.) 69 | // {element.children[0].children[0]} 70 | return ( 71 |
72 | {/* {element} */} 73 |

74 | 75 | {element.children[0].children[0].children 76 | ? element.children[0].children[0].children.default()[0].children 77 | : null} 78 | 79 |

80 | {element.children[1]} 81 |
82 | ) 83 | }) 84 | // renderMyVersion = (this as any).$slots.default().children.map((element: any, index: any) => { 85 | // console.log('element', element) 86 | // return ( 87 | //
88 | //

{element}

89 | 90 | //
91 | // ) 92 | // }) 93 | } 94 | 95 | return ( 96 |
97 |

MY VERISON

98 |
99 | {renderMyVersion} 100 |
101 | 102 |

REAL

103 | 104 |
105 |
106 |

107 | 116 |

117 |
122 |
123 | This is the first item's accordion body. It is hidden by default, until the collapse 124 | plugin adds the appropriate classes that we use to style each element. These classes control the overall 125 | appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with 126 | custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go 127 | within the .accordion-body, though the transition does limit overflow. 128 |
129 |
130 |
131 | 132 |
133 |

134 | 143 |

144 |
149 |
150 | This is the second item's accordion body. It is hidden by default, until the collapse 151 | plugin adds the appropriate classes that we use to style each element. These classes control the overall 152 | appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with 153 | custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go 154 | within the .accordion-body, though the transition does limit overflow. 155 |
156 |
157 |
158 | 159 |
160 |

161 | 170 |

171 |
176 |
177 | This is the third item's accordion body. It is hidden by default, until the collapse 178 | plugin adds the appropriate classes that we use to style each element. These classes control the overall 179 | appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with 180 | custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go 181 | within the .accordion-body, though the transition does limit overflow. 182 |
183 |
184 |
185 |
186 |
187 | ) 188 | 189 | // return ( 190 | // 191 | // ) 192 | } 193 | }) 194 | 195 | export default SBAccordion 196 | -------------------------------------------------------------------------------- /src/components/accordion/example.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue' 2 | import SBAccordion from '../../components/accordion/SBAccordion' 3 | import SBBadge from '../../components/badge/SBBadge' 4 | import SBBreadcrumb from '../../components/breadcrumb/SBBreadcrumb' 5 | import SBButton from '../../components/button/SBButton' 6 | import SBCloseButton from '../../components/closeButton/SBCloseButton' 7 | import SBSpinners from '../../components/spinners/SBSpinners' 8 | 9 | const App = defineComponent({ 10 | name: 'App', 11 | data() { 12 | return { 13 | state: { 14 | class: 'accordion-button collapsed', 15 | class2: 'accordion-collapse collapse', 16 | items: [{ isAccordionCollapsed: false, buttonClass: 'accordion-button collapsed' }] 17 | } 18 | } 19 | }, 20 | methods: { 21 | handleSetName(event: any) { 22 | console.log('enter handleSetName', event) 23 | }, 24 | handleShowCollapse(event: Event, id: number) { 25 | let result = '' 26 | let result2 = '' 27 | if (this.state.class === 'accordion-button collapsed') { 28 | result = 'accordion-button' 29 | result2 = 'accordion-collapse collapse' 30 | } else { 31 | result = 'accordion-button collapsed' 32 | result2 = 'accordion-collapse collapsed show' 33 | } 34 | this.state.class = result 35 | this.state.class2 = result2 36 | } 37 | }, 38 | computed: { 39 | myClass(): string { 40 | return this.state.class 41 | }, 42 | myClass2(): string { 43 | return this.state.class2 44 | } 45 | }, 46 | render() { 47 | return ( 48 |
49 | App tsx component 50 | 51 |
52 |

53 | this.handleShowCollapse(event, 1)} 56 | type="button" 57 | data-bs-toggle="collapse" 58 | data-bs-target="#collapseOne" 59 | aria-expanded="true" 60 | aria-controls="collapseOne"> 61 | Accordion Item #1 62 | 63 |

64 |
65 |
66 | This is the first item's accordion body. It is hidden by default, until the collapse 67 | plugin adds the appropriate classes that we use to style each element. These classes control the overall 68 | appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with 69 | custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go 70 | within the .accordion-body, though the transition does limit overflow. 71 |
72 |
73 |
74 | 75 |
76 |

77 | 86 |

87 |
92 |
93 | This is the second item's accordion body. It is hidden by default, until the collapse 94 | plugin adds the appropriate classes that we use to style each element. These classes control the overall 95 | appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with 96 | custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go 97 | within the .accordion-body, though the transition does limit overflow. 98 |
99 |
100 |
101 |
102 | {/* 103 | 104 | 105 | */} 106 |
107 | ) 108 | } 109 | }) 110 | 111 | export default App 112 | -------------------------------------------------------------------------------- /src/components/alert/SBAlert.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType, reactive } from 'vue' 2 | 3 | interface SBAlertProps { 4 | fade?: boolean 5 | show?: boolean 6 | dismissLabel?: boolean 7 | dismissible?: boolean 8 | variant?: string 9 | class?: string 10 | } 11 | 12 | type TVariant = 13 | | 'primary' 14 | | 'secondary' 15 | | 'success' 16 | | 'danger' 17 | | 'warning' 18 | | 'info' 19 | | 'light' 20 | | 'dark' 21 | | 'outline-primary' 22 | | 'outline-secondary' 23 | | 'outline-success' 24 | | 'outline-danger' 25 | | 'outline-warning' 26 | | 'outline-info' 27 | | 'outline-light' 28 | | 'outline-dark' 29 | | 'link' 30 | 31 | /*** TODO: PENDING... NOT COMPLETE */ 32 | const SBAlert = defineComponent({ 33 | name: 'SBAlert', 34 | props: { 35 | fade: { 36 | type: Boolean, 37 | required: false 38 | }, 39 | show: { 40 | type: Boolean, 41 | required: false 42 | }, 43 | dismissLabel: { 44 | type: Boolean, 45 | required: false 46 | }, 47 | dismissible: { 48 | type: Boolean, 49 | required: false 50 | }, 51 | variant: { 52 | type: String as PropType, 53 | required: false, 54 | default: 'info' 55 | // validator: function (payload: string) { 56 | // return ( 57 | // [ 58 | // 'primary', 59 | // 'secondary', 60 | // 'success', 61 | // 'danger', 62 | // 'warning', 63 | // 'info', 64 | // 'light', 65 | // 'dark', 66 | // 'outline-primary', 67 | // 'outline-secondary', 68 | // 'outline-success', 69 | // 'outline-danger', 70 | // 'outline-warning', 71 | // 'outline-info', 72 | // 'outline-light', 73 | // 'outline-dark', 74 | // 'link' 75 | // ].indexOf(payload) !== -1 76 | // ) 77 | // } 78 | }, 79 | class: { 80 | type: String, 81 | required: false 82 | } 83 | }, 84 | setup() { 85 | let state = reactive({ 86 | countDown: 0 87 | // If initially shown, we need to set these for SSR 88 | // localShow: parseShow(this.show) 89 | }) 90 | return { 91 | state 92 | } 93 | }, 94 | render() { 95 | let computeClass = (props: SBAlertProps) => { 96 | return [ 97 | 'alert', 98 | `alert-${props.variant || 'secondary'}`, 99 | { 100 | 'alert-dismissible': props.dismissible 101 | }, 102 | props.class 103 | ] 104 | } 105 | let renderAlertElement = null 106 | let renderOnlyAlertElement = null 107 | 108 | if ((this.$slots as any).default) { 109 | // Nested child elements 110 | if ((this.$slots as any).default().length !== 1) { 111 | console.log('(this.$slots as any).default', (this.$slots as any).default()) 112 | renderAlertElement = (this.$slots as any).default().map((element: any) => { 113 | // console.log('element', element) 114 | return element 115 | }) 116 | } 117 | 118 | renderOnlyAlertElement = ( 119 | 122 | ) 123 | } 124 | 125 | if (renderAlertElement) { 126 | return renderAlertElement 127 | } else { 128 | return renderOnlyAlertElement 129 | } 130 | // return ( 131 | // 134 | // ) 135 | // return renderOnlyAlertElement 136 | } 137 | }) 138 | 139 | export default SBAlert 140 | -------------------------------------------------------------------------------- /src/components/alert/SBAlertExample.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /src/components/badge/SBBadge.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | 3 | interface ISBBadge { 4 | size?: string 5 | pill?: string 6 | variant?: string 7 | class?: string 8 | href?: string 9 | } 10 | 11 | type TVariant = 12 | | 'primary' 13 | | 'secondary' 14 | | 'success' 15 | | 'danger' 16 | | 'warning' 17 | | 'info' 18 | | 'light' 19 | | 'dark' 20 | | 'outline-primary' 21 | | 'outline-secondary' 22 | | 'outline-success' 23 | | 'outline-danger' 24 | | 'outline-warning' 25 | | 'outline-info' 26 | | 'outline-light' 27 | | 'outline-dark' 28 | | 'link' 29 | 30 | const SBBadge = defineComponent({ 31 | name: 'SBBadge', 32 | props: { 33 | size: { 34 | type: String as PropType<'sm' | 'md' | 'lg'>, 35 | required: false, 36 | validator: function (payload: string) { 37 | return ['sm', 'md', 'lg'].indexOf(payload) !== -1 38 | } 39 | }, 40 | pill: { 41 | type: Boolean, 42 | required: false 43 | }, 44 | variant: { 45 | type: String as PropType, 46 | required: false, 47 | default: 'secondary', 48 | validator: function (payload: string) { 49 | return ( 50 | [ 51 | 'primary', 52 | 'secondary', 53 | 'success', 54 | 'danger', 55 | 'warning', 56 | 'info', 57 | 'light', 58 | 'dark', 59 | 'outline-primary', 60 | 'outline-secondary', 61 | 'outline-success', 62 | 'outline-danger', 63 | 'outline-warning', 64 | 'outline-info', 65 | 'outline-light', 66 | 'outline-dark', 67 | 'link' 68 | ].indexOf(payload) !== -1 69 | ) 70 | } 71 | }, 72 | class: { 73 | type: String, 74 | required: false 75 | }, 76 | href: { 77 | type: String as PropType<'#'>, 78 | required: false, 79 | validator: function (payload: string) { 80 | return ['#'].indexOf(payload) !== -1 81 | } 82 | } 83 | }, 84 | methods: { 85 | mergeAttrs() { 86 | let listedOfAttrs = [] 87 | for (let key in this.$attrs) { 88 | listedOfAttrs.push(`${key}= ${this.$attrs[key]}`) 89 | } 90 | return listedOfAttrs 91 | } 92 | }, 93 | created() { 94 | this.mergeAttrs() 95 | }, 96 | render() { 97 | let computeClass = (props: ISBBadge) => { 98 | return ['badge', props.variant ? `bg-${props.variant}` : null, props.class, props.pill ? 'rounded-pill' : null] 99 | } 100 | 101 | return {(this.$slots as any).default()[0].children} 102 | } 103 | }) 104 | 105 | export default SBBadge 106 | -------------------------------------------------------------------------------- /src/components/breadcrumb/SBBreadcrumb.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | import { RouterLink } from 'vue-router' 3 | 4 | interface ISBBadge { 5 | items: TItems[] 6 | class?: string 7 | href?: string 8 | } 9 | 10 | type TItems = { 11 | text: string 12 | href?: string 13 | active?: boolean 14 | disabled?: boolean 15 | } 16 | 17 | type TVariant = 18 | | 'primary' 19 | | 'secondary' 20 | | 'success' 21 | | 'danger' 22 | | 'warning' 23 | | 'info' 24 | | 'light' 25 | | 'dark' 26 | | 'outline-primary' 27 | | 'outline-secondary' 28 | | 'outline-success' 29 | | 'outline-danger' 30 | | 'outline-warning' 31 | | 'outline-info' 32 | | 'outline-light' 33 | | 'outline-dark' 34 | | 'link' 35 | 36 | const SBBreadcrumb = defineComponent({ 37 | name: 'SBBreadcrumb', 38 | props: { 39 | items: { 40 | type: Array as PropType, 41 | required: true 42 | // default: [] 43 | }, 44 | variant: { 45 | type: String as PropType, 46 | required: false, 47 | default: 'secondary', 48 | validator: function (payload: string) { 49 | return ( 50 | [ 51 | 'primary', 52 | 'secondary', 53 | 'success', 54 | 'danger', 55 | 'warning', 56 | 'info', 57 | 'light', 58 | 'dark', 59 | 'outline-primary', 60 | 'outline-secondary', 61 | 'outline-success', 62 | 'outline-danger', 63 | 'outline-warning', 64 | 'outline-info', 65 | 'outline-light', 66 | 'outline-dark', 67 | 'link' 68 | ].indexOf(payload) !== -1 69 | ) 70 | } 71 | }, 72 | class: { 73 | type: String, 74 | required: false 75 | } 76 | }, 77 | methods: { 78 | mergeAttrs() { 79 | let listedOfAttrs = [] 80 | for (let key in this.$attrs) { 81 | listedOfAttrs.push(`${key}= ${this.$attrs[key]}`) 82 | } 83 | return listedOfAttrs 84 | } 85 | }, 86 | created() { 87 | this.mergeAttrs() 88 | }, 89 | render() { 90 | let computeClass = (props: ISBBadge, itemObj: TItems) => { 91 | return ['breadcrumb-item', props.class, itemObj.active ? 'active' : null] 92 | } 93 | 94 | let renderElementItems = this.items.map((value: TItems) => { 95 | if (value.href) { 96 | return ( 97 |
  • 98 | {value.text} 99 | {/* {value.text} */} 100 |
  • 101 | ) 102 | } else { 103 | return
  • {value.text}
  • 104 | } 105 | }) 106 | 107 | return ( 108 | 111 | ) 112 | } 113 | }) 114 | 115 | export default SBBreadcrumb 116 | -------------------------------------------------------------------------------- /src/components/button/SBButton.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, Fragment, PropType, ref } from 'vue' 2 | 3 | interface ISBButtonProps { 4 | disabled?: boolean 5 | toggle?: boolean 6 | pressed?: boolean 7 | block?: boolean 8 | pill?: boolean 9 | squared?: boolean 10 | active?: boolean 11 | size?: string 12 | type?: string 13 | variant?: string 14 | href?: string 15 | class?: string 16 | } 17 | 18 | type TVariant = 19 | | 'primary' 20 | | 'secondary' 21 | | 'success' 22 | | 'danger' 23 | | 'warning' 24 | | 'info' 25 | | 'light' 26 | | 'dark' 27 | | 'outline-primary' 28 | | 'outline-secondary' 29 | | 'outline-success' 30 | | 'outline-danger' 31 | | 'outline-warning' 32 | | 'outline-info' 33 | | 'outline-light' 34 | | 'outline-dark' 35 | | 'link' 36 | 37 | type SBButtononClick = { 38 | onClick?: (event: Event) => void 39 | [key: string]: any 40 | } 41 | 42 | const SBButton = defineComponent({ 43 | name: 'SBButton', 44 | props: { 45 | disabled: { 46 | type: Boolean, 47 | required: false 48 | }, 49 | toggle: { 50 | type: Boolean, 51 | required: false 52 | }, 53 | pressed: { 54 | type: Boolean, 55 | required: false 56 | }, 57 | block: { 58 | type: Boolean, 59 | required: false 60 | }, 61 | pill: { 62 | type: Boolean, 63 | required: false 64 | }, 65 | squared: { 66 | type: Boolean, 67 | required: false 68 | }, 69 | active: { 70 | type: Boolean, 71 | required: false 72 | }, 73 | size: { 74 | type: String as PropType<'sm' | 'md' | 'lg'>, 75 | required: false, 76 | default: 'md', 77 | validator: function (payload: string) { 78 | return ['sm', 'md', 'lg'].indexOf(payload) !== -1 79 | } 80 | }, 81 | type: { 82 | type: String as PropType<'button' | 'submit' | 'reset'>, 83 | required: false, 84 | default: 'button', 85 | validator: function (payload: string) { 86 | return ['button', 'submit', 'reset'].indexOf(payload) !== -1 87 | } 88 | }, 89 | variant: { 90 | type: String as PropType, 91 | required: false, 92 | validator: function (payload: string) { 93 | return ( 94 | [ 95 | 'primary', 96 | 'secondary', 97 | 'success', 98 | 'danger', 99 | 'warning', 100 | 'info', 101 | 'light', 102 | 'dark', 103 | 'outline-primary', 104 | 'outline-secondary', 105 | 'outline-success', 106 | 'outline-danger', 107 | 'outline-warning', 108 | 'outline-info', 109 | 'outline-light', 110 | 'outline-dark', 111 | 'link' 112 | ].indexOf(payload) !== -1 113 | ) 114 | } 115 | }, 116 | href: { 117 | type: String, 118 | required: false, 119 | // validator: function (payload: string) { 120 | // return ['#'].indexOf(payload) !== -1 121 | // } 122 | }, 123 | onClick: { 124 | type: Function, 125 | required: false, 126 | default: () => {} 127 | }, 128 | class: { 129 | type: String, 130 | required: false 131 | } 132 | }, 133 | methods: { 134 | emitToParent(event: MouseEvent) { 135 | this.onClick(event) 136 | } 137 | }, 138 | render() { 139 | const computeClass = (props: ISBButtonProps) => { 140 | return [ 141 | 'btn', 142 | props.class, 143 | props.variant ? `btn-${props.variant}` : null, 144 | { 145 | // [`btn-${props.size}`]: props.size, 146 | 'btn-block': props.block, 147 | 'rounded-pill': props.pill, 148 | // 'rounded-0': props.squared && !props.pill, 149 | disabled: props.disabled 150 | // active: props.pressed 151 | } 152 | ] 153 | } 154 | // const toggle = isToggle(props) 155 | // const link = isLink(props) 156 | // const nonStandardTag = isNonStandardTag(props) 157 | // const hashLink = link && props.href === '#' 158 | // const on = { 159 | // keydown(evt) { 160 | // // When the link is a `href="#"` or a non-standard tag (has `role="button"`), 161 | // // we add a keydown handlers for CODE_SPACE/CODE_ENTER 162 | // /* istanbul ignore next */ 163 | // if (props.disabled || !(nonStandardTag || hashLink)) { 164 | // return 165 | // } 166 | // const { keyCode } = evt 167 | // // Add CODE_SPACE handler for `href="#"` and CODE_ENTER handler for non-standard tags 168 | // if (keyCode === CODE_SPACE || (keyCode === CODE_ENTER && nonStandardTag)) { 169 | // const target = evt.currentTarget || evt.target 170 | // stopEvent(evt, { propagation: false }) 171 | // target.click() 172 | // } 173 | // }, 174 | // click(evt) { 175 | // /* istanbul ignore if: blink/button disabled should handle this */ 176 | // if (props.disabled && isEvent(evt)) { 177 | // stopEvent(evt) 178 | // } else if (toggle && listeners && listeners['update:pressed']) { 179 | // // Send `.sync` updates to any "pressed" prop (if `.sync` listeners) 180 | // // `concat()` will normalize the value to an array without 181 | // // double wrapping an array value in an array 182 | // concat(listeners['update:pressed']).forEach(fn => { 183 | // if (isFunction(fn)) { 184 | // fn(!props.pressed) 185 | // } 186 | // }) 187 | // } 188 | // } 189 | // } 190 | if ((this.$slots as any).default) { 191 | if ((this.$slots as any).default().length === 1) { 192 | if (typeof (this.$slots as any).default()[0].type === 'symbol') { 193 | if (this.onClick) { 194 | return ( 195 | 201 | ) 202 | } else { 203 | return ( 204 | 207 | ) 208 | } 209 | } else { 210 | // If there any nest element like loading component 211 | if (this.onClick) { 212 | return ( 213 | 219 | ) 220 | } else { 221 | return ( 222 | 225 | ) 226 | } 227 | } 228 | } 229 | 230 | if ((this.$slots as any).default().length === 2) { 231 | if (typeof (this.$slots as any).default()[0].type === 'symbol') { 232 | // console.log('true') 233 | if (this.onClick) { 234 | return ( 235 | 241 | ) 242 | } else { 243 | return ( 244 | 247 | ) 248 | } 249 | } else { 250 | // If there any nest element like loading component 251 | if (this.onClick) { 252 | return ( 253 | 259 | ) 260 | } else { 261 | return ( 262 | 265 | ) 266 | } 267 | } 268 | } 269 | } else { 270 | // 271 | return 272 | } 273 | } 274 | }) 275 | 276 | export default SBButton 277 | -------------------------------------------------------------------------------- /src/components/button/__tests__/SBButton.spec.ts: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import SBButtonCopy from '../SBButton' 3 | 4 | describe('SBButtonCopy Components', () => { 5 | it('renders button element to DOM node', () => { 6 | let wrapper = shallowMount(SBButtonCopy) 7 | 8 | expect(wrapper.vm).toBeDefined() 9 | }) 10 | 11 | it('button pill to true', () => { 12 | let wrapper = shallowMount(SBButtonCopy) 13 | 14 | wrapper 15 | .setProps({ 16 | pill: true 17 | }) 18 | .then(function () { 19 | expect(wrapper.vm.pill).toBe(true) 20 | }) 21 | .catch((err) => console.error(err)) 22 | }) 23 | 24 | it('button default html output', () => { 25 | let wrapper = shallowMount(SBButtonCopy) 26 | expect(wrapper.html()).toContain('') 27 | }) 28 | 29 | it('button type="reset" html output', () => { 30 | let wrapper = shallowMount(SBButtonCopy) 31 | wrapper 32 | .setProps({ 33 | type: 'reset' 34 | }) 35 | .then(function () { 36 | expect(wrapper.html()).toContain('') 37 | }) 38 | .catch((err) => console.error(err)) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /src/components/buttonGroup/SBButtonGroup.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType, VNode, defineProps, DefineComponent } from 'vue' 2 | 3 | interface ISBButtonGroup { 4 | vertical?: boolean 5 | size?: string 6 | ariaRole?: string 7 | type?: string 8 | variant?: string 9 | class?: string 10 | } 11 | 12 | type TVariant = 13 | | 'primary' 14 | | 'secondary' 15 | | 'success' 16 | | 'danger' 17 | | 'warning' 18 | | 'info' 19 | | 'light' 20 | | 'dark' 21 | | 'outline-primary' 22 | | 'outline-secondary' 23 | | 'outline-success' 24 | | 'outline-danger' 25 | | 'outline-warning' 26 | | 'outline-info' 27 | | 'outline-light' 28 | | 'outline-dark' 29 | | 'link' 30 | 31 | const TButtonGroupProps = defineProps({ 32 | vertical: { 33 | type: Boolean, 34 | required: false 35 | }, 36 | size: { 37 | type: String as PropType<'sm' | 'md' | 'lg'>, 38 | required: false, 39 | default: 'md', 40 | validator: function (payload: string) { 41 | return ['sm', 'md', 'lg'].indexOf(payload) !== -1 42 | } 43 | }, 44 | ariaRole: { 45 | type: String, 46 | required: false 47 | }, 48 | type: { 49 | type: String as PropType<'button' | 'submit' | 'reset'>, 50 | required: false, 51 | default: 'button', 52 | validator: function (payload: string) { 53 | return ['button', 'submit', 'reset'].indexOf(payload) !== -1 54 | } 55 | }, 56 | variant: { 57 | type: String as PropType, 58 | required: false, 59 | validator: function (payload: string) { 60 | return ['danger', 'success', 'outline-primary'].indexOf(payload) !== -1 61 | } 62 | }, 63 | class: { 64 | type: String, 65 | required: false 66 | } 67 | }) 68 | 69 | const SBButtonGroup = defineComponent({ 70 | name: 'SBButtonGroup', 71 | 72 | props: { 73 | vertical: { 74 | type: Boolean, 75 | required: false 76 | }, 77 | size: { 78 | type: String as PropType<'sm' | 'md' | 'lg'>, 79 | required: false, 80 | validator: function (payload: string) { 81 | return ['sm', 'md', 'lg'].indexOf(payload) !== -1 82 | } 83 | }, 84 | ariaRole: { 85 | type: String, 86 | required: false 87 | }, 88 | type: { 89 | type: String as PropType<'button' | 'submit' | 'reset'>, 90 | required: false, 91 | default: 'button', 92 | validator: function (payload: string) { 93 | return ['button', 'submit', 'reset'].indexOf(payload) !== -1 94 | } 95 | }, 96 | variant: { 97 | type: String as PropType, 98 | required: false, 99 | validator: function (payload: string) { 100 | return ['danger', 'success', 'outline-primary'].indexOf(payload) !== -1 101 | } 102 | }, 103 | class: { 104 | type: String, 105 | required: false 106 | } 107 | }, 108 | // setup(props: ISBButtonGroup, context) { 109 | // console.log({props, context}) 110 | 111 | // function mergeAttrs() { 112 | // let listed = [] 113 | // for (let key in context.attrs) { 114 | // console.log(key, context.attrs[key]) 115 | // listed.push(`${key}= ${context.attrs[key]}`) 116 | // } 117 | // console.log({listed}) 118 | // return listed 119 | // } 120 | 121 | // mergeAttrs() 122 | // return { 123 | // mergeAttrs, 124 | // props 125 | // } 126 | // }, 127 | methods: { 128 | mergeAttrs() { 129 | let listedOfAttrs = [] 130 | for (let key in this.$attrs) { 131 | listedOfAttrs.push(`${key}= ${this.$attrs[key]}`) 132 | } 133 | return listedOfAttrs 134 | } 135 | }, 136 | created() { 137 | this.mergeAttrs() 138 | }, 139 | render() { 140 | let renderChildElements = null 141 | let renderButtonGroup = null 142 | let computeClass = (props: ISBButtonGroup) => { 143 | if (props.vertical) { 144 | return [`btn-group-vertical`, props.size ? `btn-group-${props.size}` : null, props.class] 145 | } else { 146 | return ['btn-group', props.size ? `btn-group-${props.size}` : null, props.class] 147 | } 148 | } 149 | if ((this.$slots as any).default) { 150 | renderChildElements = (this.$slots as any).default().map((element: VNode) => { 151 | return element 152 | }) 153 | } 154 | if ((this.$props as any).ariaRole) { 155 | renderButtonGroup = ( 156 |
    161 | {renderChildElements} 162 |
    163 | ) 164 | } else { 165 | renderButtonGroup = ( 166 |
    167 | {renderChildElements} 168 |
    169 | ) 170 | } 171 | 172 | // return ( 173 | //
    174 | //

    My version

    175 | // {renderButtonGroup} 176 | 177 | //

    Real

    178 | 179 | //
    180 | // 181 | // 182 | 183 | //
    184 | // 187 | // 191 | //
    192 | //
    193 | 194 | //
    195 | // ) 196 | return renderButtonGroup 197 | } 198 | }) 199 | 200 | export default SBButtonGroup 201 | -------------------------------------------------------------------------------- /src/components/buttonGroup/exmaple.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue' 2 | import SBButtonGroup from './SBButtonGroup' 3 | import SBButton from '../button/SBButton' 4 | 5 | const Exmaple = defineComponent({ 6 | name: 'Exmaple', 7 | render() { 8 | return ( 9 | 10 | Left 11 | Middle 12 | Right 13 | 14 | ) 15 | } 16 | }) 17 | 18 | export default Exmaple 19 | -------------------------------------------------------------------------------- /src/components/card/Example.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/card/SBCard.tsx: -------------------------------------------------------------------------------- 1 | import { ComponentPublicInstance, defineComponent, PropType } from 'vue' 2 | // import SBButton from '../button/SBButton' 3 | 4 | interface ISBCardProps { 5 | title?: string 6 | imgSrc?: string 7 | imgTop?: boolean, 8 | imgBottom?: boolean, 9 | // imgEnd?: boolean, 10 | // imgLeft?: boolean, 11 | // imgRight?: boolean, 12 | imgWidth?: boolean, 13 | imgHeight?: boolean, 14 | imgAlt?: string, 15 | overlay?: boolean, 16 | borderVariant?: string, 17 | header?: string, 18 | headerBgVariant?: string, 19 | headerTextVariant?: string, 20 | align?: string, 21 | class?: string, 22 | id?: string, 23 | style?: string 24 | } 25 | 26 | // TODO: NOT DONE.. NEED MORE WORK ON... 27 | type TVariant = 28 | | 'primary' 29 | | 'secondary' 30 | | 'success' 31 | | 'danger' 32 | | 'warning' 33 | | 'info' 34 | | 'light' 35 | | 'dark' 36 | | 'outline-primary' 37 | | 'outline-secondary' 38 | | 'outline-success' 39 | | 'outline-danger' 40 | | 'outline-warning' 41 | | 'outline-info' 42 | | 'outline-light' 43 | | 'outline-dark' 44 | | 'link' 45 | | 'white' 46 | 47 | 48 | const SBCard = defineComponent({ 49 | name: 'SBCard', 50 | props: { 51 | title: { 52 | type: String, 53 | required: false 54 | }, 55 | imgSrc: { 56 | type: String, 57 | required: false 58 | }, 59 | imgTop: { 60 | type: Boolean, 61 | required: false 62 | }, 63 | imgBottom: { 64 | type: Boolean, 65 | required: false 66 | }, 67 | // imgEnd: { 68 | // type: Boolean, 69 | // required: false 70 | // }, 71 | // imgLeft: { 72 | // type: Boolean, 73 | // required: false 74 | // }, 75 | // imgRight: { 76 | // type: Boolean, 77 | // required: false 78 | // }, 79 | imgWidth: { 80 | type: Boolean, 81 | required: false 82 | }, 83 | imgHeight: { 84 | type: Boolean, 85 | required: false 86 | }, 87 | imgAlt: { 88 | type: String, 89 | required: false 90 | }, 91 | overlay: { 92 | type: Boolean, 93 | required: false 94 | }, 95 | borderVariant: { 96 | type: String as PropType, 97 | required: false, 98 | }, 99 | header: { 100 | type: String, 101 | required: false 102 | }, 103 | headerBgVariant: { 104 | type: String as PropType, 105 | required: false, 106 | }, 107 | headerTextVariant: { 108 | type: String as PropType, 109 | required: false, 110 | }, 111 | align: { 112 | type: String, 113 | required: false 114 | }, 115 | class: { 116 | type: String, 117 | required: false 118 | }, 119 | id: { 120 | type: String, 121 | required: false 122 | }, 123 | style: { 124 | type: String, 125 | required: false, 126 | default: "" 127 | }, 128 | 129 | }, 130 | data() { 131 | return { 132 | state: { 133 | listed: [{ id: 0, isShow: false, class: '', isCollapse: false }] 134 | } 135 | } 136 | }, 137 | methods: { 138 | // emitToParent(event: MouseEvent) { 139 | // this.onClick(event) 140 | // }, 141 | // handleShowCollapse(id: string) { 142 | // console.log({ id }) 143 | // } 144 | }, 145 | computed: { 146 | imgPositionClass(): string | null { 147 | if (this.imgTop) { 148 | return 'card-img-top' 149 | } else if (this.imgBottom) { 150 | return 'card-img-bottom' 151 | } 152 | return '' 153 | } 154 | }, 155 | render() { 156 | let computeClass = (props: ISBCardProps) => { 157 | return [ 158 | 'card', 159 | props.borderVariant ? props.borderVariant : null, 160 | props.class 161 | ] 162 | } 163 | let computeCardHeaderClass = (props: ISBCardProps) => { 164 | return [ 165 | 'card-header', 166 | props.headerBgVariant ? `bg-${props.headerBgVariant}` : null, 167 | props.headerTextVariant ? `text-${props.headerTextVariant}` : null 168 | ] 169 | } 170 | let renderNestedChildrenElement: JSX.Element | null = null 171 | let renderImgElement: JSX.Element | null = null 172 | 173 | if ((this as ComponentPublicInstance).$slots.default) { 174 | 175 | // For inside card 176 | if ( (this as any).$slots.default()[0].type.name === "SBListgroup") { 177 | renderNestedChildrenElement = ( 178 |
    179 | {(this as any).$slots.default()} 180 |
    181 | ) 182 | 183 | // Header and footer element 184 | } else if (this.header) { 185 | renderNestedChildrenElement = ( 186 |
    187 |
    188 | {this.header} 189 |
    190 | {(this as any).$slots.default()} 191 |
    192 | ) 193 | 194 | // For overlay or not 195 | } else { 196 | renderNestedChildrenElement = ( 197 |
    198 | {(this as any).$slots.default()} 199 |
    200 | ) 201 | } 202 | 203 | } 204 | 205 | if (this.imgSrc) { 206 | renderImgElement = ( 207 | {this.imgAlt 208 | ) 209 | } 210 | 211 | return ( 212 |
    213 | {renderImgElement} 214 | {renderNestedChildrenElement} 215 |
    216 | ) 217 | } 218 | }) 219 | 220 | export default SBCard 221 | -------------------------------------------------------------------------------- /src/components/card/SBCardHeader.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | 3 | interface ISBCardHeaderProps { 4 | imgSrc: string 5 | alt?: string 6 | top?: boolean 7 | bottom?: boolean 8 | start?: boolean 9 | left?: boolean 10 | end?: boolean 11 | right?: boolean 12 | height?: string | number 13 | width?: string | number 14 | [key: string]: any 15 | } 16 | type TVariant = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark' 17 | 18 | // TODO: NOT DONE.. NEED MORE WORK ON... 19 | 20 | const SBCardHeader = defineComponent({ 21 | name: 'SBCardHeader', 22 | props: { 23 | bgVariant: { 24 | type: String as PropType, 25 | required: true, 26 | validator: function (payload: string) { 27 | return ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'].indexOf(payload) !== -1 28 | } 29 | }, 30 | borderVariant: { 31 | type: String as PropType, 32 | required: false, 33 | validator: function (payload: string) { 34 | return ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'].indexOf(payload) !== -1 35 | } 36 | }, 37 | textVariant: { 38 | type: String as PropType, 39 | required: false, 40 | validator: function (payload: string) { 41 | return ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'].indexOf(payload) !== -1 42 | } 43 | }, 44 | class: { 45 | type: String, 46 | required: false 47 | }, 48 | style: { 49 | type: String, 50 | required: false 51 | } 52 | }, 53 | render() { 54 | let computeClass = (props: ISBCardHeaderProps) => { 55 | let cardImgPost = 'card-img-top' 56 | for (let key in props) { 57 | if (props[key] === true) { 58 | cardImgPost = `card-img-${key}` 59 | } 60 | } 61 | return [cardImgPost] 62 | } 63 | let computeStyle = (props: ISBCardHeaderProps) => { 64 | console.log('props', props) 65 | return '' 66 | } 67 | 68 | // return ( 69 | //
    70 | //

    MY VERISON

    71 | //

    REAL

    72 | // ... 73 | //
    74 | // ) 75 | 76 | return
    Card title
    77 | } 78 | }) 79 | 80 | export default SBCardHeader 81 | -------------------------------------------------------------------------------- /src/components/card/SBCardImg.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | 3 | interface ISBCardImgProps { 4 | imgSrc: string 5 | alt?: string 6 | top?: boolean 7 | bottom?: boolean 8 | start?: boolean 9 | left?: boolean 10 | end?: boolean 11 | right?: boolean 12 | height?: string | number 13 | width?: string | number 14 | [key: string]: any 15 | } 16 | 17 | // TODO: NOT DONE.. NEED MORE WORK ON... 18 | const SBCardImg = defineComponent({ 19 | name: 'SBCardImg', 20 | props: { 21 | imgSrc: { 22 | type: String, 23 | required: true 24 | }, 25 | alt: { 26 | type: String, 27 | required: false 28 | }, 29 | top: { 30 | type: Boolean, 31 | required: false 32 | }, 33 | bottom: { 34 | type: Boolean, 35 | required: false 36 | }, 37 | start: { 38 | type: Boolean, 39 | required: false 40 | }, 41 | left: { 42 | type: Boolean, 43 | required: false 44 | }, 45 | end: { 46 | type: Boolean, 47 | required: false 48 | }, 49 | right: { 50 | type: Boolean, 51 | required: false 52 | }, 53 | height: { 54 | type: String || Number, 55 | required: false 56 | }, 57 | width: { 58 | type: String || Number, 59 | required: false 60 | }, 61 | class: { 62 | type: String, 63 | required: false 64 | }, 65 | style: { 66 | type: String, 67 | required: false 68 | } 69 | }, 70 | render() { 71 | let computeClass = (props: ISBCardImgProps) => { 72 | let cardImgPost = 'card-img-top' 73 | for (let key in props) { 74 | if (props[key] === true) { 75 | cardImgPost = `card-img-${key}` 76 | } 77 | } 78 | return [cardImgPost] 79 | } 80 | let computeStyle = (props: ISBCardImgProps) => { 81 | console.log('props', props) 82 | return '' 83 | } 84 | 85 | // return ( 86 | //
    87 | //

    MY VERISON

    88 | //

    REAL

    89 | // ... 90 | //
    91 | // ) 92 | 93 | return {this.alt} 94 | } 95 | }) 96 | 97 | export default SBCardImg 98 | -------------------------------------------------------------------------------- /src/components/card/SBCardTitle.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, h } from 'vue' 2 | 3 | const SBCardTitle = defineComponent({ 4 | name: 'SBCardTitle', 5 | props: { 6 | 7 | /** Specify the HTML tag to render instead of the default tag for the text content */ 8 | textTag: { 9 | type: String, 10 | required: false, 11 | default: 'p' 12 | } 13 | }, 14 | render() { 15 | let renderElement = null 16 | 17 | if ((this as any).$slots.default) { 18 | if (this.textTag) { 19 | renderElement = h(this.textTag, { class: "card-title" } , (this as any).$slots.default()[0]) 20 | } else { 21 | renderElement = ( 22 |

    {(this as any).$slots.default()[0]}

    23 | ) 24 | } 25 | } 26 | 27 | return renderElement 28 | } 29 | }) 30 | 31 | export default SBCardTitle 32 | -------------------------------------------------------------------------------- /src/components/card/SBListgroup.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue' 2 | 3 | const SBListgroup = defineComponent({ 4 | name: 'SBListgroup', 5 | props: { 6 | flush: { 7 | type: Boolean, 8 | required: false 9 | } 10 | }, 11 | render() { 12 | let renderElement: JSX.Element | null = null 13 | 14 | if ((this as any).$slots.default) { 15 | renderElement = (this as any).$slots.default() 16 | } 17 | 18 | return ( 19 |
      20 | {renderElement} 21 |
    22 | ) 23 | } 24 | }) 25 | 26 | export default SBListgroup 27 | -------------------------------------------------------------------------------- /src/components/card/SBListgroupItem.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue' 2 | 3 | const SBListgroupItem = defineComponent({ 4 | name: 'SBListgroupItem', 5 | render() { 6 | let renderElement = null 7 | 8 | if ((this as any).$slots.default) { 9 | renderElement = ( 10 |
  • {(this as any).$slots.default()[0]}
  • 11 | ) 12 | } 13 | 14 | return renderElement 15 | } 16 | }) 17 | 18 | export default SBListgroupItem 19 | -------------------------------------------------------------------------------- /src/components/closeButton/SBCloseButton.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | 3 | interface ISBCloseButtonProps { 4 | disabled?: boolean 5 | onClick?: () => void 6 | class?: string 7 | } 8 | 9 | const SBCloseButton = defineComponent({ 10 | name: 'SBCloseButton', 11 | props: { 12 | disabled: { 13 | type: Boolean, 14 | required: false 15 | }, 16 | onClick: { 17 | type: Function as PropType<(event: MouseEvent) => void>, 18 | required: false, 19 | default: () => {} 20 | }, 21 | class: { 22 | type: String, 23 | required: false 24 | } 25 | }, 26 | methods: { 27 | emitToParent(event: MouseEvent) { 28 | this.onClick(event) 29 | } 30 | }, 31 | render() { 32 | let computeClass = (props: ISBCloseButtonProps) => { 33 | return [ 34 | 'btn-close', 35 | { 36 | disabled: props.disabled 37 | }, 38 | props.class 39 | ] 40 | } 41 | 42 | return ( 43 | 48 | ) 49 | } 50 | }) 51 | 52 | export default SBCloseButton 53 | -------------------------------------------------------------------------------- /src/components/dropDown/SBDropdown.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType, reactive } from 'vue' 2 | // import SBDropdownsBar from './SBDropdownsBar' 3 | 4 | interface SBDropdownsProps { 5 | text?: boolean 6 | split?: boolean 7 | size?: string 8 | precision?: boolean 9 | showProgress?: boolean 10 | showValue?: boolean 11 | variant?: string 12 | dropLeft?: boolean 13 | dropRight?: boolean 14 | dropUp?: boolean 15 | 16 | class?: string 17 | id?: string 18 | } 19 | 20 | type TVariant = 21 | | 'primary' 22 | | 'secondary' 23 | | 'success' 24 | | 'danger' 25 | | 'warning' 26 | | 'info' 27 | | 'light' 28 | | 'dark' 29 | | 'outline-primary' 30 | | 'outline-secondary' 31 | | 'outline-success' 32 | | 'outline-danger' 33 | | 'outline-warning' 34 | | 'outline-info' 35 | | 'outline-light' 36 | | 'outline-dark' 37 | | 'link' 38 | 39 | const SBDropdowns = defineComponent({ 40 | name: 'SBDropdowns', 41 | props: { 42 | text: { 43 | type: String, 44 | required: false 45 | }, 46 | split: { 47 | type: Boolean, 48 | required: false 49 | }, 50 | size: { 51 | type: String as PropType<'sm' | 'md' | 'lg'>, 52 | required: false, 53 | default: 'md' 54 | // validator: function (payload: string) { 55 | // return ['sm', 'md', 'lg'].indexOf(payload) !== -1 56 | // } 57 | }, 58 | dropLeft: { 59 | type: Boolean, 60 | required: false 61 | }, 62 | dropRight: { 63 | type: Boolean, 64 | required: false 65 | }, 66 | dropUp: { 67 | type: Boolean, 68 | required: false 69 | }, 70 | variant: { 71 | type: String as PropType, 72 | required: false, 73 | default: 'secondary' 74 | // validator: function (payload: string) { 75 | // return ( 76 | // [ 77 | // 'primary', 78 | // 'secondary', 79 | // 'success', 80 | // 'danger', 81 | // 'warning', 82 | // 'info', 83 | // 'light', 84 | // 'dark', 85 | // 'outline-primary', 86 | // 'outline-secondary', 87 | // 'outline-success', 88 | // 'outline-danger', 89 | // 'outline-warning', 90 | // 'outline-info', 91 | // 'outline-light', 92 | // 'outline-dark', 93 | // 'link' 94 | // ].indexOf(payload) !== -1 95 | // ) 96 | // }, 97 | }, 98 | class: { 99 | type: String, 100 | required: false 101 | }, 102 | id: { 103 | type: String, 104 | required: false 105 | } 106 | }, 107 | data() { 108 | return { 109 | state: { 110 | isShow: false 111 | } 112 | } 113 | }, 114 | methods: { 115 | handleSetShow() { 116 | this.state.isShow = !this.state.isShow 117 | } 118 | }, 119 | computed: { 120 | // computeClassForDropdown() { 121 | 122 | // }, 123 | classForDropdownPositions() { 124 | if (this.dropLeft) { 125 | return 'dropstart' 126 | } 127 | if (this.dropRight) { 128 | return 'dropend' 129 | } 130 | if (this.dropUp) { 131 | return 'dropup' 132 | } 133 | return null 134 | } 135 | }, 136 | render() { 137 | let computeClassForDropdown = (props: SBDropdownsProps) => { 138 | return [ 139 | 'btn', 140 | 'dropdown-toggle', 141 | props.variant ? `btn-${props.variant}` : 'btn-secondary', 142 | props.size ? `btn-${props.size}` : 'btn-md', 143 | // props.striped ? 'progress-bar-striped' : null, 144 | // props.animated ? 'progress-bar-animated' : null, 145 | { show: this.state.isShow }, 146 | props.class 147 | ] 148 | } 149 | let computeClassForDropdownSplit = (props: SBDropdownsProps) => { 150 | return [ 151 | 'btn', 152 | 'dropdown-toggle', 153 | 'dropdown-toggle-split', 154 | props.variant ? `btn-${props.variant}` : 'btn-secondary', 155 | props.size ? `btn-${props.size}` : 'btn-md', 156 | { show: this.state.isShow }, 157 | props.class 158 | ] 159 | } 160 | let renderDropdownElement = null 161 | let renderDropdownSplitElement = null 162 | 163 | if (this.split) { 164 | renderDropdownSplitElement = ( 165 |
    166 | 169 | 176 | 201 |
    202 | ) 203 | } else { 204 | console.log('{(this.$slots as any).default()[0]}', (this.$slots as any).default()) 205 | renderDropdownElement = ( 206 |
    207 | 215 |
      {(this.$slots as any).default()}
    216 |
    217 | ) 218 | } 219 | // if ((this.$slots as any).default) { 220 | // // 221 | // // 222 | // // 223 | // renderDropdownElement = ( 224 | //
    225 | // {(this.$slots as any).default()} 226 | //
    227 | // ) 228 | // } else { 229 | // renderDropdownElement = ( 230 | //
    231 | // 232 | // {this.label ? `${this.label}%` : null} 233 | // 234 | //
    235 | // ) 236 | // } 237 | 238 | return ( 239 |
    240 |

    My

    241 | {/* {renderDropdownSplitElement} */} 242 | {renderDropdownElement} 243 | {/* */} 253 | 254 |

    Example

    255 |
    256 | 259 | 266 | 291 |
    292 |
    293 | ) 294 | } 295 | }) 296 | 297 | export default SBDropdowns 298 | -------------------------------------------------------------------------------- /src/components/dropDown/SBDropdownItem.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | 3 | interface ISBDropdownItemProps { 4 | href?: string 5 | rel?: string 6 | target?: string 7 | active?: boolean 8 | disabled?: boolean 9 | to?: string | object 10 | append?: boolean 11 | replace?: boolean 12 | activeClass?: string 13 | exact?: boolean 14 | exactActiveClass?: string 15 | style?: string 16 | id?: string 17 | linkClasses?: string 18 | class?: string 19 | } 20 | 21 | // TODO: Need to more work on link or vue-router. Not sure about it. Or just element only 22 | const SBDropdownItem = defineComponent({ 23 | name: 'SBDropdownItem', 24 | props: { 25 | href: { 26 | type: String, 27 | required: false, 28 | default: '' 29 | }, 30 | rel: { 31 | type: String, 32 | require: false 33 | }, 34 | target: { 35 | type: String, 36 | required: false 37 | }, 38 | active: { 39 | type: Boolean, 40 | required: false 41 | }, 42 | disabled: { 43 | type: Boolean, 44 | required: false 45 | }, 46 | to: { 47 | type: String || Object, 48 | required: false 49 | }, 50 | append: { 51 | type: Boolean, 52 | required: false 53 | }, 54 | replace: { 55 | type: Boolean, 56 | required: false 57 | }, 58 | activeClass: { 59 | type: String, 60 | required: false 61 | }, 62 | exact: { 63 | type: Boolean, 64 | required: false 65 | }, 66 | exactActiveClass: { 67 | type: String, 68 | required: false 69 | }, 70 | style: { 71 | type: String, 72 | required: false 73 | }, 74 | id: { 75 | type: String, 76 | required: false 77 | }, 78 | size: { 79 | type: String as PropType<'sm' | 'md' | 'lg'>, 80 | required: false 81 | }, 82 | linkClasses: { 83 | type: String, 84 | required: false 85 | }, 86 | class: { 87 | type: String, 88 | required: false 89 | } 90 | }, 91 | // methods: { 92 | // handleRouteTo(event: Event, urlPath: string) { 93 | // console.log('enter',event, urlPath, ) 94 | // console.log(this.href) 95 | // // this.$router.push({ 96 | // // path: this.href 97 | // // }) 98 | // // this.$router.push(this.href) 99 | // } 100 | // }, 101 | setup(props) { 102 | // route.push({}) 103 | function handleRouteTo(event: Event, urlPath: string) { 104 | event.preventDefault() 105 | // window.location.assign(props.href) 106 | // event.preventDefault() 107 | // console.log(route) 108 | // console.log(router) 109 | 110 | // router.push(props.href) 111 | // console.log(this.href) 112 | // this.$router.push({ 113 | // path: this.href 114 | // }) 115 | // this.$router.push(this.href) 116 | } 117 | 118 | return { 119 | handleRouteTo 120 | } 121 | }, 122 | render() { 123 | let renderNavItem = null 124 | let computeClass = (props: ISBDropdownItemProps) => { 125 | return ['dropdown-item', props.active ? 'active' : null, props.class] 126 | } 127 | 128 | if ((this.$slots as any).default()) { 129 | renderNavItem = ( 130 |
  • 131 | this.handleRouteTo(event, this.href)}> 135 | {(this.$slots as any).default()[0]} 136 | 137 | 138 | {/* this.handleRouteTo(event, this.href)}> 139 | {(this.$slots as any).default()[0]} 140 | */} 141 | {/* */} 142 |
  • 143 | ) 144 | } 145 | 146 | return renderNavItem 147 | } 148 | }) 149 | 150 | export default SBDropdownItem 151 | -------------------------------------------------------------------------------- /src/components/dropDown/example.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/components/form/SBForm.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | 3 | interface ISBFormProps { 4 | imgSrc: string 5 | alt?: string 6 | top?: boolean 7 | bottom?: boolean 8 | start?: boolean 9 | left?: boolean 10 | end?: boolean 11 | right?: boolean 12 | height?: string | number 13 | width?: string | number 14 | [key: string]: any 15 | } 16 | 17 | const SBForm = defineComponent({ 18 | name: 'SBForm', 19 | props: { 20 | imgSrc: { 21 | type: String, 22 | required: false 23 | }, 24 | alt: { 25 | type: String, 26 | required: false 27 | }, 28 | top: { 29 | type: Boolean, 30 | required: false 31 | }, 32 | bottom: { 33 | type: Boolean, 34 | required: false 35 | }, 36 | start: { 37 | type: Boolean, 38 | required: false 39 | }, 40 | left: { 41 | type: Boolean, 42 | required: false 43 | }, 44 | end: { 45 | type: Boolean, 46 | required: false 47 | }, 48 | right: { 49 | type: Boolean, 50 | required: false 51 | }, 52 | height: { 53 | type: String || Number, 54 | required: false 55 | }, 56 | width: { 57 | type: String || Number, 58 | required: false 59 | }, 60 | class: { 61 | type: String, 62 | required: false 63 | }, 64 | style: { 65 | type: String, 66 | required: false 67 | } 68 | }, 69 | render() { 70 | let computeClass = (props: ISBFormProps) => { 71 | let cardImgPost = 'card-img-top' 72 | for (let key in props) { 73 | if (props[key] === true) { 74 | cardImgPost = `card-img-${key}` 75 | } 76 | } 77 | return [cardImgPost] 78 | } 79 | let computeStyle = (props: ISBFormProps) => { 80 | console.log('props', props) 81 | return '' 82 | } 83 | 84 | return ( 85 |
    86 |

    MY VERISON

    87 |

    REAL

    88 | 89 | 98 |
    99 | ) 100 | 101 | // return ( 102 | // {this.alt} 103 | // ) 104 | } 105 | }) 106 | 107 | export default SBForm 108 | -------------------------------------------------------------------------------- /src/components/form/SBFormGroup.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | 3 | interface ISBFormGroupProps { 4 | imgSrc: string 5 | alt?: string 6 | top?: boolean 7 | bottom?: boolean 8 | start?: boolean 9 | left?: boolean 10 | end?: boolean 11 | right?: boolean 12 | height?: string | number 13 | width?: string | number 14 | [key: string]: any 15 | } 16 | 17 | const SBFormGroup = defineComponent({ 18 | name: 'SBFormGroup', 19 | props: { 20 | imgSrc: { 21 | type: String, 22 | required: false 23 | }, 24 | alt: { 25 | type: String, 26 | required: false 27 | }, 28 | top: { 29 | type: Boolean, 30 | required: false 31 | }, 32 | bottom: { 33 | type: Boolean, 34 | required: false 35 | }, 36 | start: { 37 | type: Boolean, 38 | required: false 39 | }, 40 | left: { 41 | type: Boolean, 42 | required: false 43 | }, 44 | end: { 45 | type: Boolean, 46 | required: false 47 | }, 48 | right: { 49 | type: Boolean, 50 | required: false 51 | }, 52 | height: { 53 | type: String || Number, 54 | required: false 55 | }, 56 | width: { 57 | type: String || Number, 58 | required: false 59 | }, 60 | class: { 61 | type: String, 62 | required: false 63 | }, 64 | style: { 65 | type: String, 66 | required: false 67 | } 68 | }, 69 | render() { 70 | let computeClass = (props: ISBFormGroupProps) => { 71 | let cardImgPost = 'card-img-top' 72 | for (let key in props) { 73 | if (props[key] === true) { 74 | cardImgPost = `card-img-${key}` 75 | } 76 | } 77 | return [cardImgPost] 78 | } 79 | let computeStyle = (props: ISBFormGroupProps) => { 80 | console.log('props', props) 81 | return '' 82 | } 83 | 84 | return ( 85 |
    86 |

    MY VERISON

    87 |

    REAL

    88 | 89 | 98 |
    99 | ) 100 | 101 | // return ( 102 | // {this.alt} 103 | // ) 104 | } 105 | }) 106 | 107 | export default SBFormGroup 108 | -------------------------------------------------------------------------------- /src/components/form/SBFormInput.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | 3 | interface ISBFormInputProps { 4 | placeholder?: string 5 | modelValue?: string 6 | type?: string 7 | class?: string 8 | style?: string 9 | min?: string | number 10 | max?: string | number 11 | step?: string | number 12 | id?: string 13 | disabled?: boolean 14 | size?: string 15 | } 16 | 17 | type TInputType = 18 | | 'text' 19 | | 'password' 20 | | 'email' 21 | | 'number' 22 | | 'url' 23 | | 'tel' 24 | | 'search' 25 | | 'range' 26 | | 'color' 27 | | 'date' 28 | | 'time' 29 | | 'datetime' 30 | | 'datetime-local' 31 | | 'month' 32 | | 'week' 33 | 34 | const emitsType = { 35 | updateModelValue: 'update:modelValue' 36 | } 37 | 38 | const SBFormInput = defineComponent({ 39 | name: 'SBFormInput', 40 | props: { 41 | placeholder: { 42 | type: String, 43 | required: false 44 | }, 45 | modelValue: { 46 | type: String, 47 | required: false 48 | }, 49 | type: { 50 | type: String as PropType, 51 | required: false, 52 | default: 'text', 53 | validator: function (payload: string) { 54 | return ( 55 | [ 56 | 'text', 57 | 'password', 58 | 'email', 59 | 'number', 60 | 'url', 61 | 'tel', 62 | 'search', 63 | 'range', 64 | 'color', 65 | 'date', 66 | 'time', 67 | 'datetime', 68 | 'datetime-local', 69 | 'month', 70 | 'week' 71 | ].indexOf(payload) !== -1 72 | ) 73 | } 74 | }, 75 | value: { 76 | type: String, 77 | require: false 78 | }, 79 | class: { 80 | type: String, 81 | required: false 82 | }, 83 | style: { 84 | type: String, 85 | required: false 86 | }, 87 | min: { 88 | type: String || Number, 89 | required: false 90 | }, 91 | max: { 92 | type: String || Number, 93 | required: false 94 | }, 95 | step: { 96 | type: String || Number, 97 | required: false 98 | }, 99 | id: { 100 | type: String, 101 | required: false, 102 | default: null 103 | }, 104 | disabled: { 105 | type: Boolean, 106 | required: false 107 | }, 108 | size: { 109 | type: String as PropType<'sm' | 'md' | 'lg'>, 110 | required: false 111 | }, 112 | onInput: Function 113 | }, 114 | emits: { 115 | [emitsType.updateModelValue]: function (payload: string) { 116 | if (typeof payload === 'string') { 117 | return true 118 | } else { 119 | console.warn('Invalid submit event payload!') 120 | return false 121 | } 122 | } 123 | }, 124 | methods: { 125 | handleEmitValue(event: Event) { 126 | let result = (event.target as HTMLInputElement).value 127 | this.$emit(emitsType.updateModelValue, result) 128 | 129 | // If user use jsx/tsx emit $event 130 | if (this.onInput) { 131 | this.onInput(event) 132 | } 133 | } 134 | }, 135 | render() { 136 | console.log(this) 137 | let computeClass = (props: ISBFormInputProps) => { 138 | if (props.type === 'range') { 139 | return ['form-range', props.class] 140 | } else { 141 | return ['form-control', props.class, props.size ? `form-control-${props.size}` : null] 142 | } 143 | } 144 | let computeStyle = (props: ISBFormInputProps) => { 145 | // console.log('props', props) 146 | return '' 147 | } 148 | 149 | if (this.type === 'range') { 150 | return ( 151 |
    152 |

    MY VERISON

    153 | 166 |

    REAL

    167 | 168 | 171 | 172 |
    173 | ) 174 | } else { 175 | return ( 176 |
    177 |

    MY VERISON

    178 | 188 |

    REALs

    189 | 190 |
    191 | 192 | @ 193 | 194 | 201 |
    202 |
    203 | ) 204 | } 205 | 206 | // return ( 207 | // {this.alt} 208 | // ) 209 | } 210 | }) 211 | 212 | export default SBFormInput 213 | -------------------------------------------------------------------------------- /src/components/form/SBFormRadios.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, Fragment, PropType } from 'vue' 2 | 3 | interface ISBFormRadiosProps { 4 | modelValue?: string 5 | value?: string 6 | class?: string 7 | style?: string 8 | name?: string 9 | id?: string 10 | for?: string 11 | 12 | disabled?: boolean 13 | size?: string 14 | type?: string 15 | } 16 | 17 | type TOptionsItem = { 18 | value?: string 19 | text?: string 20 | disabled?: boolean 21 | label?: string 22 | options: TOptionsItem[] 23 | } 24 | 25 | const emitsType = { 26 | updateModelValue: 'update:modelValue' 27 | } 28 | 29 | const SBFormRadios = defineComponent({ 30 | name: 'SBFormRadios', 31 | props: { 32 | // options: { 33 | // type: Array as PropType, 34 | // required: true 35 | // }, 36 | modelValue: { 37 | type: String, 38 | required: false 39 | }, 40 | value: { 41 | type: String, 42 | require: false 43 | }, 44 | class: { 45 | type: String, 46 | required: false 47 | }, 48 | style: { 49 | type: String, 50 | required: false 51 | }, 52 | name: { 53 | type: String, 54 | required: false 55 | }, 56 | id: { 57 | type: String, 58 | required: false 59 | }, 60 | for: { 61 | type: String, 62 | required: false 63 | }, 64 | disabled: { 65 | type: Boolean, 66 | required: false 67 | }, 68 | size: { 69 | type: String as PropType<'sm' | 'md' | 'lg'>, 70 | required: false 71 | }, 72 | type: { 73 | type: String as PropType<'checkbox' | 'radio'>, 74 | required: false, 75 | default: 'checkbox', 76 | validator: function (payload: string) { 77 | return ['checkbox', 'radio'].indexOf(payload) !== -1 78 | } 79 | } 80 | }, 81 | emits: { 82 | [emitsType.updateModelValue]: function (payload: string) { 83 | if (typeof payload === 'string') { 84 | return true 85 | } else { 86 | console.warn('Invalid submit event payload!') 87 | return false 88 | } 89 | } 90 | }, 91 | methods: { 92 | handleEmitValue(event: Event) { 93 | let result: any = (event.target as HTMLInputElement).value 94 | this.$emit(emitsType.updateModelValue, result) 95 | } 96 | }, 97 | render() { 98 | // console.log(this.$slots.default) 99 | let computeClass = (props: ISBFormRadiosProps) => { 100 | return ['form-check', props.class, props.size ? `form-select-${props.size}` : null] 101 | } 102 | let computeStyle = (props: ISBFormRadiosProps) => { 103 | // console.log('props', props) 104 | return '' 105 | } 106 | if (this.$slots.default) { 107 | return ( 108 |
    109 | 118 | 121 |
    122 | ) 123 | } else { 124 | return ( 125 |
    126 | 135 | 139 |
    140 | ) 141 | } 142 | 143 | // let renderOptionItems = this.options.map((value: TOptionsItem) => { 144 | // console.log({value}) 145 | 146 | // // option groups 147 | // if (value.options) { 148 | // let renderNestOptionItem = value.options.map((value2: TOptionsItem) => { 149 | // return 150 | // }) 151 | 152 | // return ( 153 | // 154 | // 155 | // 156 | // {renderNestOptionItem} 157 | // 158 | // 159 | // ) 160 | // } else { 161 | // return ( 162 | // 163 | // ) 164 | // } 165 | // }) 166 | // return ( 167 | //
    168 | // {/*

    MY VERISON

    */} 169 | // 170 | // 173 | //
    174 | // ) 175 | } 176 | }) 177 | 178 | export default SBFormRadios 179 | -------------------------------------------------------------------------------- /src/components/form/SBFormRadiosGroup.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, Fragment, PropType } from 'vue' 2 | import SBFormRadios from './SBFormRadios' 3 | 4 | interface ISBFormRadiosGroupProps { 5 | modelValue?: string 6 | required?: boolean 7 | form?: string 8 | autofocus?: boolean 9 | options?: {} | [] 10 | checked?: any 11 | validated?: boolean 12 | stacked?: boolean 13 | buttons?: boolean 14 | value?: string | number 15 | class?: string 16 | style?: string 17 | name?: string 18 | id?: string 19 | disabled?: boolean 20 | size?: string 21 | label?: string 22 | } 23 | 24 | type TOptionsItem = { 25 | value?: string 26 | text?: string 27 | disabled?: boolean 28 | label?: string 29 | options: TOptionsItem[] 30 | } 31 | 32 | const emitsType = { 33 | updateModelValue: 'update:modelValue' 34 | } 35 | 36 | const SBFormRadiosGroup = defineComponent({ 37 | name: 'SBFormRadiosGroup', 38 | props: { 39 | options: { 40 | type: Array as PropType, 41 | required: true 42 | }, 43 | modelValue: { 44 | type: String, 45 | required: false 46 | }, 47 | value: { 48 | type: String, 49 | require: false 50 | }, 51 | class: { 52 | type: String, 53 | required: false 54 | }, 55 | style: { 56 | type: String, 57 | required: false 58 | }, 59 | name: { 60 | type: String, 61 | required: false 62 | }, 63 | id: { 64 | type: String, 65 | required: false 66 | }, 67 | disabled: { 68 | type: Boolean, 69 | required: false 70 | }, 71 | size: { 72 | type: String as PropType<'sm' | 'md' | 'lg'>, 73 | required: false 74 | }, 75 | label: { 76 | type: String, 77 | required: false 78 | } 79 | }, 80 | emits: { 81 | [emitsType.updateModelValue]: function (payload: string) { 82 | if (typeof payload === 'string') { 83 | return true 84 | } else { 85 | console.warn('Invalid submit event payload!') 86 | return false 87 | } 88 | } 89 | }, 90 | methods: { 91 | handleEmitValue(event: Event) { 92 | let result: any = (event.target as HTMLInputElement).value 93 | console.log({ result }) 94 | this.$emit(emitsType.updateModelValue, result) 95 | } 96 | }, 97 | render() { 98 | console.log(this) 99 | let computeClass = (props: ISBFormRadiosGroupProps) => { 100 | return ['form-select', props.class, props.size ? `form-select-${props.size}` : null] 101 | } 102 | let computeStyle = (props: ISBFormRadiosGroupProps) => { 103 | // console.log('props', props) 104 | return '' 105 | } 106 | 107 | let renderRadiosItems = this.options.map((value: TOptionsItem) => { 108 | // console.log({value}) 109 | 110 | // option groups 111 | // if (value.options) { 112 | // let renderNestOptionItem = value.options.map((value2: TOptionsItem) => { 113 | // return 114 | // }) 115 | 116 | // return ( 117 | // 118 | // 119 | // 120 | // {renderNestOptionItem} 121 | // 122 | // 123 | // ) 124 | // } else { 125 | // return ( 126 | // 127 | // ) 128 | // } 129 | return ( 130 | {value.text} 131 | // 132 | ) 133 | }) 134 | 135 | return ( 136 |
    137 |

    MY VERISON

    138 |
    139 | {this.label} 140 | {renderRadiosItems} 141 | {/* 142 | */} 145 |
    146 | 147 | {/*
    148 | 149 | 152 |
    */} 153 | {/*
    Selected: {this.value}
    */} 154 | 155 |

    REAL

    156 | 157 | {/*
    158 | 159 | 162 |
    163 |
    164 | 165 | 168 |
    */} 169 |
    170 | ) 171 | } 172 | }) 173 | 174 | export default SBFormRadiosGroup 175 | -------------------------------------------------------------------------------- /src/components/form/SBFormSelect.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, Fragment, PropType } from 'vue' 2 | 3 | interface ISBFormSelectProps { 4 | options: TOptionsItem[] 5 | modelValue?: string 6 | type?: string 7 | class?: string 8 | style?: string 9 | min?: string | number 10 | max?: string | number 11 | step?: string | number 12 | id?: string 13 | disabled?: boolean 14 | size?: string 15 | } 16 | 17 | type TOptionsItem = { 18 | value?: any 19 | text?: string 20 | disabled?: boolean 21 | label?: string 22 | options?: TOptionsItem[] 23 | } 24 | 25 | const emitsType = { 26 | updateModelValue: 'update:modelValue' 27 | } 28 | 29 | const SBFormSelect = defineComponent({ 30 | name: 'SBFormSelect', 31 | props: { 32 | options: { 33 | type: Array as PropType, 34 | required: true 35 | }, 36 | modelValue: { 37 | type: String, 38 | required: false 39 | }, 40 | value: { 41 | type: String, 42 | require: false 43 | }, 44 | class: { 45 | type: String, 46 | required: false 47 | }, 48 | style: { 49 | type: String, 50 | required: false 51 | }, 52 | disabled: { 53 | type: Boolean, 54 | required: false 55 | }, 56 | size: { 57 | type: String as PropType<'sm' | 'md' | 'lg'>, 58 | required: false 59 | }, 60 | onChange: { 61 | type: Function, 62 | required: false 63 | } 64 | }, 65 | emits: { 66 | [emitsType.updateModelValue]: function (payload: string) { 67 | if (typeof payload === 'string') { 68 | return true 69 | } else { 70 | console.warn('Invalid submit event payload!') 71 | return false 72 | } 73 | } 74 | }, 75 | setup(props, { attrs, slots, expose, emit }) { 76 | console.log({ attrs, slots, expose, emit }) 77 | }, 78 | methods: { 79 | handleEmitValue(event: Event) { 80 | let result: any = (event.target as HTMLInputElement).value 81 | this.$emit(emitsType.updateModelValue, result) 82 | 83 | // If user use jsx/tsx emit $event 84 | if (this.onChange) { 85 | this.onChange(event) 86 | } 87 | } 88 | }, 89 | render() { 90 | console.log(this.options) 91 | let computeClass = (props: ISBFormSelectProps) => { 92 | return ['form-select', props.class, props.size ? `form-select-${props.size}` : null] 93 | } 94 | let computeStyle = (props: ISBFormSelectProps) => { 95 | // console.log('props', props) 96 | return '' 97 | } 98 | 99 | let renderOptionItems = this.options.map((value: TOptionsItem) => { 100 | // console.log({ value }) 101 | 102 | // option groups 103 | if (value.options) { 104 | let renderNestOptionItem = value.options.map((value2: TOptionsItem) => { 105 | return ( 106 | 109 | ) 110 | }) 111 | 112 | return ( 113 | 114 | 117 | {renderNestOptionItem} 118 | 119 | ) 120 | } else { 121 | return ( 122 | 125 | ) 126 | } 127 | }) 128 | return ( 129 | 132 | ) 133 | } 134 | }) 135 | 136 | export default SBFormSelect 137 | -------------------------------------------------------------------------------- /src/components/form/example-SBFormSelect.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 51 | -------------------------------------------------------------------------------- /src/components/layoutAndGridSystem/SBCol.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | 3 | interface ISBColProps { 4 | cols?: string 5 | widths?: string 6 | sm?: boolean 7 | md?: boolean 8 | lg?: boolean 9 | xl?: boolean 10 | alignSelf?: string 11 | order?: string 12 | orderSmall?: string 13 | orderMiddle?: string 14 | orderLarge?: string 15 | offset?: string 16 | offsetSmall?: string 17 | offsetMiddle?: string 18 | offsetLarge?: string 19 | class?: string 20 | style?: string 21 | [key: string]: any 22 | } 23 | 24 | const SBCol = defineComponent({ 25 | name: 'SBCol', 26 | props: { 27 | cols: { 28 | type: String as PropType<'1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '10' | '11' | '12'>, 29 | required: false 30 | }, 31 | widths: { 32 | type: String as PropType<'sm' | 'md' | 'lg' | 'xl' | 'xxl'>, 33 | required: false 34 | }, 35 | sm: { 36 | type: Boolean, 37 | required: false 38 | }, 39 | md: { 40 | type: Boolean, 41 | required: false 42 | }, 43 | lg: { 44 | type: Boolean, 45 | required: false 46 | }, 47 | xl: { 48 | type: Boolean, 49 | required: false 50 | }, 51 | /** horizontal alignment */ 52 | alignSelf: { 53 | type: String as PropType<'start' | 'center' | 'end' | 'baseline' | 'stretch'>, 54 | required: false 55 | }, 56 | order: { 57 | type: String as PropType<'0' | '1' | '2' | '3' | '4' | '5' | 'first' | 'last'>, 58 | required: false 59 | }, 60 | orderSmall: { 61 | type: String as PropType<'0' | '1' | '2' | '3' | '4' | '5' | 'first' | 'last'>, 62 | required: false 63 | }, 64 | orderMiddle: { 65 | type: String as PropType<'0' | '1' | '2' | '3' | '4' | '5' | 'first' | 'last'>, 66 | required: false 67 | }, 68 | orderLarge: { 69 | type: String as PropType<'0' | '1' | '2' | '3' | '4' | '5' | 'first' | 'last'>, 70 | required: false 71 | }, 72 | offset: { 73 | type: String as PropType<'0' | '1' | '2' | '3' | '4' | '5' | 'first' | 'last'>, 74 | required: false 75 | }, 76 | offsetSmall: { 77 | type: String as PropType<'0' | '1' | '2' | '3' | '4' | '5' | 'first' | 'last'>, 78 | required: false 79 | }, 80 | offsetMiddle: { 81 | type: String as PropType<'0' | '1' | '2' | '3' | '4' | '5' | 'first' | 'last'>, 82 | required: false 83 | }, 84 | offsetLarge: { 85 | type: String as PropType<'0' | '1' | '2' | '3' | '4' | '5' | 'first' | 'last'>, 86 | required: false 87 | }, 88 | class: { 89 | type: String, 90 | required: false 91 | }, 92 | style: { 93 | type: String, 94 | required: false 95 | } 96 | }, 97 | render() { 98 | let computeClass = (props: ISBColProps) => { 99 | let containerTypeClass = '' 100 | let alignSelfTypeClass = '' 101 | let orderTypeClass = '' 102 | let offsetTypeClass = '' 103 | if (this.cols) { 104 | containerTypeClass = `col-${this.cols}` 105 | } else if (this.sm) { 106 | containerTypeClass = 'col-sm' 107 | } else if (this.md) { 108 | containerTypeClass = 'col-md' 109 | } else if (this.lg) { 110 | containerTypeClass = 'col-lg' 111 | } else if (this.xl) { 112 | containerTypeClass = 'col-xl' 113 | } else { 114 | containerTypeClass = 'col' 115 | } 116 | if (this.alignSelf) { 117 | alignSelfTypeClass = `align-self-${this.alignSelf}` 118 | } 119 | if (this.order) { 120 | orderTypeClass = `order-${this.order}` 121 | } else if (this.orderSmall) { 122 | orderTypeClass = `order-sm-${this.orderSmall}` 123 | } else if (this.orderMiddle) { 124 | orderTypeClass = `order-md-${this.orderMiddle}` 125 | } else if (this.orderLarge) { 126 | orderTypeClass = `order-lg-${this.orderLarge}` 127 | } 128 | if (this.offset) { 129 | offsetTypeClass = `offset-${this.offset}` 130 | } else if (this.offsetSmall) { 131 | offsetTypeClass = `offset-sm-${this.offsetSmall}` 132 | } else if (this.offsetMiddle) { 133 | offsetTypeClass = `offset-md-${this.offsetMiddle}` 134 | } else if (this.offsetLarge) { 135 | offsetTypeClass = `offset-lg-${this.offsetLarge}` 136 | } 137 | return [containerTypeClass, props.class, alignSelfTypeClass, orderTypeClass, offsetTypeClass] 138 | } 139 | 140 | let computeStyle = (props: ISBColProps) => { 141 | // console.log('props', props) 142 | return '' 143 | } 144 | let renderContainer = null 145 | 146 | if (this.$slots.default) { 147 | console.log(this.$slots.default()) 148 | renderContainer = ( 149 |
    150 | {this.$slots.default()} 151 |
    152 | ) 153 | } 154 | 155 | return renderContainer 156 | } 157 | }) 158 | 159 | export default SBCol 160 | -------------------------------------------------------------------------------- /src/components/layoutAndGridSystem/SBContainer.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | 3 | interface ISBContainerProps { 4 | fluid?: boolean 5 | widths?: string 6 | tag?: string 7 | class?: string 8 | style?: string 9 | [key: string]: any 10 | } 11 | 12 | const SBContainer = defineComponent({ 13 | name: 'SBContainer', 14 | props: { 15 | fluid: { 16 | type: Boolean, 17 | required: false 18 | }, 19 | widths: { 20 | type: String as PropType<'sm' | 'md' | 'lg' | 'xl' | 'xxl'>, 21 | required: false 22 | }, 23 | tag: { 24 | type: String, 25 | required: false 26 | }, 27 | class: { 28 | type: String, 29 | required: false 30 | }, 31 | style: { 32 | type: String, 33 | required: false 34 | } 35 | }, 36 | render() { 37 | let computeClass = (props: ISBContainerProps) => { 38 | let containerType = '' 39 | if (this.widths) { 40 | containerType = `container-${this.widths}` 41 | } else if (this.fluid) { 42 | containerType = 'container-fluid' 43 | } else { 44 | containerType = 'container' 45 | } 46 | return [containerType, props.class] 47 | } 48 | 49 | let computeStyle = (props: ISBContainerProps) => { 50 | console.log('props', props) 51 | return '' 52 | } 53 | let renderContainer = null 54 | 55 | if (this.$slots.default) { 56 | console.log(this.$slots.default()) 57 | renderContainer = ( 58 |
    59 | {this.$slots.default()} 60 |
    61 | ) 62 | } 63 | 64 | return renderContainer 65 | } 66 | }) 67 | 68 | export default SBContainer 69 | -------------------------------------------------------------------------------- /src/components/layoutAndGridSystem/SBRow.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | 3 | interface ISBRowProps { 4 | fluid?: boolean 5 | widths?: string 6 | align?: string 7 | justify?: string 8 | tag?: string 9 | class?: string 10 | style?: string 11 | [key: string]: any 12 | } 13 | 14 | const SBRow = defineComponent({ 15 | name: 'SBRow', 16 | props: { 17 | fluid: { 18 | type: Boolean, 19 | required: false 20 | }, 21 | widths: { 22 | type: String as PropType<'sm' | 'md' | 'lg' | 'xl' | 'xxl'>, 23 | required: false 24 | }, 25 | align: { 26 | type: String as PropType<'start' | 'center' | 'end' | 'baseline' | 'stretch'>, 27 | required: false 28 | }, 29 | justify: { 30 | type: String as PropType<'start' | 'center' | 'end' | 'baseline' | 'around' | 'between' | 'evenly'>, 31 | required: false 32 | }, 33 | tag: { 34 | type: String, 35 | required: false 36 | }, 37 | class: { 38 | type: String, 39 | required: false 40 | }, 41 | style: { 42 | type: String, 43 | required: false 44 | } 45 | }, 46 | render() { 47 | let computeClass = (props: ISBRowProps) => { 48 | let containerType = '' 49 | let alignType = '' 50 | let justifyType = '' 51 | if (this.widths) { 52 | containerType = `container-${this.widths}` 53 | } else if (this.fluid) { 54 | containerType = 'container-fluid' 55 | } else { 56 | containerType = 'row' 57 | } 58 | if (this.align) { 59 | alignType = `align-items-${this.align}` 60 | } 61 | if (this.justify) { 62 | justifyType = `justify-content-${this.justify}` 63 | } 64 | return [containerType, props.class, alignType, justifyType] 65 | } 66 | let renderContainer = null 67 | 68 | if (this.$slots.default) { 69 | console.log(this.$slots.default()) 70 | renderContainer = ( 71 |
    72 | {this.$slots.default()} 73 |
    74 | ) 75 | } 76 | 77 | return renderContainer 78 | } 79 | }) 80 | 81 | export default SBRow 82 | -------------------------------------------------------------------------------- /src/components/nav/SBNav.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, Fragment, PropType } from 'vue' 2 | import SBNavLink from './SBNavLink' 3 | 4 | interface ISBNavProps { 5 | // options: TOptionsItem[], 6 | fill?: boolean 7 | justified?: boolean 8 | align?: string 9 | tabs?: boolean 10 | pills?: boolean 11 | vertical?: boolean 12 | class?: string 13 | style?: string 14 | id?: string 15 | size?: string 16 | } 17 | 18 | type TOptionsItem = { 19 | value?: string 20 | text?: string 21 | disabled?: boolean 22 | label?: string 23 | options: TOptionsItem[] 24 | } 25 | 26 | const emitsType = { 27 | updateModelValue: 'update:modelValue' 28 | } 29 | 30 | const SBNav = defineComponent({ 31 | name: 'SBNav', 32 | props: { 33 | fill: { 34 | type: Boolean, 35 | required: false 36 | }, 37 | justified: { 38 | type: Boolean, 39 | require: false 40 | }, 41 | align: { 42 | type: String, 43 | required: false 44 | }, 45 | tabs: { 46 | type: Boolean, 47 | required: false 48 | }, 49 | pills: { 50 | type: Boolean, 51 | required: false 52 | }, 53 | vertical: { 54 | type: Boolean, 55 | required: false 56 | }, 57 | class: { 58 | type: String, 59 | required: false 60 | }, 61 | style: { 62 | type: String, 63 | required: false 64 | }, 65 | id: { 66 | type: String, 67 | required: false 68 | }, 69 | size: { 70 | type: String as PropType<'sm' | 'md' | 'lg'>, 71 | required: false 72 | } 73 | }, 74 | emits: { 75 | [emitsType.updateModelValue]: function (payload: string) { 76 | if (typeof payload === 'string') { 77 | return true 78 | } else { 79 | console.warn('Invalid submit event payload!') 80 | return false 81 | } 82 | } 83 | }, 84 | methods: { 85 | handleEmitValue(event: Event) { 86 | let result: any = (event.target as HTMLInputElement).value 87 | this.$emit(emitsType.updateModelValue, result) 88 | } 89 | }, 90 | render() { 91 | let renderNav = null 92 | let computeClass = (props: ISBNavProps) => { 93 | return ['nav', props.vertical ? 'flex-column' : null, props.class] 94 | } 95 | 96 | if ((this.$slots as any).default()) { 97 | renderNav =
      {(this.$slots as any).default()}
    98 | } 99 | 100 | return
    {renderNav}
    101 | } 102 | }) 103 | 104 | export default SBNav 105 | -------------------------------------------------------------------------------- /src/components/nav/SBNavLink.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | 3 | interface ISBNavLinkProps { 4 | href?: string 5 | rel?: string 6 | target?: string 7 | active?: boolean 8 | disabled?: boolean 9 | to?: string | object 10 | append?: boolean 11 | replace?: boolean 12 | activeClass?: string 13 | exact?: boolean 14 | exactActiveClass?: string 15 | style?: string 16 | id?: string 17 | linkClasses?: string 18 | class?: string 19 | } 20 | 21 | const SBNavLink = defineComponent({ 22 | name: 'SBNavLink', 23 | props: { 24 | href: { 25 | type: String, 26 | required: false, 27 | default: '' 28 | }, 29 | rel: { 30 | type: String, 31 | require: false 32 | }, 33 | target: { 34 | type: String, 35 | required: false 36 | }, 37 | active: { 38 | type: Boolean, 39 | required: false 40 | }, 41 | disabled: { 42 | type: Boolean, 43 | required: false 44 | }, 45 | to: { 46 | type: String || Object, 47 | required: false 48 | }, 49 | append: { 50 | type: Boolean, 51 | required: false 52 | }, 53 | replace: { 54 | type: Boolean, 55 | required: false 56 | }, 57 | activeClass: { 58 | type: String, 59 | required: false 60 | }, 61 | exact: { 62 | type: Boolean, 63 | required: false 64 | }, 65 | exactActiveClass: { 66 | type: String, 67 | required: false 68 | }, 69 | style: { 70 | type: String, 71 | required: false 72 | }, 73 | id: { 74 | type: String, 75 | required: false 76 | }, 77 | size: { 78 | type: String as PropType<'sm' | 'md' | 'lg'>, 79 | required: false 80 | }, 81 | linkClasses: { 82 | type: String, 83 | required: false 84 | }, 85 | class: { 86 | type: String, 87 | required: false 88 | } 89 | }, 90 | render() { 91 | let renderNavItem = null 92 | let computeClass = (props: ISBNavLinkProps) => { 93 | return ['nav-link', props.active ? 'active' : null, props.class] 94 | } 95 | 96 | if ((this.$slots as any).default()) { 97 | renderNavItem = ( 98 | 103 | ) 104 | } 105 | 106 | return renderNavItem 107 | } 108 | }) 109 | 110 | export default SBNavLink 111 | -------------------------------------------------------------------------------- /src/components/pagination/Pagination.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, Fragment, PropType } from 'vue' 2 | 3 | import './pagination.css' 4 | 5 | interface ISBSBPagination { 6 | align?: string 7 | perPage?: string | number, 8 | totalRows?: string | number, 9 | ariacontrols?: string 10 | ariaLabel?: string 11 | disabled?: boolean 12 | labelFirstPage?: string, 13 | labelLastPage?: string, 14 | labelNextPage?: string, 15 | labelPrevPage?: string, 16 | firstText?: string, 17 | lastText?: string, 18 | nextText?: string, 19 | prevText?: string, 20 | limit?: string | number, 21 | size?: string, 22 | class?: string, 23 | modelValue?: string 24 | id?: string, 25 | pills?: boolean 26 | } 27 | 28 | type TVariant = 29 | | 'primary' 30 | | 'secondary' 31 | | 'success' 32 | | 'danger' 33 | | 'warning' 34 | | 'info' 35 | | 'light' 36 | | 'dark' 37 | | 'outline-primary' 38 | | 'outline-secondary' 39 | | 'outline-success' 40 | | 'outline-danger' 41 | | 'outline-warning' 42 | | 'outline-info' 43 | | 'outline-light' 44 | | 'outline-dark' 45 | | 'link' 46 | 47 | type TItems = { 48 | isActive?: boolean 49 | rowVariant?: string 50 | cellVariant?: { 51 | [key: string]: string 52 | } 53 | [key: string]: any 54 | } 55 | 56 | type TFields = { 57 | key?: string 58 | sortable?: boolean 59 | }[] 60 | 61 | type Partial = { 62 | [P in keyof T]?: T[P] 63 | } 64 | interface Test1 { 65 | [key: string]: any 66 | } 67 | interface Test2 extends Test1 { 68 | [key: string]: any 69 | } 70 | // Partial 71 | 72 | const emitsType = { 73 | updateModelValue: 'update:modelValue' 74 | } 75 | 76 | const SBPagination = defineComponent({ 77 | name: 'SBPagination', 78 | props: { 79 | // fields: { 80 | // type: Array as PropType, 81 | // required: true 82 | // }, 83 | // items: { 84 | // type: Array as PropType, 85 | // required: true 86 | // }, 87 | modelValue: { 88 | type: [Number, String] as PropType, 89 | required: false 90 | }, 91 | align: { 92 | type: String as PropType<'left' | 'center' | 'right' | 'fill'>, 93 | required: false, 94 | default: 'left' 95 | }, 96 | perPage: { 97 | type: [Number, String] as PropType, 98 | required: false, 99 | }, 100 | totalRows: { 101 | type: [Number, String] as PropType, 102 | required: false, 103 | default: 0 104 | }, 105 | ariacontrols: { 106 | type: String, 107 | required: false 108 | }, 109 | ariaLabel: { 110 | type: String, 111 | required: false 112 | }, 113 | disabled: { 114 | type: Boolean, 115 | required: false 116 | }, 117 | labelFirstPage: { 118 | type: String, 119 | required: false 120 | }, 121 | labelLastPage: { 122 | type: String, 123 | required: false 124 | }, 125 | labelNextPage: { 126 | type: String, 127 | required: false 128 | }, 129 | labelPrevPage: { 130 | type: String, 131 | required: false 132 | }, 133 | firstText: { 134 | type: String, 135 | required: false 136 | }, 137 | lastText: { 138 | type: String, 139 | required: false 140 | }, 141 | nextText: { 142 | type: String, 143 | required: false 144 | }, 145 | prevText: { 146 | type: String, 147 | required: false 148 | }, 149 | limit: { 150 | type: String || Number, 151 | required: false 152 | }, 153 | size: { 154 | type: String as PropType<'sm' | 'md' | 'lg'>, 155 | required: false 156 | }, 157 | class: { 158 | type: String, 159 | required: false 160 | }, 161 | id: { 162 | type: String, 163 | required: false 164 | }, 165 | pills: { 166 | type: Boolean, 167 | required: false 168 | }, 169 | onInput: Function, 170 | onChange: Function 171 | }, 172 | emits: { 173 | [emitsType.updateModelValue]: function (payload: string) { 174 | if (typeof payload === 'string') { 175 | return true 176 | } else { 177 | console.warn('Invalid submit event payload!') 178 | return false 179 | } 180 | } 181 | }, 182 | methods: { 183 | handleEmitValue(event: Event) { 184 | let result = (event.target as HTMLInputElement).value 185 | this.$emit(emitsType.updateModelValue, result) 186 | 187 | // If user use jsx/tsx emit $event 188 | if (this.onInput) { 189 | this.onInput(event) 190 | } else if (this.onChange) { 191 | this.onChange(event) 192 | } 193 | } 194 | }, 195 | computed: { 196 | computeClass(): unknown[] { 197 | return [ 198 | 'pagination', 199 | this.alignClass, 200 | this.pills ? 'sb-pagination-pills' : null, 201 | this.size ? `pagination-${this.size}` : null, 202 | this.class ? this.class : null 203 | ] 204 | }, 205 | alignClass(): string | null { 206 | if (this.align === 'left') { 207 | return null 208 | } else if (this.align === 'center') { 209 | return 'justify-content-center' 210 | } else if (this.align === 'right') { 211 | return 'justify-content-end' 212 | } else if (this.align === 'fill') { 213 | return 'text-center' 214 | } 215 | return null 216 | } 217 | // CurrentItems(): any { 218 | // let me: any = [] 219 | 220 | // me.push(this.items.map((value)=> Object.getOwnPropertyNames(value))) 221 | 222 | // // this.items.forEach((value) => { 223 | // // console.log('value', value) 224 | // // // for (let [key, value1] of Object.entries(value)) { 225 | // // // // for (let [key, value1] of Object.entries(value)) { 226 | // // // console.log(`${key}: ${value1}`) 227 | // // // me. 228 | 229 | // // // // } 230 | // // // } 231 | // // }) 232 | // // // console.log('me', ) 233 | // // // me.map((value) => value) 234 | // // return '' 235 | // // me.map((value) => value.f) 236 | // } 237 | }, 238 | render() { 239 | type ArrayElem = A extends Array ? Elem : never 240 | 241 | function elemT(array: T): Array> { 242 | return array as any 243 | } 244 | // console.log(this.CurrentItems) 245 | // let computeClass = (props: ISBSBPagination) => { 246 | // return [ 247 | // 'btn-close', 248 | // { 249 | // // disabled: props.disabled 250 | // }, 251 | // props.class 252 | // ] 253 | // } 254 | let amountOfPaginationRequired: number = Number(this.totalRows) / Number(this.perPage) 255 | 256 | // return ( 257 | // 262 | // ) 263 | let renderPageItemElement = [] 264 | for (let i = 1; i < amountOfPaginationRequired + 1; i++) { 265 | renderPageItemElement.push(( 266 | 267 |
  • 268 | 269 |
  • 270 |
    271 | )) 272 | } 273 | let renderPrevTextElement = this.prevText ? ( 274 |
  • 275 | 278 |
  • 279 | ) : null 280 | 281 | let renderLastTextElement = this.lastText ? ( 282 |
  • 283 | 286 |
  • 287 | ) : null 288 | 289 | 290 | return ( 291 |
    308 | ) 309 | } 310 | }) 311 | 312 | export default SBPagination 313 | -------------------------------------------------------------------------------- /src/components/pagination/pagination.css: -------------------------------------------------------------------------------- 1 | /* .b-pagination-pills { 2 | .page-item { 3 | .page-link { 4 | border-radius: 50rem !important; 5 | margin-left: 0.25rem; 6 | line-height: 1; 7 | } 8 | 9 | &:first-child { 10 | .page-link { 11 | margin-left: 0; 12 | } 13 | } 14 | } 15 | } */ 16 | 17 | .sb-pagination-pills > .page-item > .page-link { 18 | border-radius: 50rem; 19 | margin-left: 0.25rem; 20 | line-height: 1; 21 | } -------------------------------------------------------------------------------- /src/components/progress/SBProgress.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType, reactive } from 'vue' 2 | import SBProgressBar from './SBProgressBar' 3 | 4 | interface SBProgressProps { 5 | striped?: boolean 6 | animated?: boolean 7 | height?: string 8 | precision?: boolean 9 | showProgress?: boolean 10 | showValue?: boolean 11 | variant?: string 12 | class?: string 13 | max?: string | number 14 | value?: string | number 15 | label?: string | number 16 | } 17 | 18 | type TVariant = 19 | | 'primary' 20 | | 'secondary' 21 | | 'success' 22 | | 'danger' 23 | | 'warning' 24 | | 'info' 25 | | 'light' 26 | | 'dark' 27 | | 'outline-primary' 28 | | 'outline-secondary' 29 | | 'outline-success' 30 | | 'outline-danger' 31 | | 'outline-warning' 32 | | 'outline-info' 33 | | 'outline-light' 34 | | 'outline-dark' 35 | | 'link' 36 | 37 | const SBProgress = defineComponent({ 38 | name: 'SBProgress', 39 | props: { 40 | striped: { 41 | type: Boolean, 42 | required: false 43 | }, 44 | animated: { 45 | type: Boolean, 46 | required: false 47 | }, 48 | height: { 49 | type: String, 50 | required: false 51 | }, 52 | precision: { 53 | type: Boolean, 54 | required: false 55 | }, 56 | showProgress: { 57 | type: Boolean, 58 | required: false 59 | }, 60 | showValue: { 61 | type: Boolean, 62 | required: false 63 | }, 64 | variant: { 65 | type: String as PropType, 66 | required: false, 67 | default: 'info' 68 | // validator: function (payload: string) { 69 | // return ( 70 | // [ 71 | // 'primary', 72 | // 'secondary', 73 | // 'success', 74 | // 'danger', 75 | // 'warning', 76 | // 'info', 77 | // 'light', 78 | // 'dark', 79 | // 'outline-primary', 80 | // 'outline-secondary', 81 | // 'outline-success', 82 | // 'outline-danger', 83 | // 'outline-warning', 84 | // 'outline-info', 85 | // 'outline-light', 86 | // 'outline-dark', 87 | // 'link' 88 | // ].indexOf(payload) !== -1 89 | // ) 90 | // } 91 | }, 92 | max: { 93 | type: String || Number, 94 | required: false, 95 | default: '100' 96 | }, 97 | value: Number, 98 | label: Number 99 | }, 100 | setup() { 101 | let state = reactive({ 102 | countDown: 0 103 | // If initially shown, we need to set these for SSR 104 | // localShow: parseShow(this.show) 105 | }) 106 | return { 107 | state 108 | } 109 | }, 110 | render() { 111 | let computeClass = (props: SBProgressProps) => { 112 | return [ 113 | 'progress-bar', 114 | props.variant ? `bg-${props.variant}` : null, 115 | props.striped ? 'progress-bar-striped' : null, 116 | props.animated ? 'progress-bar-animated' : null, 117 | props.class 118 | ] 119 | } 120 | let renderProgressElement = null 121 | if ((this.$slots as any).default) { 122 | // 123 | // 124 | // 125 | renderProgressElement = ( 126 |
    127 | {(this.$slots as any).default()} 128 |
    129 | ) 130 | } else { 131 | renderProgressElement = ( 132 |
    133 | 134 | {this.label ? `${this.label}%` : null} 135 | 136 |
    137 | ) 138 | } 139 | return renderProgressElement 140 | } 141 | }) 142 | 143 | export default SBProgress 144 | -------------------------------------------------------------------------------- /src/components/progress/SBProgressBar.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType, reactive } from 'vue' 2 | 3 | interface SBProgressBarProps { 4 | striped?: boolean 5 | animated?: boolean 6 | precision?: boolean 7 | showProgress?: boolean 8 | showValue?: boolean 9 | variant?: string 10 | class?: string 11 | max?: string | number 12 | value?: string | number 13 | label?: string | number 14 | } 15 | 16 | type TVariant = 17 | | 'primary' 18 | | 'secondary' 19 | | 'success' 20 | | 'danger' 21 | | 'warning' 22 | | 'info' 23 | | 'light' 24 | | 'dark' 25 | | 'outline-primary' 26 | | 'outline-secondary' 27 | | 'outline-success' 28 | | 'outline-danger' 29 | | 'outline-warning' 30 | | 'outline-info' 31 | | 'outline-light' 32 | | 'outline-dark' 33 | | 'link' 34 | 35 | const SBProgressBar = defineComponent({ 36 | name: 'SBProgressBar', 37 | props: { 38 | striped: { 39 | type: Boolean, 40 | required: false 41 | }, 42 | animated: { 43 | type: Boolean, 44 | required: false 45 | }, 46 | precision: { 47 | type: Boolean, 48 | required: false 49 | }, 50 | showProgress: { 51 | type: Boolean, 52 | required: false 53 | }, 54 | showValue: { 55 | type: Boolean, 56 | required: false 57 | }, 58 | variant: { 59 | type: String as PropType, 60 | required: false, 61 | default: 'info' 62 | // validator: function (payload: string) { 63 | // return ( 64 | // [ 65 | // 'primary', 66 | // 'secondary', 67 | // 'success', 68 | // 'danger', 69 | // 'warning', 70 | // 'info', 71 | // 'light', 72 | // 'dark', 73 | // 'outline-primary', 74 | // 'outline-secondary', 75 | // 'outline-success', 76 | // 'outline-danger', 77 | // 'outline-warning', 78 | // 'outline-info', 79 | // 'outline-light', 80 | // 'outline-dark', 81 | // 'link' 82 | // ].indexOf(payload) !== -1 83 | // ) 84 | // } 85 | }, 86 | max: { 87 | type: String || Number, 88 | required: false, 89 | default: '100' 90 | }, 91 | value: Number, 92 | label: Number 93 | }, 94 | render() { 95 | let computeClass = (props: SBProgressBarProps) => { 96 | return [ 97 | 'progress-bar', 98 | props.variant ? `bg-${props.variant}` : null, 99 | props.striped ? 'progress-bar-striped' : null, 100 | props.animated ? 'progress-bar-animated' : null, 101 | props.class 102 | ] 103 | } 104 | let renderAlertElement = null 105 | 106 | // if ((this.$slots as any).default) { 107 | // // Nested child elements 108 | // if ((this.$slots as any).default().length !== 1) { 109 | // console.log('(this.$slots as any).default', (this.$slots as any).default()) 110 | // renderAlertElement = (this.$slots as any).default().map((element: any) => { 111 | // // console.log('element', element) 112 | // return element 113 | // }) 114 | // } 115 | 116 | // // renderAlertElement = ( 117 | // // 120 | // // ) 121 | // } 122 | 123 | // // return ( 124 | // // 127 | // // ) 128 | 129 | return ( 130 | 137 | {this.label ? `${this.label}%` : null} 138 | 139 | ) 140 | } 141 | }) 142 | 143 | export default SBProgressBar 144 | -------------------------------------------------------------------------------- /src/components/progress/example.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/components/spinners/SBSpinners.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | 3 | interface ISBSpinners { 4 | label?: string 5 | type?: string 6 | small?: boolean 7 | variant?: string 8 | class?: string 9 | } 10 | 11 | type TVariant = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark' 12 | 13 | const SBSpinners = defineComponent({ 14 | name: 'SBSpinners', 15 | props: { 16 | label: { 17 | type: String, 18 | required: false 19 | }, 20 | type: { 21 | type: String as PropType<'grow'>, 22 | required: false 23 | }, 24 | small: { 25 | type: Boolean, 26 | required: false 27 | }, 28 | variant: { 29 | type: String as PropType, 30 | required: false, 31 | validator: function (payload: string) { 32 | return ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'].indexOf(payload) !== -1 33 | } 34 | }, 35 | class: { 36 | type: String, 37 | required: false 38 | } 39 | }, 40 | methods: { 41 | mergeAttrs() { 42 | let listedOfAttrs = [] 43 | for (let key in this.$attrs) { 44 | listedOfAttrs.push(`${key}= ${this.$attrs[key]}`) 45 | } 46 | return listedOfAttrs 47 | } 48 | }, 49 | created() { 50 | this.mergeAttrs() 51 | }, 52 | render() { 53 | let computeClass = (props: ISBSpinners) => { 54 | return [ 55 | props.type ? 'spinner-grow' : 'spinner-border', 56 | props.small ? 'spinner-border-sm' : null, 57 | props.variant ? `text-${props.variant}` : null, 58 | props.class 59 | ] 60 | } 61 | // let renderLabel = this.$props.label ? this.$props.label : "Loading..." 62 | // let renderSlot = null 63 | // if ( (this.$slots as any).default) { 64 | // console.log('true') 65 | // renderSlot = (this.$slots as any).default()[0].children !== '' ? (this.$slots as any).default()[0].children : false 66 | // } 67 | // // console.log('object', (this.$slots as any).default()[0]) 68 | 69 | // console.log({renderLabel, renderSlot}) 70 | // if (renderSlot === null) { 71 | // return ( 72 | //
    73 | // {renderLabel} 74 | //
    75 | // ) 76 | // } else { 77 | // console.log(' (this.$slots as any).default()[0].children', (this.$slots as any).default()[0].children) 78 | // return ( 79 | //
    80 | // {renderSlot} 81 | //
    82 | // ) 83 | // } 84 | return ( 85 |
    86 | {this.$props.label ? this.$props.label : 'Loading...'} 87 |
    88 | ) 89 | } 90 | }) 91 | 92 | export default SBSpinners 93 | -------------------------------------------------------------------------------- /src/components/table/SBTable.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, Fragment, PropType } from 'vue' 2 | 3 | import './table.css' 4 | 5 | interface ISBTable { 6 | fields: string[] 7 | items?: TItems[] 8 | striped?: boolean 9 | hover?: boolean 10 | class?: string, 11 | perPage?: string | number, 12 | currentPage?: string | number, 13 | dark?: boolean 14 | } 15 | 16 | type TVariant = 17 | | 'primary' 18 | | 'secondary' 19 | | 'success' 20 | | 'danger' 21 | | 'warning' 22 | | 'info' 23 | | 'light' 24 | | 'dark' 25 | | 'outline-primary' 26 | | 'outline-secondary' 27 | | 'outline-success' 28 | | 'outline-danger' 29 | | 'outline-warning' 30 | | 'outline-info' 31 | | 'outline-light' 32 | | 'outline-dark' 33 | | 'link' 34 | 35 | type TItems = { 36 | isActive?: boolean 37 | rowVariant?: string 38 | cellVariant?: { 39 | [key: string]: string 40 | } 41 | [key: string]: any 42 | } 43 | 44 | type TFields = { 45 | key?: string 46 | sortable?: boolean 47 | }[] 48 | 49 | type Partial = { 50 | [P in keyof T]?: T[P] 51 | } 52 | interface Test1 { 53 | [key: string]: any 54 | } 55 | interface Test2 extends Test1 { 56 | [key: string]: any 57 | } 58 | // Partial 59 | const SBTable = defineComponent({ 60 | name: 'SBTable', 61 | props: { 62 | fields: { 63 | // type: Array as PropType, 64 | required: true 65 | }, 66 | items: { 67 | type: Array as PropType, 68 | required: true 69 | }, 70 | striped: { 71 | type: Boolean, 72 | required: false 73 | }, 74 | hover: { 75 | type: Boolean, 76 | required: false 77 | }, 78 | class: { 79 | type: String, 80 | required: false 81 | }, 82 | perPage: { 83 | type: [Number, String] as PropType, 84 | required: false, 85 | }, 86 | currentPage: { 87 | type: [Number, String] as PropType, 88 | required: false, 89 | default: 1 90 | }, 91 | dark: { 92 | type: Boolean, 93 | required: false 94 | } 95 | }, 96 | methods: { 97 | // emitToParent(event: MouseEvent) { 98 | // this.onClick(event) 99 | // } 100 | }, 101 | computed: { 102 | computeClass(): unknown[] { 103 | return ['table', this.striped ? 'table-striped' : null, this.hover ? 'table-hover' : null, this.class] 104 | }, 105 | // CurrentItems(): any { 106 | // let me: any = [] 107 | 108 | // me.push(this.items.map((value)=> Object.getOwnPropertyNames(value))) 109 | 110 | // // this.items.forEach((value) => { 111 | // // console.log('value', value) 112 | // // // for (let [key, value1] of Object.entries(value)) { 113 | // // // // for (let [key, value1] of Object.entries(value)) { 114 | // // // console.log(`${key}: ${value1}`) 115 | // // // me. 116 | 117 | // // // // } 118 | // // // } 119 | // // }) 120 | // // // console.log('me', ) 121 | // // // me.map((value) => value) 122 | // // return '' 123 | // // me.map((value) => value.f) 124 | // } 125 | paginationFilterList() { 126 | if (this.currentPage && this.perPage) { 127 | let list: any[] = Object.assign([], this.items) 128 | let currentPageNumber = 1 129 | if (this.currentPage !== 1) { 130 | // console.log('(Number(this.currentPage) - 1)', this.currentPage) 131 | 132 | let startSplice: number = ((Number(this.currentPage) - 1) * Number(this.perPage)) 133 | let endSplice: number = startSplice + Number(this.perPage) 134 | return list.slice(startSplice, endSplice) 135 | } else { 136 | let startSplice: number = ((Number(this.currentPage)) * Number(this.perPage)) 137 | let endSplice: number = startSplice + Number(this.perPage) 138 | // let list: any[] = [...this.items] 139 | 140 | // console.log('startSplice', startSplice, 'endSplice', endSplice) 141 | // console.log('(Number(this.currentPage) - 1)', (Number(this.currentPage) - 1)) 142 | // list.slice(3) 143 | list.splice(Number(this.perPage)) 144 | // list.splice(startSplice, endSplice) 145 | // console.log({list}) 146 | return list 147 | } 148 | 149 | } 150 | return [] 151 | } 152 | }, 153 | render() { 154 | 155 | type ArrayElem = A extends Array ? Elem : never 156 | 157 | function elemT(array: T): Array> { 158 | return array as any 159 | } 160 | // console.log(this.CurrentItems) 161 | // let computeClass = (props: ISBTable) => { 162 | // return [ 163 | // 'btn-close', 164 | // { 165 | // // disabled: props.disabled 166 | // }, 167 | // props.class 168 | // ] 169 | // } 170 | 171 | // return ( 172 | // 177 | // ) 178 | let renderTableRowHeader = null 179 | let renderTableRowItems = null 180 | 181 | if (this.fields) { 182 | renderTableRowHeader = elemT(this.fields).map((value: any, index: any) => { 183 | // console.log({value}) 184 | 185 | // TODO: sortable feature. WIP... 186 | if (value.key) { 187 | // return ( 188 | // {value.key} 189 | // ) 190 | } else { 191 | // only ['Ages', 'First', 'Last'] 192 | return ( 193 | 194 | {value} 195 | 196 | ) 197 | } 198 | }) 199 | } 200 | if (this.items) { 201 | 202 | if (this.perPage && this.currentPage) { 203 | // With pagination mode 204 | // console.log('TODO: With pagination mode') 205 | // console.log(this.paginationFilterList) 206 | renderTableRowItems = this.paginationFilterList.map((value: any, index: any) => { 207 | // console.log({value}) 208 | if (value.rowVariant) { 209 | let oldObj = Object.assign({}, value) 210 | delete oldObj.rowVariant 211 | let renderTableRowDataWithRowVariant: any = [] 212 | let mergerOrginalObj = Object.assign({}, oldObj) 213 | for (let i = 1; i < Object.keys(mergerOrginalObj).length; i++) { 214 | renderTableRowDataWithRowVariant.push({value[Object.keys(mergerOrginalObj)[i]]}) 215 | } 216 | 217 | return ( 218 | {renderTableRowDataWithRowVariant} 219 | ) 220 | } else if (value.cellVariant) { 221 | let oldObj = Object.assign({}, value) 222 | delete oldObj.cellVariant 223 | delete oldObj.isActive 224 | let renderTableRowDataWithCellVariants: any = [] 225 | let mergerOrginalObj = Object.assign({}, oldObj) 226 | // console.log({mergerOrginalObj}) 227 | for (let key in mergerOrginalObj) { 228 | if (key === Object.keys(value.cellVariant)[0]) { 229 | renderTableRowDataWithCellVariants.push({value[key]}) 230 | } else { 231 | // console.log('Object.keys(mergerOrginalObj', value, 'key', key, Object.keys(value.cellVariant)) 232 | 233 | renderTableRowDataWithCellVariants.push({value[key]}) 234 | } 235 | // console.log(Object.getOwnPropertyNames(value)) 236 | // if ( Object.keys(mergerOrginalObj).includes(Object.keys(value.cellVariant)[0])) { 237 | // renderTableRowDataWithCellVariants.push(( 238 | // {value[Object.keys(mergerOrginalObj)[i]]} 239 | // )) 240 | // } else { 241 | // renderTableRowDataWithCellVariants.push(( 242 | // {value[Object.keys(mergerOrginalObj)[i]]} 243 | // )) 244 | // } 245 | // if (value.cellVariant) { 246 | 247 | // renderTableRowDataWithCellVariants.push(( 248 | // {value[Object.keys(mergerOrginalObj)[i]]} 249 | // )) 250 | // } else { 251 | // renderTableRowDataWithCellVariants.push(( 252 | // {value[Object.keys(mergerOrginalObj)[i]]} 253 | // )) 254 | // } 255 | } 256 | 257 | return ( 258 | 259 | {renderTableRowDataWithCellVariants} 260 | 261 | ) 262 | } else { 263 | let renderTableRowData: any = [] 264 | 265 | for (let i = 1; i < Object.keys(value).length; i++) { 266 | renderTableRowData.push({value[Object.keys(value)[i]]}) 267 | } 268 | 269 | return {renderTableRowData} 270 | } 271 | }) 272 | 273 | } else { 274 | // Non-pagination mode 275 | renderTableRowItems = this.items.map((value, index) => { 276 | if (value.rowVariant) { 277 | let oldObj = Object.assign({}, value) 278 | delete oldObj.rowVariant 279 | let renderTableRowDataWithRowVariant: any = [] 280 | let mergerOrginalObj = Object.assign({}, oldObj) 281 | for (let i = 1; i < Object.keys(mergerOrginalObj).length; i++) { 282 | renderTableRowDataWithRowVariant.push({value[Object.keys(mergerOrginalObj)[i]]}) 283 | } 284 | 285 | return ( 286 | {renderTableRowDataWithRowVariant} 287 | ) 288 | } else if (value.cellVariant) { 289 | let oldObj = Object.assign({}, value) 290 | delete oldObj.cellVariant 291 | delete oldObj.isActive 292 | let renderTableRowDataWithCellVariants: any = [] 293 | let mergerOrginalObj = Object.assign({}, oldObj) 294 | // console.log({mergerOrginalObj}) 295 | for (let key in mergerOrginalObj) { 296 | if (key === Object.keys(value.cellVariant)[0]) { 297 | renderTableRowDataWithCellVariants.push({value[key]}) 298 | } else { 299 | console.log('Object.keys(mergerOrginalObj', value, 'key', key, Object.keys(value.cellVariant)) 300 | 301 | renderTableRowDataWithCellVariants.push({value[key]}) 302 | } 303 | // console.log(Object.getOwnPropertyNames(value)) 304 | // if ( Object.keys(mergerOrginalObj).includes(Object.keys(value.cellVariant)[0])) { 305 | // renderTableRowDataWithCellVariants.push(( 306 | // {value[Object.keys(mergerOrginalObj)[i]]} 307 | // )) 308 | // } else { 309 | // renderTableRowDataWithCellVariants.push(( 310 | // {value[Object.keys(mergerOrginalObj)[i]]} 311 | // )) 312 | // } 313 | // if (value.cellVariant) { 314 | 315 | // renderTableRowDataWithCellVariants.push(( 316 | // {value[Object.keys(mergerOrginalObj)[i]]} 317 | // )) 318 | // } else { 319 | // renderTableRowDataWithCellVariants.push(( 320 | // {value[Object.keys(mergerOrginalObj)[i]]} 321 | // )) 322 | // } 323 | } 324 | 325 | return ( 326 | 327 | {renderTableRowDataWithCellVariants} 328 | 329 | ) 330 | } else { 331 | let renderTableRowData: any = [] 332 | 333 | for (let i = 1; i < Object.keys(value).length; i++) { 334 | renderTableRowData.push({value[Object.keys(value)[i]]}) 335 | } 336 | 337 | return {renderTableRowData} 338 | } 339 | }) 340 | } 341 | 342 | } 343 | 344 | 345 | 346 | return ( 347 |
    348 | 349 | 350 | {renderTableRowHeader} 351 | 352 | {renderTableRowItems} 353 |
    354 |
    355 | ) 356 | } 357 | }) 358 | 359 | export default SBTable 360 | -------------------------------------------------------------------------------- /src/components/table/example.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 34 | -------------------------------------------------------------------------------- /src/components/table/table.css: -------------------------------------------------------------------------------- 1 | .table > * [aria-sort='descending'] { 2 | background-position: right 0.375rem center; 3 | padding-right: calc(0.75rem + 0.65em); 4 | cursor: pointer; 5 | background-image: none; 6 | background-repeat: no-repeat; 7 | background-size: 0.65em 1em; 8 | 9 | background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath opacity='.3' d='M51 1l25 23 24 22H1l25-22z'/%3E%3Cpath d='M51 101l25-23 24-22H1l25 22z'/%3E%3C/svg%3E"); 10 | } 11 | 12 | .table > * [aria-sort='ascending'] { 13 | background-position: right 0.375rem center; 14 | padding-right: calc(0.75rem + 0.65em); 15 | cursor: pointer; 16 | background-image: none; 17 | background-repeat: no-repeat; 18 | background-size: 0.65em 1em; 19 | 20 | background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath d='M51 1l25 23 24 22H1l25-22z'/%3E%3Cpath opacity='.3' d='M51 101l25-23 24-22H1l25 22z'/%3E%3C/svg%3E"); 21 | } 22 | -------------------------------------------------------------------------------- /src/components/toasts/SBToasts.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType } from 'vue' 2 | 3 | interface ISBToastsProps { 4 | imgSrc: string 5 | alt?: string 6 | top?: boolean 7 | bottom?: boolean 8 | start?: boolean 9 | left?: boolean 10 | end?: boolean 11 | right?: boolean 12 | height?: string | number 13 | width?: string | number 14 | [key: string]: any 15 | } 16 | 17 | // TODO: NOT DONE.. NEED MORE WORK ON... 18 | const SBToasts = defineComponent({ 19 | name: 'SBToasts', 20 | props: { 21 | imgSrc: { 22 | type: String, 23 | required: false 24 | }, 25 | alt: { 26 | type: String, 27 | required: false 28 | }, 29 | top: { 30 | type: Boolean, 31 | required: false 32 | }, 33 | bottom: { 34 | type: Boolean, 35 | required: false 36 | }, 37 | start: { 38 | type: Boolean, 39 | required: false 40 | }, 41 | left: { 42 | type: Boolean, 43 | required: false 44 | }, 45 | end: { 46 | type: Boolean, 47 | required: false 48 | }, 49 | right: { 50 | type: Boolean, 51 | required: false 52 | }, 53 | height: { 54 | type: String || Number, 55 | required: false 56 | }, 57 | width: { 58 | type: String || Number, 59 | required: false 60 | }, 61 | class: { 62 | type: String, 63 | required: false 64 | }, 65 | style: { 66 | type: String, 67 | required: false 68 | } 69 | }, 70 | render() { 71 | let computeClass = (props: ISBToastsProps) => { 72 | let cardImgPost = 'card-img-top' 73 | for (let key in props) { 74 | if (props[key] === true) { 75 | cardImgPost = `card-img-${key}` 76 | } 77 | } 78 | return [cardImgPost] 79 | } 80 | let computeStyle = (props: ISBToastsProps) => { 81 | console.log('props', props) 82 | return '' 83 | } 84 | 85 | return ( 86 |
    87 |

    MY VERISON

    88 |

    REAL

    89 | 90 | 99 |
    100 | ) 101 | 102 | // return ( 103 | // {this.alt} 104 | // ) 105 | } 106 | }) 107 | 108 | export default SBToasts 109 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import SBAccordion from './components/accordion/SBAccordion' 2 | import SBAlert from './components/alert/SBAlert' 3 | import SBBadge from './components/badge/SBBadge' 4 | import SBBreadcrumb from './components/breadcrumb/SBBreadcrumb' 5 | import SBButton from './components/button/SBButton' 6 | import SBButtonGroup from './components/buttonGroup/SBButtonGroup' 7 | import SBCard from './components/card/SBCard' 8 | import SBCardHeader from './components/card/SBCardHeader' 9 | import SBListgroup from './components/card/SBListgroup' 10 | import SBListgroupItem from './components/card/SBListgroupItem' 11 | import SBCardTitle from './components/card/SBCardTitle' 12 | import SBCloseButton from './components/closeButton/SBCloseButton' 13 | import SBContainer from './components/layoutAndGridSystem/SBContainer' 14 | import SBCol from './components/layoutAndGridSystem/SBCol' 15 | import SBFormSelect from './components/form/SBFormSelect' 16 | import SBFormInput from './components/form/SBFormInput' 17 | import SBNav from './components/nav/SBNav' 18 | import SBNavLink from './components/nav/SBNavLink' 19 | import SBProgress from './components/progress/SBProgress' 20 | import SBProgressBar from './components/progress/SBProgressBar' 21 | import SBPagination from './components/pagination/Pagination' 22 | import SBRow from './components/layoutAndGridSystem/SBRow' 23 | import SBTable from './components/table/SBTable' 24 | 25 | export { 26 | SBAccordion, 27 | SBAlert, 28 | SBBadge, 29 | SBBreadcrumb, 30 | SBButton, 31 | SBButtonGroup, 32 | SBCard, 33 | SBCardHeader, 34 | SBListgroup, 35 | SBListgroupItem, 36 | SBCardTitle, 37 | SBCloseButton, 38 | SBContainer, 39 | SBCol, 40 | SBFormSelect, 41 | SBFormInput, 42 | SBNav, 43 | SBNavLink, 44 | SBProgress, 45 | SBProgressBar, 46 | SBPagination, 47 | SBRow, 48 | SBTable 49 | } 50 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import SBAccordion from './components/accordion/SBAccordion' 2 | import SBAlert from './components/alert/SBAlert' 3 | import SBBadge from './components/badge/SBBadge' 4 | import SBBreadcrumb from './components/breadcrumb/SBBreadcrumb' 5 | import SBButton from './components/button/SBButton' 6 | import SBButtonGroup from './components/buttonGroup/SBButtonGroup' 7 | import SBCard from './components/card/SBCard' 8 | import SBCardHeader from './components/card/SBCardHeader' 9 | import SBListgroup from './components/card/SBListgroup' 10 | import SBListgroupItem from './components/card/SBListgroupItem' 11 | import SBCardTitle from './components/card/SBCardTitle' 12 | import SBCloseButton from './components/closeButton/SBCloseButton' 13 | import SBContainer from './components/layoutAndGridSystem/SBContainer' 14 | import SBCol from './components/layoutAndGridSystem/SBCol' 15 | import SBFormSelect from './components/form/SBFormSelect' 16 | import SBFormInput from './components/form/SBFormInput' 17 | import SBNav from './components/nav/SBNav' 18 | import SBNavLink from './components/nav/SBNavLink' 19 | import SBProgress from './components/progress/SBProgress' 20 | import SBProgressBar from './components/progress/SBProgressBar' 21 | import SBPagination from './components/pagination/Pagination' 22 | import SBRow from './components/layoutAndGridSystem/SBRow' 23 | import SBTable from './components/table/SBTable' 24 | 25 | export { 26 | SBAccordion, 27 | SBAlert, 28 | SBBadge, 29 | SBBreadcrumb, 30 | SBButton, 31 | SBButtonGroup, 32 | SBCard, 33 | SBCardHeader, 34 | SBListgroup, 35 | SBListgroupItem, 36 | SBCardTitle, 37 | SBCloseButton, 38 | SBContainer, 39 | SBCol, 40 | SBFormSelect, 41 | SBFormInput, 42 | SBNav, 43 | SBNavLink, 44 | SBProgress, 45 | SBProgressBar, 46 | SBPagination, 47 | SBRow, 48 | SBTable 49 | } 50 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App' 3 | // import { RouterLink, useRoute, useRouter } from 'vue-router' 4 | // import router from './router' 5 | // import BButton from './components/buttons/BButton' 6 | 7 | import './assets/bootstrap.css' 8 | 9 | const app = createApp(App) 10 | // app.use(router) 11 | // app.component('BButton', BButton) 12 | 13 | app.mount('#app') 14 | 15 | // export { 16 | // BButton 17 | // } 18 | -------------------------------------------------------------------------------- /src/router.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' 2 | 3 | // const routes: Array = [] 4 | 5 | // const router = createRouter({ 6 | // history: createWebHistory(), 7 | // routes 8 | // }) 9 | 10 | // export default router 11 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import type { DefineComponent } from 'vue' 3 | const component: DefineComponent<{}, {}, any> 4 | export default component 5 | } 6 | -------------------------------------------------------------------------------- /tests/unit/example.spec.ts: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | 3 | describe('example', () => { 4 | // let wrapper = shallowMount(HelloWorld); // setting the type 5 | 6 | it('demo example', () => { 7 | 8 | expect(1 + 1).toBe(2) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "types": [ 15 | "webpack-env", 16 | "jest" 17 | ], 18 | "paths": { 19 | "@/*": [ 20 | "src/*" 21 | ] 22 | }, 23 | "lib": [ 24 | "esnext", 25 | "dom", 26 | "dom.iterable", 27 | "scripthost" 28 | ] 29 | }, 30 | "typedocOptions": { 31 | "inputFiles": [ 32 | "index.ts", 33 | "src/main.ts" 34 | ], 35 | "mode": "library", 36 | "name": "superbvue", 37 | "includeVersion": true, 38 | }, 39 | "include": [ 40 | "src/**/*.ts", 41 | "src/**/*.tsx", 42 | "src/**/*.vue", 43 | "tests/**/*.ts", 44 | "tests/**/*.tsx" 45 | ], 46 | "exclude": [ 47 | "node_modules" 48 | ] 49 | } 50 | --------------------------------------------------------------------------------