Merge branch 'master' into clean-dependencies
38
.github/ISSUE_TEMPLATE.md
vendored
@ -1,18 +1,22 @@
|
||||
<!-- ⚠️ If you do not respect this template your issue will be closed. -->
|
||||
|
||||
<!-- ISSUE TEMPLATE -->
|
||||
<!-- =============================================================================== -->
|
||||
<!-- ⚠️ If you are not using the current Strapi release, you will be asked to update. -->
|
||||
<!-- Please see the wiki for guides on upgrading to the latest release. -->
|
||||
<!-- =============================================================================== -->
|
||||
|
||||
<!-- ⚠️ Make sure to browse the opened and closed issues before submitting your issue. -->
|
||||
|
||||
<!-- ⚠️ Before writing your issue make sure you are using:-->
|
||||
<!-- Node 9.x.x -->
|
||||
<!-- npm 5.x.x -->
|
||||
<!-- The latest version of Strapi. -->
|
||||
<!-- Node 10.x.x -->
|
||||
<!-- npm 6.x.x -->
|
||||
|
||||
**Informations**
|
||||
- **Node.js version**:
|
||||
- **npm version**:
|
||||
- **Strapi version**:
|
||||
- **Database**:
|
||||
- **Operating system**:
|
||||
- **Node.js version**:
|
||||
- **npm version**:
|
||||
- **Strapi version**: <!-- Please make sure you are on the latest version -->
|
||||
- **Database**:
|
||||
- **Operating system**:
|
||||
|
||||
|
||||
**What is the current behavior?**
|
||||
@ -28,19 +32,3 @@
|
||||
|
||||
|
||||
**Suggested solutions**
|
||||
|
||||
|
||||
|
||||
<!-- ⚠️ Make sure to browse the opened and closed issues before submitting your issue. -->
|
||||
|
||||
|
||||
|
||||
<!-- FEATURE REQUEST TEMPLATE -->
|
||||
|
||||
<!-- ⚠️ If you do not respect this template your issue will be closed. -->
|
||||
|
||||
**What is the expected behavior?**
|
||||
|
||||
|
||||
|
||||
<!-- ⚠️ Make sure to browse the opened and closed issues before submitting your issue. -->
|
25
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
@ -5,17 +5,24 @@ about: Create a report to help us improve 🤔.
|
||||
|
||||
<!-- ⚠️ If you do not respect this template your issue will be closed. -->
|
||||
|
||||
<!-- =============================================================================== -->
|
||||
<!-- ⚠️ If you are not using the current Strapi release, you will be asked to update. -->
|
||||
<!-- Please see the wiki for guides on upgrading to the latest release. -->
|
||||
<!-- =============================================================================== -->
|
||||
|
||||
<!-- ⚠️ Make sure to browse the opened and closed issues before submitting your issue. -->
|
||||
|
||||
<!-- ⚠️ Before writing your issue make sure you are using:-->
|
||||
<!-- Node 9.x.x -->
|
||||
<!-- npm 5.x.x -->
|
||||
<!-- Node 10.x.x -->
|
||||
<!-- npm 6.x.x -->
|
||||
<!-- The latest version of Strapi. -->
|
||||
|
||||
**Informations**
|
||||
- **Node.js version**:
|
||||
- **npm version**:
|
||||
- **Strapi version**:
|
||||
- **Database**:
|
||||
- **Operating system**:
|
||||
- **Node.js version**:
|
||||
- **npm version**:
|
||||
- **Strapi version**: <!-- Please make sure you are on the latest version -->
|
||||
- **Database**:
|
||||
- **Operating system**:
|
||||
|
||||
|
||||
**What is the current behavior?**
|
||||
@ -31,7 +38,3 @@ about: Create a report to help us improve 🤔.
|
||||
|
||||
|
||||
**Suggested solutions**
|
||||
|
||||
|
||||
|
||||
<!-- ⚠️ Make sure to browse the opened and closed issues before submitting your issue. -->
|
||||
|
4
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
vendored
@ -5,8 +5,8 @@ about: Suggest an idea for this project 💡!
|
||||
|
||||
<!-- ⚠️ If you do not respect this template your issue will be closed. -->
|
||||
|
||||
<!-- ⚠️ Make sure to browse the opened and closed issues before submitting your issue. -->
|
||||
|
||||
**What is the expected behavior?**
|
||||
|
||||
|
||||
|
||||
<!-- ⚠️ Make sure to browse the opened and closed issues before submitting your issue. -->
|
||||
|
2
.gitignore
vendored
@ -109,7 +109,7 @@ coverage
|
||||
# Documentation
|
||||
############################
|
||||
|
||||
_book
|
||||
dist
|
||||
|
||||
############################
|
||||
# Builds
|
||||
|
@ -38,14 +38,14 @@ Then, please follow the instructions below:
|
||||
|
||||
[Go to the repository](https://github.com/strapi/strapi) and fork it to your own GitHub account.
|
||||
|
||||
#### 2. 💿 Clone the repository
|
||||
#### 2. 💿 Clone from your repository
|
||||
|
||||
```bash
|
||||
git clone git@github.com:strapi/strapi.git
|
||||
git clone git@github.com:YOUR_USERNAME/strapi.git
|
||||
```
|
||||
|
||||
#### 3. ⏳ Installation
|
||||
|
||||
|
||||
Go to the root of the repository.
|
||||
```bash
|
||||
cd strapi
|
||||
|
11
README.md
@ -80,16 +80,21 @@ strapi start
|
||||
|
||||
Congratulations, you made it! Enjoy 🎉
|
||||
|
||||
<br>
|
||||
### Try on Heroku
|
||||
|
||||
You can also give it a try using Heroku! Be aware that one of the content type builder won't work due to the writing files restriction on the Heroku servers.
|
||||
You can also give it a try using Heroku in one click!
|
||||
|
||||
<a href="https://heroku.com/deploy?template=https://github.com/strapi/strapi-heroku-app">
|
||||
<img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy">
|
||||
</a>
|
||||
|
||||
<br>
|
||||
Be aware that one of the content type builder won't work due to the writing files restriction on the Heroku servers. If you do want to change content types, you need to follow these steps:
|
||||
|
||||
1. Click the button above and deploy your app
|
||||
2. Clone that repo by using `heroku git:clone -a ` followed by your repo's name
|
||||
3. Go into the cloned projects' folder using `cd` followed by your repo's name
|
||||
4. Add the Heroku boilerplate as a remote by running `git remote add boilerplate https://github.com/strapi/strapi-heroku-app`
|
||||
5. Pull from this new origin by running `git pull boilerplate master`
|
||||
|
||||
## Features
|
||||
|
||||
|
165
docs/.vuepress/config.js
Normal file
@ -0,0 +1,165 @@
|
||||
const container = require('markdown-it-container')
|
||||
|
||||
let ogprefix = 'og: http://ogp.me/ns#'
|
||||
let title = 'Strapi Documentation'
|
||||
let description = 'API creation made simple, secure and fast.'
|
||||
let color = '#2F80ED'
|
||||
let author = 'Strapi'
|
||||
let url = 'https://strapi.io/documentation/'
|
||||
|
||||
|
||||
module.exports = {
|
||||
head: [
|
||||
['link', { rel: 'icon', href: `/rocket.png` }],
|
||||
['meta', { name: 'theme-color', content: color }],
|
||||
['meta', { prefix: ogprefix, property: 'og:title', content: title }],
|
||||
['meta', { prefix: ogprefix, property: 'twitter:title', content: title }],
|
||||
['meta', { prefix: ogprefix, property: 'og:type', content: 'article' }],
|
||||
['meta', { prefix: ogprefix, property: 'og:url', content: url }],
|
||||
['meta', { prefix: ogprefix, property: 'og:description', content: description }],
|
||||
['meta', { prefix: ogprefix, property: 'og:image', content: `${url}rocket.png` }],
|
||||
['meta', { prefix: ogprefix, property: 'og:article:author', content: author }],
|
||||
['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
|
||||
['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }],
|
||||
// ['link', { rel: 'apple-touch-icon', href: `/assets/apple-touch-icon.png` }],
|
||||
// ['link', { rel: 'mask-icon', href: '/assets/safari-pinned-tab.svg', color: color }],
|
||||
['meta', { name: 'msapplication-TileImage', content: '/rocket.png' }],
|
||||
['meta', { name: 'msapplication-TileColor', content: color }],
|
||||
],
|
||||
markdown: {
|
||||
anchor: {
|
||||
permalink: true,
|
||||
},
|
||||
config: md => {
|
||||
md
|
||||
.use(require('markdown-it-decorate'))
|
||||
.use(...createContainer('intro'))
|
||||
.use(...createContainer('note'))
|
||||
}
|
||||
},
|
||||
title,
|
||||
description,
|
||||
base: '/documentation/',
|
||||
themeConfig: {
|
||||
versions: [
|
||||
['Version 3.x.x', '/3.x.x/'],
|
||||
['Version 1.x.x', '/1.x.x/'],
|
||||
],
|
||||
repo: 'strapi/strapi',
|
||||
docsDir: 'docs',
|
||||
editLinks: true,
|
||||
editLinkText: 'Improve this page',
|
||||
serviceWorker: true,
|
||||
sidebar: {
|
||||
'/3.x.x/': [
|
||||
{
|
||||
collapsable: false,
|
||||
title: 'Getting started',
|
||||
children: [
|
||||
'/3.x.x/getting-started/installation',
|
||||
'/3.x.x/getting-started/quick-start',
|
||||
'/3.x.x/concepts/concepts',
|
||||
],
|
||||
},
|
||||
{
|
||||
collapsable: false,
|
||||
title: 'Guides',
|
||||
children: [
|
||||
'/3.x.x/guides/authentication.md',
|
||||
'/3.x.x/configurations/configurations.md',
|
||||
'/3.x.x/guides/controllers.md',
|
||||
'/3.x.x/guides/deployment.md',
|
||||
'/3.x.x/guides/email.md',
|
||||
'/3.x.x/guides/upload.md',
|
||||
'/3.x.x/guides/filters.md',
|
||||
'/3.x.x/guides/graphql.md',
|
||||
'/3.x.x/guides/i18n.md',
|
||||
'/3.x.x/guides/models.md',
|
||||
'/3.x.x/guides/policies.md',
|
||||
'/3.x.x/guides/public-assets.md',
|
||||
'/3.x.x/guides/requests.md',
|
||||
'/3.x.x/guides/responses.md',
|
||||
'/3.x.x/guides/routing.md',
|
||||
'/3.x.x/guides/services.md',
|
||||
],
|
||||
},
|
||||
{
|
||||
collapsable: false,
|
||||
title: 'Globals',
|
||||
children: [
|
||||
'/3.x.x/api-reference/reference',
|
||||
'/3.x.x/cli/CLI',
|
||||
'/3.x.x/configurations/configurations',
|
||||
],
|
||||
},
|
||||
{
|
||||
collapsable: false,
|
||||
title: 'Advanced',
|
||||
children: [
|
||||
'/3.x.x/advanced/customize-admin',
|
||||
'/3.x.x/advanced/hooks',
|
||||
'/3.x.x/advanced/logging',
|
||||
'/3.x.x/advanced/middlewares',
|
||||
'/3.x.x/advanced/usage-tracking',
|
||||
],
|
||||
},
|
||||
{
|
||||
collapsable: false,
|
||||
title: 'Resources',
|
||||
children: [
|
||||
['https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md', 'Contributing guide'],
|
||||
['https://github.com/strapi/strapi/wiki', 'Migration guides'],
|
||||
'/3.x.x/tutorials/',
|
||||
],
|
||||
},
|
||||
],
|
||||
'/1.x.x/': [
|
||||
{
|
||||
collapsable: false,
|
||||
title: 'UsefulLinks',
|
||||
children: [
|
||||
['/1.x.x/', 'Introduction'],
|
||||
['https://strapi.io', 'Strapi Website'],
|
||||
['https://github.com/strapi/strapi', 'GitHub Repository'],
|
||||
['https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md', 'Contribution Guide'],
|
||||
],
|
||||
},
|
||||
'/1.x.x/admin.md',
|
||||
'/1.x.x/configuration.md',
|
||||
'/1.x.x/email.md',
|
||||
'/1.x.x/introduction.md',
|
||||
'/1.x.x/queries.md',
|
||||
'/1.x.x/response.md',
|
||||
'/1.x.x/sessions.md',
|
||||
'/1.x.x/testing.md',
|
||||
'/1.x.x/views.md',
|
||||
'/1.x.x/blueprints.md',
|
||||
'/1.x.x/context.md',
|
||||
'/1.x.x/graphql.md',
|
||||
'/1.x.x/logging.md',
|
||||
'/1.x.x/router.md',
|
||||
'/1.x.x/upload.md',
|
||||
'/1.x.x/cli.md',
|
||||
'/1.x.x/customization.md',
|
||||
'/1.x.x/internationalization.md',
|
||||
'/1.x.x/models.md',
|
||||
'/1.x.x/request.md',
|
||||
'/1.x.x/services.md',
|
||||
'/1.x.x/users.md',
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
function createContainer(className) {
|
||||
return [container, className, {
|
||||
render(tokens, idx) {
|
||||
const token = tokens[idx]
|
||||
if (token.nesting === 1) {
|
||||
return `<div class="${className} custom-block">\n`
|
||||
} else {
|
||||
return `</div>\n`
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
BIN
docs/.vuepress/public/rocket.png
Normal file
After Width: | Height: | Size: 44 KiB |
131
docs/.vuepress/theme/AlgoliaSearchBox.vue
Normal file
@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<form id="search-form" class="algolia-search-wrapper search-box">
|
||||
<input id="algolia-search-input" class="search-query">
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['options'],
|
||||
mounted () {
|
||||
this.initialize()
|
||||
},
|
||||
methods: {
|
||||
initialize () {
|
||||
Promise.all([
|
||||
import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.js'),
|
||||
import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.css')
|
||||
]).then(([docsearch]) => {
|
||||
docsearch = docsearch.default
|
||||
docsearch(Object.assign(this.options, {
|
||||
debug: true,
|
||||
inputSelector: '#algolia-search-input'
|
||||
}))
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
options (newValue) {
|
||||
this.$el.innerHTML = '<input id="algolia-search-input" class="search-query">'
|
||||
this.initialize(newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@import './styles/config.styl'
|
||||
|
||||
.algolia-search-wrapper
|
||||
& > span
|
||||
vertical-align middle
|
||||
.algolia-autocomplete
|
||||
line-height normal
|
||||
.ds-dropdown-menu
|
||||
background-color #fff
|
||||
border 1px solid #999
|
||||
border-radius 4px
|
||||
font-size 16px
|
||||
margin 6px 0 0
|
||||
padding 4px
|
||||
text-align left
|
||||
&:before
|
||||
border-color #999
|
||||
[class*=ds-dataset-]
|
||||
border none
|
||||
padding 0
|
||||
.ds-suggestions
|
||||
margin-top 0
|
||||
.ds-suggestion
|
||||
border-bottom 1px solid $borderColor
|
||||
.algolia-docsearch-suggestion--highlight
|
||||
color #2c815b
|
||||
.algolia-docsearch-suggestion
|
||||
border-color $borderColor
|
||||
padding 0
|
||||
.algolia-docsearch-suggestion--category-header
|
||||
padding 5px 10px
|
||||
margin-top 0
|
||||
background $accentColor
|
||||
color #fff
|
||||
font-weight 600
|
||||
.algolia-docsearch-suggestion--highlight
|
||||
background rgba(255, 255, 255, 0.6)
|
||||
.algolia-docsearch-suggestion--wrapper
|
||||
padding 0
|
||||
.algolia-docsearch-suggestion--title
|
||||
font-weight 600
|
||||
margin-bottom 0
|
||||
color $textColor
|
||||
.algolia-docsearch-suggestion--subcategory-column
|
||||
vertical-align top
|
||||
padding 5px 7px 5px 5px
|
||||
border-color $borderColor
|
||||
background #f1f3f5
|
||||
&:after
|
||||
display none
|
||||
.algolia-docsearch-suggestion--subcategory-column-text
|
||||
color #555
|
||||
.algolia-docsearch-footer
|
||||
border-color $borderColor
|
||||
.ds-cursor .algolia-docsearch-suggestion--content
|
||||
background-color #e7edf3 !important
|
||||
color $textColor
|
||||
|
||||
@media (min-width: $MQMobile)
|
||||
.algolia-search-wrapper
|
||||
.algolia-autocomplete
|
||||
.algolia-docsearch-suggestion
|
||||
.algolia-docsearch-suggestion--subcategory-column
|
||||
float none
|
||||
width 150px
|
||||
min-width 150px
|
||||
display table-cell
|
||||
.algolia-docsearch-suggestion--content
|
||||
float none
|
||||
display table-cell
|
||||
width 100%
|
||||
vertical-align top
|
||||
.ds-dropdown-menu
|
||||
min-width 515px !important
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.algolia-search-wrapper
|
||||
.ds-dropdown-menu
|
||||
min-width calc(100vw - 4rem) !important
|
||||
max-width calc(100vw - 4rem) !important
|
||||
.algolia-docsearch-suggestion--wrapper
|
||||
padding 5px 7px 5px 5px !important
|
||||
.algolia-docsearch-suggestion--subcategory-column
|
||||
padding 0 !important
|
||||
background white !important
|
||||
.algolia-docsearch-suggestion--subcategory-column-text:after
|
||||
content " > "
|
||||
font-size 10px
|
||||
line-height 14.4px
|
||||
display inline-block
|
||||
width 5px
|
||||
margin -3px 3px 0
|
||||
vertical-align middle
|
||||
|
||||
</style>
|
32
docs/.vuepress/theme/AppLink.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<component v-bind="linkProps(to)">
|
||||
<slot></slot>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
to: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
linkProps (url) {
|
||||
if (url.match(/^(http(s)?|ftp):\/\//)) {
|
||||
return {
|
||||
is: 'a',
|
||||
href: url,
|
||||
target: '_blank',
|
||||
rel: 'noopener'
|
||||
}
|
||||
}
|
||||
return {
|
||||
is: 'router-link',
|
||||
to: url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
154
docs/.vuepress/theme/DropdownLink.vue
Normal file
@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<div class="dropdown-wrapper" :class="{ open }">
|
||||
<a class="dropdown-title" @click="toggle">
|
||||
<span class="title">{{ item.text }}</span>
|
||||
<span class="arrow" :class="open ? 'down' : 'right'"></span>
|
||||
</a>
|
||||
<DropdownTransition>
|
||||
<ul class="nav-dropdown" v-show="open">
|
||||
<li
|
||||
class="dropdown-item"
|
||||
v-for="(subItem, index) in item.items"
|
||||
:key="subItem.link || index">
|
||||
<h4 v-if="subItem.type === 'links'">{{ subItem.text }}</h4>
|
||||
<ul class="dropdown-subitem-wrapper" v-if="subItem.type === 'links'">
|
||||
<li
|
||||
class="dropdown-subitem"
|
||||
v-for="childSubItem in subItem.items"
|
||||
:key="childSubItem.link">
|
||||
<NavLink :item="childSubItem"/>
|
||||
</li>
|
||||
</ul>
|
||||
<NavLink v-else :item="subItem"/>
|
||||
</li>
|
||||
</ul>
|
||||
</DropdownTransition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NavLink from './NavLink.vue'
|
||||
import DropdownTransition from './DropdownTransition.vue'
|
||||
|
||||
export default {
|
||||
components: { NavLink, DropdownTransition },
|
||||
data () {
|
||||
return {
|
||||
open: false
|
||||
}
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle () {
|
||||
this.open = !this.open
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@import './styles/config.styl'
|
||||
|
||||
.dropdown-wrapper
|
||||
.dropdown-title
|
||||
display block
|
||||
&:hover
|
||||
border-color transparent
|
||||
.arrow
|
||||
vertical-align middle
|
||||
margin-top -1px
|
||||
margin-left 0.4rem
|
||||
.nav-dropdown
|
||||
.dropdown-item
|
||||
color inherit
|
||||
line-height 1.7rem
|
||||
h4
|
||||
margin 0.45rem 0 0
|
||||
border-top 1px solid #eee
|
||||
padding 0.45rem 1.5rem 0 1.25rem
|
||||
.dropdown-subitem-wrapper
|
||||
padding 0
|
||||
list-style none
|
||||
.dropdown-subitem
|
||||
font-size 0.9em
|
||||
a
|
||||
display block
|
||||
line-height 1.7rem
|
||||
position relative
|
||||
border-bottom none
|
||||
font-weight 400
|
||||
margin-bottom 0
|
||||
padding 0 1.5rem 0 1.25rem
|
||||
&:hover
|
||||
color $accentColor
|
||||
&.router-link-active
|
||||
color $accentColor
|
||||
&::after
|
||||
content ""
|
||||
width 0
|
||||
height 0
|
||||
border-left 5px solid $accentColor
|
||||
border-top 3px solid transparent
|
||||
border-bottom 3px solid transparent
|
||||
position absolute
|
||||
top calc(50% - 2px)
|
||||
left 9px
|
||||
&:first-child h4
|
||||
margin-top 0
|
||||
padding-top 0
|
||||
border-top 0
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.dropdown-wrapper
|
||||
&.open .dropdown-title
|
||||
margin-bottom 0.5rem
|
||||
.nav-dropdown
|
||||
transition height .1s ease-out
|
||||
overflow hidden
|
||||
.dropdown-item
|
||||
h4
|
||||
border-top 0
|
||||
margin-top 0
|
||||
padding-top 0
|
||||
h4, & > a
|
||||
font-size 15px
|
||||
line-height 2rem
|
||||
.dropdown-subitem
|
||||
font-size 14px
|
||||
padding-left 1rem
|
||||
|
||||
@media (min-width: $MQMobile)
|
||||
.dropdown-wrapper
|
||||
height 1.8rem
|
||||
&:hover .nav-dropdown
|
||||
// override the inline style.
|
||||
display block !important
|
||||
.dropdown-title .arrow
|
||||
// make the arrow always down at desktop
|
||||
border-left 4px solid transparent
|
||||
border-right 4px solid transparent
|
||||
border-top 6px solid $arrowBgColor
|
||||
border-bottom 0
|
||||
.nav-dropdown
|
||||
display none
|
||||
// Avoid height shaked by clicking
|
||||
height auto !important
|
||||
box-sizing border-box;
|
||||
max-height calc(100vh - 2.7rem)
|
||||
overflow-y auto
|
||||
position absolute
|
||||
top 100%
|
||||
right 0
|
||||
background-color #fff
|
||||
padding 0.6rem 0
|
||||
border 1px solid #ddd
|
||||
border-bottom-color #ccc
|
||||
text-align left
|
||||
border-radius 0.25rem
|
||||
white-space nowrap
|
||||
margin 0
|
||||
</style>
|
29
docs/.vuepress/theme/DropdownTransition.vue
Normal file
@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<transition name="dropdown"
|
||||
@enter="setHeight"
|
||||
@after-enter="unsetHeight"
|
||||
@before-leave="setHeight">
|
||||
<slot></slot>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DropdownTransition',
|
||||
methods: {
|
||||
setHeight (items) {
|
||||
// explicitly set height so that it can be transitioned
|
||||
items.style.height = items.scrollHeight + 'px'
|
||||
},
|
||||
unsetHeight (items) {
|
||||
items.style.height = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.dropdown-enter, .dropdown-leave-to
|
||||
height 0 !important
|
||||
|
||||
</style>
|
134
docs/.vuepress/theme/Home.vue
Normal file
@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<div class="hero">
|
||||
<img v-if="data.heroImage" :src="$withBase(data.heroImage)" alt="hero">
|
||||
<h1>{{ data.heroText || $title || 'Hello' }}</h1>
|
||||
<p class="description">
|
||||
{{ data.tagline || $description || 'Welcome to your VuePress site' }}
|
||||
</p>
|
||||
<p class="action" v-if="data.actionText && data.actionLink">
|
||||
<NavLink class="action-button" :item="actionLink"/>
|
||||
</p>
|
||||
</div>
|
||||
<div class="features" v-if="data.features && data.features.length">
|
||||
<div class="feature" v-for="feature in data.features">
|
||||
<h2>{{ feature.title }}</h2>
|
||||
<p>{{ feature.details }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Content custom/>
|
||||
<div class="footer" v-if="data.footer">
|
||||
{{ data.footer }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NavLink from './NavLink.vue'
|
||||
|
||||
export default {
|
||||
components: { NavLink },
|
||||
computed: {
|
||||
data () {
|
||||
return this.$page.frontmatter
|
||||
},
|
||||
actionLink () {
|
||||
return {
|
||||
link: this.data.actionLink,
|
||||
text: this.data.actionText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@import './styles/config.styl'
|
||||
|
||||
.home
|
||||
padding $navbarHeight 2rem 0
|
||||
max-width 960px
|
||||
margin 0px auto
|
||||
.hero
|
||||
text-align center
|
||||
img
|
||||
max-height 280px
|
||||
display block
|
||||
margin 3rem auto 1.5rem
|
||||
h1
|
||||
font-size 3rem
|
||||
h1, .description, .action
|
||||
margin 1.8rem auto
|
||||
.description
|
||||
max-width 35rem
|
||||
font-size 1.6rem
|
||||
line-height 1.3
|
||||
color lighten($textColor, 40%)
|
||||
.action-button
|
||||
display inline-block
|
||||
font-size 1.2rem
|
||||
color #fff
|
||||
background-color $accentColor
|
||||
padding 0.8rem 1.6rem
|
||||
border-radius 4px
|
||||
transition background-color .1s ease
|
||||
box-sizing border-box
|
||||
border-bottom 1px solid darken($accentColor, 10%)
|
||||
&:hover
|
||||
background-color lighten($accentColor, 10%)
|
||||
.features
|
||||
border-top 1px solid $borderColor
|
||||
padding 1.2rem 0
|
||||
margin-top 2.5rem
|
||||
display flex
|
||||
flex-wrap wrap
|
||||
align-items flex-start
|
||||
align-content stretch
|
||||
justify-content space-between
|
||||
.feature
|
||||
flex-grow 1
|
||||
flex-basis 30%
|
||||
max-width 30%
|
||||
h2
|
||||
font-size 1.4rem
|
||||
font-weight 500
|
||||
border-bottom none
|
||||
padding-bottom 0
|
||||
color lighten($textColor, 10%)
|
||||
p
|
||||
color lighten($textColor, 25%)
|
||||
.footer
|
||||
padding 2.5rem
|
||||
border-top 1px solid $borderColor
|
||||
text-align center
|
||||
color lighten($textColor, 25%)
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.home
|
||||
.features
|
||||
flex-direction column
|
||||
.feature
|
||||
max-width 100%
|
||||
padding 0 2.5rem
|
||||
|
||||
@media (max-width: $MQMobileNarrow)
|
||||
.home
|
||||
padding-left 1.5rem
|
||||
padding-right 1.5rem
|
||||
.hero
|
||||
img
|
||||
max-height 210px
|
||||
margin 2rem auto 1.2rem
|
||||
h1
|
||||
font-size 2rem
|
||||
h1, .description, .action
|
||||
margin 1.2rem auto
|
||||
.description
|
||||
font-size 1.2rem
|
||||
.action-button
|
||||
font-size 1rem
|
||||
padding 0.6rem 1.2rem
|
||||
.feature
|
||||
h2
|
||||
font-size 1.25rem
|
||||
</style>
|
147
docs/.vuepress/theme/Layout.vue
Normal file
@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div class="theme-container"
|
||||
:class="pageClasses"
|
||||
@touchstart="onTouchStart"
|
||||
@touchend="onTouchEnd">
|
||||
<Navbar v-if="shouldShowNavbar" @toggle-sidebar="toggleSidebar"/>
|
||||
<div class="sidebar-mask" @click="toggleSidebar(false)"></div>
|
||||
<Sidebar :items="sidebarItems" @toggle-sidebar="toggleSidebar">
|
||||
<div slot="top">
|
||||
<select @input="changeVersion($event.target.value)" class="version-selector">
|
||||
<option v-for="version in versions" :value="version.path" :selected="!!~$page.path.indexOf(version.path)">{{version.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<slot name="sidebar-bottom" slot="bottom"/>
|
||||
</Sidebar>
|
||||
<div class="custom-layout" v-if="$page.frontmatter.layout">
|
||||
<component :is="$page.frontmatter.layout"/>
|
||||
</div>
|
||||
<Home v-else-if="$page.frontmatter.home"/>
|
||||
<Page v-else :sidebar-items="sidebarItems">
|
||||
<slot name="page-top" slot="top"/>
|
||||
<slot name="page-bottom" slot="bottom"/>
|
||||
</Page>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import nprogress from 'nprogress'
|
||||
import Home from './Home.vue'
|
||||
import Navbar from './Navbar.vue'
|
||||
import Page from './Page.vue'
|
||||
import Sidebar from './Sidebar.vue'
|
||||
import { resolveSidebarItems } from './util'
|
||||
|
||||
export default {
|
||||
components: { Home, Page, Sidebar, Navbar },
|
||||
data () {
|
||||
return {
|
||||
isSidebarOpen: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
versions() {
|
||||
const { themeConfig } = this.$site
|
||||
return themeConfig.versions.map(version => ({
|
||||
name: version[0],
|
||||
path: version[1],
|
||||
}))
|
||||
},
|
||||
shouldShowNavbar () {
|
||||
const { themeConfig } = this.$site
|
||||
const { frontmatter } = this.$page
|
||||
if (
|
||||
frontmatter.navbar === false ||
|
||||
themeConfig.navbar === false) {
|
||||
return false
|
||||
}
|
||||
return (
|
||||
this.$title ||
|
||||
themeConfig.logo ||
|
||||
themeConfig.repo ||
|
||||
themeConfig.nav ||
|
||||
this.$themeLocaleConfig.nav
|
||||
)
|
||||
},
|
||||
shouldShowSidebar () {
|
||||
const { frontmatter } = this.$page
|
||||
return (
|
||||
!frontmatter.layout &&
|
||||
!frontmatter.home &&
|
||||
frontmatter.sidebar !== false &&
|
||||
this.sidebarItems.length
|
||||
)
|
||||
},
|
||||
sidebarItems () {
|
||||
return resolveSidebarItems(
|
||||
this.$page,
|
||||
this.$route,
|
||||
this.$site,
|
||||
this.$localePath
|
||||
)
|
||||
},
|
||||
pageClasses () {
|
||||
const userPageClass = this.$page.frontmatter.pageClass
|
||||
return [
|
||||
{
|
||||
'no-navbar': !this.shouldShowNavbar,
|
||||
'sidebar-open': this.isSidebarOpen,
|
||||
'no-sidebar': !this.shouldShowSidebar
|
||||
},
|
||||
userPageClass
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
window.addEventListener('scroll', this.onScroll)
|
||||
|
||||
// configure progress bar
|
||||
nprogress.configure({ showSpinner: false })
|
||||
|
||||
this.$router.beforeEach((to, from, next) => {
|
||||
if (to.path !== from.path && !Vue.component(to.name)) {
|
||||
nprogress.start()
|
||||
}
|
||||
next()
|
||||
})
|
||||
|
||||
this.$router.afterEach(() => {
|
||||
nprogress.done()
|
||||
this.isSidebarOpen = false
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
changeVersion(to) {
|
||||
this.$router.push(to)
|
||||
},
|
||||
toggleSidebar (to) {
|
||||
this.isSidebarOpen = typeof to === 'boolean' ? to : !this.isSidebarOpen
|
||||
},
|
||||
// side swipe
|
||||
onTouchStart (e) {
|
||||
this.touchStart = {
|
||||
x: e.changedTouches[0].clientX,
|
||||
y: e.changedTouches[0].clientY
|
||||
}
|
||||
},
|
||||
onTouchEnd (e) {
|
||||
const dx = e.changedTouches[0].clientX - this.touchStart.x
|
||||
const dy = e.changedTouches[0].clientY - this.touchStart.y
|
||||
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40) {
|
||||
if (dx > 0 && this.touchStart.x <= 80) {
|
||||
this.toggleSidebar(true)
|
||||
} else {
|
||||
this.toggleSidebar(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style src="prismjs/themes/prism-tomorrow.css"></style>
|
||||
<style src="./styles/theme.styl" lang="stylus"></style>
|
46
docs/.vuepress/theme/NavLink.vue
Normal file
@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<router-link
|
||||
class="nav-link"
|
||||
:to="link"
|
||||
v-if="!isExternal(link)"
|
||||
:exact="exact"
|
||||
>{{ item.text }}</router-link>
|
||||
<a
|
||||
v-else
|
||||
:href="link"
|
||||
class="nav-link external"
|
||||
:target="isMailto(link) || isTel(link) ? null : '_blank'"
|
||||
:rel="isMailto(link) || isTel(link) ? null : 'noopener noreferrer'"
|
||||
>
|
||||
{{ item.text }}
|
||||
<OutboundLink/>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isExternal, isMailto, isTel, ensureExt } from './util'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
item: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
link () {
|
||||
return ensureExt(this.item.link)
|
||||
},
|
||||
exact () {
|
||||
if (this.$site.locales) {
|
||||
return Object.keys(this.$site.locales).some(rootLink => rootLink === this.link)
|
||||
}
|
||||
return this.link === '/'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isExternal,
|
||||
isMailto,
|
||||
isTel
|
||||
}
|
||||
}
|
||||
</script>
|
132
docs/.vuepress/theme/NavLinks.vue
Normal file
@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<nav class="nav-links" v-if="userLinks.length || repoLink">
|
||||
<!-- user links -->
|
||||
<div
|
||||
class="nav-item"
|
||||
v-for="item in userLinks"
|
||||
:key="item.link">
|
||||
<DropdownLink v-if="item.type === 'links'" :item="item"/>
|
||||
<NavLink v-else :item="item"/>
|
||||
</div>
|
||||
<!-- repo link -->
|
||||
<a v-if="repoLink"
|
||||
:href="repoLink"
|
||||
class="repo-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
{{ repoLabel }}
|
||||
<OutboundLink/>
|
||||
</a>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DropdownLink from './DropdownLink.vue'
|
||||
import { resolveNavLinkItem } from './util'
|
||||
import NavLink from './NavLink.vue'
|
||||
|
||||
export default {
|
||||
components: { NavLink, DropdownLink },
|
||||
computed: {
|
||||
userNav () {
|
||||
return this.$themeLocaleConfig.nav || this.$site.themeConfig.nav || []
|
||||
},
|
||||
nav () {
|
||||
const { locales } = this.$site
|
||||
if (locales && Object.keys(locales).length > 1) {
|
||||
const currentLink = this.$page.path
|
||||
const routes = this.$router.options.routes
|
||||
const themeLocales = this.$site.themeConfig.locales || {}
|
||||
const languageDropdown = {
|
||||
text: this.$themeLocaleConfig.selectText || 'Languages',
|
||||
items: Object.keys(locales).map(path => {
|
||||
const locale = locales[path]
|
||||
const text = themeLocales[path] && themeLocales[path].label || locale.lang
|
||||
let link
|
||||
// Stay on the current page
|
||||
if (locale.lang === this.$lang) {
|
||||
link = currentLink
|
||||
} else {
|
||||
// Try to stay on the same page
|
||||
link = currentLink.replace(this.$localeConfig.path, path)
|
||||
// fallback to homepage
|
||||
if (!routes.some(route => route.path === link)) {
|
||||
link = path
|
||||
}
|
||||
}
|
||||
return { text, link }
|
||||
})
|
||||
}
|
||||
return [...this.userNav, languageDropdown]
|
||||
}
|
||||
return this.userNav
|
||||
},
|
||||
userLinks () {
|
||||
return (this.nav || []).map(link => {
|
||||
return Object.assign(resolveNavLinkItem(link), {
|
||||
items: (link.items || []).map(resolveNavLinkItem)
|
||||
})
|
||||
})
|
||||
},
|
||||
repoLink () {
|
||||
const { repo } = this.$site.themeConfig
|
||||
if (repo) {
|
||||
return /^https?:/.test(repo)
|
||||
? repo
|
||||
: `https://github.com/${repo}`
|
||||
}
|
||||
},
|
||||
repoLabel () {
|
||||
if (!this.repoLink) return
|
||||
if (this.$site.themeConfig.repoLabel) {
|
||||
return this.$site.themeConfig.repoLabel
|
||||
}
|
||||
|
||||
const repoHost = this.repoLink.match(/^https?:\/\/[^/]+/)[0]
|
||||
const platforms = ['GitHub', 'GitLab', 'Bitbucket']
|
||||
for (let i = 0; i < platforms.length; i++) {
|
||||
const platform = platforms[i]
|
||||
if (new RegExp(platform, 'i').test(repoHost)) {
|
||||
return platform
|
||||
}
|
||||
}
|
||||
|
||||
return 'Source'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@import './styles/config.styl'
|
||||
|
||||
.nav-links
|
||||
display inline-block
|
||||
a
|
||||
line-height 1.4rem
|
||||
color inherit
|
||||
&:hover, &.router-link-active
|
||||
color $accentColor
|
||||
.nav-item
|
||||
cursor pointer
|
||||
position relative
|
||||
display inline-block
|
||||
margin-left 1.5rem
|
||||
line-height 2rem
|
||||
.repo-link
|
||||
margin-left 1.5rem
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.nav-links
|
||||
.nav-item, .repo-link
|
||||
margin-left 0
|
||||
|
||||
@media (min-width: $MQMobile)
|
||||
.nav-links a
|
||||
&:hover, &.router-link-active
|
||||
color $textColor
|
||||
.nav-item > a:not(.external)
|
||||
&:hover, &.router-link-active
|
||||
margin-bottom -2px
|
||||
border-bottom 2px solid lighten($accentColor, 8%)
|
||||
</style>
|
71
docs/.vuepress/theme/Navbar.vue
Normal file
@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<header class="navbar">
|
||||
<SidebarButton @toggle-sidebar="$emit('toggle-sidebar')"/>
|
||||
<router-link :to="$localePath" class="home-link">
|
||||
<img class="logo"
|
||||
v-if="$site.themeConfig.logo"
|
||||
:src="$withBase($site.themeConfig.logo)">
|
||||
<span class="site-name"
|
||||
v-if="$siteTitle"
|
||||
:class="{ 'can-hide': $site.themeConfig.logo }">
|
||||
{{ $siteTitle }}
|
||||
</span>
|
||||
</router-link>
|
||||
<div class="links">
|
||||
<AlgoliaSearchBox v-if="isAlgoliaSearch" :options="algolia"/>
|
||||
<SearchBox v-else-if="$site.themeConfig.search !== false"/>
|
||||
<NavLinks class="can-hide"/>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SidebarButton from './SidebarButton.vue'
|
||||
import AlgoliaSearchBox from '@AlgoliaSearchBox'
|
||||
import SearchBox from './SearchBox.vue'
|
||||
import NavLinks from './NavLinks.vue'
|
||||
|
||||
export default {
|
||||
components: { SidebarButton, NavLinks, SearchBox, AlgoliaSearchBox },
|
||||
computed: {
|
||||
algolia () {
|
||||
return this.$themeLocaleConfig.algolia || this.$site.themeConfig.algolia || {}
|
||||
},
|
||||
isAlgoliaSearch () {
|
||||
return this.algolia && this.algolia.apiKey && this.algolia.indexName
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@import './styles/config.styl'
|
||||
|
||||
.navbar
|
||||
padding 0.7rem 1.5rem
|
||||
line-height $navbarHeight - 1.4rem
|
||||
position relative
|
||||
a, span, img
|
||||
display inline-block
|
||||
.logo
|
||||
height $navbarHeight - 1.4rem
|
||||
min-width $navbarHeight - 1.4rem
|
||||
margin-right 0.8rem
|
||||
vertical-align top
|
||||
.site-name
|
||||
font-size 1.3rem
|
||||
font-weight 600
|
||||
color $textColor
|
||||
position relative
|
||||
.links
|
||||
font-size 0.9rem
|
||||
position absolute
|
||||
right 1.5rem
|
||||
top 0.7rem
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.navbar
|
||||
padding-left 4rem
|
||||
.can-hide
|
||||
display none
|
||||
</style>
|
26
docs/.vuepress/theme/NotFound.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div class="theme-container">
|
||||
<div class="content">
|
||||
<h1>404</h1>
|
||||
<blockquote>{{ getMsg() }}</blockquote>
|
||||
<router-link to="/">Take me home.</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const msgs = [
|
||||
`There's nothing here.`,
|
||||
`How did we get here?`,
|
||||
`That's a Four-Oh-Four.`,
|
||||
`Looks like we've got some broken links.`
|
||||
]
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
getMsg () {
|
||||
return msgs[Math.floor(Math.random() * msgs.length)]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
207
docs/.vuepress/theme/Page.vue
Normal file
@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<Content :custom="false"/>
|
||||
<div class="page-edit">
|
||||
<div class="edit-link" v-if="editLink">
|
||||
<a :href="editLink" target="_blank" rel="noopener noreferrer">{{ editLinkText }}</a>
|
||||
<OutboundLink/>
|
||||
</div>
|
||||
<div class="last-updated" v-if="lastUpdated">
|
||||
<span class="prefix">{{ lastUpdatedText }}: </span>
|
||||
<span class="time">{{ lastUpdated }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-nav" v-if="prev || next">
|
||||
<p class="inner">
|
||||
<span v-if="prev" class="prev">
|
||||
← <router-link v-if="prev" class="prev" :to="prev.path">
|
||||
{{ prev.title || prev.path }}
|
||||
</router-link>
|
||||
</span>
|
||||
<span v-if="next" class="next">
|
||||
<router-link v-if="next" :to="next.path">
|
||||
{{ next.title || next.path }}
|
||||
</router-link> →
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<slot name="bottom"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { resolvePage, normalize, outboundRE, endingSlashRE } from './util'
|
||||
|
||||
export default {
|
||||
props: ['sidebarItems'],
|
||||
computed: {
|
||||
lastUpdated () {
|
||||
if (this.$page.lastUpdated) {
|
||||
return new Date(this.$page.lastUpdated).toLocaleString(this.$lang)
|
||||
}
|
||||
},
|
||||
lastUpdatedText () {
|
||||
if (typeof this.$themeLocaleConfig.lastUpdated === 'string') {
|
||||
return this.$themeLocaleConfig.lastUpdated
|
||||
}
|
||||
if (typeof this.$site.themeConfig.lastUpdated === 'string') {
|
||||
return this.$site.themeConfig.lastUpdated
|
||||
}
|
||||
return 'Last Updated'
|
||||
},
|
||||
prev () {
|
||||
const prev = this.$page.frontmatter.prev
|
||||
if (prev === false) {
|
||||
return
|
||||
} else if (prev) {
|
||||
return resolvePage(this.$site.pages, prev, this.$route.path)
|
||||
} else {
|
||||
return resolvePrev(this.$page, this.sidebarItems)
|
||||
}
|
||||
},
|
||||
next () {
|
||||
const next = this.$page.frontmatter.next
|
||||
if (next === false) {
|
||||
return
|
||||
} else if (next) {
|
||||
return resolvePage(this.$site.pages, next, this.$route.path)
|
||||
} else {
|
||||
return resolveNext(this.$page, this.sidebarItems)
|
||||
}
|
||||
},
|
||||
editLink () {
|
||||
if (this.$page.frontmatter.editLink === false) {
|
||||
return
|
||||
}
|
||||
const {
|
||||
repo,
|
||||
editLinks,
|
||||
docsDir = '',
|
||||
docsBranch = 'master',
|
||||
docsRepo = repo
|
||||
} = this.$site.themeConfig
|
||||
|
||||
let path = normalize(this.$page.path)
|
||||
if (endingSlashRE.test(path)) {
|
||||
path += 'README.md'
|
||||
} else {
|
||||
path += '.md'
|
||||
}
|
||||
if (docsRepo && editLinks) {
|
||||
return this.createEditLink(repo, docsRepo, docsDir, docsBranch, path)
|
||||
}
|
||||
},
|
||||
editLinkText () {
|
||||
return (
|
||||
this.$themeLocaleConfig.editLinkText ||
|
||||
this.$site.themeConfig.editLinkText ||
|
||||
`Edit this page`
|
||||
)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
createEditLink (repo, docsRepo, docsDir, docsBranch, path) {
|
||||
const bitbucket = /bitbucket.org/
|
||||
if (bitbucket.test(repo)) {
|
||||
const base = outboundRE.test(docsRepo)
|
||||
? docsRepo
|
||||
: repo
|
||||
return (
|
||||
base.replace(endingSlashRE, '') +
|
||||
`/${docsBranch}` +
|
||||
(docsDir ? '/' + docsDir.replace(endingSlashRE, '') : '') +
|
||||
path +
|
||||
`?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default`
|
||||
)
|
||||
}
|
||||
|
||||
const base = outboundRE.test(docsRepo)
|
||||
? docsRepo
|
||||
: `https://github.com/${docsRepo}`
|
||||
|
||||
return (
|
||||
base.replace(endingSlashRE, '') +
|
||||
`/edit/${docsBranch}` +
|
||||
(docsDir ? '/' + docsDir.replace(endingSlashRE, '') : '') +
|
||||
path
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolvePrev (page, items) {
|
||||
return find(page, items, -1)
|
||||
}
|
||||
|
||||
function resolveNext (page, items) {
|
||||
return find(page, items, 1)
|
||||
}
|
||||
|
||||
function find (page, items, offset) {
|
||||
const res = []
|
||||
items.forEach(item => {
|
||||
if (item.type === 'group') {
|
||||
res.push(...item.children || [])
|
||||
} else {
|
||||
res.push(item)
|
||||
}
|
||||
})
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
const cur = res[i]
|
||||
if (cur.type === 'page' && cur.path === page.path) {
|
||||
return res[i + offset]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@import './styles/config.styl'
|
||||
@require './styles/wrapper.styl'
|
||||
|
||||
.page
|
||||
padding-bottom 2rem
|
||||
|
||||
.page-edit
|
||||
@extend $wrapper
|
||||
padding-top 1rem
|
||||
padding-bottom 1rem
|
||||
overflow auto
|
||||
.edit-link
|
||||
display inline-block
|
||||
a
|
||||
color lighten($textColor, 25%)
|
||||
margin-right 0.25rem
|
||||
.last-updated
|
||||
float right
|
||||
font-size 0.9em
|
||||
.prefix
|
||||
font-weight 500
|
||||
color lighten($textColor, 25%)
|
||||
.time
|
||||
font-weight 400
|
||||
color #aaa
|
||||
|
||||
.page-nav
|
||||
@extend $wrapper
|
||||
padding-top 1rem
|
||||
padding-bottom 0
|
||||
.inner
|
||||
min-height 2rem
|
||||
margin-top 0
|
||||
border-top 1px solid $borderColor
|
||||
padding-top 1rem
|
||||
overflow auto // clear float
|
||||
.next
|
||||
float right
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.page-edit
|
||||
.edit-link
|
||||
margin-bottom .5rem
|
||||
.last-updated
|
||||
font-size .8em
|
||||
float none
|
||||
text-align left
|
||||
|
||||
</style>
|
221
docs/.vuepress/theme/SearchBox.vue
Normal file
@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<div class="search-box">
|
||||
<input
|
||||
@input="query = $event.target.value"
|
||||
aria-label="Search"
|
||||
:value="query"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false"
|
||||
@keyup.enter="go(focusIndex)"
|
||||
@keyup.up="onUp"
|
||||
@keyup.down="onDown">
|
||||
<ul class="suggestions"
|
||||
v-if="showSuggestions"
|
||||
:class="{ 'align-right': alignRight }"
|
||||
@mouseleave="unfocus">
|
||||
<li class="suggestion" v-for="(s, i) in suggestions"
|
||||
:class="{ focused: i === focusIndex }"
|
||||
@mousedown="go(i)"
|
||||
@mouseenter="focus(i)">
|
||||
<a :href="s.path" @click.prevent>
|
||||
<span class="page-title">{{ s.title || s.path }}</span>
|
||||
<span v-if="s.header" class="header">> {{ s.header.title }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
query: '',
|
||||
focused: false,
|
||||
focusIndex: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showSuggestions () {
|
||||
return (
|
||||
this.focused &&
|
||||
this.suggestions &&
|
||||
this.suggestions.length
|
||||
)
|
||||
},
|
||||
suggestions () {
|
||||
const query = this.query.trim().toLowerCase()
|
||||
if (!query) {
|
||||
return
|
||||
}
|
||||
|
||||
const { pages, themeConfig } = this.$site
|
||||
const max = themeConfig.searchMaxSuggestions || 5
|
||||
const searchContext = this.$route.path.split('/').slice(1)[0]
|
||||
const localePath = this.$localePath
|
||||
const matches = item => (
|
||||
item.title &&
|
||||
item.title.toLowerCase().indexOf(query) > -1
|
||||
)
|
||||
const res = []
|
||||
for (let i = 0; i < pages.length; i++) {
|
||||
if (res.length >= max) break
|
||||
const p = pages[i]
|
||||
// filter out results that do not match current ersion context
|
||||
if (~p.path.slice(1).indexOf(searchContext)) continue
|
||||
// filter out results that do not match current locale
|
||||
if (this.getPageLocalePath(p) !== localePath) continue
|
||||
if (matches(p)) {
|
||||
res.push(p)
|
||||
} else if (p.headers) {
|
||||
for (let j = 0; j < p.headers.length; j++) {
|
||||
if (res.length >= max) break
|
||||
const h = p.headers[j]
|
||||
if (matches(h)) {
|
||||
res.push(Object.assign({}, p, {
|
||||
path: p.path + '#' + h.slug,
|
||||
header: h
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
},
|
||||
// make suggestions align right when there are not enough items
|
||||
alignRight () {
|
||||
const navCount = (this.$site.themeConfig.nav || []).length
|
||||
const repo = this.$site.repo ? 1 : 0
|
||||
return navCount + repo <= 2
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPageLocalePath (page) {
|
||||
for (const localePath in this.$site.locales || {}) {
|
||||
if (localePath !== '/' && page.path.indexOf(localePath) === 0) {
|
||||
return localePath
|
||||
}
|
||||
}
|
||||
return '/'
|
||||
},
|
||||
onUp () {
|
||||
if (this.showSuggestions) {
|
||||
if (this.focusIndex > 0) {
|
||||
this.focusIndex--
|
||||
} else {
|
||||
this.focusIndex = this.suggestions.length - 1
|
||||
}
|
||||
}
|
||||
},
|
||||
onDown () {
|
||||
if (this.showSuggestions) {
|
||||
if (this.focusIndex < this.suggestions.length - 1) {
|
||||
this.focusIndex++
|
||||
} else {
|
||||
this.focusIndex = 0
|
||||
}
|
||||
}
|
||||
},
|
||||
go (i) {
|
||||
if (!this.showSuggestions) {
|
||||
return
|
||||
}
|
||||
this.$router.push(this.suggestions[i].path)
|
||||
this.query = ''
|
||||
this.focusIndex = 0
|
||||
},
|
||||
focus (i) {
|
||||
this.focusIndex = i
|
||||
},
|
||||
unfocus () {
|
||||
this.focusIndex = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@import './styles/config.styl'
|
||||
|
||||
.search-box
|
||||
display inline-block
|
||||
position relative
|
||||
margin-right 0.5rem
|
||||
input
|
||||
cursor text
|
||||
width 10rem
|
||||
color lighten($textColor, 25%)
|
||||
display inline-block
|
||||
border 1px solid darken($borderColor, 10%)
|
||||
border-radius 2rem
|
||||
font-size 0.9rem
|
||||
line-height 2rem
|
||||
padding 0 0.5rem 0 2rem
|
||||
outline none
|
||||
transition all .2s ease
|
||||
background #fff url(./search.svg) 0.6rem 0.5rem no-repeat
|
||||
background-size 1rem
|
||||
&:focus
|
||||
cursor auto
|
||||
border-color $accentColor
|
||||
.suggestions
|
||||
background #fff
|
||||
width 20rem
|
||||
position absolute
|
||||
top 1.5rem
|
||||
border 1px solid darken($borderColor, 10%)
|
||||
border-radius 6px
|
||||
padding 0.4rem
|
||||
list-style-type none
|
||||
&.align-right
|
||||
right 0
|
||||
.suggestion
|
||||
line-height 1.4
|
||||
padding 0.4rem 0.6rem
|
||||
border-radius 4px
|
||||
cursor pointer
|
||||
a
|
||||
color lighten($textColor, 35%)
|
||||
.page-title
|
||||
font-weight 600
|
||||
.header
|
||||
font-size 0.9em
|
||||
margin-left 0.25em
|
||||
&.focused
|
||||
background-color #f3f4f5
|
||||
a
|
||||
color $accentColor
|
||||
|
||||
@media (max-width: $MQNarrow)
|
||||
.search-box
|
||||
input
|
||||
cursor pointer
|
||||
width 0
|
||||
border-color transparent
|
||||
position relative
|
||||
left 1rem
|
||||
&:focus
|
||||
cursor text
|
||||
left 0
|
||||
width 10rem
|
||||
|
||||
@media (max-width: $MQNarrow) and (min-width: $MQMobile)
|
||||
.search-box
|
||||
.suggestions
|
||||
left 0
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.search-box
|
||||
margin-right 0
|
||||
.suggestions
|
||||
right 0
|
||||
|
||||
@media (max-width: $MQMobileNarrow)
|
||||
.search-box
|
||||
.suggestions
|
||||
width calc(100vw - 4rem)
|
||||
input:focus
|
||||
width 8rem
|
||||
</style>
|
104
docs/.vuepress/theme/Sidebar.vue
Normal file
@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div class="sidebar">
|
||||
<NavLinks/>
|
||||
<slot name="top"/>
|
||||
<ul class="sidebar-links" v-if="items.length">
|
||||
<li v-for="(item, i) in items" :key="i">
|
||||
<SidebarGroup v-if="item.type === 'group'"
|
||||
:item="item"
|
||||
:first="i === 0"
|
||||
:open="i === openGroupIndex"
|
||||
:collapsable="item.collapsable"
|
||||
@toggle="toggleGroup(i)"/>
|
||||
<SidebarLink v-else :item="item"/>
|
||||
</li>
|
||||
</ul>
|
||||
<slot name="bottom"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SidebarGroup from './SidebarGroup.vue'
|
||||
import SidebarLink from './SidebarLink.vue'
|
||||
import NavLinks from './NavLinks.vue'
|
||||
import { isActive } from './util'
|
||||
|
||||
export default {
|
||||
components: { SidebarGroup, SidebarLink, NavLinks },
|
||||
props: ['items'],
|
||||
data () {
|
||||
return {
|
||||
openGroupIndex: 0
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.refreshIndex()
|
||||
},
|
||||
watch: {
|
||||
'$route' () {
|
||||
this.refreshIndex()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refreshIndex () {
|
||||
const index = resolveOpenGroupIndex(
|
||||
this.$route,
|
||||
this.items
|
||||
)
|
||||
if (index > -1) {
|
||||
this.openGroupIndex = index
|
||||
}
|
||||
},
|
||||
toggleGroup (index) {
|
||||
this.openGroupIndex = index === this.openGroupIndex ? -1 : index
|
||||
},
|
||||
isActive (page) {
|
||||
return isActive(this.$route, page.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolveOpenGroupIndex (route, items) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i]
|
||||
if (item.type === 'group' && item.children.some(c => isActive(route, c.path))) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@import './styles/config.styl'
|
||||
|
||||
.sidebar
|
||||
ul
|
||||
padding 0
|
||||
margin 0
|
||||
list-style-type none
|
||||
a
|
||||
display inline-block
|
||||
.nav-links
|
||||
display none
|
||||
border-bottom 1px solid $borderColor
|
||||
padding 0.5rem 0 0.75rem 0
|
||||
a
|
||||
font-weight 600
|
||||
.nav-item, .repo-link
|
||||
display block
|
||||
line-height 1.25rem
|
||||
font-size 1.1em
|
||||
padding 0.5rem 0 0.5rem 1.5rem
|
||||
.sidebar-links
|
||||
padding 1.5rem 0
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.sidebar
|
||||
.nav-links
|
||||
display block
|
||||
.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after
|
||||
top calc(1rem - 2px)
|
||||
.sidebar-links
|
||||
padding 1rem 0
|
||||
</style>
|
28
docs/.vuepress/theme/SidebarButton.vue
Normal file
@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="sidebar-button" @click="$emit('toggle-sidebar')">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512">
|
||||
<path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z" class=""></path>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="stylus">
|
||||
@import './styles/config.styl'
|
||||
|
||||
.sidebar-button
|
||||
display none
|
||||
width 1.25rem
|
||||
height 1.25rem
|
||||
position absolute
|
||||
padding 0.6rem
|
||||
top 0.6rem
|
||||
left 1rem
|
||||
.icon
|
||||
display block
|
||||
width 1.25rem
|
||||
height 1.25rem
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.sidebar-button
|
||||
display block
|
||||
</style>
|
63
docs/.vuepress/theme/SidebarGroup.vue
Normal file
@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="sidebar-group" :class="{ first, collapsable }">
|
||||
<p class="sidebar-heading" :class="{ open }" @click="$emit('toggle')">
|
||||
<span>{{ item.title }}</span>
|
||||
<span class="arrow"
|
||||
v-if="collapsable"
|
||||
:class="open ? 'down' : 'right'"></span>
|
||||
</p>
|
||||
<DropdownTransition>
|
||||
<ul class="sidebar-group-items" ref="items" v-if="open || !collapsable">
|
||||
<li v-for="child in item.children">
|
||||
<SidebarLink :item="child"/>
|
||||
</li>
|
||||
</ul>
|
||||
</DropdownTransition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SidebarLink from './SidebarLink.vue'
|
||||
import DropdownTransition from './DropdownTransition.vue'
|
||||
|
||||
export default {
|
||||
name: 'SidebarGroup',
|
||||
props: ['item', 'first', 'open', 'collapsable'],
|
||||
components: { SidebarLink, DropdownTransition }
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.sidebar-group
|
||||
&:not(.first)
|
||||
margin-top 1em
|
||||
.sidebar-group
|
||||
padding-left 0.5em
|
||||
&:not(.collapsable)
|
||||
.sidebar-heading
|
||||
cursor auto
|
||||
color inherit
|
||||
|
||||
.sidebar-heading
|
||||
color #999
|
||||
transition color .15s ease
|
||||
cursor pointer
|
||||
font-size 1.1em
|
||||
font-weight bold
|
||||
// text-transform uppercase
|
||||
padding 0 1.5rem
|
||||
margin-top 0
|
||||
margin-bottom 0.5rem
|
||||
&.open, &:hover
|
||||
color inherit
|
||||
.arrow
|
||||
position relative
|
||||
top -0.12em
|
||||
left 0.5em
|
||||
&:.open .arrow
|
||||
top -0.18em
|
||||
|
||||
.sidebar-group-items
|
||||
transition height .1s ease-out
|
||||
overflow hidden
|
||||
</style>
|
98
docs/.vuepress/theme/SidebarLink.vue
Normal file
@ -0,0 +1,98 @@
|
||||
<script>
|
||||
import { isActive, hashRE, groupHeaders } from './util'
|
||||
import AppLink from './AppLink'
|
||||
|
||||
export default {
|
||||
functional: true,
|
||||
components: {AppLink},
|
||||
props: ['item'],
|
||||
render (h, { parent: { $page, $site, $route }, props: { item }}) {
|
||||
// use custom active class matching logic
|
||||
// due to edge case of paths ending with / + hash
|
||||
const selfActive = isActive($route, item.path)
|
||||
// for sidebar: auto pages, a hash link should be active if one of its child
|
||||
// matches
|
||||
const active = item.type === 'auto'
|
||||
? selfActive || item.children.some(c => isActive($route, item.basePath + '#' + c.slug))
|
||||
: selfActive
|
||||
const link = renderLink(h, item.path, item.title || item.path, active)
|
||||
const configDepth = $page.frontmatter.sidebarDepth != null
|
||||
? $page.frontmatter.sidebarDepth
|
||||
: $site.themeConfig.sidebarDepth
|
||||
const maxDepth = configDepth == null ? 1 : configDepth
|
||||
if (item.type === 'auto') {
|
||||
return [link, renderChildren(h, item.children, item.basePath, $route, maxDepth)]
|
||||
} else if (active && item.headers && !hashRE.test(item.path)) {
|
||||
const children = groupHeaders(item.headers)
|
||||
return [link, renderChildren(h, children, item.path, $route, maxDepth)]
|
||||
} else {
|
||||
return link
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderLink (h, to, text, active) {
|
||||
if (~to.indexOf('http')) return h('a',
|
||||
{
|
||||
attrs: {
|
||||
href: to
|
||||
},
|
||||
class: 'sidebar-link'
|
||||
},
|
||||
text)
|
||||
return h('router-link', {
|
||||
props: {
|
||||
to,
|
||||
activeClass: '',
|
||||
exactActiveClass: ''
|
||||
},
|
||||
class: {
|
||||
active,
|
||||
'sidebar-link': true
|
||||
}
|
||||
}, text)
|
||||
}
|
||||
|
||||
function renderChildren (h, children, path, route, maxDepth, depth = 1) {
|
||||
if (!children || depth > maxDepth) return null
|
||||
return h('ul', { class: 'sidebar-sub-headers' }, children.map(c => {
|
||||
const active = isActive(route, path + '#' + c.slug)
|
||||
return h('li', { class: 'sidebar-sub-header' }, [
|
||||
renderLink(h, '#' + c.slug, c.title, active),
|
||||
renderChildren(h, c.children, path, route, maxDepth, depth + 1)
|
||||
])
|
||||
}))
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@import './styles/config.styl'
|
||||
|
||||
.sidebar .sidebar-sub-headers
|
||||
padding-left 1rem
|
||||
font-size 0.95em
|
||||
|
||||
a.sidebar-link
|
||||
font-weight 400
|
||||
display inline-block
|
||||
color $textColor
|
||||
border-left 0.25rem solid transparent
|
||||
padding 0.35rem 1rem 0.35rem 1.25rem
|
||||
line-height 1.4
|
||||
width: 100%
|
||||
box-sizing: border-box
|
||||
&:hover
|
||||
color $accentColor
|
||||
&.active
|
||||
font-weight 600
|
||||
color $accentColor
|
||||
border-left-color $accentColor
|
||||
.sidebar-group &
|
||||
padding-left 2rem
|
||||
.sidebar-sub-headers &
|
||||
padding-top 0.25rem
|
||||
padding-bottom 0.25rem
|
||||
border-left none
|
||||
&.active
|
||||
font-weight 500
|
||||
</style>
|
1
docs/.vuepress/theme/search.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="12" height="13"><g stroke-width="2" stroke="#aaa" fill="none"><path d="M11.29 11.71l-4-4"/><circle cx="5" cy="5" r="4"/></g></svg>
|
After Width: | Height: | Size: 216 B |
22
docs/.vuepress/theme/styles/arrow.styl
Normal file
@ -0,0 +1,22 @@
|
||||
@require './config'
|
||||
|
||||
.arrow
|
||||
display inline-block
|
||||
width 0
|
||||
height 0
|
||||
&.up
|
||||
border-left 4px solid transparent
|
||||
border-right 4px solid transparent
|
||||
border-bottom 6px solid $arrowBgColor
|
||||
&.down
|
||||
border-left 4px solid transparent
|
||||
border-right 4px solid transparent
|
||||
border-top 6px solid $arrowBgColor
|
||||
&.right
|
||||
border-top 4px solid transparent
|
||||
border-bottom 4px solid transparent
|
||||
border-left 6px solid $arrowBgColor
|
||||
&.left
|
||||
border-top 4px solid transparent
|
||||
border-bottom 4px solid transparent
|
||||
border-right 6px solid $arrowBgColor
|
127
docs/.vuepress/theme/styles/code.styl
Normal file
@ -0,0 +1,127 @@
|
||||
@require './config'
|
||||
|
||||
.content
|
||||
code
|
||||
color lighten($textColor, 20%)
|
||||
padding 0.25rem 0.5rem
|
||||
margin 0
|
||||
font-size 0.85em
|
||||
background-color rgba(27,31,35,0.05)
|
||||
border-radius 3px
|
||||
|
||||
.content
|
||||
pre, pre[class*="language-"]
|
||||
line-height 1.4
|
||||
padding 1.25rem 1.5rem
|
||||
margin 0.85rem 0
|
||||
background transparent
|
||||
overflow auto
|
||||
code
|
||||
color #fff
|
||||
padding 0
|
||||
background-color transparent
|
||||
border-radius 0
|
||||
|
||||
div[class*="language-"]
|
||||
position relative
|
||||
background-color $codeBgColor
|
||||
border-radius 6px
|
||||
.highlight-lines
|
||||
user-select none
|
||||
padding-top 1.3rem
|
||||
position absolute
|
||||
top 0
|
||||
left 0
|
||||
width 100%
|
||||
line-height 1.4
|
||||
.highlighted
|
||||
background-color rgba(0, 0, 0, 66%)
|
||||
pre
|
||||
position relative
|
||||
z-index 1
|
||||
&::before
|
||||
position absolute
|
||||
z-index 3
|
||||
top 0.8em
|
||||
right 1em
|
||||
font-size 0.75rem
|
||||
color rgba(255, 255, 255, 0.4)
|
||||
&:not(.line-numbers-mode)
|
||||
.line-numbers-wrapper
|
||||
display none
|
||||
&.line-numbers-mode
|
||||
.highlight-lines .highlighted
|
||||
position relative
|
||||
&:before
|
||||
content ' '
|
||||
position absolute
|
||||
z-index 3
|
||||
left 0
|
||||
top 0
|
||||
display block
|
||||
width $lineNumbersWrapperWidth
|
||||
height 100%
|
||||
background-color rgba(0, 0, 0, 66%)
|
||||
pre
|
||||
padding-left $lineNumbersWrapperWidth + 1 rem
|
||||
vertical-align middle
|
||||
.line-numbers-wrapper
|
||||
position absolute
|
||||
top 0
|
||||
width $lineNumbersWrapperWidth
|
||||
text-align center
|
||||
color rgba(255, 255, 255, 0.3)
|
||||
padding 1.25rem 0
|
||||
line-height 1.4
|
||||
br
|
||||
user-select none
|
||||
.line-number
|
||||
position relative
|
||||
z-index 4
|
||||
user-select none
|
||||
font-size 0.85em
|
||||
&::after
|
||||
content ''
|
||||
position absolute
|
||||
z-index 2
|
||||
top 0
|
||||
left 0
|
||||
width $lineNumbersWrapperWidth
|
||||
height 100%
|
||||
border-radius 6px 0 0 6px
|
||||
border-right 1px solid rgba(0, 0, 0, 66%)
|
||||
background-color $codeBgColor
|
||||
|
||||
|
||||
for lang in js ts html md vue css sass scss less stylus go java c sh yaml
|
||||
div{'[class~="language-' + lang + '"]'}
|
||||
&:before
|
||||
content ('' + lang)
|
||||
|
||||
div[class~="language-javascript"]
|
||||
&:before
|
||||
content "js"
|
||||
|
||||
div[class~="language-typescript"]
|
||||
&:before
|
||||
content "ts"
|
||||
|
||||
div[class~="language-markup"]
|
||||
&:before
|
||||
content "html"
|
||||
|
||||
div[class~="language-markdown"]
|
||||
&:before
|
||||
content "md"
|
||||
|
||||
div[class~="language-json"]:before
|
||||
content "json"
|
||||
|
||||
div[class~="language-ruby"]:before
|
||||
content "rb"
|
||||
|
||||
div[class~="language-python"]:before
|
||||
content "py"
|
||||
|
||||
div[class~="language-bash"]:before
|
||||
content "sh"
|
19
docs/.vuepress/theme/styles/config.styl
Normal file
@ -0,0 +1,19 @@
|
||||
// colors
|
||||
$accentColor = #2F80ED
|
||||
$textColor = #2c3e50
|
||||
$borderColor = #eaecef
|
||||
$codeBgColor = #282c34
|
||||
$arrowBgColor = #ccc
|
||||
|
||||
// layout
|
||||
$navbarHeight = 3.6rem
|
||||
$sidebarWidth = 20rem
|
||||
$contentWidth = 740px
|
||||
|
||||
// responsive breakpoints
|
||||
$MQNarrow = 959px
|
||||
$MQMobile = 719px
|
||||
$MQMobileNarrow = 419px
|
||||
|
||||
// code
|
||||
$lineNumbersWrapperWidth = 3.5rem
|
31
docs/.vuepress/theme/styles/custom-blocks.styl
Normal file
@ -0,0 +1,31 @@
|
||||
.custom-block
|
||||
.custom-block-title
|
||||
font-weight 600
|
||||
margin-bottom -0.4rem
|
||||
&.tip, &.warning, &.danger, &.note
|
||||
padding .1rem 1.5rem
|
||||
border-left-width .5rem
|
||||
border-left-style solid
|
||||
margin 1rem 0
|
||||
&.tip
|
||||
background-color #f3f5f7
|
||||
border-color #42b983
|
||||
&.note
|
||||
background-color #f3f5f7
|
||||
border-color $accentColor
|
||||
&.warning
|
||||
background-color rgba(255,229,100,.3)
|
||||
border-color darken(#ffe564, 35%)
|
||||
color darken(#ffe564, 70%)
|
||||
.custom-block-title
|
||||
color darken(#ffe564, 50%)
|
||||
a
|
||||
color $textColor
|
||||
&.danger
|
||||
background-color #ffe6e6
|
||||
border-color darken(red, 20%)
|
||||
color darken(red, 70%)
|
||||
.custom-block-title
|
||||
color darken(red, 40%)
|
||||
a
|
||||
color $textColor
|
37
docs/.vuepress/theme/styles/mobile.styl
Normal file
@ -0,0 +1,37 @@
|
||||
@require './config'
|
||||
|
||||
$mobileSidebarWidth = $sidebarWidth * 0.82
|
||||
|
||||
// narrow desktop / iPad
|
||||
@media (max-width: $MQNarrow)
|
||||
.sidebar
|
||||
font-size 15px
|
||||
width $mobileSidebarWidth
|
||||
.page
|
||||
padding-left $mobileSidebarWidth
|
||||
|
||||
// wide mobile
|
||||
@media (max-width: $MQMobile)
|
||||
.sidebar
|
||||
top 0
|
||||
padding-top $navbarHeight
|
||||
transform translateX(-100%)
|
||||
transition transform .2s ease
|
||||
.page
|
||||
padding-left 0
|
||||
.theme-container
|
||||
&.sidebar-open
|
||||
.sidebar
|
||||
transform translateX(0)
|
||||
&.no-navbar
|
||||
.sidebar
|
||||
padding-top: 0
|
||||
|
||||
// narrow mobile
|
||||
@media (max-width: $MQMobileNarrow)
|
||||
h1
|
||||
font-size 1.9rem
|
||||
.content
|
||||
div[class*="language-"]
|
||||
margin 0.85rem -1.5rem
|
||||
border-radius 0
|
48
docs/.vuepress/theme/styles/nprogress.styl
Normal file
@ -0,0 +1,48 @@
|
||||
#nprogress
|
||||
pointer-events none
|
||||
.bar
|
||||
background $accentColor
|
||||
position fixed
|
||||
z-index 1031
|
||||
top 0
|
||||
left 0
|
||||
width 100%
|
||||
height 2px
|
||||
.peg
|
||||
display block
|
||||
position absolute
|
||||
right 0px
|
||||
width 100px
|
||||
height 100%
|
||||
box-shadow 0 0 10px $accentColor, 0 0 5px $accentColor
|
||||
opacity 1.0
|
||||
transform rotate(3deg) translate(0px, -4px)
|
||||
.spinner
|
||||
display block
|
||||
position fixed
|
||||
z-index 1031
|
||||
top 15px
|
||||
right 15px
|
||||
.spinner-icon
|
||||
width 18px
|
||||
height 18px
|
||||
box-sizing border-box
|
||||
border solid 2px transparent
|
||||
border-top-color $accentColor
|
||||
border-left-color $accentColor
|
||||
border-radius 50%
|
||||
animation nprogress-spinner 400ms linear infinite
|
||||
|
||||
.nprogress-custom-parent
|
||||
overflow hidden
|
||||
position relative
|
||||
|
||||
.nprogress-custom-parent #nprogress .spinner,
|
||||
.nprogress-custom-parent #nprogress .bar
|
||||
position absolute
|
||||
|
||||
@keyframes nprogress-spinner
|
||||
0%
|
||||
transform rotate(0deg)
|
||||
100%
|
||||
transform rotate(360deg)
|
222
docs/.vuepress/theme/styles/theme.styl
Normal file
@ -0,0 +1,222 @@
|
||||
@require './config'
|
||||
@require './nprogress'
|
||||
@require './code'
|
||||
@require './custom-blocks'
|
||||
@require './arrow'
|
||||
@require './wrapper'
|
||||
@require './toc'
|
||||
|
||||
html, body
|
||||
padding 0
|
||||
margin 0
|
||||
|
||||
body
|
||||
font-family -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif
|
||||
-webkit-font-smoothing antialiased
|
||||
-moz-osx-font-smoothing grayscale
|
||||
font-size 16px
|
||||
color $textColor
|
||||
|
||||
.intro
|
||||
text-align center
|
||||
font-size 1.15em
|
||||
margin 0 2em
|
||||
|
||||
// hide outboundlinks for shields
|
||||
a img + svg
|
||||
display none !important
|
||||
|
||||
.version-selector
|
||||
margin 1em 1em 0 1.5em
|
||||
color lighten($textColor, 25%)
|
||||
display block
|
||||
border 1px solid darken($borderColor, 10%)
|
||||
font-size .9rem
|
||||
line-height 2rem
|
||||
padding .5em 1rem .5em 1em
|
||||
outline none
|
||||
transition all .2s ease
|
||||
&:focus
|
||||
cursor auto
|
||||
border-color $accentColor
|
||||
|
||||
.text-center
|
||||
text-align center
|
||||
|
||||
.flex
|
||||
display flex
|
||||
|
||||
.justify-around
|
||||
justify-content space-around
|
||||
|
||||
.page
|
||||
padding-left $sidebarWidth
|
||||
|
||||
.navbar
|
||||
position fixed
|
||||
z-index 20
|
||||
top 0
|
||||
left 0
|
||||
right 0
|
||||
height $navbarHeight
|
||||
background-color #fff
|
||||
box-sizing border-box
|
||||
border-bottom 1px solid $borderColor
|
||||
|
||||
.sidebar-mask
|
||||
position fixed
|
||||
z-index 9
|
||||
top 0
|
||||
left 0
|
||||
width 100vw
|
||||
height 100vh
|
||||
display none
|
||||
|
||||
.sidebar
|
||||
font-size 15px
|
||||
background-color #fff
|
||||
width $sidebarWidth
|
||||
position fixed
|
||||
z-index 10
|
||||
margin 0
|
||||
top $navbarHeight
|
||||
left 0
|
||||
bottom 0
|
||||
box-sizing border-box
|
||||
border-right 1px solid $borderColor
|
||||
overflow-y auto
|
||||
|
||||
.content:not(.custom)
|
||||
@extend $wrapper
|
||||
> *:first-child
|
||||
margin-top $navbarHeight
|
||||
a:hover
|
||||
text-decoration underline
|
||||
p.demo
|
||||
padding 1rem 1.5rem
|
||||
border 1px solid #ddd
|
||||
border-radius 4px
|
||||
img
|
||||
max-width 100%
|
||||
|
||||
.content.custom
|
||||
padding 0
|
||||
margin 0
|
||||
img
|
||||
max-width 100%
|
||||
|
||||
a
|
||||
font-weight 500
|
||||
color $accentColor
|
||||
text-decoration none
|
||||
|
||||
p a code
|
||||
font-weight 400
|
||||
color $accentColor
|
||||
|
||||
kbd
|
||||
background #eee
|
||||
border solid 0.15rem #ddd
|
||||
border-bottom solid 0.25rem #ddd
|
||||
border-radius 0.15rem
|
||||
padding 0 0.15em
|
||||
|
||||
blockquote
|
||||
font-size 1.2rem
|
||||
color #999
|
||||
border-left .25rem solid #dfe2e5
|
||||
margin-left 0
|
||||
padding-left 1rem
|
||||
|
||||
ul, ol
|
||||
padding-left 1.2em
|
||||
|
||||
strong
|
||||
font-weight 600
|
||||
|
||||
h1, h2, h3, h4, h5, h6
|
||||
font-weight 600
|
||||
line-height 1.25
|
||||
.content:not(.custom) > &
|
||||
margin-top (0.5rem - $navbarHeight)
|
||||
padding-top ($navbarHeight + 1rem)
|
||||
margin-bottom 0
|
||||
&:first-child
|
||||
margin-top -1.5rem
|
||||
margin-bottom 1rem
|
||||
+ p, + pre, + .custom-block
|
||||
margin-top 2rem
|
||||
&:hover .header-anchor
|
||||
opacity: 1
|
||||
|
||||
h1
|
||||
font-size 2.2rem
|
||||
|
||||
h2
|
||||
font-size 1.65rem
|
||||
padding-bottom .3rem
|
||||
border-bottom 1px solid $borderColor
|
||||
|
||||
h3
|
||||
font-size 1.35rem
|
||||
|
||||
a.header-anchor
|
||||
font-size 0.85em
|
||||
float left
|
||||
margin-left -0.87em
|
||||
padding-right 0.23em
|
||||
margin-top 0.125em
|
||||
opacity 0
|
||||
&:hover
|
||||
text-decoration none
|
||||
|
||||
code, kbd, .line-number
|
||||
font-family source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace
|
||||
|
||||
p, ul, ol
|
||||
line-height 1.7
|
||||
|
||||
hr
|
||||
border 0
|
||||
border-top 1px solid $borderColor
|
||||
|
||||
table
|
||||
border-collapse collapse
|
||||
margin 1rem 0
|
||||
display: block
|
||||
overflow-x: auto
|
||||
|
||||
tr
|
||||
border-top 1px solid #dfe2e5
|
||||
&:nth-child(2n)
|
||||
background-color #f6f8fa
|
||||
|
||||
th, td
|
||||
border 1px solid #dfe2e5
|
||||
padding .6em 1em
|
||||
|
||||
.custom-layout
|
||||
padding-top $navbarHeight
|
||||
|
||||
.theme-container
|
||||
&.sidebar-open
|
||||
.sidebar-mask
|
||||
display: block
|
||||
&.no-navbar
|
||||
.content:not(.custom) > h1, h2, h3, h4, h5, h6
|
||||
margin-top 1.5rem
|
||||
padding-top 0
|
||||
.sidebar
|
||||
top 0
|
||||
.custom-layout
|
||||
padding-top 0
|
||||
|
||||
|
||||
@media (min-width: ($MQMobile + 1px))
|
||||
.theme-container.no-sidebar
|
||||
.sidebar
|
||||
display none
|
||||
.page
|
||||
padding-left 0
|
||||
|
||||
@require './mobile.styl'
|
3
docs/.vuepress/theme/styles/toc.styl
Normal file
@ -0,0 +1,3 @@
|
||||
.table-of-contents
|
||||
.badge
|
||||
vertical-align middle
|
9
docs/.vuepress/theme/styles/wrapper.styl
Normal file
@ -0,0 +1,9 @@
|
||||
$wrapper
|
||||
max-width $contentWidth
|
||||
margin 0 auto
|
||||
padding 2rem 2.5rem
|
||||
@media (max-width: $MQNarrow)
|
||||
padding 2rem
|
||||
@media (max-width: $MQMobileNarrow)
|
||||
padding 1.5rem
|
||||
|
218
docs/.vuepress/theme/util.js
Normal file
@ -0,0 +1,218 @@
|
||||
export const hashRE = /#.*$/
|
||||
export const extRE = /\.(md|html)$/
|
||||
export const endingSlashRE = /\/$/
|
||||
export const outboundRE = /^(https?:|mailto:|tel:)/
|
||||
|
||||
export function normalize (path) {
|
||||
return path
|
||||
.replace(hashRE, '')
|
||||
.replace(extRE, '')
|
||||
}
|
||||
|
||||
export function getHash (path) {
|
||||
const match = path.match(hashRE)
|
||||
if (match) {
|
||||
return match[0]
|
||||
}
|
||||
}
|
||||
|
||||
export function isExternal (path) {
|
||||
return outboundRE.test(path)
|
||||
}
|
||||
|
||||
export function isMailto (path) {
|
||||
return /^mailto:/.test(path)
|
||||
}
|
||||
|
||||
export function isTel (path) {
|
||||
return /^tel:/.test(path)
|
||||
}
|
||||
|
||||
export function ensureExt (path) {
|
||||
if (isExternal(path)) {
|
||||
return path
|
||||
}
|
||||
const hashMatch = path.match(hashRE)
|
||||
const hash = hashMatch ? hashMatch[0] : ''
|
||||
const normalized = normalize(path)
|
||||
|
||||
if (endingSlashRE.test(normalized)) {
|
||||
return path
|
||||
}
|
||||
return normalized + '.html' + hash
|
||||
}
|
||||
|
||||
export function isActive (route, path) {
|
||||
const routeHash = route.hash
|
||||
const linkHash = getHash(path)
|
||||
if (linkHash && routeHash !== linkHash) {
|
||||
return false
|
||||
}
|
||||
const routePath = normalize(route.path)
|
||||
const pagePath = normalize(path)
|
||||
return routePath === pagePath
|
||||
}
|
||||
|
||||
export function resolvePage (pages, rawPath, base) {
|
||||
if (rawPath.includes('http')) return {
|
||||
type: 'external-link',
|
||||
path: rawPath
|
||||
}
|
||||
if (base) rawPath = resolvePath(rawPath, base)
|
||||
const path = normalize(rawPath)
|
||||
for (let i = 0; i < pages.length; i++) {
|
||||
if (normalize(pages[i].path) === path) {
|
||||
return Object.assign({}, pages[i], {
|
||||
type: 'page',
|
||||
path: ensureExt(rawPath)
|
||||
})
|
||||
}
|
||||
}
|
||||
console.error(`[vuepress] No matching page found for sidebar item "${rawPath}"`)
|
||||
return {}
|
||||
}
|
||||
|
||||
function resolvePath (relative, base, append) {
|
||||
const firstChar = relative.charAt(0)
|
||||
if (firstChar === '/') {
|
||||
return relative
|
||||
}
|
||||
|
||||
if (firstChar === '?' || firstChar === '#') {
|
||||
return base + relative
|
||||
}
|
||||
|
||||
const stack = base.split('/')
|
||||
|
||||
// remove trailing segment if:
|
||||
// - not appending
|
||||
// - appending to trailing slash (last segment is empty)
|
||||
if (!append || !stack[stack.length - 1]) {
|
||||
stack.pop()
|
||||
}
|
||||
|
||||
// resolve relative path
|
||||
const segments = relative.replace(/^\//, '').split('/')
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
const segment = segments[i]
|
||||
if (segment === '..') {
|
||||
stack.pop()
|
||||
} else if (segment !== '.') {
|
||||
stack.push(segment)
|
||||
}
|
||||
}
|
||||
|
||||
// ensure leading slash
|
||||
if (stack[0] !== '') {
|
||||
stack.unshift('')
|
||||
}
|
||||
|
||||
return stack.join('/')
|
||||
}
|
||||
|
||||
export function resolveSidebarItems (page, route, site, localePath) {
|
||||
const { pages, themeConfig } = site
|
||||
|
||||
const localeConfig = localePath && themeConfig.locales
|
||||
? themeConfig.locales[localePath] || themeConfig
|
||||
: themeConfig
|
||||
|
||||
const pageSidebarConfig = page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar
|
||||
if (pageSidebarConfig === 'auto') {
|
||||
return resolveHeaders(page)
|
||||
}
|
||||
|
||||
const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar
|
||||
if (!sidebarConfig) {
|
||||
return []
|
||||
} else {
|
||||
const { base, config } = resolveMatchingConfig(route, sidebarConfig)
|
||||
return config
|
||||
? config.map(item => resolveItem(item, pages, base))
|
||||
: []
|
||||
}
|
||||
}
|
||||
|
||||
function resolveHeaders (page) {
|
||||
const headers = groupHeaders(page.headers || [])
|
||||
return [{
|
||||
type: 'group',
|
||||
collapsable: false,
|
||||
title: page.title,
|
||||
children: headers.map(h => ({
|
||||
type: 'auto',
|
||||
title: h.title,
|
||||
basePath: page.path,
|
||||
path: page.path + '#' + h.slug,
|
||||
children: h.children || []
|
||||
}))
|
||||
}]
|
||||
}
|
||||
|
||||
export function groupHeaders (headers) {
|
||||
// group h3s under h2
|
||||
headers = headers.map(h => Object.assign({}, h))
|
||||
let lastH2
|
||||
headers.forEach(h => {
|
||||
if (h.level === 2) {
|
||||
lastH2 = h
|
||||
} else if (lastH2) {
|
||||
(lastH2.children || (lastH2.children = [])).push(h)
|
||||
}
|
||||
})
|
||||
return headers.filter(h => h.level === 2)
|
||||
}
|
||||
|
||||
export function resolveNavLinkItem (linkItem) {
|
||||
return Object.assign(linkItem, {
|
||||
type: linkItem.items && linkItem.items.length ? 'links' : 'link'
|
||||
})
|
||||
}
|
||||
|
||||
export function resolveMatchingConfig (route, config) {
|
||||
if (Array.isArray(config)) {
|
||||
return {
|
||||
base: '/',
|
||||
config: config
|
||||
}
|
||||
}
|
||||
for (const base in config) {
|
||||
if (ensureEndingSlash(route.path).indexOf(base) === 0) {
|
||||
return {
|
||||
base,
|
||||
config: config[base]
|
||||
}
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
function ensureEndingSlash (path) {
|
||||
return /(\.html|\/)$/.test(path)
|
||||
? path
|
||||
: path + '/'
|
||||
}
|
||||
|
||||
function resolveItem (item, pages, base, isNested) {
|
||||
if (typeof item === 'string') {
|
||||
return resolvePage(pages, item, base)
|
||||
} else if (Array.isArray(item)) {
|
||||
return Object.assign(resolvePage(pages, item[0], base), {
|
||||
title: item[1]
|
||||
})
|
||||
} else {
|
||||
if (isNested) {
|
||||
console.error(
|
||||
'[vuepress] Nested sidebar groups are not supported. ' +
|
||||
'Consider using navbar + categories instead.'
|
||||
)
|
||||
}
|
||||
const children = item.children || []
|
||||
return {
|
||||
type: 'group',
|
||||
title: item.title,
|
||||
children: children.map(child => resolveItem(child, pages, base, true)),
|
||||
collapsable: item.collapsable !== false
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
|
||||
# Languages
|
||||
|
||||
* [English](en/)
|
||||
|
@ -1,8 +1,11 @@
|
||||
# Strapi [](https://travis-ci.org/wistityhq/strapi) [](http://slack.strapi.io)
|
||||
::: intro
|
||||
# Strapi
|
||||
|
||||
[Website](http://strapi.io/) - [Getting Started](#user-content-getting-started-in-a-minute) - [Documentation](http://strapi.io/documentation/introduction) - [Support](http://strapi.io/support)
|
||||
[](https://travis-ci.org/wistityhq/strapi)
|
||||
[](http://slack.strapi.io)
|
||||
|
||||
Strapi is an open-source Node.js rich framework for building applications and services.
|
||||
:::
|
||||
|
||||
Strapi enables developers to focus on writing reusable application logic instead of spending time
|
||||
building infrastructure. It is designed for building practical, production-ready Node.js applications
|
||||
@ -111,11 +114,10 @@ Strapi comes with a simple but yet powerful dashboard.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Roadmap](ROADMAP.md)
|
||||
- [Contributing guide](CONTRIBUTING.md)
|
||||
- [MIT License](LICENSE.md)
|
||||
- [Contributing guide]((https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md))
|
||||
- [MIT License]((https://github.com/strapi/strapi/blob/master/LICENSE.md))
|
||||
|
||||
## Links
|
||||
|
||||
- [Strapi website](http://strapi.io/)
|
||||
- [Strapi website](https://strapi.io/)
|
||||
- [Strapi news on Twitter](https://twitter.com/strapijs)
|
@ -1,42 +0,0 @@
|
||||
{
|
||||
"title": "Strapi documentation",
|
||||
"author": "Strapi",
|
||||
"gitbook": "3.2.2",
|
||||
"plugins": [
|
||||
"edit-link",
|
||||
"anchorjs",
|
||||
"-search",
|
||||
"-fontsettings",
|
||||
"-sharing",
|
||||
"versions",
|
||||
"ga",
|
||||
"github",
|
||||
"feathers-collapsible-menu"
|
||||
],
|
||||
"pluginsConfig": {
|
||||
"edit-link": {
|
||||
"label": "Edit This Page",
|
||||
"base": "https://github.com/strapi/strapi/tree/master/docs/1.x.x"
|
||||
},
|
||||
"ga": {
|
||||
"token": "UA-54313258-1"
|
||||
},
|
||||
"github": {
|
||||
"url": "https://github.com/strapi/strapi"
|
||||
},
|
||||
"versions": {
|
||||
"options": [
|
||||
{
|
||||
"value": "/documentation/1.x.x/",
|
||||
"text": "Versions 1.x.x",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "/documentation/3.x.x/",
|
||||
"text": "Versions 3.x.x",
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
|
||||
# Languages
|
||||
|
||||
* [English](en/)
|
||||
|
13
docs/3.x.x/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
::: intro
|
||||

|
||||
|
||||
### API creation made simple, secure and fast.
|
||||
The most advanced open-source Content Management Framework to build powerful API with no effort.
|
||||
|
||||
[](https://www.npmjs.org/package/strapi)
|
||||
[](https://www.npmjs.org/package/strapi)
|
||||
[](https://travis-ci.org/strapi/strapi)
|
||||
[](http://slack.strapi.io)
|
||||
[](https://heroku.com/deploy?template=https://github.com/strapi/strapi-heroku-app)<!-- {height=20} -->
|
||||
<!-- {p:.flex.justify-around} -->
|
||||
:::
|
@ -76,7 +76,9 @@ Run `npm start` from the `./admin` folder. That's all.
|
||||
|
||||
You should be able to see the admin at [http://localhost:4000/admin](http://localhost:4000/admin).
|
||||
|
||||
> Note: In development, all the plugins of your app are mounted in the same build as the administration panel.
|
||||
::: note
|
||||
In development, all the plugins of your app are mounted in the same build as the administration panel.
|
||||
:::
|
||||
|
||||
### Colors
|
||||
|
||||
@ -95,7 +97,9 @@ Fonts can also be overridden:
|
||||
|
||||
To change the top-left displayed admin panel's logo, replace the image located at `./admin/admin/src/assets/images/logo-strapi.png`.
|
||||
|
||||
Note: make sure the size of your image is the same as the existing one (434px x 120px).
|
||||
::: note
|
||||
make sure the size of your image is the same as the existing one (434px x 120px).
|
||||
:::
|
||||
|
||||
***
|
||||
|
||||
@ -109,7 +113,11 @@ npm run setup
|
||||
|
||||
This will replace the folder's content located at `./admin/admin/build`. Visit http://localhost:1337/admin/ to make sure your updates have been taken in account.
|
||||
|
||||
After you have built the admininistration you can now create a new project to develop your API with the changes implemented. **Note:** You should now create a project without `--dev`
|
||||
After you have built the admininistration you can now create a new project to develop your API with the changes implemented.
|
||||
|
||||
::: note
|
||||
You should now create a project without `--dev`
|
||||
:::
|
||||
|
||||
***
|
||||
|
||||
@ -178,7 +186,9 @@ It's very common to deploy the front-end and the back-end on different servers.
|
||||
|
||||
The administration URL will be http://yourfrontend.com and every request from the panel will hit the backend at http://yourbackend.com. The plugins will be injected through the `origin` (means the API itself). In other words, the plugins URLs will be `http://yourbackend.com/dashboard/content-manager/main.js`.
|
||||
|
||||
> Note: How it is possible? The API (the Strapi server) owns the plugin and these plugins are exposed through `http://yourbackend.com/admin/**/main.js`
|
||||
::: note
|
||||
How it is possible? The API (the Strapi server) owns the plugin and these plugins are exposed through `http://yourbackend.com/admin/**/main.js`
|
||||
:::
|
||||
|
||||
|
||||
The DOM should look like this:
|
||||
@ -198,7 +208,9 @@ The DOM should look like this:
|
||||
</html>
|
||||
```
|
||||
|
||||
> Note: The plugins are injected using the `./admin/admin/build/config/plugins.json`. To see the plugins URLs in the `index.html`, you need to launch the administration panel in the browser.
|
||||
::: note
|
||||
The plugins are injected using the `./admin/admin/build/config/plugins.json`. To see the plugins URLs in the `index.html`, you need to launch the administration panel in the browser.
|
||||
:::
|
||||
|
||||
#### Deploy the administration panel and the plugins on another server than the API
|
||||
|
||||
@ -275,5 +287,6 @@ The generated `index.html` will look like this:
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
> Note: The plugins are injected using the `./admin/admin/build/config/plugins.json`. To see the plugins URLs in the `index.html`, you need to launch the administration panel in the browser.
|
||||
::: note
|
||||
The plugins are injected using the `./admin/admin/build/config/plugins.json`. To see the plugins URLs in the `index.html`, you need to launch the administration panel in the browser.
|
||||
:::
|
@ -58,7 +58,9 @@ The core of Strapi embraces a small list of middlewares for performances, securi
|
||||
- xframe
|
||||
- xss
|
||||
|
||||
> Note: The following middlewares cannot be disabled: responses, router, logger and boom.
|
||||
::: note
|
||||
The following middlewares cannot be disabled: responses, router, logger and boom.
|
||||
:::
|
||||
|
||||
## Structure
|
||||
|
@ -21,7 +21,9 @@ Here is the list of the collected data and why we need them.
|
||||
|
||||
We are not collecting sensitive data such as databases configurations, environment or custom variables. The data are encrypted and anonymised.
|
||||
|
||||
> GDPR: The collected data are non-sensitive or personal data. We are compliant with the European recommendations (see our [Privacy Policy](https://strapi.io/privacy)).
|
||||
::: warning GDPR
|
||||
The collected data are non-sensitive or personal data. We are compliant with the European recommendations (see our [Privacy Policy](https://strapi.io/privacy)).
|
||||
:::
|
||||
|
||||
## Disable
|
||||
|
@ -35,7 +35,9 @@ Returns the Koa instance.
|
||||
|
||||
Returns a `Promise`. When resolved, it means that the `./config/functions/bootstrap.js` has been executed. Otherwise, it throws an error.
|
||||
|
||||
> Note: You can also access to the bootstrap function through `strapi.config.functions.boostrap`.
|
||||
::: note
|
||||
You can also access to the bootstrap function through `strapi.config.functions.boostrap`.
|
||||
:::
|
||||
|
||||
## strapi.config
|
||||
|
||||
@ -45,7 +47,9 @@ Returns an object that represents the configurations of the project. Every JavaS
|
||||
|
||||
Returns an object of the controllers wich is available in the project. Every JavaScript file located in the `./api/**/controllers` folder will be parsed into the `strapi.controllers` object. Thanks to this object, you can access to every controller's actions everywhere in the project.
|
||||
|
||||
> Note: This object doesn't include the admin's controllers and plugin's controllers.
|
||||
::: note
|
||||
This object doesn't include the admin's controllers and plugin's controllers.
|
||||
:::
|
||||
|
||||
## strapi.hook
|
||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
Before Width: | Height: | Size: 288 KiB After Width: | Height: | Size: 288 KiB |
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 145 KiB |
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
Before Width: | Height: | Size: 875 KiB After Width: | Height: | Size: 875 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 146 KiB |
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 254 KiB |
@ -1,43 +0,0 @@
|
||||
{
|
||||
"title": "Strapi documentation",
|
||||
"author": "Strapi",
|
||||
"gitbook": "3.2.2",
|
||||
"plugins": [
|
||||
"edit-link",
|
||||
"anchorjs",
|
||||
"search",
|
||||
"-fontsettings",
|
||||
"-sharing",
|
||||
"versions",
|
||||
"ga",
|
||||
"github",
|
||||
"feathers-collapsible-menu",
|
||||
"block-align"
|
||||
],
|
||||
"pluginsConfig": {
|
||||
"edit-link": {
|
||||
"label": "Edit This Page",
|
||||
"base": "https://github.com/strapi/strapi/tree/master/docs/3.x.x"
|
||||
},
|
||||
"ga": {
|
||||
"token": "UA-54313258-1"
|
||||
},
|
||||
"github": {
|
||||
"url": "https://github.com/strapi/strapi"
|
||||
},
|
||||
"versions": {
|
||||
"options": [
|
||||
{
|
||||
"value": "/documentation/1.x.x/",
|
||||
"text": "Versions 1.x.x",
|
||||
"selected": false
|
||||
},
|
||||
{
|
||||
"value": "/documentation/3.x.x/",
|
||||
"text": "Versions 3.x.x",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -55,7 +55,9 @@ options: [--tpl <name>|--plugin <name>]
|
||||
|
||||
Example: `strapi generate:api product --tpl bookshelf`
|
||||
|
||||
> Note: The first letter of the filename will be uppercased.
|
||||
::: note
|
||||
The first letter of the filename will be uppercased.
|
||||
:::
|
||||
|
||||
## strapi generate:controller
|
||||
Create a new controller
|
||||
@ -79,7 +81,9 @@ options: [--api <name>|--plugin <name>]
|
||||
- **strapi generate:controller <name> --plugin <plugin>**<br/>
|
||||
Generates an empty controller called **<name>** in the `./plugins/<plugin>/controllers` folder.
|
||||
|
||||
> Note: The first letter of the filename will be uppercased.
|
||||
::: note
|
||||
The first letter of the filename will be uppercased.
|
||||
:::
|
||||
|
||||
## strapi generate:model
|
||||
Create a new model
|
||||
@ -112,7 +116,9 @@ options: [--api <name>|--plugin <name>]
|
||||
- **strapi generate:model <name> --plugin <plugin>**<br/>
|
||||
Generates an empty model called **<name>** in the `./plugins/<plugin>/models` folder.
|
||||
|
||||
> Note: The first letter of the filename will be uppercased.
|
||||
::: note
|
||||
The first letter of the filename will be uppercased.
|
||||
:::
|
||||
|
||||
## strapi generate:service
|
||||
Create a new service
|
||||
@ -136,7 +142,9 @@ options: [--api <name>|--plugin <name>]
|
||||
- **strapi generate:service <name> --plugin <plugin>**<br/>
|
||||
Generates an empty service called **<name>** in the `./plugins/<plugin>/services` folder.
|
||||
|
||||
> Note: The first letter of the filename will be uppercased.
|
||||
::: note
|
||||
The first letter of the filename will be uppercased.
|
||||
:::
|
||||
|
||||
## strapi generate:policy
|
||||
Create a new policy
|
||||
@ -200,7 +208,9 @@ options: [--dev]
|
||||
> Checkout the [CONTRIBUTING guide](https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md) for more details about the local Strapi development workflow.
|
||||
|
||||
|
||||
> **Note: You have to restart the server to load the plugin into your project.**
|
||||
::: warning
|
||||
You have to restart the server to load the plugin into your project.
|
||||
:::
|
||||
|
||||
Please refer to the [plugins documentation](../plugin-development/quick-start.md) to know more.
|
||||
|
@ -63,7 +63,9 @@ By default, your project's structure will look like this:
|
||||
- [`/public`](../concepts/concepts.html#public-assets): contains the file accessible to the outside world.
|
||||
|
||||
|
||||
> Tips: Inside the `/config` folder, every folder will be parsed and injected into the global object `strapi.config`. Let's say, you added a folder named `credentials` with two files `stripe.json` and `paypal.json` into it. The content of these files will be accessible through `strapi.config.credentials.stripe` and `strapi.config.credentials.paypal`.
|
||||
::: note
|
||||
Inside the `/config` folder, every folder will be parsed and injected into the global object `strapi.config`. Let's say, you added a folder named `credentials` with two files `stripe.json` and `paypal.json` into it. The content of these files will be accessible through `strapi.config.credentials.stripe` and `strapi.config.credentials.paypal`.
|
||||
:::
|
||||
|
||||
***
|
||||
|
@ -95,7 +95,9 @@ Here are some use cases:
|
||||
|
||||
CRON tasks allow you to schedule jobs (arbitrary functions) for execution at specific dates, with optional recurrence rules. It only uses a single timer at any given time (rather than reevaluating upcoming jobs every second/minute).
|
||||
|
||||
> Note: Make sure the `enabled` cron config is set to `true` in your environment's variables.
|
||||
::: note
|
||||
Make sure the `enabled` cron config is set to `true` in your environment's variables.
|
||||
:::
|
||||
|
||||
The cron format consists of:
|
||||
|
||||
@ -153,7 +155,9 @@ Each JSON file located in the folder must have the name of its corresponding tra
|
||||
|
||||
Most of the application's configurations are defined by environment. It means that you can specify settings for each environment (`development`, `production`, `test`, etc.).
|
||||
|
||||
> Note: You can access the config of the current environment through `strapi.config.currentEnvironment`.
|
||||
::: note
|
||||
You can access the config of the current environment through `strapi.config.currentEnvironment`.
|
||||
:::
|
||||
|
||||
***
|
||||
|
||||
@ -246,6 +250,33 @@ Most of the application's configurations are defined by environment. It means th
|
||||
|
||||
> Please refer to the [dynamic configurations section](#dynamic-configurations) to use global environment variable to configure the databases.
|
||||
|
||||
#### MLab Example
|
||||
|
||||
**Path —** `./config/environments/**/database.json`.
|
||||
```json
|
||||
{
|
||||
"defaultConnection": "default",
|
||||
"connections": {
|
||||
"default": {
|
||||
"connector": "strapi-hook-mongoose",
|
||||
"settings": {
|
||||
"client": "mongo",
|
||||
"host": "ds123456.mlab.com",
|
||||
"port": 12345,
|
||||
"database": "mlab_db_name",
|
||||
"username": "mlab_user_name",
|
||||
"password": "mlab_pass"
|
||||
},
|
||||
"options": {
|
||||
"authenticationDatabase": "mlab_db_name",
|
||||
"ssl": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> Please note that you must give your MLab database name as the authenticationDatabase and your password can not contain the "@" symbol.
|
||||
|
||||
|
||||
|
||||
@ -270,10 +301,10 @@ Most of the application's configurations are defined by environment. It means th
|
||||
- `parser`
|
||||
- `enabled`(boolean): Enable or disable parser. Default value: `true`.
|
||||
- `multipart` (boolean): Enable or disable multipart bodies parsing. Default value: `true`.
|
||||
- `router`
|
||||
- `prefix` (string): API url prefix (eg. `/v1`).
|
||||
|
||||
> Note: The session doesn't work with `mongo` as a client. The package that we should use is broken for now.
|
||||
::: note
|
||||
The session doesn't work with `mongo` as a client. The package that we should use is broken for now.
|
||||
:::
|
||||
|
||||
***
|
||||
|
||||
@ -413,7 +444,9 @@ In any JSON configurations files in your project, you can inject dynamic values
|
||||
}
|
||||
```
|
||||
|
||||
> Note: You can't execute functions inside the curly braces. Only strings are allowed.
|
||||
::: note
|
||||
You can't execute functions inside the curly braces. Only strings are allowed.
|
||||
:::
|
||||
|
||||
***
|
||||
|
@ -1,41 +0,0 @@
|
||||
{% center %}
|
||||

|
||||
|
||||
### API creation made simple, secure and fast.
|
||||
The most advanced open-source Content Management Framework to build powerful API with no effort.
|
||||
|
||||
<br />
|
||||
[](https://www.npmjs.org/package/strapi)
|
||||
[](https://www.npmjs.org/package/strapi)
|
||||
[](https://travis-ci.org/strapi/strapi)
|
||||
[](http://slack.strapi.io)
|
||||
<a href="https://heroku.com/deploy?template=https://github.com/strapi/strapi-heroku-app">
|
||||
<img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy" height="20px">
|
||||
</a>
|
||||
|
||||
{% endcenter %}
|
||||
|
||||
## v3@alpha.14.2 is available!
|
||||
We've been working on a major update for Strapi during the past months, rewriting the core framework and the dashboard.
|
||||
|
||||
This documentation is only related to Strapi v3@alpha.14.2 ([v1 documentation is still available](http://strapi.io/documentation/1.x.x)).
|
||||
|
||||
**[Get Started](getting-started/installation.md)**<br />
|
||||
Learn how to install Strapi and start developing your API.
|
||||
|
||||
**[Command Line Interface](cli/CLI.md)**<br />
|
||||
Get to know our CLI to make features the hacker way!
|
||||
|
||||
**[Concepts](concepts/concepts.md)**<br />
|
||||
Get to know more about Strapi and how it works.
|
||||
|
||||
**[Guides](configurations/configurations.md)**<br />
|
||||
Get familiar with Strapi. Discover concrete examples on how to develop the features you need.
|
||||
|
||||
**[Plugin Development](plugin-development/quick-start.md)**<br />
|
||||
Understand how to develop your own plugin.
|
||||
|
||||
**[API Reference](api-reference/reference.md)**<br />
|
||||
Learn about Strapi's API, the `strapi` object that is available in your backend.
|
||||
|
||||
**[Migration guide](migration/migration-guide.md)**<br />
|
@ -1,39 +0,0 @@
|
||||
# Installation
|
||||
|
||||
Installation is very easy and only takes a few seconds.
|
||||
|
||||
## Requirements
|
||||
|
||||
Please make sure your computer/server meets the following requirements:
|
||||
- [Node.js](https://nodejs.org) >= 9: Node.js is a server platform which runs JavaScript. Installation guide [here](https://nodejs.org/en/download/).
|
||||
- [MongoDB](https://www.mongodb.com/) >= 2.6: MongoDB is a powerful document store. Installation guide [here](https://www.mongodb.com/download-center?j#community).
|
||||
|
||||
## Setup
|
||||
|
||||
Time to install Strapi!
|
||||
|
||||
```bash
|
||||
npm install strapi@alpha -g
|
||||
```
|
||||
|
||||
Note: if you encounter npm permissions issues, [change the permissions to npm default directory](https://docs.npmjs.com/getting-started/fixing-npm-permissions#option-1-change-the-permission-to-npms-default-directory).
|
||||
|
||||
It takes about 20 seconds with a good Internet connection. You can take a coffee ☕️ if you have a slow one.
|
||||
|
||||
Having troubles during the installation? Check if someone already had the same issue https://github.com/strapi/strapi/issues. If not, you can [post one](https://github.com/strapi/strapi/issues/new), or ask for help https://strapi.io/support.
|
||||
|
||||
## Check installation
|
||||
|
||||
Once completed, please check that the installation went well, by running:
|
||||
|
||||
```bash
|
||||
strapi -v
|
||||
```
|
||||
|
||||
That should print `3.0.0-alpha.x`.
|
||||
|
||||
Strapi is installed globally on your computer. Type `strapi` in your terminal you will have access to every available command lines.
|
||||
|
||||
***
|
||||
|
||||
Congrats! Now that Strapi is installed you can [create your first API](quick-start.md).
|
@ -1,25 +0,0 @@
|
||||
li.header{
|
||||
color: #101622 !important;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.07rem;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
}
|
||||
|
||||
li.chapter > a{
|
||||
transition: background-color 0.15s ease;
|
||||
-webkit-transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
li.chapter:hover > a{
|
||||
background: #eee !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.versions-select select{
|
||||
height: 36px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.book-summary{
|
||||
background-color: #f7f7f7 !important;
|
||||
}
|
46
docs/3.x.x/getting-started/installation.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Installation
|
||||
|
||||
Installation is very easy and only takes a few seconds.
|
||||
|
||||
## Requirements
|
||||
|
||||
Please make sure your computer/server meets the following requirements:
|
||||
- [Node.js](https://nodejs.org) >= 10.x: Node.js is a server platform which runs JavaScript. Installation guide [here](https://nodejs.org/en/download/).
|
||||
- [NPM](https://www.npmjs.com/) >= 6.x: NPM is the package manager for Javascript. Installation guide [here](https://nodejs.org/en/download/).
|
||||
*(Make sure the database that you are using meets the requirement.)*
|
||||
- [MongoDB](https://www.mongodb.com/) >= 3.x: MongoDB is a powerful document store. Installation guide [here](https://www.mongodb.com/download-center?j#community).
|
||||
- [MySQL](https://www.mysql.com/) >= 5.6: MySQL is an open-source relational database management system. Installation guide [here](https://dev.mysql.com/downloads/).
|
||||
- [MariaDB](https://mariadb.org/) >= 10.1: MarialDB is a fork of MySQL and is guaranteed to stay open source. Installation guide [here](https://mariadb.org/download/).
|
||||
- [PostgreSQL](https://www.postgresql.org/) >= 10: PostgreSQL is an open-source object-relational database management system. Installation guide [here](https://www.postgresql.org/download/).
|
||||
|
||||
## Setup
|
||||
|
||||
Time to install Strapi!
|
||||
|
||||
```bash
|
||||
npm install strapi@alpha -g
|
||||
```
|
||||
|
||||
::: note
|
||||
If you encounter npm permissions issues, [change the permissions to npm default directory](https://docs.npmjs.com/getting-started/fixing-npm-permissions#option-1-change-the-permission-to-npms-default-directory).
|
||||
:::
|
||||
|
||||
It takes about 20 seconds with a good Internet connection. You can take a coffee ☕️ if you have a slow one.
|
||||
|
||||
Having troubles during the installation? Check if someone already had the same issue https://github.com/strapi/strapi/issues. If not, you can [post one](https://github.com/strapi/strapi/issues/new), or ask for help https://strapi.io/support.
|
||||
|
||||
## Check installation
|
||||
|
||||
Once completed, please check that the installation went well, by running:
|
||||
|
||||
```bash
|
||||
strapi -v
|
||||
```
|
||||
|
||||
That should print `3.0.0-alpha.x`.
|
||||
|
||||
Strapi is installed globally on your computer. Type `strapi` in your terminal you will have access to every available command lines.
|
||||
|
||||
***
|
||||
|
||||
Congrats! Now that Strapi is installed you can [create your first API](quick-start.md).
|
@ -3,21 +3,18 @@
|
||||
This section explains how to handle Strapi for the first time, ([check out our tutorial video](https://www.youtube.com/watch?v=yMl5IcFHA74)).
|
||||
|
||||
**Table of contents:**
|
||||
- [Create your first project](#create-your-first-project)
|
||||
- [Create your first API](#create-your-first-api)
|
||||
- [Content Type Builder plugin](#content-type-builder-plugin)
|
||||
- [Files structure](#files-structure)
|
||||
- [Manage your data](#manage-your-data)
|
||||
- [Content Manager Plugin](#content-manager-plugin)
|
||||
- [Create a product](#create-a-product)
|
||||
- [Edit a product](#edit-a-product)
|
||||
- [Delete a product](#delete-a-product)
|
||||
- [Consume your API](#consume-your-api)
|
||||
- [List entries](#list-entries)
|
||||
- [Get a specific entry](#get-a-specific-entry)
|
||||
- [Create data (POST)](#create-data-post)
|
||||
- [Update data (PUT)](#update-data-put)
|
||||
- [Delete data (DELETE)](#delete-data-delete)
|
||||
- [Quick start](#quick-start)
|
||||
- [Create your first project](#create-your-first-project)
|
||||
- [Create your first user](#create-your-first-user)
|
||||
- [Create your first API](#create-your-first-api)
|
||||
- [Files structure](#files-structure)
|
||||
- [Manage your data](#manage-your-data)
|
||||
- [Consume your API](#consume-your-api)
|
||||
- [List entries (GET)](#list-entries-get)
|
||||
- [Get a specific entry (GET)](#get-a-specific-entry-get)
|
||||
- [Create data (POST)](#create-data-post)
|
||||
- [Update data (PUT)](#update-data-put)
|
||||
- [Delete data (DELETE)](#delete-data-delete)
|
||||
|
||||
***
|
||||
|
||||
@ -92,7 +89,9 @@ At this point, your application is empty. To create your first API is to use the
|
||||
**#4 —** Save. That's it!
|
||||
|
||||
|
||||
> Note: See the [CLI documentation](../cli/CLI.md#strapi-generateapi) for more information on how to do it the hacker way.
|
||||
::: note
|
||||
See the [CLI documentation](../cli/CLI.md#strapi-generateapi) for more information on how to do it the hacker way.
|
||||
:::
|
||||
|
||||
### Files structure
|
||||
|
@ -1,6 +1,8 @@
|
||||
# Authentication
|
||||
|
||||
> ⚠️ This feature requires the Users & Permissions plugin (installed by default).
|
||||
::: warning
|
||||
This feature requires the Users & Permissions plugin (installed by default).
|
||||
:::
|
||||
|
||||
## Register a new user
|
||||
|
@ -39,4 +39,6 @@ module.exports = {
|
||||
};
|
||||
```
|
||||
|
||||
> Note: A route handler can only access the controllers defined in the `./api/**/controllers` folders.
|
||||
::: note
|
||||
A route handler can only access the controllers defined in the `./api/**/controllers` folders.
|
||||
:::
|
@ -1,6 +1,6 @@
|
||||
# Deployment
|
||||
|
||||
#### #1 - Configurate
|
||||
#### #1 - Configure
|
||||
|
||||
Update the `production` settings with the IP and domain name where the project will be running.
|
||||
|
||||
@ -32,7 +32,9 @@ cd /path/to/the/project
|
||||
npm run setup
|
||||
```
|
||||
|
||||
> Note: To display the build logs use the --debug option `npm run setup --debug`.
|
||||
::: note
|
||||
To display the build logs use the --debug option `npm run setup --debug`.
|
||||
:::
|
||||
|
||||
#### #3 - Launch the server
|
||||
|
||||
@ -42,7 +44,9 @@ Run the server with the `production` settings.
|
||||
NODE_ENV=production npm start
|
||||
```
|
||||
|
||||
> We highly recommend to use [pm2](https://github.com/Unitech/pm2/) to manage your process.
|
||||
::: warning
|
||||
We highly recommend to use [pm2](https://github.com/Unitech/pm2/) to manage your process.
|
||||
:::
|
||||
|
||||
### Advanced configurations
|
||||
|
@ -1,6 +1,8 @@
|
||||
# Email
|
||||
|
||||
> ⚠️ This feature requires the Email plugin (installed by default).
|
||||
::: warning
|
||||
This feature requires the Email plugin (installed by default).
|
||||
:::
|
||||
|
||||
Thanks to the plugin `Email`, you can send email on your server or externals providers such as Sendgrid.
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
See the [filters' concepts](../concepts/concepts.md#filters) for details.
|
||||
|
||||
> Note: by default, the filters can only be used from `find` endpoints generated by the Content Type Builder and the [CLI](../cli/CLI.md). If you need to implement a filters system somewhere else, read the [programmatic usage](#programmatic-usage) section.
|
||||
::: note
|
||||
by default, the filters can only be used from `find` endpoints generated by the Content Type Builder and the [CLI](../cli/CLI.md). If you need to implement a filters system somewhere else, read the [programmatic usage](#programmatic-usage) section.
|
||||
:::
|
||||
|
||||
## Available operators
|
||||
|
||||
@ -74,7 +76,9 @@ Requests system can be implemented in custom code sections.
|
||||
|
||||
To extract the filters from an JavaScript object or a request, you need to call the [`strapi.utils.models.convertParams` helper](../api-reference/reference.md#strapiutils).
|
||||
|
||||
> Note: The returned objects is formatted according to the ORM used by the model.
|
||||
::: note
|
||||
The returned objects is formatted according to the ORM used by the model.
|
||||
:::
|
||||
|
||||
#### Example
|
||||
|