Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ui tweaks & fixes #1409

Merged
merged 19 commits into from
May 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/localization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Localization

# Translate the UI

Follow those simple steps to propose a new language for the CLI UI!

1. Run `navigator.languages` or `navigator.language` to get the language code for the new locale. *For example: `'fr'`.*

2. Search NPM to see if a package called `vue-cli-locale-<language code>` doesn't already exist. If it does, please contribute to it by submitting PRs! If you don't find any, create a new package called `vue-cli-locale-<language code>`. *For example: `vue-cli-locale-fr`*

3. Put the locale JSON file in a `locales` folder and give it the name of the language code. *For example: `locales/fr.json`*

4. In the `package.json` file, set the `unpkg` field to the path to the locale file. *For example: `"unpkg": "./locales/fr.json"`*

5. Publish the package on NPM.

The English reference locale is [here](https://github.com/vuejs/vue-cli/blob/dev/packages/%40vue/cli-ui/locales/en.json).

Take a look at [the french localization package](https://github.com/Akryum/vue-cli-locale-fr) as an example.
160 changes: 133 additions & 27 deletions docs/plugin-dev-ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This guide will walk you through the development of cli-ui specific features for
- [Dev mode](#dev-mode)
- [Project configurations](#project-configurations)
- [Project tasks](#project-tasks)
- [Prompts](#prompts)
- [Client addon](#client-addon)
- [Custom views](#custom-views)
- [Shared data](#shared-data)
Expand Down Expand Up @@ -111,6 +112,8 @@ api.describeConfig({
})
```

#### Config icon

Specify an icon with either a file type (like `'json'`) or a file name (like `.babelrc` to get the babel icon). This is powered by file-icons.

```js
Expand All @@ -121,7 +124,11 @@ api.describeConfig({
})
```

Then you can specify which files will be read when loading the configuration and then written to:
#### Config file

By default, a configuration UI might read and write to a configuration file, for example `.eslintrc.js`.

You can provide what are the possible files to be detected in the user project:

```js
api.describeConfig({
Expand All @@ -138,6 +145,10 @@ api.describeConfig({

Supported types: `json`, `yaml`, `js`, `package`.

**⚠️ Currently, only 1 file can be read and written to at a time.**

#### Display config prompts

Use the `onRead` hook to return a list of prompts to be displayed for the configuration:

```js
Expand All @@ -151,25 +162,13 @@ api.describeConfig({
})
```

The prompt objects must be valid [inquirer](https://github.com/SBoudrias/Inquirer.js) prompts with the following additional fields (which are optional):

```js
{
/* ... */
// Used to group the prompts into sections
group: 'Strongly recommended',
// Additional description
description: 'Enforce attribute naming style in template (`my-prop` or `myProp`)',
// "More info" link
link: 'https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/attribute-hyphenation.md',
}
```
Those prompts will be displayed in the configuration details pane.

Supported inquirer types: `checkbox`, `confirm`, `input`, `password`, `list`, `rawlist`.
See [Prompts](#prompts) for more info.

In addition to those, the UI supports special types that only works with it: `color`.
#### Save config changes

Use the `onWrite` hook to write the data to the configuration file (or execute any node code):
Use the `onWrite` hook to write the data to the configuration file (or execute any nodejs code):

```js
api.describeConfig({
Expand All @@ -182,12 +181,12 @@ api.describeConfig({

Arguments:

- `prompts`: current prompts runtime objects
- `prompts`: current prompts runtime objects (see below)
- `answers`: answers data from the user inputs
- `data`: read-only initial data read from the file
- `file`: descriptor of the found file (`{ type: 'json', path: '...' }`)
- `cwd`: current working directory
- `api`: onWrite API
- `api`: `onWrite API` (see below)

Prompts runtime objects:

Expand All @@ -213,11 +212,28 @@ Prompts runtime objects:
}
```

onWrite API:
`onWrite` API:

- `assignData(newData)`: use `Object.assign` to update the config data before writing.
- `setData(newData)`: each key of `newData` will be deeply set (or removed if `undefined` value) to the config data before writing.
- `async getAnswer(id, mapper)`: retrieve answer for a given prompt id and map it through `mapper` function if provide (for example `JSON.parse`).
- `async getAnswer(id, mapper)`: retrieve answer for a given prompt id and map it through `mapper` function if provided (for example `JSON.parse`).

Example (from the ESLint plugin):

```js
api.describeConfig({
// ...

onWrite: async ({ api, prompts }) => {
// Update ESLint rules
const result = {}
for (const prompt of prompts) {
result[`rules.${prompt.id}`] = await api.getAnswer(prompt.id, JSON.parse)
}
api.setData(result)
}
})
```

### Project tasks

Expand All @@ -233,7 +249,20 @@ api.describeTask({
match: /vue-cli-service serve/,
description: 'Compiles and hot-reloads for development',
// "More info" link
link: 'https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#serve',
link: 'https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#serve'
})
```

#### Tasks parameters

You can add prompts to modify the command arguments. They will be displayed in a 'Parameters' modal.

Example:

```js
api.describeTask({
// ...

// Optional parameters (inquirer prompts)
prompts: [
{
Expand Down Expand Up @@ -262,7 +291,26 @@ api.describeTask({
],
description: 'Specify env mode'
}
],
]
})
```

See [Prompts](#prompts) for more info.

#### Task hooks

Several hooks are available:

- `onBeforeRun`
- `onRun`
- `onExit`

For example, you can use the answers to the prompts (see above) to add new arguments to the command:

```js
api.describeTask({
// ...

// Hooks
// Modify arguments here
onBeforeRun: async ({ answers, args }) => {
Expand All @@ -279,7 +327,18 @@ api.describeTask({
onExit: async ({ args, child, cwd, code, signal }) => {
// code: exit code
// signal: kill signal used if any
},
}
})
```

#### Task views

You can display custom views in the task details pane using the `ClientAddon` API:

```js
api.describeTask({
// ...

// Additional views (for example the webpack dashboard)
// By default, there is the 'output' view which displays the terminal output
views: [
Expand All @@ -299,9 +358,16 @@ api.describeTask({
})
```

See [Client addon](#client-addon) for more info.


#### Add new tasks

You can also add entirely new tasks which aren't in the `package.json` scripts. Those tasks will only appear in the cli UI:
You can also add entirely new tasks which aren't in the `package.json` scripts with `api.addTask` instead of `api.describeTask`. Those tasks will only appear in the cli UI.

**You need to provide a `command` option instead of `match`.**

Example:

```js
api.addTask({
Expand All @@ -323,6 +389,36 @@ api.addTask({

**⚠️ The `command` will run a node context. This means you can call node bin commands like you would normally do in the `package.json` scripts.**

### Prompts

The prompt objects must be valid [inquirer](https://github.com/SBoudrias/Inquirer.js) objects.

However, you can add the following additional fields (which are optional and only used by the UI):

```js
{
/* ... */
// Used to group the prompts into sections
group: 'Strongly recommended',
// Additional description
description: 'Enforce attribute naming style in template (`my-prop` or `myProp`)',
// "More info" link
link: 'https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/attribute-hyphenation.md',
}
```

Supported inquirer types: `checkbox`, `confirm`, `input`, `password`, `list`, `rawlist`.

In addition to those, the UI supports special types that only works with it:

- `color`: displays a color picker.

#### Prompts for invocation

In your vue-cli plugin, you may already have a `prompts.js` file which asks the user a few questions when installing the plugin (with the CLI or the UI). You can add the additional UI-only fields (see above) to those prompt objects as well so they will provide more information if the user is using the UI.

**⚠️ Currently, the inquirer types which aren't supported (see above) whill not work properly in the UI.**

### Client addon

A Client addon is a JS bundle which is dynamically loaded into the cli-ui. It is useful to load custom components and routes.
Expand Down Expand Up @@ -518,7 +614,9 @@ ClientAddonApi.addRoutes('vue-webpack', [

Use Shared data to communicate info with custom components in an easy way.

In the plugin `ui.js`:
> For example, the Webpack dashboard shares the build stats between the UI client and the UI server using this API.

In the plugin `ui.js` (nodejs):

```js
// Set or update
Expand Down Expand Up @@ -554,7 +652,8 @@ const {
In the custom component:

```js
{
// Vue component
export default {
// Sync Shared data
sharedData () {
return {
Expand Down Expand Up @@ -605,6 +704,8 @@ This is very usefull if you create a settings component for example.

Plugin actions are calls sent between the cli-ui (browser) and plugins (nodejs).

> For example, you might have a button in a custom component (see [Client addon](#client-addon)) which calls some nodejs code on the server using this API.

In the `ui.js` file in the plugin (nodejs), you can use two methods from `PluginApi`:

```js
Expand Down Expand Up @@ -632,6 +733,7 @@ const { onAction, callAction } = api.namespace('vue-webpack-')
In the client addon components (browser), you have access to `$onPluginActionCalled`, `$onPluginActionResolved` and `$callPluginAction`:

```js
// Vue component
export default {
created () {
this.$onPluginActionCalled(action => {
Expand Down Expand Up @@ -780,6 +882,10 @@ locales.keys().forEach(key => {
})
```

#### Help translate the main UI!

See [how to help translating the main UI](./localization.md).

### Hooks

Hooks allows to react to certain cli-ui events.
Expand Down
6 changes: 5 additions & 1 deletion packages/@vue/cli-ui/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,18 @@
"tooltip": "Logs<br><i>Click to toggle Vue CLI logs</i>",
"empty": "No logs yet"
},
"report-bug": "Report bug"
"report-bug": "Report bug",
"translate": "Help translate"
},
"terminal-view": {
"buttons": {
"clear": "Clear console",
"scroll": "Scroll to bottom"
}
},
"top-bar": {
"no-favorites": "No favorite projects"
},
"view-badge": {
"labels": {
"tasks": {
Expand Down
5 changes: 3 additions & 2 deletions packages/@vue/cli-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"semver": "^5.5.0",
"shortid": "^2.2.8",
"terminate": "^2.1.0",
"vue-cli-plugin-apollo": "^0.13.0",
"vue-cli-plugin-apollo": "^0.13.4",
"watch": "^1.0.2"
},
"devDependencies": {
Expand All @@ -55,11 +55,12 @@
"eslint-plugin-graphql": "^2.1.1",
"file-icons-js": "^1.0.3",
"lint-staged": "^6.0.0",
"portal-vue": "^1.3.0",
"start-server-and-test": "^1.4.1",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.1",
"vue": "^2.5.16",
"vue-apollo": "^3.0.0-beta.5",
"vue-apollo": "^3.0.0-beta.16",
"vue-color": "^2.4.6",
"vue-i18n": "^7.6.0",
"vue-instantsearch": "^1.5.1",
Expand Down
23 changes: 23 additions & 0 deletions packages/@vue/cli-ui/src/components/AppLoading.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<template>
<div class="app-loading">
<transition name="vue-ui-fade">
<VueLoadingIndicator
v-if="loading"
class="primary"
/>
</transition>
</div>
</template>

<script>
import LOADING from '../graphql/loading.gql'

export default {
apollo: {
loading: {
query: LOADING,
fetchPolicy: 'cache-only'
}
}
}
</script>
1 change: 1 addition & 0 deletions packages/@vue/cli-ui/src/components/ConnectionStatus.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<ApolloQuery
:query="require('../graphql/connected.gql')"
fetch-policy="cache-only"
class="connection-status"
>
<template slot-scope="{ result: { data: { connected } } }">
Expand Down
Loading