├── .gitignore ├── keymaps └── element-helper.json ├── resources ├── repos.json ├── element │ ├── fix2.js │ ├── fix1.js │ ├── style.css │ └── index.json └── style.css ├── styles └── element-helper.less ├── CHANGELOG.md ├── package.json ├── LICENSE.md ├── lib ├── docset.js ├── search-view.js ├── element-helper.js ├── resource.js ├── library.js ├── document-view.js └── provider.js ├── snippets └── element.cson └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | resources/element-gh-pages 5 | resources/element/versions.json 6 | -------------------------------------------------------------------------------- /keymaps/element-helper.json: -------------------------------------------------------------------------------- 1 | { 2 | "atom-workspace": { 3 | "ctrl-cmd-z": "element-helper:search-under-cursor" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /resources/repos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "element", 4 | "name": "Element UI", 5 | "links": { 6 | "home": "http://element.eleme.io", 7 | "source": "https://github.com/ElemeFE/element" 8 | } 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /styles/element-helper.less: -------------------------------------------------------------------------------- 1 | // The ui-variables file is provided by base themes provided by Atom. 2 | // 3 | // See https://github.com/atom/atom-dark-ui/blob/master/styles/ui-variables.less 4 | // for a full listing of what's available. 5 | @import "ui-variables"; 6 | 7 | .element-helper-docs-item-left { 8 | color: white; 9 | } 10 | .element-helper-docs-item-right { 11 | float: right; 12 | } 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.3.2 2 | * add loading animation 3 | * fix title change problem 4 | * fetch attrs and tags json files from npm 5 | * update document.gif 6 | * other optimize 7 | 8 | ## 0.3.1 - Modify description 9 | 10 | ## 0.3.0 - Add menu 11 | 12 | ## 0.2.3 - Update Document 13 | 14 | ## 0.2.2 - Change keymap 15 | 16 | ## 0.2.1 - Support el-table-column tag to search 17 | 18 | ## 0.2.0 - Update some examples 19 | 20 | ## 0.1.0 - First Release 21 | * Every feature added 22 | * Every bug fixed 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "element-helper", 3 | "main": "./lib/element-helper", 4 | "version": "0.5.4", 5 | "description": "Element-Helper is a Atom package and support snippets, autocomplete and document.", 6 | "keywords": [], 7 | "repository": "https://github.com/ElemeFE/element-helper", 8 | "license": "MIT", 9 | "engines": { 10 | "atom": ">=1.0.0 <2.0.0" 11 | }, 12 | "providedServices": { 13 | "autocomplete.provider": { 14 | "versions": { 15 | "2.0.0": "provide" 16 | } 17 | } 18 | }, 19 | "dependencies": { 20 | "atom-space-pen-views": "^2.0.0", 21 | "cheerio": "^1.0.0-rc.1", 22 | "element-gh-pages": "^1.0.13", 23 | "element-helper-json": "^1.0.0", 24 | "follow-redirects": "^1.2.3", 25 | "jquery": "^3.3.1", 26 | "mkdirp": "^0.5.0", 27 | "pretty": "^2.0.0", 28 | "shelljs": "^0.7.8" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 ElemeFE 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 | -------------------------------------------------------------------------------- /lib/docset.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import Path from 'path'; 4 | import Resource from './resource'; 5 | 6 | class DocSet { 7 | constructor(item) { 8 | this.id_ = item.type; 9 | this.indexPath = this.id_ + '/index.json'; 10 | this.index_ = null; 11 | this.language_ = atom.config.get('element-helper.language'); 12 | 13 | atom.config.observe('element-helper.' + this.id_, this.setEnabled.bind(this)); 14 | } 15 | 16 | setEnabled(enabled) { 17 | if (!enabled) { 18 | this.index_ = null; 19 | return; 20 | } 21 | 22 | Resource.get(Path.join(Resource.RESOURCE_PATH, this.indexPath)) 23 | .then(result => { 24 | this.index_ = JSON.parse(result); 25 | 26 | for (var i = 0 ; i < this.index_.entries.length; ++i) { 27 | this.index_.entries[i].id = this.id_; 28 | this.index_.entries[i].url = `elements-docs://${this.id_}/${this.index_.entries[i].path}`; 29 | } 30 | }); 31 | } 32 | 33 | getTitle(path) { 34 | for (let i = 0; i < this.index_.entries.length; ++i) { 35 | if (this.index_.entries[i].path == path) { 36 | return this.language_ === 'zh-CN' ? this.index_.entries[i].name : this.index_.entries[i].name.split(' ').shift(); 37 | } 38 | } 39 | return ''; 40 | } 41 | 42 | queryAll() { 43 | return !this.index_ ? [] : this.index_.entries; 44 | } 45 | } 46 | 47 | export default DocSet; 48 | -------------------------------------------------------------------------------- /resources/element/fix2.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | $(document.body).css('display', 'none'); 4 | $(window).on('load', () => { 5 | const container = $('.page-container.page-component'); 6 | container.find('.page-component__content').first().addClass('page-container-right').css('margin-left', '20px'); 7 | let menu = container.find('.page-component__nav').first(); 8 | menu.addClass('page-container-left').find('.nav-dropdown').remove(); 9 | menu.append(''); 10 | 11 | $('.headerWrapper, .footer-nav, .page-component-up, .demo-block-control button').remove(); 12 | container.addClass('hide-menu'); 13 | 14 | const page = $('.el-scrollbar__view'); 15 | page.on('click', '.show-menu .menu-button, .page-container-right', function () { 16 | container.addClass('hide-menu').removeClass('show-menu'); 17 | container.find('.menu-button').text('>>'); 18 | }); 19 | page.on('click', '.hide-menu .menu-button', function () { 20 | container.addClass('show-menu').removeClass('hide-menu'); 21 | container.find('.menu-button').text('<<'); 22 | }); 23 | page.on('click', '.side-nav .nav-item a', function () { 24 | window.parent.postMessage({ title: this.textContent, hash: this.href.split('#').pop() }, '*'); 25 | $('.demo-block-control button, .page-component-up').remove(); 26 | }); 27 | $(document.body).css('display', 'block'); 28 | window.parent.postMessage({ loaded: true }, '*'); 29 | }); 30 | -------------------------------------------------------------------------------- /resources/element/fix1.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | $(document.body).css('display', 'none'); 4 | $(window).on('load', () => { 5 | const container = $('.page-container.page-component'); 6 | container.find('.el-row .el-col:nth-child(2)').first().attr('class', "page-container-right").css('margin-left', '20px'); 7 | let menu = container.find('.el-row .el-col:nth-child(1)').first(); 8 | menu.attr('class', 'page-container-left').find('.nav-dropdown').remove(); 9 | menu.append(''); 10 | 11 | $('.headerWrapper, .footer, .footer-nav, .page-component-up, .header-anchor, .description button').remove(); 12 | container.css({padding: 0, margin: 0}).children().attr('class', 'hide-menu'); 13 | 14 | container.on('click', '.show-menu .menu-button, .page-container-right', function () { 15 | container.children().attr('class', 'hide-menu'); 16 | container.find('.menu-button').text('>>'); 17 | }); 18 | container.on('click', '.hide-menu .menu-button', function () { 19 | container.children().attr('class', 'show-menu'); 20 | container.find('.menu-button').text('<<'); 21 | }); 22 | container.on('click', '.side-nav .nav-item a', function () { 23 | window.parent.postMessage({ title: this.textContent, hash: this.href.split('#').pop() }, '*'); 24 | $('.description button').remove(); 25 | }); 26 | $(document.body).css('display', 'block'); 27 | window.parent.postMessage({ loaded: true }, '*'); 28 | }); 29 | -------------------------------------------------------------------------------- /lib/search-view.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import $ from 'jquery'; 4 | import { SelectListView } from 'atom-space-pen-views'; 5 | 6 | class SearchView extends SelectListView { 7 | 8 | constructor(word, items) { 9 | super(); 10 | this.docView_ = null; 11 | this.confirmed_ = false; 12 | this.setViewPromise_ = null; 13 | this.panel_ = atom.workspace.addModalPanel({item: this}); 14 | 15 | this.filterEditorView.setText(word); 16 | this.setMaxItems(50); 17 | this.setItems(items); 18 | this.storeFocusedElement(); 19 | this.focusFilterEditor(); 20 | } 21 | 22 | viewForItem(item) { 23 | const tag = $('
').text(item.tag).html(); 24 | const name = $('
').text(item.name).html(); 25 | return `
  • ${tag}${name}
  • `; 26 | } 27 | 28 | confirmed(item) { 29 | this.confirmed_ = true; 30 | this.showViewForItem(item); 31 | this.filterEditorView.blur(); 32 | } 33 | 34 | cancelled() { 35 | if (!this.confirmed_ && this.docView_) { 36 | this.docView_.destroy(); 37 | } 38 | this.panel_.destroy(); 39 | } 40 | 41 | getFilterKey() { 42 | return 'description'; 43 | } 44 | 45 | showViewForItem(item) { 46 | if (!this.setViewPromise_) { 47 | this.setViewPromise_ = atom.workspace.open('element-docs://', { split: 'right', activatePane: false}) 48 | .then(docView => { 49 | this.docView_ = docView; 50 | this.docView_.setView(item.url); 51 | }); 52 | } else { 53 | this.setViewPromise_ = this.setViewPromise_.then(() => { 54 | this.docView_.setView(item.url); 55 | }) 56 | } 57 | } 58 | } 59 | 60 | module.exports = SearchView; 61 | -------------------------------------------------------------------------------- /resources/element/style.css: -------------------------------------------------------------------------------- 1 | .page-container h2 {font-size: 1.5rem;}.page-container h3 {font-size: 1.17rem;}.page-container p {font-size: 12px;} 2 | /* Menu */ 3 | .side-nav { 4 | position: fixed; 5 | z-index: 1001; 6 | width: 230px; 7 | height: 100%; 8 | background: #eef1f6; 9 | transform: translate(-230px); 10 | transition: transform 0.4s cubic-bezier(0.7,0,0.3,1); 11 | overflow: auto; 12 | padding: 0 10px; 13 | } 14 | .side-nav ul > li a, .side-nav .nav-group__title { 15 | cursor: pointer; 16 | } 17 | .page-container-right { 18 | transition: 0.4s cubic-bezier(0.7,0,0.3,1); 19 | } 20 | .hide-menu .menu-button{ 21 | left: 0; 22 | transition: left 0.4s cubic-bezier(0.7,0,0.3,1); 23 | } 24 | .show-menu .menu-button { 25 | left: 230px; 26 | transition: left 0.4s cubic-bezier(0.7,0,0.3,1); 27 | } 28 | .menu-button { 29 | cursor: pointer; 30 | width: 15px; 31 | height: 30px; 32 | line-height: 30px; 33 | background: #20a0ff; 34 | position: fixed; 35 | top: 50%; 36 | font-size: 12px; 37 | z-index: 10; 38 | color: white; 39 | border-radius: 0 5px 5px 0; 40 | } 41 | /* Shown menu */ 42 | .show-menu .side-nav { 43 | transform: translate(0); 44 | transition: transform 0.4s cubic-bezier(0.7,0,0.3,1); 45 | } 46 | .show-menu .page-container-right { 47 | transform: translate(245px); 48 | transition: transform 0.4s cubic-bezier(0.7,0,0.3,1); 49 | } 50 | .show-menu .menu-button { 51 | background-color: #eef1f6; 52 | color: #666; 53 | transition: 0.5s; 54 | } 55 | 56 | .page-component__scroll { 57 | height: calc(100%) !important; 58 | margin-top: 0 !important; 59 | } 60 | 61 | .page-component__nav { 62 | margin-top: 0px !important; 63 | } 64 | 65 | .page-component__content { 66 | padding: 0px !important; 67 | } 68 | 69 | .page-container { 70 | width: 100%; 71 | } 72 | -------------------------------------------------------------------------------- /snippets/element.cson: -------------------------------------------------------------------------------- 1 | ".source.js": 2 | "message": 3 | "prefix": "msg", 4 | "body": """ 5 | this.$message({ 6 | message: '${1:text}', 7 | type: '${2:info}' 8 | }); 9 | """ 10 | "msgbox-alert": 11 | "prefix": "alert", 12 | "body": """ 13 | this.$alert('${1:content}', '${2:title}', { 14 | confirmButtonText: '${3:confirm}', 15 | callback: ${4:action} => { 16 | 17 | } 18 | }); 19 | """ 20 | "msgbox-confirm": 21 | "prefix": "confirm", 22 | "body": """ 23 | this.$confirm('${1:content}', '${2:title}', { 24 | confirmButtonText: '${3:confirm}', 25 | cancelButtonText: '${4:cancel}', 26 | type: '${5:warning}' 27 | }).then(() => { 28 | 29 | }).catch(() => {}); 30 | """ 31 | "msgbox-prompt": 32 | "prefix": "prompt", 33 | "body": """ 34 | this.$prompt('${1:content}', '${2:title}', { 35 | confirmButtonText: '${3:confirm}', 36 | cancelButtonText: '${4:cancel}', 37 | inputPattern: /${5:regExp}/, 38 | inputErrorMessage: '${6:errormsg}' 39 | }).then(({ value }) => { 40 | 41 | }).catch(() => {}); 42 | """ 43 | "msgbox": 44 | "prefix": "msgb", 45 | "body": """ 46 | this.$msgbox({ 47 | title: '${1:title}', 48 | message: '${2:string|VNode}', 49 | showCancelButton: ${3:true}, 50 | confirmButtonText: '${4:confirm}', 51 | cancelButtonText: '${5:cancel}', 52 | beforeClose: (action, instance, done) => {} 53 | }).then(action => { 54 | 55 | }); 56 | """ 57 | "notification": 58 | "prefix": "notify", 59 | "body": """ 60 | this.$notify({ 61 | title: '${1:title}', 62 | message: '${2:string|VNode}' 63 | }); 64 | """ 65 | -------------------------------------------------------------------------------- /lib/element-helper.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import Url from 'url'; 4 | import { CompositeDisposable } from 'atom'; 5 | import Library from './library'; 6 | import provider from './provider'; 7 | 8 | let DocView = null; 9 | let SearchView = null; 10 | 11 | export default { 12 | config: { 13 | 'language': { 14 | title: 'Document Language', 15 | type: 'string', 16 | default: 'zh-CN', 17 | enum: ['zh-CN', 'en-US'], 18 | order: 2, 19 | } 20 | }, 21 | activate(state) { 22 | this.subscriptions = new CompositeDisposable(); 23 | this.library = new Library(); 24 | 25 | this.lazyLoad(); 26 | // Register command 27 | this.subscriptions.add(atom.commands.add('atom-workspace', { 28 | 'element-helper:search-under-cursor': this.search.bind(this) 29 | })); 30 | 31 | this.subscriptions.add(atom.workspace.addOpener(this.opener.bind(this))); 32 | }, 33 | 34 | deactivate() { 35 | this.subscriptions.dispose(); 36 | }, 37 | 38 | search() { 39 | if (localStorage.getItem('element-helper.loading')) { 40 | atom.notifications.addInfo('Document is loading, please wait a minute.'); 41 | return; 42 | } 43 | if (editor = atom.workspace.getActiveTextEditor()) { 44 | const selectedText = editor.getSelectedText(); 45 | const wordUnderCursor = editor.getWordUnderCursor({ includeNonWordCharacters: false }); 46 | 47 | const items = this.library.queryAll(); 48 | const queryText = selectedText ? selectedText : wordUnderCursor; 49 | 50 | new SearchView(queryText, items); 51 | } 52 | }, 53 | 54 | opener(url) { 55 | if (Url.parse(url).protocol == 'element-docs:') { 56 | return new DocView(this.library, url); 57 | } 58 | }, 59 | 60 | lazyLoad() { 61 | if (!SearchView) { 62 | SearchView = require('./search-view'); 63 | } 64 | if (!DocView) { 65 | DocView = require('./document-view'); 66 | } 67 | }, 68 | 69 | provide() { 70 | return provider; 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /resources/style.css: -------------------------------------------------------------------------------- 1 | .element-helper-docs-container { 2 | background-color: white; 3 | overflow: auto; 4 | } 5 | .element-helper-docs-container .docs-version { 6 | color: #50bfff; 7 | } 8 | .element-helper-docs-container .docs-notice { 9 | position: absolute; 10 | right: 10px; 11 | background: #50bfff; 12 | color: white; 13 | padding: 2px 8px; 14 | border-radius: 2px; 15 | } 16 | 17 | .element-helper-docs-container .docs-notice a { 18 | color: inherit; 19 | text-decoration: underline; 20 | } 21 | 22 | .element-helper-loading-mask, .element-helper-move-mask { 23 | position: absolute; 24 | z-index: 10000; 25 | background-color: hsla(0,0%,100%,.9); 26 | margin: 0; 27 | top: 0; 28 | right: 0; 29 | bottom: 0; 30 | left: 0; 31 | transition: opacity .3s 32 | } 33 | .element-helper-move-mask { 34 | display: none; 35 | background-color: hsla(0,0%,100%,0); 36 | } 37 | 38 | .element-helper-loading-mask.is-fullscreen { 39 | position: fixed 40 | } 41 | 42 | .element-helper-loading-mask.is-fullscreen .element-helper-loading-spinner { 43 | margin-top: -25px 44 | } 45 | 46 | .element-helper-loading-mask.is-fullscreen .element-helper-loading-spinner .circular { 47 | width: 50px; 48 | height: 50px 49 | } 50 | 51 | .element-helper-loading-spinner { 52 | top: 50%; 53 | margin-top: -21px; 54 | width: 100%; 55 | text-align: center; 56 | position: absolute 57 | } 58 | 59 | .element-helper-loading-spinner .element-helper-loading-text { 60 | color: #20a0ff; 61 | margin: 3px 0; 62 | font-size: 14px 63 | } 64 | 65 | .element-helper-loading-spinner .circular { 66 | width: 42px; 67 | height: 42px; 68 | animation: loading-rotate 2s linear infinite 69 | } 70 | 71 | .element-helper-loading-spinner .path { 72 | animation: loading-dash 1.5s ease-in-out infinite; 73 | stroke-dasharray: 90,150; 74 | stroke-dashoffset: 0; 75 | stroke-width: 2; 76 | stroke: #20a0ff; 77 | stroke-linecap: round 78 | } 79 | 80 | .element-helper-loading-fade-enter,.element-helper-loading-fade-leave-active { 81 | opacity: 0 82 | } 83 | 84 | @keyframes loading-rotate { 85 | to { 86 | transform: rotate(1turn) 87 | } 88 | } 89 | 90 | @keyframes loading-dash { 91 | 0% { 92 | stroke-dasharray: 1,200; 93 | stroke-dashoffset: 0 94 | } 95 | 96 | 50% { 97 | stroke-dasharray: 90,150; 98 | stroke-dashoffset: -40px 99 | } 100 | 101 | to { 102 | stroke-dasharray: 90,150; 103 | stroke-dashoffset: -120px 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Element-Helper 2 | 3 | > Element-Helper is a Atom package for Element-UI, if you use VSCode editor, please go to [VSCode VERSION](https://marketplace.visualstudio.com/items?itemName=ElemeFE.vscode-element-helper) 4 | 5 | Element-UI is a great library. More and more projects use it. So, For helping developer write by Element-UI more efficient, Element-Helper is born. 6 | 7 | ## Feature 8 | 9 | * Document 10 | 11 | * Autocomplete 12 | 13 | * Snippets 14 | 15 | 16 | ## Document 17 | 18 | ### Usage 19 | 20 | 1 - Move cursor to Element-UI tag or select it 21 | 22 | 2 - Press default hot key `ctrl + cmd + z` 23 | 24 | 3 - Select tag you want to search 25 | 26 | 4 - Enter and trigger document browser 27 | 28 | ![document](https://user-images.githubusercontent.com/1659577/27280445-8077e646-551a-11e7-93c0-fb577020c841.gif) 29 | 30 | ### Version and Language Switching 31 | 32 | 1 - Enter `Atom` -> `Preferences` 33 | 34 | 2 - Enter `Packages` and search Element-Helper package 35 | 36 | 3 - Enter `Setting` 37 | 38 | 4 - Switch version and language 39 | 40 | ### Auto Update Mechanism 41 | 42 | Document is off-line and auto synchronize with Element-UI official site. 43 | 44 | ### Keymap 45 | 46 | Default hot key is `ctrl + cmd + z`. If it has conflicted with other software's hot key. You can customize it. 47 | 48 | 1 - Enter `Atom` -> `Keymap` 49 | 50 | 2 - Customize your config. like 51 | 52 | ``` 53 | "atom-workspace": 54 | "ctrl-alt-z": "element-helper:search-under-cursor" 55 | ``` 56 | 57 | 58 | ## Autocomplete 59 | 60 | ![autocomplete](https://cloud.githubusercontent.com/assets/1659577/26758337/e0417b1e-490d-11e7-87be-c2640d239285.gif) 61 | 62 | * Distinguish and auto complete property and method for every Element-UI tag 63 | 64 | * Prompt value when value is some special type like Boolean or ICON. 65 | 66 | 67 | ## Snippets 68 | 69 | ![snippets](https://cloud.githubusercontent.com/assets/1659577/26758333/b8c2b3c8-490d-11e7-9349-666e47712860.gif) 70 | 71 | Support snippets list: 72 | 73 | * `msg` 74 | 75 | ``` 76 | this.$message({ 77 | message: '', 78 | type: '' 79 | }) 80 | ``` 81 | 82 | * `alert` 83 | 84 | ``` 85 | this.$alert('', '', { 86 | confirmButtonText: '', 87 | callback: () => {} 88 | }); 89 | ``` 90 | 91 | * `confirm` 92 | 93 | ``` 94 | this.$confirm('', '', { 95 | confirmButtonText: '', 96 | cancelButtonText: '', 97 | type: '' 98 | }).then(() => {}) 99 | .catch(() => {}); 100 | ``` 101 | 102 | * `prompt` 103 | 104 | ``` 105 | this.$prompt('', '', { 106 | confirmButtonText: '', 107 | cancelButtonText: '', 108 | inputPattern: //, 109 | inputErrorMessage: '' 110 | }).then(({ value }) => {}) 111 | .catch(() => {}); 112 | ``` 113 | 114 | * `msgb` 115 | 116 | ``` 117 | this.$msgbox({ 118 | title: '', 119 | message: '', 120 | showCancelButton: '', 121 | confirmButtonText: '', 122 | cancelButtonText: '', 123 | beforeClose: (action, instance, done) => {} 124 | }).then(action => {}); 125 | ``` 126 | 127 | * `notify` 128 | 129 | ``` 130 | this.$notify({ 131 | title: '', 132 | message: '' 133 | }); 134 | ``` 135 | 136 | ## Contribution 137 | 138 | Your pull request will make Element-Helper better. 139 | 140 | ## LICENSE 141 | 142 | MIT 143 | -------------------------------------------------------------------------------- /lib/resource.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import { File } from 'atom'; 4 | import Path from 'path'; 5 | import Mkdirp from 'mkdirp'; 6 | import cheerio from "cheerio"; 7 | import { http as Http } from "follow-redirects"; 8 | import Fs from 'fs'; 9 | 10 | class Resource { 11 | // resources local path 12 | static RESOURCE_PATH = Path.join(__dirname, '..', 'resources'); 13 | static ELEMENT_PATH = Path.join(__dirname, '..', 'node_modules', 'element-gh-pages'); 14 | static URL_REG = /((?:src|href)\s*=\s*)(['"])(\/\/[^'"]*)\2/g; 15 | static ELEMENT_VERSION_URL = 'http://element.eleme.io/versions.json'; 16 | static ELEMENT_HOME_URL = 'http://element.eleme.io/'; 17 | static RESOURCE_REPO = 'repos.json'; 18 | 19 | static get(filename) { 20 | 21 | return new File(filename).read() 22 | .then(result => result ? result : Promise.reject('ReadFail')); 23 | } 24 | 25 | static getFromUrl(url, filename) { 26 | return new Promise((resolve, reject) => { 27 | Http.get(url, response => { 28 | response.on('error', reject); 29 | let buffer = ''; 30 | response.on('data', chunk => { buffer += chunk; }); 31 | response.on('end', () => { resolve(buffer); }); 32 | }).on('error', reject); 33 | }).then(result => { 34 | if (filename) { 35 | Mkdirp(Path.dirname(filename)); 36 | new File(filename).write(result); 37 | } 38 | return result; 39 | }).catch(error => console.log(error)); 40 | } 41 | 42 | static fixResource(file, vs) { 43 | const htmlPath = Path.join(Resource.ELEMENT_PATH, file); 44 | Resource.get(htmlPath) 45 | .then(content => { 46 | const matched = []; 47 | content = content.replace(Resource.URL_REG, (match, one, two, three)=> { 48 | const name = Path.basename(three); 49 | const url = `http:${three}`; 50 | Resource.getFromUrl(url, Path.join(Path.dirname(htmlPath), name)).catch(error =>{ 51 | // one more again 52 | Resource.getFromUrl(url, Path.join(Path.dirname(htmlPath), name)); 53 | }); 54 | return `${one}${two}${name}${two}`; 55 | }); 56 | 57 | let $ = cheerio.load(content); 58 | 59 | const jqScript = $(``); 60 | const fixScript = $(``); 61 | const style = $(``); 62 | $('body').append(jqScript).append(fixScript); 63 | $('head').append(style); 64 | 65 | const indexPath = Path.join(Resource.ELEMENT_PATH, file); 66 | const dir = Path.dirname(indexPath); 67 | new File(Path.join(dir, 'main.html')).write($.html()); 68 | return content; 69 | }); 70 | } 71 | 72 | static updateResource() { 73 | Fs.readdir(Resource.ELEMENT_PATH, (err, files) => { 74 | if (err) { 75 | return; 76 | } 77 | 78 | for(let i = 0; i < files.length; ++i) { 79 | const status = Fs.lstatSync(Path.join(Resource.ELEMENT_PATH, files[i])); 80 | if (status.isFile() && /index.html$/.test(files[i])) { // index.html entry 81 | Resource.fixResource(files[i], 1); 82 | } else if (status.isDirectory() && /^\d+\./.test(files[i])) { // version directory 83 | Resource.fixResource(Path.join(files[i], 'index.html'), files[i].split('.')[0] || 1); 84 | } else { 85 | continue; 86 | } 87 | } 88 | }); 89 | } 90 | } 91 | 92 | export default Resource; 93 | -------------------------------------------------------------------------------- /lib/library.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import { File } from 'atom'; 4 | import Path from 'path'; 5 | import DocSet from './docset'; 6 | import Resource from './resource'; 7 | import { exec, cd } from 'shelljs'; 8 | const fs = require('fs'); 9 | 10 | class Library { 11 | static REFRESH_PERIOD_MS_ = 6 * 60 * 60 * 1000; 12 | static DEFAULT_DOCSETS = new Set([ 13 | 'element' 14 | ]); 15 | 16 | constructor() { 17 | this.catalog = null; 18 | this.fetchRepo(); 19 | this.cmd = ''; 20 | setInterval(() => { this.fetchAllVersion(this.repos); }, Library.REFRESH_PERIOD_MS_); 21 | } 22 | 23 | // id: type 24 | get(id) { 25 | return this.catalog[id]; 26 | } 27 | 28 | queryAll() { 29 | let ret = []; 30 | for (const id in this.catalog) { 31 | ret = ret.concat(this.catalog[id].queryAll()); 32 | } 33 | return ret; 34 | } 35 | 36 | fetchRepo() { 37 | return Resource.get(Path.join(Resource.RESOURCE_PATH, Resource.RESOURCE_REPO)) 38 | .then(result => { 39 | this.repos = JSON.parse(result) 40 | this.buildCatalog(this.repos); 41 | this.fetchAllVersion(this.repos); 42 | }).catch(error => { 43 | console.log('fetchRepo error: ', error); 44 | }); 45 | } 46 | 47 | fetchAllVersion(repos) { 48 | cd(`${Resource.RESOURCE_PATH}/..`); 49 | exec('npm update element-helper-json --save', { async: true }); 50 | for (let i = 0; i < repos.length; ++i) { 51 | let repo = repos[i]; 52 | this.fetchVersion(repo); 53 | } 54 | } 55 | 56 | setVersionSchema(versions, repo) { 57 | const versionSchema = { 58 | title: `${repo.name} Version`, 59 | description: `Document version of ${repo.name}.`, 60 | type: 'string', 61 | default: versions[versions.length -1], 62 | enum: versions, 63 | order: 1 64 | }; 65 | atom.config.setSchema(`element-helper.${repo.type}_version`, versionSchema); 66 | } 67 | 68 | fetchVersion(repo) { 69 | Resource.get(Path.join(Resource.ELEMENT_PATH, 'versions.json')).then(local => { 70 | Resource.getFromUrl(Resource.ELEMENT_VERSION_URL) 71 | .then(online => { 72 | const oldVersions = this.getValues(JSON.parse(local)); 73 | const newVersions = this.getValues(JSON.parse(online)); 74 | if (!this.isSame(JSON.parse(local), JSON.parse(online))) { 75 | cd(`${Resource.RESOURCE_PATH}/..`); 76 | exec('npm update element-gh-pages --save', (error, stdout, stderr) => { 77 | if (error) { 78 | return; 79 | } 80 | const versionsStr = fs.readFileSync(Path.join(Resource.ELEMENT_PATH, 'versions.json'), 'utf8'); 81 | if (!this.isSame(JSON.parse(local), JSON.parse(versionsStr))) { 82 | this.setVersionSchema(newVersions, repo); 83 | atom.notifications.addInfo(`${repo.name} version updated to lasted version`, { 84 | dismissable: true 85 | }); 86 | } else { 87 | this.setVersionSchema(oldVersions, repo); 88 | } 89 | Resource.updateResource(); 90 | }); 91 | } else { 92 | this.setVersionSchema(oldVersions, repo); 93 | if (!fs.existsSync(Path.join(Resource.ELEMENT_PATH, 'main.html'))) { 94 | Resource.updateResource(); 95 | } 96 | } 97 | }); 98 | }); 99 | } 100 | 101 | isSame(local, online) { 102 | for (let key in online) { 103 | if (!local[key]) { 104 | return false; 105 | } 106 | } 107 | return true; 108 | } 109 | 110 | getValues(obj) { 111 | let values = []; 112 | for (let key in obj) { 113 | values.push(obj[key]); 114 | } 115 | return values; 116 | } 117 | 118 | buildCatalog(repos) { 119 | const catalog = {}; 120 | 121 | for (let i = 0; i < repos.length; ++i) { 122 | const repo = repos[i]; 123 | catalog[repo.type] = new DocSet(repo); 124 | } 125 | 126 | for (let i = 0; i < repos.length; ++i) { 127 | const repo = repos[i]; 128 | 129 | let title = repo.name; 130 | if ('version' in repo && repo.version) { 131 | title += ' ' + repo.version; 132 | } 133 | 134 | let schema = { 135 | title: title, 136 | type: 'boolean', 137 | default: Library.DEFAULT_DOCSETS.has(repo.type) 138 | }; 139 | atom.config.setSchema(`element-helper.${repo.type}`, schema); 140 | } 141 | 142 | this.catalog = catalog; 143 | } 144 | } 145 | 146 | export default Library; 147 | -------------------------------------------------------------------------------- /lib/document-view.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import { ScrollView } from 'atom-space-pen-views'; 4 | import { Emitter, Disposable } from 'atom'; 5 | import $ from 'jquery'; 6 | import Url from 'url'; 7 | import Resource from './resource'; 8 | import Path from 'path'; 9 | 10 | class DocView extends ScrollView { 11 | static DOC_STYLE = ""; 12 | 13 | static content() { 14 | return this.div({class: 'element-helper-docs-container native-key-bindings', tabindex: -1}); 15 | } 16 | 17 | constructor(library, url) { 18 | super(); 19 | this.emitter_ = new Emitter(); 20 | this.title_ = 'Loading...'; 21 | this.library_ = library; 22 | this.url_ = url; 23 | this.pane_ = null; 24 | this.stylePromise = Resource.get(Path.join(Resource.RESOURCE_PATH, 'style.css')).then(result => DocView.DOC_STYLE = result); 25 | this.version_ = atom.config.get('element-helper.element_version'); 26 | this.language_ = atom.config.get('element-helper.language'); 27 | } 28 | 29 | setView(url) { 30 | this.stylePromise.then(() => { 31 | const parsedUrl = Url.parse(url, true); 32 | const versions = atom.config.getSchema('element-helper.element_version').enum; 33 | const docset = this.library_.get(parsedUrl.hostname); 34 | const path = url.split('/').pop(); 35 | const componentPath = `${this.version_}/main.html#/${this.language_}/component/${path}`; 36 | const href = Resource.ELEMENT_HOME_URL + componentPath.replace('main.html', 'index.html'); 37 | const iframeSrc = 'file://' + Path.join(Resource.ELEMENT_PATH, componentPath).split(Path.sep).join('/'); 38 | 39 | let opts = [''); 46 | const html = opts.join(''); 47 | 48 | const notice = ({ 49 | 'zh-CN': `版本:${html},在线示例请在浏览器中查看`, 50 | 'en-US': `Version: ${html}, view online examples in browser` 51 | })[this.language_]; 52 | $(this.element).html(`
    ${notice}
    `); 53 | 54 | let iframe = $('.element-helper-docs-container #docs-frame'); 55 | let link = $('.element-helper-docs-container .docs-notice a'); 56 | window.addEventListener('message', (e) => { 57 | e.data.loaded && $('.element-helper-loading-mask').css('display', 'none'); 58 | if (e.data.title) { 59 | this.title_ = e.data.title; 60 | this.emitter_.emit('did-change-title'); 61 | } 62 | if (e.data.hash) { 63 | let pathArr = link.attr('href').split('#'); 64 | pathArr.pop(); 65 | pathArr.push(e.data.hash); 66 | link.attr('href', pathArr.join('#')); 67 | let srcArr = iframe.attr('src').split('#'); 68 | srcArr.pop(); 69 | srcArr.push(e.data.hash); 70 | iframe.attr('src', srcArr.join('#')); 71 | } 72 | }, false); 73 | $('.element-helper-docs-container .docs-version').on('change', () => { 74 | var version = $('.element-helper-docs-container .docs-version').val(); 75 | var originalSrc = iframe.attr('src'); 76 | var arr = originalSrc.split(new RegExp('/?[0-9.]*/main.html')); 77 | if(this.version_ === version) { 78 | iframe.attr('src', arr.join('/main.html')); 79 | link.attr('href', link.attr('href').replace(new RegExp('/?[0-9.]*/index.html'), '/index.html')); 80 | } else { 81 | iframe.attr('src', arr.join('/' + version + '/main.html')); 82 | link.attr('href', link.attr('href').replace(new RegExp('/?[0-9.]*/index.html'), '/' + version + '/index.html')); 83 | } 84 | }); 85 | this.title_ = docset.getTitle(path); 86 | this.emitter_.emit('did-change-title'); 87 | }); 88 | } 89 | 90 | onDidChangeTitle(callback) { 91 | return this.emitter_.on('did-change-title', callback); 92 | } 93 | 94 | onDidChangeModified() { 95 | return new Disposable(); 96 | } 97 | 98 | destroy() { 99 | this.pane_.destroyItem(this); 100 | if (this.pane_.getItems().length === 0) { 101 | this.pane_.destroy(); 102 | } 103 | } 104 | 105 | attached() { 106 | this.pane_ = atom.workspace.paneForURI(this.getURI()); 107 | this.pane_.activateItem(this); 108 | let timer = null; 109 | // resolve block problem 110 | this.pane_.onDidChangeFlexScale(f => { 111 | $('.element-helper-move-mask').css('display', 'block'); 112 | clearTimeout(timer); 113 | timer = setTimeout(() => { 114 | $('.element-helper-move-mask').css('display', 'none'); 115 | }, 500); 116 | }); 117 | } 118 | 119 | getURI() { 120 | return this.url_; 121 | } 122 | 123 | getTitle() { 124 | return this.title_; 125 | } 126 | } 127 | 128 | export default DocView; 129 | -------------------------------------------------------------------------------- /lib/provider.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import prettyHTML from 'pretty'; 4 | import * as TAGS from 'element-helper-json/element-tags.json'; 5 | import * as ATTRS from 'element-helper-json/element-attributes.json'; 6 | 7 | // https://github.com/atom/autocomplete-plus/wiki/Provider-API 8 | const provider = { 9 | selector: '.text.html', 10 | disableForSelector: '.text.html .comment', 11 | filterSuggestions: true, 12 | attrReg: /\s+[:@]*([a-zA-Z][-a-zA-Z]*)\s*=\s*$/, 13 | tagReg: /<([a-zA-Z][-a-zA-Z]*)(?:\s|$)/, 14 | bindReg: /\s+:$/, 15 | methodReg: /^\s+@$/, 16 | 17 | getSuggestions(request) { 18 | if (this.isAttrValueStart(request)) { 19 | return this.getAttrValueSuggestion(request); 20 | } else if (this.isAttrStart(request)) { 21 | return this.getAttrSuggestion(request); 22 | } else if (this.isTagStart(request)) { 23 | return this.getTagSuggestion(request); 24 | } else { 25 | return []; 26 | } 27 | }, 28 | // called when a suggestion from your provider was inserted into the buffer 29 | onDidInsertSuggestion({editor, suggestion}) { 30 | (suggestion.type === 'property') && setTimeout(() => this.triggerAutocomplete(editor), 1); 31 | }, 32 | 33 | triggerAutocomplete(editor) { 34 | atom.commands.dispatch(atom.views.getView(editor), 'autocomplete-plus:activate', {activatedManually: false}); 35 | }, 36 | 37 | isAttrStart({editor, scopeDescriptor, bufferPosition, prefix}) { 38 | const preTwoLetter = editor.getTextInBufferRange([[bufferPosition.row, bufferPosition.column - 2], bufferPosition]); 39 | const scopes = scopeDescriptor.getScopesArray(); 40 | if (!this.getPreAttr(editor, bufferPosition) && ((prefix && !prefix.trim())|| this.bindReg.test(preTwoLetter) || this.methodReg.test(preTwoLetter))) { 41 | return this.hasTagScope(scopes); 42 | } 43 | 44 | const preBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - 1)]; 45 | const preScopeDescriptor = editor.scopeDescriptorForBufferPosition(preBufferPosition); 46 | const preScopes = preScopeDescriptor.getScopesArray(); 47 | 48 | if (preScopes.includes('entity.other.attribute-name.html')) { 49 | return true; 50 | } 51 | if (!this.hasTagScope(scopes) || !prefix) { 52 | return false; 53 | } 54 | return (scopes.includes('punctuation.definition.tag.end.html') && 55 | !preScopes.includes('punctuation.definition.tag.end.html')); 56 | }, 57 | 58 | isAttrValueStart({scopeDescriptor, bufferPosition, editor}) { 59 | const scopes = scopeDescriptor.getScopesArray(); 60 | 61 | const preBufferPosition = [bufferPosition.row, Math.max(0, bufferPosition.column - 1)]; 62 | const preScopeDescriptor = editor.scopeDescriptorForBufferPosition(preBufferPosition); 63 | const preScopes = preScopeDescriptor.getScopesArray(); 64 | 65 | return (this.hasStringScope(scopes) && 66 | this.hasStringScope(preScopes) && 67 | !preScopes.includes('punctuation.definition.string.end.html') && 68 | this.hasTagScope(scopes) && 69 | this.getPreAttr(editor, bufferPosition) 70 | ); 71 | }, 72 | 73 | isTagStart({editor, bufferPosition, scopeDescriptor, prefix}) { 74 | if (prefix.trim() && !prefix.includes('<')) { 75 | return this.hasTagScope(scopeDescriptor.getScopesArray()); 76 | } 77 | // autocomplete-plus's default prefix setting does not capture <. Manually check for it. 78 | prefix = editor.getTextInBufferRange([[bufferPosition.row, bufferPosition.column - 1], bufferPosition]); 79 | const scopes = scopeDescriptor.getScopesArray(); 80 | 81 | return (prefix === '<' && 82 | ((scopes.includes('text.html.basic') && scopes.length === 1) || 83 | (scopes.includes('text.html.vue') && scopes.includes('invalid.illegal.bad-angle-bracket.html')))); 84 | }, 85 | 86 | getPreAttr(editor, bufferPosition) { 87 | let quoteIndex = bufferPosition.column - 1; 88 | let preScopeDescriptor = null; 89 | let scopes = null; 90 | while (quoteIndex) { 91 | preScopeDescriptor = editor.scopeDescriptorForBufferPosition([bufferPosition.row, quoteIndex]); 92 | scopes = preScopeDescriptor.getScopesArray(); 93 | if (!this.hasStringScope(scopes) || scopes.includes('punctuation.definition.string.begin.html')) { 94 | break; 95 | } 96 | quoteIndex--; 97 | } 98 | let attr = this.attrReg.exec(editor.getTextInRange([[bufferPosition.row, 0], [bufferPosition.row, quoteIndex]])); 99 | return attr && attr[1]; 100 | }, 101 | 102 | getPreTag(editor, bufferPosition) { 103 | let row = bufferPosition.row; 104 | let tag = null; 105 | while (row) { 106 | tag = this.tagReg.exec(editor.lineTextForBufferRow(row)); 107 | if (tag && tag[1]) { 108 | return tag[1]; 109 | } 110 | row--; 111 | } 112 | return; 113 | }, 114 | 115 | getAttrValues(tag, attr) { 116 | let attrItem = this.getAttrItem(tag, attr); 117 | let options = attrItem && attrItem.options; 118 | if (!options && attrItem) { 119 | if (attrItem.type === 'boolean') { 120 | options = ['true', 'false']; 121 | } else if (attrItem.type === 'icon') { 122 | options = ATTRS['icons']; 123 | } else if (attrItem.type === 'shortcut-icon') { 124 | options = []; 125 | ATTRS['icons'].forEach(icon => { 126 | options.push(icon.replace(/^el-icon-/, '')); 127 | }); 128 | } 129 | } 130 | return options || []; 131 | }, 132 | 133 | getTagAttrs(tag) { 134 | return (TAGS[tag] && TAGS[tag].attributes) || []; 135 | }, 136 | 137 | getTagSuggestion({editor, bufferPosition, prefix}) { 138 | const preLetter = editor.getTextInBufferRange([[bufferPosition.row, bufferPosition.column - 1], bufferPosition]); 139 | const suggestions = []; 140 | for (let tag in TAGS) { 141 | if (preLetter === '<' || this.firstCharsEqual(tag, prefix)) { 142 | suggestions.push(this.buildTagSuggestion(tag, TAGS[tag])); 143 | } 144 | } 145 | return suggestions; 146 | }, 147 | 148 | getAttrSuggestion({editor, bufferPosition, prefix}) { 149 | const suggestions = []; 150 | const tag = this.getPreTag(editor, bufferPosition); 151 | const tagAttrs = this.getTagAttrs(tag); 152 | const preText = editor.getTextInBufferRange([[bufferPosition.row, 0], bufferPosition]); 153 | 154 | // method attribute 155 | const method = preText.split(/\s+/).pop()[0] === '@'; 156 | // bind attribute 157 | const bind = preText.split(/\s+/).pop()[0] === ':'; 158 | 159 | tagAttrs.forEach(attr => { 160 | // autocomplete-plus会忽略@和<符号 161 | // 属性存在 and 为空 or 绑定元素(' :') or 首字母相等 162 | const attrItem = this.getAttrItem(tag, attr); 163 | if (attrItem && (!prefix.trim() || this.bindReg.test(prefix) || this.firstCharsEqual(attr, prefix))) { 164 | const sug = this.buildAttrSuggestion({attr, tag, bind, method}, attrItem); 165 | sug && suggestions.push(sug); 166 | } 167 | }); 168 | for (let attr in ATTRS) { 169 | const attrItem = this.getAttrItem(tag, attr); 170 | if (attrItem && attrItem.global && (!prefix.trim() || this.bindReg.test(prefix) || this.firstCharsEqual(attr, prefix))) { 171 | const sug = this.buildAttrSuggestion({attr, bind, method}, attrItem); 172 | sug && suggestions.push(sug); 173 | } 174 | } 175 | return suggestions; 176 | }, 177 | 178 | getAttrValueSuggestion({editor, bufferPosition, prefix}) { 179 | const suggestions = []; 180 | const tag = this.getPreTag(editor, bufferPosition); 181 | const attr = this.getPreAttr(editor, bufferPosition); 182 | const values = this.getAttrValues(tag, attr); 183 | values.forEach(value => { 184 | if (this.firstCharsEqual(value, prefix) || !prefix) { 185 | suggestions.push(this.buildAttrValueSuggestion(tag, attr, value)); 186 | } 187 | }); 188 | 189 | return suggestions; 190 | }, 191 | 192 | buildAttrSuggestion({attr, tag, bind, method}, {description, type}) { 193 | const attrItem = this.getAttrItem(tag, attr); 194 | if ((method && attrItem.type === "method") || (bind && attrItem.type !== "method") || (!method && !bind)) { 195 | return { 196 | snippet: (type && (type === 'flag')) ? `${attr} ` : `${attr}=\"$1\"$0`, 197 | displayText: attr, 198 | type: (type && (type === 'method')) ? 'method' : 'property', 199 | description: description, 200 | rightLabel: tag ? `<${tag}>` : 'element-ui' 201 | }; 202 | } else { return; } 203 | }, 204 | 205 | buildAttrValueSuggestion(tag, attr, value) {; 206 | const attrItem = this.getAttrItem(tag, attr) 207 | return { 208 | text: value, 209 | type: 'value', 210 | description: attrItem.description, 211 | rightLabel: attrItem.global ? 'element-ui' : `<${tag}>` 212 | }; 213 | }, 214 | 215 | buildTagSuggestion(tag, tagVal) { 216 | const snippets = []; 217 | let index = 0; 218 | function build(tag, {subtags, defaults}, snippets) { 219 | let attrs = ''; 220 | defaults && defaults.forEach((item,i) => { 221 | attrs +=` ${item}="$${index + i + 1}"`; 222 | }); 223 | snippets.push(`${index > 0 ? '<':''}${tag}${attrs}>`); 224 | index++; 225 | subtags && subtags.forEach(item => build(item, TAGS[item], snippets)); 226 | snippets.push(``); 227 | }; 228 | build(tag, tagVal, snippets); 229 | 230 | return { 231 | displayText: tag, 232 | snippet: prettyHTML('<' + snippets.join('')).substr(1), 233 | type: 'tag', 234 | rightLabel: 'element-ui', 235 | description: tagVal.description 236 | }; 237 | }, 238 | 239 | //two types: double and single quote 240 | hasStringScope(scopes) { 241 | return (scopes.includes('string.quoted.double.html') || 242 | scopes.includes('string.quoted.single.html')); 243 | }, 244 | 245 | hasTagScope(scopes) { 246 | return (scopes.includes('meta.tag.any.html') || 247 | scopes.includes('meta.tag.other.html') || 248 | scopes.includes('meta.tag.block.any.html') || 249 | scopes.includes('meta.tag.inline.any.html') || 250 | scopes.includes('meta.tag.structure.any.html')); 251 | }, 252 | 253 | firstCharsEqual(str1, str2) { 254 | if (str2 && str1) { 255 | return str1[0].toLowerCase() === str2[0].toLowerCase(); 256 | } 257 | return false; 258 | }, 259 | 260 | getAttrItem(tag, attr) { 261 | return ATTRS[`${tag}/${attr}`] || ATTRS[attr]; 262 | } 263 | 264 | }; 265 | 266 | export default provider; 267 | -------------------------------------------------------------------------------- /resources/element/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "entries": [ 3 | { 4 | "type": "basic", 5 | "name": "Layout 布局", 6 | "path": "layout", 7 | "tag": "el-row", 8 | "description": "el-row,Layout 布局" 9 | }, 10 | { 11 | "type": "basic", 12 | "name": "Layout 布局", 13 | "path": "layout", 14 | "tag": "el-col", 15 | "description": "el-col,Layout 布局" 16 | }, 17 | { 18 | "type": "basic", 19 | "name": "Color 色彩", 20 | "path": "color", 21 | "tag": "color", 22 | "description": "color,Color 色彩" 23 | }, 24 | { 25 | "type": "basic", 26 | "name": "Typography 字体", 27 | "path": "typography", 28 | "tag": "typography", 29 | "description": "typography,Typography 字体" 30 | }, 31 | { 32 | "type": "basic", 33 | "name": "Icon 图标", 34 | "path": "icon", 35 | "tag": "icon", 36 | "description": "icon,Icon 图标" 37 | }, 38 | { 39 | "type": "basic", 40 | "name": "Button 按钮", 41 | "path": "button", 42 | "tag": "el-button", 43 | "description": "el-button,Button 按钮" 44 | }, 45 | { 46 | "type": "form", 47 | "name": "Radio 单选框组", 48 | "path": "radio", 49 | "tag": "el-radio", 50 | "description": "el-radio,Radio 单选框" 51 | }, 52 | { 53 | "type": "form", 54 | "name": "Radio 单选框组", 55 | "path": "radio", 56 | "tag": "el-radio-group", 57 | "description": "el-radio-group,Radio 单选框组" 58 | }, 59 | { 60 | "type": "form", 61 | "name": "Checkbox 多选框", 62 | "path": "checkbox", 63 | "tag": "el-checkbox", 64 | "description": "el-checkbox,Checkbox 多选框" 65 | }, 66 | { 67 | "type": "form", 68 | "name": "Checkbox 多选框", 69 | "path": "checkbox", 70 | "tag": "el-checkbox-group", 71 | "description": "el-checkbox-group,Checkbox 多选框" 72 | }, 73 | { 74 | "type": "form", 75 | "name": "Input 输入框", 76 | "path": "input", 77 | "tag": "el-input", 78 | "description": "el-input,Input 输入框" 79 | }, 80 | { 81 | "type": "form", 82 | "name": "Input 输入框", 83 | "path": "input", 84 | "tag": "el-autocomplete", 85 | "description": "el-autocomplete,Input 输入框" 86 | }, 87 | { 88 | "type": "form", 89 | "name": "InputNumber 计数器", 90 | "path": "input-number", 91 | "tag": "el-input-number", 92 | "description": "el-input-number,InputNumber 计数器" 93 | }, 94 | { 95 | "type": "form", 96 | "name": "Select 选择器", 97 | "path": "select", 98 | "tag": "el-select", 99 | "description": "el-select,Select 选择器" 100 | }, 101 | { 102 | "type": "form", 103 | "name": "Select 选择器", 104 | "path": "select", 105 | "tag": "el-option", 106 | "description": "el-option,Select 选择器" 107 | }, 108 | { 109 | "type": "form", 110 | "name": "Select 选择器", 111 | "path": "select", 112 | "tag": "el-option-group", 113 | "description": "el-option-group,Select 选择器" 114 | }, 115 | { 116 | "type": "form", 117 | "name": "Cascader 级联选择器", 118 | "path": "cascader", 119 | "tag": "el-cascader", 120 | "description": "el-cascader,Cascader 级联选择器" 121 | }, 122 | { 123 | "type": "form", 124 | "name": "Switch 开关", 125 | "path": "switch", 126 | "tag": "el-switch", 127 | "description": "el-switch,Switch 开关" 128 | }, 129 | { 130 | "type": "form", 131 | "name": "Slider 滑块", 132 | "path": "slider", 133 | "tag": "el-slider", 134 | "description": "el-slider,Slider 滑块" 135 | }, 136 | { 137 | "type": "form", 138 | "name": "TimerPicker 时间选择器", 139 | "path": "time-picker", 140 | "tag": "el-time-picker", 141 | "description": "el-time-picker,TimerPicker 时间选择器" 142 | }, 143 | { 144 | "type": "form", 145 | "name": "TimerPicker 时间选择器", 146 | "path": "time-picker", 147 | "tag": "el-time-select", 148 | "description": "el-time-select,TimerPicker 时间选择器" 149 | }, 150 | { 151 | "type": "form", 152 | "name": "DatePicker 日期选择器", 153 | "path": "date-picker", 154 | "tag": "el-date-picker", 155 | "description": "el-date-picker,DatePicker 日期选择器" 156 | }, 157 | { 158 | "type": "form", 159 | "name": "DateTimePicker 日期时间选择器", 160 | "path": "datetime-picker", 161 | "tag": "el-datetime-picker", 162 | "description": "el-datetime-picker,DateTimePicker 日期时间选择器" 163 | }, 164 | { 165 | "type": "form", 166 | "name": "Upload 上传", 167 | "path": "upload", 168 | "tag": "el-upload", 169 | "description": "el-upload,Upload 上传" 170 | }, 171 | { 172 | "type": "form", 173 | "name": "Rate 评分", 174 | "path": "rate", 175 | "tag": "el-rate", 176 | "description": "el-rate,Rate 评分" 177 | }, 178 | { 179 | "type": "form", 180 | "name": "ColorPicker 颜色选择器", 181 | "path": "color-picker", 182 | "tag": "el-color-picker", 183 | "description": "el-color-picker,ColorPicker 颜色选择器" 184 | }, 185 | { 186 | "type": "form", 187 | "name": "ransfer 穿梭框", 188 | "path": "transfer", 189 | "tag": "el-transfer", 190 | "description": "el-transfer,ransfer 穿梭框" 191 | }, 192 | { 193 | "type": "form", 194 | "name": "Form 表单", 195 | "path": "form", 196 | "tag": "el-form", 197 | "description": "el-form,Form 表单" 198 | }, 199 | { 200 | "type": "form", 201 | "name": "Form 表单", 202 | "path": "form", 203 | "tag": "el-form-item", 204 | "description": "el-form-item,Form 表单" 205 | }, 206 | { 207 | "type": "data", 208 | "name": "Table 表格", 209 | "path": "table", 210 | "tag": "el-table", 211 | "description": "el-table,Table 表格" 212 | }, 213 | { 214 | "type": "data", 215 | "name": "Table 表格", 216 | "path": "table", 217 | "tag": "el-table-column", 218 | "description": "el-table-column,Table 表格" 219 | }, 220 | { 221 | "type": "data", 222 | "name": "Tag 标签", 223 | "path": "tag", 224 | "tag": "el-tag", 225 | "description": "el-tag,Tag 标签" 226 | }, 227 | { 228 | "type": "data", 229 | "name": "Progress 进度条", 230 | "path": "progress", 231 | "tag": "el-progress", 232 | "description": "el-progress,Progress 进度条" 233 | }, 234 | { 235 | "type": "data", 236 | "name": "Tree 树形控件", 237 | "path": "tree", 238 | "tag": "el-tree", 239 | "description": "el-tree,Tree 树形控件" 240 | }, 241 | { 242 | "type": "data", 243 | "name": "Pagination 分页", 244 | "path": "pagination", 245 | "tag": "el-pagination", 246 | "description": "el-pagination,Pagination 分页" 247 | }, 248 | { 249 | "type": "data", 250 | "name": "Badge 标记", 251 | "path": "badge", 252 | "tag": "el-badge", 253 | "description": "el-badge,Badge 标记" 254 | }, 255 | { 256 | "type": "notice", 257 | "name": "Alert 警告", 258 | "path": "alert", 259 | "tag": "el-alert", 260 | "description": "el-alert,Alert 警告" 261 | }, 262 | { 263 | "type": "notice", 264 | "name": "Loading 加载", 265 | "path": "loading", 266 | "tag": "v-loading", 267 | "description": "v-loading,Loading 加载" 268 | }, 269 | { 270 | "type": "notice", 271 | "name": "Message 消息提示", 272 | "path": "message", 273 | "tag": "$message", 274 | "description": "$message,Message 消息提示" 275 | }, 276 | { 277 | "type": "notice", 278 | "name": "MessageBox 弹框", 279 | "path": "message-box", 280 | "tag": "$alert", 281 | "description": "$alert,MessageBox 弹框" 282 | }, 283 | { 284 | "type": "notice", 285 | "name": "MessageBox 弹框", 286 | "path": "message-box", 287 | "tag": "$prompt", 288 | "description": "$prompt,MessageBox 弹框" 289 | }, 290 | { 291 | "type": "notice", 292 | "name": "MessageBox 弹框", 293 | "path": "message-box", 294 | "tag": "$msgbox", 295 | "description": "$msgbox,MessageBox 弹框" 296 | }, 297 | { 298 | "type": "notice", 299 | "name": "Notification 通知", 300 | "path": "notification", 301 | "tag": "$notify", 302 | "description": "$notify,Notification 通知" 303 | }, 304 | { 305 | "type": "navigation", 306 | "name": "NavMenu 导航菜单", 307 | "path": "menu", 308 | "tag": "el-menu", 309 | "description": "el-menu,NavMenu 导航菜单" 310 | }, 311 | { 312 | "type": "navigation", 313 | "name": "NavMenu 导航菜单", 314 | "path": "menu", 315 | "tag": "el-submenu", 316 | "description": "el-submenu,NavMenu 导航菜单" 317 | }, 318 | { 319 | "type": "navigation", 320 | "name": "NavMenu 导航菜单", 321 | "path": "menu", 322 | "tag": "el-menu-item", 323 | "description": "el-menu-item,NavMenu 导航菜单" 324 | }, 325 | { 326 | "type": "navigation", 327 | "name": "Tabs 标签页", 328 | "path": "tabs", 329 | "tag": "el-tabs", 330 | "description": "el-tabs,Tabs 标签页" 331 | }, 332 | { 333 | "type": "navigation", 334 | "name": "Tabs 标签页", 335 | "path": "tabs", 336 | "tag": "el-tab-pane", 337 | "description": "el-tab-pane,Tabs 标签页" 338 | }, 339 | { 340 | "type": "navigation", 341 | "name": "Breadcrumb 面包屑", 342 | "path": "breadcrumb", 343 | "tag": "el-breadcrumb", 344 | "description": "el-breadcrumb,Breadcrumb 面包屑" 345 | }, 346 | { 347 | "type": "navigation", 348 | "name": "Dropdown 下拉菜单", 349 | "path": "dropdown", 350 | "tag": "el-dropdown", 351 | "description": "el-dropdown,Dropdown 下拉菜单" 352 | }, 353 | { 354 | "type": "navigation", 355 | "name": "Dropdown 下拉菜单", 356 | "path": "dropdown", 357 | "tag": "el-dropdown-menu", 358 | "description": "el-dropdown-menu,Dropdown 下拉菜单" 359 | }, 360 | { 361 | "type": "navigation", 362 | "name": "Dropdown 下拉菜单", 363 | "path": "dropdown", 364 | "tag": "el-dropdown-item", 365 | "description": "el-dropdown-item,Dropdown 下拉菜单" 366 | }, 367 | { 368 | "type": "navigation", 369 | "name": "Steps 步骤条", 370 | "path": "steps", 371 | "tag": "el-steps", 372 | "description": "el-steps,Steps 步骤条" 373 | }, 374 | { 375 | "type": "others", 376 | "name": "Dialog 对话框", 377 | "path": "dialog", 378 | "tag": "el-dialog", 379 | "description": "el-dialog,Dialog 对话框" 380 | }, 381 | { 382 | "type": "others", 383 | "name": "Tooltip 文字提示", 384 | "path": "tooltip", 385 | "tag": "el-tooltip", 386 | "description": "el-tooltip,Tooltip 文字提示" 387 | }, 388 | { 389 | "type": "others", 390 | "name": "Popover 弹出框", 391 | "path": "popover", 392 | "tag": "el-popover", 393 | "description": "el-popover,Popover 弹出框" 394 | }, 395 | { 396 | "type": "others", 397 | "name": "Card 卡片", 398 | "path": "card", 399 | "tag": "el-card", 400 | "description": "el-card,Card 卡片" 401 | }, 402 | { 403 | "type": "others", 404 | "name": "Carousel 走马灯", 405 | "path": "carousel", 406 | "tag": "el-carousel", 407 | "description": "el-carousel,Carousel 走马灯" 408 | }, 409 | { 410 | "type": "others", 411 | "name": "Carousel 走马灯", 412 | "path": "carousel", 413 | "tag": "el-carousel-item", 414 | "description": "el-carousel-item,Carousel 走马灯" 415 | }, 416 | { 417 | "type": "others", 418 | "name": "Collapse 折叠面板", 419 | "path": "collapse", 420 | "tag": "el-collapse", 421 | "description": "el-collapse,Collapse 折叠面板" 422 | }, 423 | { 424 | "type": "others", 425 | "name": "Collapse 折叠面板", 426 | "path": "collapse", 427 | "tag": "el-collapse-item", 428 | "description": "el-collapse-item,Collapse 折叠面板" 429 | } 430 | ], 431 | "types": [ 432 | {"name": "basic"}, 433 | {"name": "form"}, 434 | {"name": "data"}, 435 | {"name": "notice"}, 436 | {"name": "navigation"}, 437 | {"name": "others"} 438 | ] 439 | } 440 | --------------------------------------------------------------------------------