├── src ├── scss │ ├── _preflight.scss │ ├── components │ │ ├── _tooltip.scss │ │ ├── _pagination.scss │ │ ├── _collapse.scss │ │ ├── _card.scss │ │ ├── _upload.scss │ │ ├── _list.scss │ │ ├── _tab.scss │ │ ├── _treeview.scss │ │ ├── _toggle.scss │ │ ├── _dropdown.scss │ │ ├── _modal.scss │ │ ├── _toast.scss │ │ ├── _select.scss │ │ ├── _button.scss │ │ └── _form.scss │ ├── _utilities.scss │ ├── _components.scss │ └── proton.scss ├── components │ ├── Img │ │ ├── index.js │ │ └── Img.vue │ ├── Card │ │ ├── index.js │ │ └── Card.vue │ ├── Slug │ │ ├── index.js │ │ └── Slug.vue │ ├── Chart │ │ ├── index.js │ │ └── Chart.vue │ ├── Input │ │ ├── index.js │ │ └── Input.vue │ ├── Modal │ │ ├── index.js │ │ └── Modal.vue │ ├── Toast │ │ ├── index.js │ │ └── Toast.vue │ ├── Banner │ │ ├── index.js │ │ └── Banner.vue │ ├── Button │ │ ├── index.js │ │ └── Button.vue │ ├── Loader │ │ ├── index.js │ │ └── Loader.vue │ ├── Number │ │ ├── index.js │ │ └── Number.vue │ ├── Toggle │ │ ├── index.js │ │ └── Toggle.vue │ ├── Upload │ │ ├── index.js │ │ └── Upload.vue │ ├── Tooltip │ │ ├── index.js │ │ └── Tooltip.vue │ ├── Collapse │ │ ├── index.js │ │ └── Collapse.vue │ ├── Password │ │ ├── index.js │ │ └── Password.vue │ ├── Textarea │ │ ├── index.js │ │ └── Textarea.vue │ ├── Datatable │ │ ├── index.js │ │ └── Datatable.vue │ ├── Pagination │ │ ├── index.js │ │ └── Pagination.vue │ ├── Tags │ │ ├── index.js │ │ └── RenderlessTags.vue │ ├── Autocomplete │ │ ├── index.js │ │ └── RenderlessAutocomplete.vue │ ├── Tabs │ │ ├── index.js │ │ ├── Tab.vue │ │ └── Tabs.vue │ ├── Select │ │ ├── index.js │ │ ├── Option.vue │ │ └── Select.vue │ ├── Dropdown │ │ ├── index.js │ │ ├── DropdownItem.vue │ │ └── Dropdown.vue │ ├── Treeview │ │ ├── index.js │ │ ├── Treeview.vue │ │ └── TreeviewNode.vue │ ├── Checkbox │ │ ├── index.js │ │ ├── CheckboxGroup.vue │ │ └── Checkbox.vue │ ├── Sortable │ │ ├── SortableItem.vue │ │ ├── SortableHandle.vue │ │ ├── index.js │ │ └── SortableList.vue │ ├── Radio │ │ ├── index.js │ │ ├── RadioGroup.vue │ │ ├── Radio.vue │ │ └── RenderlessRadio.vue │ └── index.js ├── directives │ ├── modal │ │ ├── index.js │ │ └── v-modal.js │ ├── toast │ │ ├── index.js │ │ └── v-toast.js │ ├── collapse │ │ ├── index.js │ │ └── v-collapse.js │ ├── tooltip │ │ ├── index.js │ │ └── v-tooltip.js │ ├── click-outside │ │ ├── index.js │ │ └── v-click-outside.js │ └── index.js ├── support │ └── eventbus.js ├── index.js └── mixins │ └── stackable.js ├── .gitignore ├── mix-manifest.json ├── webpack.mix.js ├── README.md ├── LICENSE └── package.json /src/scss/_preflight.scss: -------------------------------------------------------------------------------- 1 | @tailwind preflight; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /docs/node_modules/ 2 | /node_modules/ 3 | .DS_Store 4 | *.log -------------------------------------------------------------------------------- /mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/dist/proton.js": "/dist/proton.js" 3 | } 4 | -------------------------------------------------------------------------------- /src/components/Img/index.js: -------------------------------------------------------------------------------- 1 | import ImgComponent from './Img' 2 | 3 | export default Vue => { 4 | Vue.component(ImgComponent.name, ImgComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Card/index.js: -------------------------------------------------------------------------------- 1 | import CardComponent from './Card' 2 | 3 | export default Vue => { 4 | Vue.component(CardComponent.name, CardComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Slug/index.js: -------------------------------------------------------------------------------- 1 | import SlugComponent from './Slug' 2 | 3 | export default Vue => { 4 | Vue.component(SlugComponent.name, SlugComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Chart/index.js: -------------------------------------------------------------------------------- 1 | import ChartComponent from './Chart' 2 | 3 | export default Vue => { 4 | Vue.component(ChartComponent.name, ChartComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Input/index.js: -------------------------------------------------------------------------------- 1 | import InputComponent from './Input' 2 | 3 | export default Vue => { 4 | Vue.component(InputComponent.name, InputComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Modal/index.js: -------------------------------------------------------------------------------- 1 | import ModalComponent from './Modal' 2 | 3 | export default Vue => { 4 | Vue.component(ModalComponent.name, ModalComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Toast/index.js: -------------------------------------------------------------------------------- 1 | import ToastComponent from './Toast' 2 | 3 | export default Vue => { 4 | Vue.component(ToastComponent.name, ToastComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Banner/index.js: -------------------------------------------------------------------------------- 1 | import BannerComponent from './Banner' 2 | 3 | export default Vue => { 4 | Vue.component(BannerComponent.name, BannerComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Button/index.js: -------------------------------------------------------------------------------- 1 | import ButtonComponent from './Button' 2 | 3 | export default Vue => { 4 | Vue.component(ButtonComponent.name, ButtonComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Loader/index.js: -------------------------------------------------------------------------------- 1 | import LoaderComponent from './Loader' 2 | 3 | export default Vue => { 4 | Vue.component(LoaderComponent.name, LoaderComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Number/index.js: -------------------------------------------------------------------------------- 1 | import NumberComponent from './Number' 2 | 3 | export default Vue => { 4 | Vue.component(NumberComponent.name, NumberComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Toggle/index.js: -------------------------------------------------------------------------------- 1 | import ToggleComponent from './Toggle' 2 | 3 | export default Vue => { 4 | Vue.component(ToggleComponent.name, ToggleComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Upload/index.js: -------------------------------------------------------------------------------- 1 | import UploadComponent from './Upload' 2 | 3 | export default Vue => { 4 | Vue.component(UploadComponent.name, UploadComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Tooltip/index.js: -------------------------------------------------------------------------------- 1 | import TooltipComponent from './Tooltip' 2 | 3 | export default Vue => { 4 | Vue.component(TooltipComponent.name, TooltipComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Collapse/index.js: -------------------------------------------------------------------------------- 1 | import CollapseComponent from './Collapse' 2 | 3 | export default Vue => { 4 | Vue.component(CollapseComponent.name, CollapseComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Password/index.js: -------------------------------------------------------------------------------- 1 | import PasswordComponent from './Password' 2 | 3 | export default Vue => { 4 | Vue.component(PasswordComponent.name, PasswordComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Textarea/index.js: -------------------------------------------------------------------------------- 1 | import TextareaComponent from './Textarea' 2 | 3 | export default Vue => { 4 | Vue.component(TextareaComponent.name, TextareaComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Datatable/index.js: -------------------------------------------------------------------------------- 1 | import DatatableComponent from './Datatable' 2 | 3 | export default Vue => { 4 | Vue.component(DatatableComponent.name, DatatableComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Pagination/index.js: -------------------------------------------------------------------------------- 1 | import PaginationComponent from './Pagination' 2 | 3 | export default Vue => { 4 | Vue.component(PaginationComponent.name, PaginationComponent) 5 | } -------------------------------------------------------------------------------- /src/scss/components/_tooltip.scss: -------------------------------------------------------------------------------- 1 | .tooltip { 2 | @apply .bg-black .text-white .px-3 .rounded .text-sm .shadow-lg .z-50; 3 | 4 | &-inner { 5 | @apply .p-1; 6 | } 7 | } -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | let mix = require('laravel-mix') 2 | 3 | mix.js('src/index.js', 'dist/proton.js') 4 | 5 | mix.webpackConfig({ 6 | output: { 7 | libraryTarget: 'umd' 8 | } 9 | }) -------------------------------------------------------------------------------- /src/components/Tags/index.js: -------------------------------------------------------------------------------- 1 | import RenderlessTagsComponent from './RenderlessTags' 2 | 3 | export default Vue => { 4 | Vue.component(RenderlessTagsComponent.name, RenderlessTagsComponent) 5 | } -------------------------------------------------------------------------------- /src/components/Autocomplete/index.js: -------------------------------------------------------------------------------- 1 | import RenderlessAutocompleteComponent from './RenderlessAutocomplete' 2 | 3 | export default Vue => { 4 | Vue.component(RenderlessAutocompleteComponent.name, RenderlessAutocompleteComponent) 5 | } -------------------------------------------------------------------------------- /src/directives/modal/index.js: -------------------------------------------------------------------------------- 1 | import directive from './v-modal' 2 | 3 | const Plugin = { 4 | install(Vue) { 5 | Vue.directive('modal', directive) 6 | }, 7 | directive, 8 | } 9 | 10 | export default Plugin -------------------------------------------------------------------------------- /src/directives/toast/index.js: -------------------------------------------------------------------------------- 1 | import directive from './v-toast' 2 | 3 | const Plugin = { 4 | install(Vue) { 5 | Vue.directive('toast', directive) 6 | }, 7 | directive, 8 | } 9 | 10 | export default Plugin -------------------------------------------------------------------------------- /src/directives/collapse/index.js: -------------------------------------------------------------------------------- 1 | import directive from './v-collapse' 2 | 3 | const Plugin = { 4 | install(Vue) { 5 | Vue.directive('collapse', directive) 6 | }, 7 | directive, 8 | } 9 | 10 | export default Plugin -------------------------------------------------------------------------------- /src/directives/tooltip/index.js: -------------------------------------------------------------------------------- 1 | import directive from './v-tooltip' 2 | 3 | const Plugin = { 4 | install(Vue) { 5 | Vue.directive('tooltip', directive) 6 | }, 7 | directive, 8 | } 9 | 10 | export default Plugin -------------------------------------------------------------------------------- /src/components/Tabs/index.js: -------------------------------------------------------------------------------- 1 | import TabComponent from './Tab' 2 | import TabsComponent from './Tabs' 3 | 4 | export default Vue => { 5 | Vue.component(TabsComponent.name, TabsComponent) 6 | Vue.component(TabComponent.name, TabComponent) 7 | } -------------------------------------------------------------------------------- /src/directives/click-outside/index.js: -------------------------------------------------------------------------------- 1 | import directive from './v-click-outside' 2 | 3 | const Plugin = { 4 | install(Vue) { 5 | Vue.directive('click-outside', directive) 6 | }, 7 | directive, 8 | } 9 | 10 | export default Plugin -------------------------------------------------------------------------------- /src/components/Select/index.js: -------------------------------------------------------------------------------- 1 | import SelectComponent from './Select' 2 | import OptionComponent from './Option' 3 | 4 | export default Vue => { 5 | Vue.component(SelectComponent.name, SelectComponent) 6 | Vue.component(OptionComponent.name, OptionComponent) 7 | } -------------------------------------------------------------------------------- /src/directives/index.js: -------------------------------------------------------------------------------- 1 | export { default as vClickOutside } from './click-outside' 2 | export { default as vCollapse } from './collapse' 3 | export { default as vModal } from './modal' 4 | export { default as vToast } from './toast' 5 | export { default as vTooltip } from './tooltip' -------------------------------------------------------------------------------- /src/components/Dropdown/index.js: -------------------------------------------------------------------------------- 1 | import DropdownComponent from './Dropdown' 2 | import DropdownItemComponent from './DropdownItem' 3 | 4 | export default Vue => { 5 | Vue.component(DropdownComponent.name, DropdownComponent) 6 | Vue.component(DropdownItemComponent.name, DropdownItemComponent) 7 | } -------------------------------------------------------------------------------- /src/components/Treeview/index.js: -------------------------------------------------------------------------------- 1 | import TreeviewComponent from './Treeview' 2 | import TreeviewNodeComponent from './TreeviewNode' 3 | 4 | export default Vue => { 5 | Vue.component(TreeviewComponent.name, TreeviewComponent) 6 | Vue.component(TreeviewNodeComponent.name, TreeviewNodeComponent) 7 | } -------------------------------------------------------------------------------- /src/scss/components/_pagination.scss: -------------------------------------------------------------------------------- 1 | .pagination { 2 | @apply .list-reset; 3 | 4 | &--item { 5 | @apply .inline-flex; 6 | } 7 | 8 | .active { 9 | @apply .bg-black .text-white; 10 | } 11 | 12 | button { 13 | @apply .button--small; 14 | } 15 | } -------------------------------------------------------------------------------- /src/components/Checkbox/index.js: -------------------------------------------------------------------------------- 1 | import CheckboxComponent from './Checkbox' 2 | import CheckboxGroupComponent from './CheckboxGroup' 3 | 4 | export default Vue => { 5 | Vue.component(CheckboxComponent.name, CheckboxComponent) 6 | Vue.component(CheckboxGroupComponent.name, CheckboxGroupComponent) 7 | } -------------------------------------------------------------------------------- /src/support/eventbus.js: -------------------------------------------------------------------------------- 1 | let EventBusPlugin = {} 2 | 3 | EventBusPlugin.install = function(Vue) { 4 | let EventBus = new Vue 5 | 6 | Object.defineProperty(Vue.prototype, '$proton', { 7 | get() { 8 | return EventBus 9 | }, 10 | }) 11 | } 12 | 13 | export default EventBusPlugin -------------------------------------------------------------------------------- /src/directives/collapse/v-collapse.js: -------------------------------------------------------------------------------- 1 | function bind(el, binding, vnode) { 2 | el.addEventListener('click', (e) => { 3 | vnode.context.$proton.$emit('toggle-collapse-' + binding.arg) 4 | }) 5 | } 6 | 7 | const directive = { 8 | bind 9 | } 10 | 11 | export default (typeof window !== 'undefined' ? directive : {}) -------------------------------------------------------------------------------- /src/directives/tooltip/v-tooltip.js: -------------------------------------------------------------------------------- 1 | import Tooltip from 'tooltip.js' 2 | 3 | function bind(el, binding) { 4 | new Tooltip(el, { 5 | placement: binding.arg || 'top', 6 | title: binding.value, 7 | html: true, 8 | }) 9 | } 10 | 11 | const directive = { 12 | bind 13 | } 14 | 15 | export default (typeof window !== 'undefined' ? directive : {}) -------------------------------------------------------------------------------- /src/components/Sortable/SortableItem.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /src/directives/modal/v-modal.js: -------------------------------------------------------------------------------- 1 | import EventBus from '../../support/eventbus' 2 | 3 | function bind(el, binding, vnode) { 4 | el.addEventListener('click', (e) => { 5 | vnode.context.$proton.$emit('toggle-modal-' + binding.arg, binding.value) 6 | }) 7 | } 8 | 9 | const directive = { 10 | bind 11 | } 12 | 13 | export default (typeof window !== 'undefined' ? directive : {}) -------------------------------------------------------------------------------- /src/components/Sortable/SortableHandle.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /src/components/Radio/index.js: -------------------------------------------------------------------------------- 1 | import RadioComponent from './Radio' 2 | import RadioGroupComponent from './RadioGroup' 3 | import RenderlessRadioComponent from './RenderlessRadio' 4 | 5 | export default Vue => { 6 | Vue.component(RadioComponent.name, RadioComponent) 7 | Vue.component(RadioGroupComponent.name, RadioGroupComponent) 8 | Vue.component(RenderlessRadioComponent.name, RenderlessRadioComponent) 9 | } -------------------------------------------------------------------------------- /src/directives/toast/v-toast.js: -------------------------------------------------------------------------------- 1 | import EventBus from '../../support/eventbus' 2 | 3 | function bind(el, binding, vnode) { 4 | el.addEventListener('click', (e) => { 5 | vnode.context.$proton.$emit('toast', { level: (binding.arg || 'default'), message: binding.value }) 6 | }) 7 | } 8 | 9 | const directive = { 10 | bind 11 | } 12 | 13 | export default (typeof window !== 'undefined' ? directive : {}) -------------------------------------------------------------------------------- /src/scss/components/_collapse.scss: -------------------------------------------------------------------------------- 1 | .smooth-enter-active, .smooth-leave-active { 2 | transition: max-height .5s; 3 | } 4 | 5 | .smooth-enter, .smooth-leave-to { 6 | max-height: 0; 7 | } 8 | 9 | .collapse { 10 | display: none; 11 | &.in { 12 | display: block; 13 | } 14 | } 15 | .collapsing { 16 | position: relative; 17 | height: 0; 18 | overflow: hidden; 19 | transition: height .377s ease; 20 | } -------------------------------------------------------------------------------- /src/components/Sortable/index.js: -------------------------------------------------------------------------------- 1 | import SortableHandleComponent from './SortableHandle' 2 | import SortableItemComponent from './SortableItem' 3 | import SortableListComponent from './SortableList' 4 | 5 | export default Vue => { 6 | Vue.component(SortableHandleComponent.name, SortableHandleComponent) 7 | Vue.component(SortableItemComponent.name, SortableItemComponent) 8 | Vue.component(SortableListComponent.name, SortableListComponent) 9 | } -------------------------------------------------------------------------------- /src/scss/_utilities.scss: -------------------------------------------------------------------------------- 1 | @tailwind utilities; 2 | 3 | /** 4 | * Here you would add any custom utilities you need that don't come out of the 5 | * box with Tailwind. 6 | * 7 | * Example : 8 | * 9 | * .bg-pattern-graph-paper { ... } 10 | * .skew-45 { ... } 11 | * 12 | * Or if using a preprocessor or `postcss-import`: 13 | * 14 | * @import "utilities/background-patterns"; 15 | * @import "utilities/skew-transforms"; 16 | */ 17 | 18 | @import "./utilities/animate"; -------------------------------------------------------------------------------- /src/scss/components/_card.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | @apply .rounded .shadow .bg-white .relative; 3 | 4 | &__body { 5 | @apply .p-6; 6 | } 7 | 8 | &--primary { 9 | @apply .rounded .shadow .bg-teal; 10 | 11 | > p { 12 | @apply .text-white; 13 | } 14 | } 15 | 16 | &--dark { 17 | @apply .rounded .shadow .bg-black .text-white; 18 | 19 | > p { 20 | @apply .text-white; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/scss/components/_upload.scss: -------------------------------------------------------------------------------- 1 | .upload { 2 | &__container { 3 | @apply .w-full .border-4 .border-grey-light .border-dashed .bg-grey-lightest .relative .rounded .p-3; 4 | } 5 | 6 | &__container--dragged { 7 | @apply .border-grey; 8 | } 9 | 10 | &__control { 11 | @apply .hidden; 12 | } 13 | 14 | &__label { 15 | @apply .block .text-center .align-middle; 16 | 17 | &:hover { 18 | @apply .cursor-pointer; 19 | } 20 | } 21 | 22 | &__files { 23 | @apply .border .rounded .shadow .mt-3; 24 | } 25 | 26 | &__file { 27 | &--actions { 28 | @apply .float-right; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/scss/components/_list.scss: -------------------------------------------------------------------------------- 1 | .list { 2 | @apply .list-reset .flex .flex-col; 3 | 4 | &--item { 5 | @apply .px-2 .text-grey-darker .rounded .bg-white; 6 | } 7 | 8 | &--item:hover { 9 | @apply .bg-grey-lightest .text-grey-darkest; 10 | } 11 | 12 | &--item:focus { 13 | @apply .outline-none; 14 | } 15 | 16 | &--separator { 17 | @apply .uppercase .px-2 .pt-6 .text-grey .text-xs .font-bold; 18 | } 19 | 20 | > .router-link-active { 21 | @apply .bg-black .text-white; 22 | } 23 | 24 | .draggable-source--is-dragging { 25 | background-color: #f1f5f8; 26 | } 27 | 28 | .draggable-source--is-dragging > * { 29 | opacity: 0; 30 | } 31 | } -------------------------------------------------------------------------------- /src/scss/_components.scss: -------------------------------------------------------------------------------- 1 | @tailwind components; 2 | 3 | /** 4 | * Here you would add any of your custom component classes; stuff that you'd 5 | * want loaded *before* the utilities so that the utilities could still 6 | * override them. 7 | */ 8 | 9 | @import "components/button"; 10 | @import "components/card"; 11 | @import "components/collapse"; 12 | @import "components/dropdown"; 13 | @import "components/form"; 14 | @import "components/list"; 15 | @import "components/modal"; 16 | @import "components/pagination"; 17 | @import "components/select"; 18 | @import "components/tab"; 19 | @import "components/toast"; 20 | @import "components/toggle"; 21 | @import "components/tooltip"; 22 | @import "components/treeview"; 23 | @import "components/upload"; -------------------------------------------------------------------------------- /src/scss/components/_tab.scss: -------------------------------------------------------------------------------- 1 | .tabs { 2 | @apply .bg-white .rounded .shadow; 3 | } 4 | 5 | .tab { 6 | @apply .flex-none; 7 | 8 | &__list { 9 | @apply .flex .border-b .border-grey-lighter .p-0 .m-0 .list-reset .overflow-x-auto; 10 | } 11 | 12 | &__link { 13 | @apply .block .px-6 .py-2 .text-grey-dark .font-normal .border-b-4 .border-transparent; 14 | } 15 | 16 | &__link:hover { 17 | @apply .bg-grey-lightest .text-grey-darkest; 18 | } 19 | 20 | &__link:focus { 21 | @apply .outline-none; 22 | } 23 | 24 | &__panel { 25 | @apply .p-6; 26 | } 27 | 28 | &--active { 29 | a { 30 | @apply .text-black .border-b-4 .border-grey-light; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/scss/components/_treeview.scss: -------------------------------------------------------------------------------- 1 | .treeview { 2 | @apply .list-reset; 3 | 4 | &--node { 5 | @apply .flex .items-center .align-middle .py-2; 6 | 7 | :hover { 8 | @apply .cursor-pointer; 9 | } 10 | } 11 | 12 | &--toggle { 13 | @apply .flex .items-center pr-2; 14 | } 15 | 16 | &--icon { 17 | @apply .pointer-events-none .inline-flex .items-center .text-grey-darker .leading-tight; 18 | } 19 | 20 | &--label { 21 | @apply .flex .items-center .leading-tight .align-middle; 22 | } 23 | 24 | &--node__folder { 25 | // 26 | } 27 | 28 | &--node__selected { 29 | @apply .font-bold; 30 | } 31 | 32 | &--children { 33 | @apply .pl-3; 34 | } 35 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import * as Components from './components' 2 | import * as Directives from './directives' 3 | import EventBusPlugin from './support/eventbus' 4 | 5 | const moment = require('moment') 6 | 7 | const Proton = { 8 | install(Vue) { 9 | Vue.use(EventBusPlugin) 10 | 11 | Vue.prototype.moment = function() { 12 | return moment 13 | } 14 | 15 | Object.values(Components).forEach((Component) => { 16 | Vue.use(Component) 17 | }) 18 | 19 | Object.values(Directives).forEach((Directive) => { 20 | Vue.use(Directive) 21 | }) 22 | } 23 | } 24 | 25 | // Automatic install Proton if Vue has been added to the global scope. 26 | if (typeof window !== 'undefined' && window.Vue) { 27 | window.Vue.use(Proton) 28 | } 29 | 30 | export default Proton 31 | -------------------------------------------------------------------------------- /src/scss/components/_toggle.scss: -------------------------------------------------------------------------------- 1 | .toggle { 2 | @apply .relative .inline-flex .rounded-full .h-6 .w-12 .cursor-pointer .flex-no-shrink; 3 | 4 | &:focus { 5 | @apply .shadow-md .outline-none; 6 | } 7 | 8 | &:before { 9 | @apply .inline-block .rounded-full .h-full .w-full .shadow .bg-grey-lightest; 10 | 11 | content: ""; 12 | transition: background-color 0.2s ease; 13 | } 14 | 15 | &:after { 16 | @apply .absolute .pin-t .pin-l .rounded-full .h-6 .w-6 .bg-white .border .border-grey-lightest .shadow-md; 17 | 18 | content: ""; 19 | transform: translateX(0); 20 | transition: transform 0.2s ease; 21 | } 22 | 23 | &:checked:before { 24 | @apply .bg-grey-darkest; 25 | } 26 | 27 | &:checked:after { 28 | transform: translateX(1.5rem); 29 | } 30 | } -------------------------------------------------------------------------------- /src/components/Tooltip/Tooltip.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 36 | -------------------------------------------------------------------------------- /src/scss/components/_dropdown.scss: -------------------------------------------------------------------------------- 1 | .dropdown { 2 | @apply .relative; 3 | } 4 | 5 | .dropdown__button { 6 | @apply .inline-block .select-none .cursor-pointer .justify-center .font-semibold .py-2 .px-3 .rounded .shadow .no-underline .text-xs .bg-white .border .border-grey-light .text-black .appearance-none .leading-tight; 7 | 8 | &:hover { 9 | @apply .bg-grey-lighter .text-black; 10 | } 11 | 12 | &:focus { 13 | @apply .outline-none .shadow-md; 14 | } 15 | } 16 | 17 | .dropdown__options { 18 | @apply .mt-2 .bg-white .shadow .border .rounded .z-50 .flex .flex-col .max-w-xs .shadow-md; 19 | 20 | min-width: 8rem; 21 | } 22 | 23 | .dropdown__item { 24 | @apply .no-underline .block .px-2 .py-1 .border-b .text-grey-darkest .bg-white .text-sm; 25 | 26 | &:hover { 27 | @apply .text-black .bg-grey-lightest .cursor-pointer; 28 | } 29 | } -------------------------------------------------------------------------------- /src/scss/proton.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * This injects Tailwind's base styles, which is a combination of 3 | * Normalize.css and some additional base styles. 4 | * 5 | * You can see the styles here: 6 | * https://github.com/tailwindcss/tailwindcss/blob/master/css/preflight.css 7 | * 8 | * If using `postcss-import`, use this import instead: 9 | * 10 | * @import "tailwindcss/preflight"; 11 | */ 12 | @import './preflight'; 13 | 14 | /** 15 | * This injects any component classes registered by plugins. 16 | * 17 | * If using `postcss-import`, use this import instead: 18 | * 19 | * @import "tailwindcss/components"; 20 | */ 21 | @import "./components"; 22 | 23 | /** 24 | * This injects all of Tailwind's utility classes, generated based on your 25 | * config file. 26 | * 27 | * If using `postcss-import`, use this import instead: 28 | * 29 | * @import "tailwindcss/utilities"; 30 | */ 31 | @import "./utilities"; -------------------------------------------------------------------------------- /src/components/Select/Option.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 36 | -------------------------------------------------------------------------------- /src/scss/components/_modal.scss: -------------------------------------------------------------------------------- 1 | .modal { 2 | @apply .rounded .shadow-md .bg-white .relative .w-full .max-w-md .m-auto; 3 | 4 | &--large { 5 | @apply .max-w-lg; 6 | } 7 | 8 | &--x-large { 9 | @apply .max-w-xl; 10 | } 11 | 12 | &--overlay { 13 | @apply .fixed .pin .overflow-auto .bg-smoke-light .flex .py-20 .px-6; 14 | } 15 | 16 | &__body { 17 | @apply .p-6; 18 | } 19 | 20 | &__header { 21 | @apply .flex .justify-between .items-center .p-6 .border-b .border-grey-lighter; 22 | 23 | &--title { 24 | @apply .text-xl .font-bold; 25 | } 26 | 27 | &--close-button { 28 | @apply .text-3xl .font-bold .text-grey-darkest; 29 | 30 | &:hover { 31 | @apply .text-black; 32 | } 33 | } 34 | } 35 | 36 | &__footer { 37 | @apply .flex .flex-row-reverse .items-center .p-6 .border-t .border-grey-lighter; 38 | } 39 | } -------------------------------------------------------------------------------- /src/components/Dropdown/DropdownItem.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 40 | -------------------------------------------------------------------------------- /src/components/Tabs/Tab.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Proton 2 | 3 |

4 | 5 | logo 6 | 7 |

8 | 9 | **Proton is under active development, and not ready for production use. There may be breaking changes between releases as we figure out our API. Thanks for understanding :v:** 10 | 11 | An elegant collection of renderless Vue.js UI components. Don't be constrained by underlying CSS frameworks. Proton provides the functionality, leaving you to roll your own styles or make use of utility-first CSS frameworks, such as [Tailwind](https://tailwindcss.com/), to fit your project and needs. 12 | 13 | The majority of components found within Proton currently are _not_ renderless. We're in the (slow) process converting these over as renderless components while supporting the current tailwind-specific components we have. Eventually, the tailwind-specific components that consume the renderless components will be moved to their own repository/package to be pulled in as needed for your projects. 14 | 15 | ## License 16 | [MIT](/LICENSE) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-present efelle creative 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/mixins/stackable.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | stackClass: 'unspecified', 5 | stackMinZIndex: 0, 6 | } 7 | }, 8 | 9 | computed: { 10 | activeZIndex() { 11 | if (! this.isActive) { 12 | return this.getZIndex(this.$refs.stackable) 13 | } 14 | 15 | return this.getMaxZIndex() + 4 16 | }, 17 | }, 18 | 19 | methods: { 20 | getZIndex(element) { 21 | if (! element || element.nodeType !== Node.ELEMENT_NODE) { 22 | return 0 23 | } 24 | 25 | const index = window.getComputedStyle(element).getPropertyValue('z-index') 26 | 27 | if (isNaN(index)) { 28 | return this.getZIndex(element.parentNode) 29 | } 30 | 31 | return index 32 | }, 33 | 34 | getMaxZIndex() { 35 | return this.stackMinZIndex + (this.getStack() * 2) 36 | }, 37 | 38 | isActiveStack() { 39 | return this.getZIndex(this.$refs.stackable) >= this.getMaxZIndex() 40 | }, 41 | 42 | getStack() { 43 | return document.getElementsByClassName(this.stackClass).length 44 | }, 45 | 46 | hasStack() { 47 | return this.getStack() > 0 48 | }, 49 | } 50 | } -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as Autocomplete } from './Autocomplete' 2 | export { default as Banner } from './Banner' 3 | export { default as Button } from './Button' 4 | export { default as Card } from './Card' 5 | export { default as Chart } from './Chart' 6 | export { default as Checkbox } from './Checkbox' 7 | export { default as Collapse } from './Collapse' 8 | export { default as Datatable } from './Datatable' 9 | export { default as Dropdown } from './Dropdown' 10 | export { default as Img } from './Img' 11 | export { default as Input } from './Input' 12 | export { default as Loader } from './Loader' 13 | export { default as Modal } from './Modal' 14 | export { default as Number } from './Number' 15 | export { default as Pagination } from './Pagination' 16 | export { default as Password } from './Password' 17 | export { default as Radio } from './Radio' 18 | export { default as Select } from './Select' 19 | export { default as Slug } from './Slug' 20 | export { default as Sortable } from './Sortable' 21 | export { default as Tabs } from './Tabs' 22 | export { default as Tags } from './Tags' 23 | export { default as Textarea } from './Textarea' 24 | export { default as Toast } from './Toast' 25 | export { default as Toggle } from './Toggle' 26 | export { default as Tooltip } from './Tooltip' 27 | export { default as Treeview } from './Treeview' 28 | export { default as Upload } from './Upload' 29 | -------------------------------------------------------------------------------- /src/components/Collapse/Collapse.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 56 | 57 | -------------------------------------------------------------------------------- /src/scss/components/_toast.scss: -------------------------------------------------------------------------------- 1 | .toast { 2 | @apply .p-2 .shadow .items-center .leading-none .flex .fixed .rounded-full .bg-black .text-grey-lightest .z-50; 3 | 4 | bottom: 25px; 5 | left: 25px; 6 | 7 | &__badge { 8 | @apply .flex .rounded-full .bg-grey-light .uppercase .px-2 .py-1 .text-xs .font-bold .mr-1; 9 | } 10 | 11 | &__message { 12 | @apply .mx-2 .text-sm .text-left .flex-auto; 13 | } 14 | 15 | &--dark { 16 | @apply .bg-black .text-grey-lightest; 17 | 18 | > .toast__badge { 19 | @apply .bg-grey-darkest .text-white; 20 | } 21 | } 22 | 23 | &--info { 24 | @apply .bg-purple .text-purple-darkest; 25 | 26 | > .toast__badge { 27 | @apply .bg-purple-lighter .text-purple-darker; 28 | } 29 | } 30 | 31 | &--success { 32 | @apply .bg-green .text-green-darkest; 33 | 34 | > .toast__badge { 35 | @apply .bg-green-lighter .text-green-darker; 36 | } 37 | } 38 | 39 | &--warning { 40 | @apply .bg-yellow .text-yellow-darkest; 41 | 42 | > .toast__badge { 43 | @apply .bg-yellow-lighter .text-yellow-darker; 44 | } 45 | } 46 | 47 | &--danger { 48 | @apply .bg-red-light .text-red-darkest; 49 | 50 | > .toast__badge { 51 | @apply .bg-red-lighter .text-red-darker; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/components/Card/Card.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 54 | -------------------------------------------------------------------------------- /src/components/Loader/Loader.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 19 | 65 | -------------------------------------------------------------------------------- /src/components/Radio/RadioGroup.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 54 | -------------------------------------------------------------------------------- /src/components/Checkbox/CheckboxGroup.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 54 | -------------------------------------------------------------------------------- /src/components/Treeview/Treeview.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 64 | -------------------------------------------------------------------------------- /src/scss/components/_select.scss: -------------------------------------------------------------------------------- 1 | .form__select { 2 | @apply .relative .w-full; 3 | 4 | &-button { 5 | @apply .appearance-none .flex .w-full .bg-grey-lighter .text-grey-darker .border .border-grey-light .rounded .px-3 .py-2 .leading-loose; 6 | 7 | &:focus { 8 | @apply .outline-none .shadow .bg-white; 9 | } 10 | } 11 | 12 | &-dropdown { 13 | @apply .my-1 .absolute .pin-r .pin-l .bg-grey-lightest .border .rounded .p-3 .shadow-lg .z-50; 14 | } 15 | 16 | &-search { 17 | @apply .flex .w-full .bg-grey-lighter .text-grey-darker .border .border-grey-light .rounded-sm .px-3 .py-2 .leading-loose; 18 | 19 | &:focus { 20 | @apply .outline-none .shadow .bg-white; 21 | } 22 | 23 | &-empty { 24 | @apply .mt-3 .px-2 .border-l-4 .border-transparent; 25 | } 26 | } 27 | 28 | &-controls { 29 | @apply .flex .justify-between .text-xs; 30 | } 31 | 32 | &-options { 33 | @apply .list-reset .p-0 .relative .mt-3 .overflow-y-auto; 34 | 35 | max-height: 14rem; 36 | } 37 | 38 | &-option { 39 | @apply .px-2; 40 | 41 | &:hover { 42 | @apply .cursor-pointer .bg-grey-lighter; 43 | } 44 | 45 | &--selected { 46 | @apply .font-bold; 47 | } 48 | 49 | &--highlighted { 50 | @apply .bg-grey-lighter; 51 | } 52 | } 53 | 54 | &--dark { 55 | .form__select-dropdown { 56 | @apply .bg-smoke-darkest .text-white; 57 | } 58 | 59 | .form__select-option:hover { 60 | @apply .cursor-pointer .bg-black; 61 | } 62 | 63 | .form__select-option--highlighted { 64 | @apply .bg-black; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/scss/components/_button.scss: -------------------------------------------------------------------------------- 1 | .buttons { 2 | @apply .flex .flex-wrap .items-center; 3 | 4 | > .button { 5 | @apply .mb-6; 6 | } 7 | 8 | > .button:not(:last-child) { 9 | @apply .mr-3; 10 | } 11 | } 12 | 13 | .button { 14 | @apply .cursor-pointer .inline-flex .justify-center .text-base .py-2 .px-3 .rounded .shadow .no-underline .bg-white .border .border-grey-light .text-grey-darker .appearance-none .leading-tight; 15 | 16 | &:hover { 17 | @apply .bg-grey-lighter .text-black; 18 | } 19 | 20 | &:focus { 21 | @apply .outline-none .shadow-md; 22 | } 23 | 24 | &--small { 25 | @apply .text-xs; 26 | } 27 | 28 | &--large { 29 | @apply .text-xl; 30 | } 31 | 32 | &--primary { 33 | @apply .bg-teal .text-white .border-0; 34 | 35 | &:hover { 36 | @apply .bg-teal-dark .text-white; 37 | } 38 | } 39 | 40 | &--secondary { 41 | @apply .bg-grey-darker .text-white .border-0; 42 | 43 | &:hover { 44 | @apply .bg-grey-darkest .text-white; 45 | } 46 | } 47 | 48 | &--info { 49 | @apply .bg-purple .text-white .border-0; 50 | 51 | &:hover { 52 | @apply .bg-purple-dark .text-white; 53 | } 54 | } 55 | 56 | &--success { 57 | @apply .bg-green .text-white .border-0; 58 | 59 | &:hover { 60 | @apply .bg-green-dark .text-white; 61 | } 62 | } 63 | 64 | &--warning { 65 | @apply .bg-yellow .border-0; 66 | 67 | &:hover { 68 | @apply .bg-yellow-dark; 69 | } 70 | } 71 | 72 | &--danger { 73 | @apply .bg-red .text-white .border-0; 74 | 75 | &:hover { 76 | @apply .bg-red-dark .text-white; 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@proton-ui/proton", 3 | "version": "0.8.0", 4 | "description": "A collection of renderless UI components for Vue.js.", 5 | "main": "dist/proton.js", 6 | "repository": "git@github.com:proton-ui/proton.git", 7 | "author": { 8 | "name": "Shea 'Kai' Lewis", 9 | "url": "https://kaidesu.com/" 10 | }, 11 | "contributors": [ 12 | { 13 | "name": "Cameron Van Orman", 14 | "url": "https://vanorman.co/" 15 | } 16 | ], 17 | "homepage": "https://proton-ui.com", 18 | "license": "MIT", 19 | "scripts": { 20 | "dev": "NODE_ENV=development webpack --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 21 | "watch": "NODE_ENV=development webpack --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 22 | "hot": "NODE_ENV=development webpack-dev-server --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", 23 | "production": "NODE_ENV=production webpack --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" 24 | }, 25 | "keywords": [ 26 | "ui", 27 | "bem", 28 | "vue", 29 | "vuejs", 30 | "atomic", 31 | "tailwind", 32 | "components", 33 | "tailwindcss", 34 | "vue-tailwind", 35 | "renderless" 36 | ], 37 | "devDependencies": { 38 | "@shopify/draggable": "^1.0.0-beta.8", 39 | "axios": "^0.18.0", 40 | "frappe-charts": "^1.1.0", 41 | "laravel-mix": "^4.0.16", 42 | "lodash": "^4.17.11", 43 | "moment": "^2.22.2", 44 | "popper.js": "^1.14.4", 45 | "query-string": "^6.2.0", 46 | "resolve-url-loader": "2.3.1", 47 | "sass": "^1.20.3", 48 | "sass-loader": "7.*", 49 | "tooltip.js": "^1.3.0", 50 | "vue": "^2.6.10", 51 | "vue-template-compiler": "^2.6.10" 52 | }, 53 | "dependencies": { 54 | "lozad": "^1.14.0" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/components/Sortable/SortableList.vue: -------------------------------------------------------------------------------- 1 | 64 | -------------------------------------------------------------------------------- /src/components/Tags/RenderlessTags.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Toast/Toast.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 78 | -------------------------------------------------------------------------------- /src/components/Banner/Banner.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 83 | -------------------------------------------------------------------------------- /src/components/Textarea/Textarea.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 79 | -------------------------------------------------------------------------------- /src/components/Toggle/Toggle.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 89 | -------------------------------------------------------------------------------- /src/directives/click-outside/v-click-outside.js: -------------------------------------------------------------------------------- 1 | const isTouch = 2 | typeof window !== 'undefined' && ('ontouchstart' in window || navigator.msMaxTouchPoints > 0) 3 | const events = isTouch ? ['touchstart', 'click'] : ['click'] 4 | 5 | const instances = [] 6 | 7 | function processDirectiveArguments(bindingValue) { 8 | const isFunction = typeof bindingValue === 'function' 9 | if (!isFunction && typeof bindingValue !== 'object') { 10 | throw new Error('v-click-outside: Binding value must be a function or an object') 11 | } 12 | 13 | return { 14 | handler: isFunction ? bindingValue : bindingValue.handler, 15 | middleware: bindingValue.middleware || ((isClickOutside) => isClickOutside), 16 | events: bindingValue.events || events, 17 | } 18 | } 19 | 20 | function onEvent({ el, event, handler, middleware }) { 21 | const isClickOutside = event.target !== el && !el.contains(event.target) 22 | 23 | if (! isClickOutside) { 24 | return 25 | } 26 | 27 | if (middleware(event, el)) { 28 | handler(event, el) 29 | } 30 | } 31 | 32 | function bind(el, { value }) { 33 | const { handler, middleware, events } = processDirectiveArguments(value) 34 | 35 | const instance = { 36 | el, 37 | eventHandlers: events.map((eventName) => ({ 38 | event: eventName, 39 | handler: (event) => onEvent({ event, el, handler, middleware }), 40 | })), 41 | } 42 | 43 | instance.eventHandlers.forEach(({ event, handler }) => document.addEventListener(event, handler)) 44 | instances.push(instance) 45 | } 46 | 47 | function update(el, { value }) { 48 | const { handler, middleware, events } = processDirectiveArguments(value) 49 | const instance = instances.find((instance) => instance.el === el) 50 | 51 | instance.eventHandlers.forEach(({ event, handler }) => 52 | document.removeEventListener(event, handler), 53 | ) 54 | 55 | instance.eventHandlers = events.map((eventName) => ({ 56 | event: eventName, 57 | handler: (event) => onEvent({ event, el, handler, middleware }), 58 | })) 59 | 60 | instance.eventHandlers.forEach(({ event, handler }) => document.addEventListener(event, handler)) 61 | } 62 | 63 | function unbind(el) { 64 | const instance = instances.find((instance) => instance.el === el) 65 | instance.eventHandlers.forEach(({ event, handler }) => 66 | document.removeEventListener(event, handler), 67 | ) 68 | } 69 | 70 | const directive = { 71 | bind, 72 | update, 73 | unbind, 74 | instances, 75 | } 76 | 77 | export default directive -------------------------------------------------------------------------------- /src/components/Radio/Radio.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 94 | -------------------------------------------------------------------------------- /src/components/Checkbox/Checkbox.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 94 | -------------------------------------------------------------------------------- /src/components/Dropdown/Dropdown.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 92 | -------------------------------------------------------------------------------- /src/components/Input/Input.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 90 | -------------------------------------------------------------------------------- /src/components/Button/Button.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 96 | -------------------------------------------------------------------------------- /src/components/Radio/RenderlessRadio.vue: -------------------------------------------------------------------------------- 1 | 108 | -------------------------------------------------------------------------------- /src/components/Img/Img.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 103 | 104 | 113 | -------------------------------------------------------------------------------- /src/components/Password/Password.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 104 | -------------------------------------------------------------------------------- /src/components/Treeview/TreeviewNode.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 92 | -------------------------------------------------------------------------------- /src/scss/components/_form.scss: -------------------------------------------------------------------------------- 1 | input, select, textarea { 2 | &.form__control { 3 | @apply .appearance-none .block .w-full .bg-grey-lighter .text-grey-darker .border .border-grey-light .rounded .px-3 .py-2 .leading-loose; 4 | 5 | &:focus { 6 | @apply .outline-none .shadow .bg-white; 7 | } 8 | 9 | &.form__error { 10 | @apply .border-red-lighter .border-2; 11 | } 12 | } 13 | } 14 | 15 | select { 16 | &.form__control { 17 | @apply .pr-8; 18 | } 19 | } 20 | 21 | .form__radio-group, 22 | .form__checkbox-group { 23 | &--inline { 24 | @apply .inline-flex; 25 | } 26 | } 27 | 28 | .form__radio-container, 29 | .form__checkbox-container { 30 | @apply .flex .items-center; 31 | 32 | :hover { 33 | @apply .cursor-pointer; 34 | } 35 | 36 | label { 37 | @apply .mr-8; 38 | } 39 | 40 | .form__radio { 41 | @apply .align-middle .appearance-none .mr-1; 42 | 43 | &:focus { 44 | @apply .outline-none; 45 | } 46 | 47 | &:after { 48 | @apply .relative .inline-flex .visible .w-4 .h-4 .rounded-full .border-2 .border-grey-light .bg-white .shadow; 49 | content: ''; 50 | } 51 | 52 | &:checked::after { 53 | @apply .relative .inline-flex .visible .w-4 .h-4 .rounded-full .border-2 .border-grey-darkest .bg-black .shadow; 54 | content: ''; 55 | } 56 | } 57 | 58 | .form__checkbox { 59 | @apply .align-middle .appearance-none .mr-1; 60 | 61 | &:focus { 62 | @apply .outline-none; 63 | } 64 | 65 | &:after { 66 | @apply .relative .inline-flex .visible .w-4 .h-4 .border-2 .border-grey-light .bg-white .shadow; 67 | content: ''; 68 | } 69 | 70 | &:checked::after { 71 | @apply .relative .inline-flex .visible .w-4 .h-4 .border-2 .border-grey-darkest .bg-black .shadow; 72 | content: ''; 73 | } 74 | } 75 | } 76 | 77 | .form__label { 78 | @apply .block .uppercase .tracking-wide .text-grey-darker .text-xs .font-bold .mb-2; 79 | } 80 | 81 | .form__control--meta { 82 | @apply .text-sm .mb-3 .mt-1 .flex .justify-between; 83 | 84 | span { 85 | @apply .block; 86 | } 87 | 88 | .form__error--message { 89 | @apply .text-red; 90 | } 91 | 92 | > .form__help { 93 | @apply .text-grey-darker .italic; 94 | } 95 | 96 | > .form__password { 97 | @apply .ml-2 .flex-none; 98 | } 99 | } 100 | 101 | .form__group, 102 | .form__radio-group, 103 | .form__checkbox-group { 104 | @apply .flex .flex-wrap .items-center .w-full .mb-6 .border-0; 105 | } 106 | 107 | .input__group { 108 | @apply .flex .flex-wrap .items-stretch .w-full .mb-6 .relative; 109 | 110 | > .form__control { 111 | @apply .flex-shrink .flex-grow .flex-auto .w-px .flex-1 .rounded-r-none .relative; 112 | 113 | &:focus { 114 | @apply .border-r-0; 115 | } 116 | } 117 | } 118 | 119 | .input__group--append { 120 | @apply .flex .-mr-px; 121 | 122 | > .button { 123 | @apply .flex .items-center .rounded .rounded-l-none .whitespace-no-wrap .shadow-none .bg-grey-light .text-grey-darker; 124 | 125 | &:focus { 126 | @apply .outline-none .shadow .bg-white; 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /src/components/Autocomplete/RenderlessAutocomplete.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Tabs/Tabs.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 157 | -------------------------------------------------------------------------------- /src/components/Pagination/Pagination.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 171 | -------------------------------------------------------------------------------- /src/components/Number/Number.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 160 | -------------------------------------------------------------------------------- /src/components/Slug/Slug.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 174 | -------------------------------------------------------------------------------- /src/components/Upload/Upload.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 192 | -------------------------------------------------------------------------------- /src/components/Modal/Modal.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 231 | -------------------------------------------------------------------------------- /src/components/Chart/Chart.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 284 | -------------------------------------------------------------------------------- /src/components/Select/Select.vue: -------------------------------------------------------------------------------- 1 | 79 | 80 | 347 | -------------------------------------------------------------------------------- /src/components/Datatable/Datatable.vue: -------------------------------------------------------------------------------- 1 | 138 | 139 | 394 | --------------------------------------------------------------------------------