├── .babelrc
├── .env
├── .eslintrc.js
├── .gitignore
├── README.md
├── afterBody.php
├── cli
├── scripts
│ └── componentGenerator.js
└── templates
│ └── component
│ ├── Component.component.html
│ ├── Component.component.js
│ ├── Component.component.scss
│ ├── Component.component.vue
│ └── package.json
├── dist
├── bundle.js
└── f5e09925eb0dc2c19ad0fc0c47e38e66.svg
├── functions.php
├── index.php
├── lib
├── alt-admin
│ ├── README.md
│ ├── alt-admin.php
│ ├── css
│ │ ├── admin-color-scheme.css
│ │ ├── admin-footer.css
│ │ └── admin-login.css
│ └── images
│ │ ├── admin-logo.png
│ │ └── altdesign.svg
├── endpoints
│ ├── addonEndpoints.php
│ └── themeEndpoints.php
├── helpers.php
└── setup.php
├── package.json
├── postcss.config.js
├── screenshot.jpg
├── server.js
├── src
├── assets
│ └── scss
│ │ ├── imports
│ │ ├── _components.scss
│ │ ├── _extends.scss
│ │ ├── _fonts.scss
│ │ ├── _forms.scss
│ │ ├── _global.scss
│ │ ├── _mixins.scss
│ │ ├── _modifiers.scss
│ │ └── _variables.scss
│ │ └── main.scss
├── components
│ ├── App
│ │ ├── App.component.html
│ │ ├── App.component.js
│ │ ├── App.component.scss
│ │ ├── App.component.vue
│ │ └── package.json
│ ├── Breadcrumbs
│ │ ├── Breadcrumbs.component.html
│ │ ├── Breadcrumbs.component.js
│ │ ├── Breadcrumbs.component.scss
│ │ ├── Breadcrumbs.component.vue
│ │ └── package.json
│ ├── Footer
│ │ ├── Footer.component.html
│ │ ├── Footer.component.js
│ │ ├── Footer.component.scss
│ │ ├── Footer.component.vue
│ │ └── package.json
│ ├── Header
│ │ ├── Header.component.html
│ │ ├── Header.component.js
│ │ ├── Header.component.scss
│ │ ├── Header.component.vue
│ │ └── package.json
│ ├── Menus
│ │ ├── AppMenu
│ │ │ ├── AppMenu.component.html
│ │ │ ├── AppMenu.component.js
│ │ │ ├── AppMenu.component.vue
│ │ │ └── package.json
│ │ └── AppMenuItem
│ │ │ ├── AppMenuItem.component.html
│ │ │ ├── AppMenuItem.component.js
│ │ │ ├── AppMenuItem.component.vue
│ │ │ └── package.json
│ ├── PageBuilder
│ │ ├── PageBuilder.component.html
│ │ ├── PageBuilder.component.js
│ │ ├── PageBuilder.component.scss
│ │ ├── PageBuilder.component.vue
│ │ └── package.json
│ └── Views
│ │ └── Page
│ │ ├── Page.component.html
│ │ ├── Page.component.js
│ │ ├── Page.component.scss
│ │ ├── Page.component.vue
│ │ └── package.json
├── imports
│ ├── components.js
│ ├── functions.js
│ └── ready.js
├── main.js
├── router
│ ├── router.js
│ └── routes.js
└── vuex
│ ├── actions.js
│ ├── getters.js
│ ├── mutations.js
│ ├── state.js
│ └── store.js
├── static
└── img
│ └── logo.svg
├── style.css
├── webpack.config.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "transform-decorators-legacy",
4 | "transform-runtime"
5 | ],
6 | "presets": [
7 | [
8 | "es2015",
9 | {
10 | "modules": false
11 | }
12 | ],
13 | "stage-0"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | ENTRY=main.js
2 | OUTPUT=bundle.js
3 | DEV_URL=http://theme.dev/
4 | PORT=8080
5 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'extends': 'standard',
3 | 'plugins': [
4 | 'standard',
5 | 'promise',
6 | 'html'
7 | ],
8 | 'env': {
9 | 'browser': true,
10 | 'es6': true,
11 | 'node': true,
12 | 'mocha': true
13 | },
14 | 'rules': {
15 | 'no-new': 'off'
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .idea
3 | webpackTemp.html
4 | *~
5 | *.swp
6 | *.swo
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WordPress Single Page Application Theme Boilerplate
2 | ##### A JavaScript SPA WordPress theme boilerplate using Vue and the WordPress REST API.
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | ## Install
13 | Install this theme just as you would install any modern theme boilerplate:
14 |
15 | 1. Navigate to your themes folder (usually /wp-content/themes/):
16 |
17 | cd /wp-content/themes/
18 |
19 | 2. Clone this repo:
20 |
21 | git clone https://github.com/alt-design/wp-spa-boilerplate NAME_OF_DIRECTORY
22 |
23 | 3. Enter the new folder (NAME_OF_DIRECTORY):
24 |
25 | cd NAME_OF_DIRECTORY
26 |
27 | 4. Install dependencies via npm or yarn:
28 |
29 | npm install
30 | or
31 |
32 | yarn
33 |
34 | ## Running and Building
35 |
36 | npm run dev // starts a hot reload dev server (make sure to update your development URL in the .env file before running this)
37 | npm run watch // watches and compiles files to the dist folder, no hot reload though
38 | npm run build // compiles to the dist folder
39 | npm run production // optimises and compresses before compiling to the dist folder (this will disable Vue Devtools)
40 |
41 | ## VueX and Data Structure
42 | All of your pages data is kept in a VueX store, by default the store will look like this:
43 |
44 | {
45 | adminUrl:"http://theme.dev/wp/wp-admin/"
46 | global:false // Global ACF Fields (Options Pages)
47 | theme:"http://theme.dev/app/themes/vue-theme"
48 | name:"Test"
49 | post:Object
50 | post_name:"sample-page"
51 | ID:2
52 | acf:false // ACF Fields
53 | breadcrumbs:Array[2]
54 | 0:Object
55 | permalink:"http://theme.dev/"
56 | title:"Home"
57 | 1:Object
58 | permalink:"http://theme.dev/sample-page/"
59 | title:"Sample Page"
60 | featured_image:null
61 | post_content:"This is an example page..."
62 | post_date:"2017-03-24 12:26:39"
63 | post_excerpt:""
64 | post_parent:0
65 | post_title:"Sample Page"
66 | post_type:"page"
67 | url:"http://theme.dev"
68 | }
69 |
70 | ## Components
71 |
72 | ### Menu
73 | The menu component is used to retrieve & display WordPress menus:
74 |
75 |
76 |
77 | ##### Props
78 |
79 | - location : string (required) - the menu's registered theme location
80 | - emitOnComplete : string (optional) - an optional Vue.$emit to be called after the menu is loaded
81 |
82 |
83 | ### Breadcrumbs
84 | The breadcrumb component is used to retrieve & display breadcrumbs for the current path:
85 |
86 |
87 |
--------------------------------------------------------------------------------
/afterBody.php:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/cli/scripts/componentGenerator.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const _ = require('lodash')
3 |
4 | const ComponentGenerator = {
5 |
6 | buildItems (args) {
7 | let returnItems = args.splice(2, Infinity)
8 |
9 | for (let [index, currentItem] of returnItems.entries()) {
10 | // Makes array from file path, e.g. '1/2/3' -> [1,2,3]
11 | let itemSplit = currentItem.split('/')
12 |
13 | returnItems[index] = {
14 | // Adds the last array item of itemSplit (the name of the component to be generated)
15 | 'name': itemSplit[itemSplit.length - 1],
16 |
17 | // Adds all the itemSplit array items which are not last (the parent directory/ies)
18 | 'path': './src/components/' + itemSplit.join('/') || ''
19 | }
20 |
21 | // Creates all of the folders
22 | this.generateDirectories(itemSplit)
23 | }
24 |
25 | return returnItems // Returns the modified array of items and their parent directories
26 | },
27 |
28 | generateDirectories (items) {
29 | let tempDir = './src/components'
30 | for (let folder of items) this.createDir(tempDir += '/' + folder)
31 | console.log(`> Component Directory Generated @ ${tempDir}`)
32 | },
33 |
34 | createDir (newDir = false) {
35 | if (!fs.existsSync(newDir)) fs.mkdirSync(newDir, '0766')
36 | },
37 |
38 | createComponents (names) {
39 | for (let name of names) {
40 | this.copyTemplates(name['path'], name['name'])
41 | }
42 | },
43 |
44 | copyTemplates (dir, name) {
45 | console.log('-------------------------')
46 | console.log('Creating Files')
47 | console.log('-------------------------')
48 |
49 | const fileTypes = ['.component.vue', '.component.js', '.component.html', '.component.scss']
50 |
51 | for (let file of fileTypes) {
52 | this.copySyncFile(`./CLI/Templates/Component/Component${file}`, `${dir}/${name}${file}`)
53 | this.replaceText(`${dir}/${name}${file}`, name)
54 |
55 | console.log(`> File "${dir}/${name}${file}" created`)
56 | }
57 |
58 | this.copySyncFile(`./CLI/Templates/Component/package.json`, `${dir}/package.json`)
59 | this.replaceText(`${dir}/package.json`, name)
60 | console.log(`> File "${dir}/package.json" created`)
61 | },
62 |
63 | replaceText (file, name) {
64 | fs.readFile(file, 'utf8', (err, data) => {
65 | if (err) return console.log(err)
66 | let result = data
67 | .replace(/ComponentName/g, name)
68 | .replace(/component-name-kebab/g, _.kebabCase(name))
69 |
70 | fs.writeFile(file, result, 'utf8', function (err) {
71 | if (err) return console.log(err)
72 | })
73 | })
74 | },
75 |
76 | copySyncFile (src, dest) {
77 | fs.writeFileSync(dest, fs.readFileSync(src))
78 | },
79 |
80 | init () {
81 | console.log('> Component Generator Initiated')
82 | this.createComponents(this.buildItems(process.argv))
83 | }
84 |
85 | }
86 |
87 | ComponentGenerator.init()
88 |
--------------------------------------------------------------------------------
/cli/templates/component/Component.component.html:
--------------------------------------------------------------------------------
1 |
2 | This is the {{message}} component
3 |
4 |
--------------------------------------------------------------------------------
/cli/templates/component/Component.component.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ComponentName
3 | *
4 | * @template : ./ComponentName.component.html
5 | * @style : ./ComponentName.component.scss
6 | */
7 |
8 | export default {
9 | data () {
10 | return {
11 | message: 'ComponentName'
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/cli/templates/component/Component.component.scss:
--------------------------------------------------------------------------------
1 | .component-name-kebab {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/cli/templates/component/Component.component.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/cli/templates/component/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "component-name-kebab",
3 | "version": "1.0.0",
4 | "main": "ComponentName.component.vue"
5 | }
6 |
--------------------------------------------------------------------------------
/dist/f5e09925eb0dc2c19ad0fc0c47e38e66.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/functions.php:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 | >
13 |
14 |
15 |
17 |
18 |
19 |
20 |
21 | >
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | base, array('profile', 'profile-network')) ) {
91 | $wp_styles->registered['colors']->deps[] = 'colors-fresh';
92 | }
93 | }
94 |
95 | /**
96 | * Single column dashboard
97 | */
98 | function altScreenLayoutColumns($columns)
99 | {
100 | $columns['dashboard'] = 1;
101 | return $columns;
102 | }
103 |
104 | function altScreenLayoutDashboard()
105 | {
106 | return 1;
107 | }
108 |
109 | /**
110 | * Remove WordPress logo from admin bar
111 | */
112 | function altRemoveWpLogo()
113 | {
114 | global $wp_admin_bar;
115 | $wp_admin_bar->remove_menu('wp-logo');
116 | }
117 |
118 | /**
119 | * Replace 'Howdy' message.
120 | */
121 | function altReplaceHowdy($wp_admin_bar)
122 | {
123 | $my_account = $wp_admin_bar->get_node('my-account');
124 | $newtitle = str_replace('How are you,', 'Ay'up', $my_account->title);
125 | $wp_admin_bar->add_node(array(
126 | 'id' => 'my-account',
127 | 'title' => $newtitle,
128 | ));
129 | }
130 |
131 | /**
132 | * Custom admin footer
133 | */
134 | function altCustomAdminFooter()
135 | {
136 | echo '' . get_bloginfo('name') . ', a site by Alt';
137 | }
138 |
139 | /**
140 | * Custom admin footer version
141 | */
142 | function altChangeFooterVersion()
143 | {
144 | return '';
145 | }
146 |
147 | }
148 |
149 | new AltAdminBranding();
--------------------------------------------------------------------------------
/lib/alt-admin/css/admin-color-scheme.css:
--------------------------------------------------------------------------------
1 | html {
2 | background: #f3f3f3;
3 | }
4 |
5 | /****************************** LINKS ******************************/
6 | a {
7 | color: #0074a2;
8 | }
9 | a:hover, a:active, a:focus {
10 | color: #0099d5;
11 | }
12 | #rightnow a:hover,
13 | #media-upload a.del-link:hover,
14 | div.dashboard-widget-submit input:hover,
15 | .subsubsub a:hover,
16 | .subsubsub a.current:hover,
17 | .ui-tabs-nav a:hover {
18 | color: #0099d5;
19 | }
20 |
21 | /****************************** FORMS ******************************/
22 | input[type=checkbox]:checked:before {
23 | color: #FF6600;
24 | }
25 | input[type=radio]:checked:before {
26 | background: #FF6600;
27 | }
28 | .wp-core-ui input[type="reset"]:hover,
29 | .wp-core-ui input[type="reset"]:active {
30 | color: #0099d5;
31 | }
32 |
33 | /*************************** LIST TABLES ***************************/
34 | .wp-core-ui {
35 | }
36 | .wp-core-ui .button-primary {
37 | background: #FF6600;
38 | border-color: #cc5200;
39 | color: white;
40 | -webkit-box-shadow: inset 0 1px 0 #ff944d, 0 1px 0 rgba(0, 0, 0, 0.15);
41 | box-shadow: inset 0 1px 0 #ff944d, 0 1px 0 rgba(0, 0, 0, 0.15);
42 | }
43 | .wp-core-ui .button-primary:hover,
44 | .wp-core-ui .button-primary:focus {
45 | background: #d65600;
46 | border-color: #b34700;
47 | color: white;
48 | -webkit-box-shadow: inset 0 1px 0 #ff8533, 0 1px 0 rgba(0, 0, 0, 0.15);
49 | box-shadow: inset 0 1px 0 #ff8533, 0 1px 0 rgba(0, 0, 0, 0.15);
50 | }
51 | .wp-core-ui .button-primary:active {
52 | background: #d65600;
53 | border-color: #b34700;
54 | color: white;
55 | -webkit-box-shadow: inset 0 2px 5px -3px rgba(0, 0, 0, 0.5);
56 | box-shadow: inset 0 2px 5px -3px rgba(0, 0, 0, 0.5);
57 | }
58 | .wp-core-ui .button-primary[disabled],
59 | .wp-core-ui .button-primary:disabled,
60 | .wp-core-ui .button-primary.button-primary-disabled {
61 | color: #d1cbc7 !important;
62 | background: #d65600 !important;
63 | border-color: #b34700 !important;
64 | text-shadow: none !important;
65 | }
66 | .wp-core-ui .wp-ui-primary {
67 | color: #FFFFFF;
68 | background-color: #363b3f;
69 | }
70 | .wp-core-ui .wp-ui-text-primary {
71 | color: #363b3f;
72 | }
73 | .wp-core-ui .wp-ui-highlight {
74 | color: #FFFFFF;
75 | background-color: #FF6600;
76 | }
77 | .wp-core-ui .wp-ui-text-highlight {
78 | color: #FF6600;
79 | }
80 | .wp-core-ui .wp-ui-notification {
81 | color: #FFFFFF;
82 | background-color: #25282b;
83 | }
84 | .wp-core-ui .wp-ui-text-notification {
85 | color: #25282b;
86 | }
87 | .wp-core-ui .wp-ui-text-icon {
88 | color: #f1f2f3;
89 | }
90 | .wrap .add-new-h2:hover,
91 | #add-new-comment a:hover,
92 | .tablenav .tablenav-pages a:hover,
93 | .tablenav .tablenav-pages a:focus {
94 | color: #FFFFFF;
95 | background-color: #363b3f;
96 | }
97 | .view-switch a.current:before {
98 | color: #363b3f;
99 | }
100 | .view-switch a:hover:before {
101 | color: #25282b;
102 | }
103 | .post-com-count:hover:after {
104 | border-top-color: #363b3f;
105 | }
106 | .post-com-count:hover span {
107 | color: #FFFFFF;
108 | background-color: #363b3f;
109 | }
110 | strong .post-com-count:after {
111 | border-top-color: #25282b;
112 | }
113 | strong .post-com-count span {
114 | background-color: #25282b;
115 | }
116 |
117 | /*************************** ADMIN MENU ****************************/
118 | #adminmenuback,
119 | #adminmenuwrap,
120 | #adminmenu {
121 | background: #363b3f;
122 | }
123 | #adminmenu a {
124 | color: #FFFFFF;
125 | }
126 | #adminmenu div.wp-menu-image:before {
127 | color: #f1f2f3;
128 | }
129 | #adminmenu a:hover,
130 | #adminmenu li.menu-top:hover,
131 | #adminmenu li.opensub > a.menu-top,
132 | #adminmenu li > a.menu-top:focus {
133 | color: #FFFFFF;
134 | background-color: #FF6600;
135 | }
136 | #adminmenu li.menu-top:hover div.wp-menu-image:before,
137 | #adminmenu li.opensub > a.menu-top div.wp-menu-image:before {
138 | color: #FFFFFF;
139 | }
140 | .about-wrap h2 .nav-tab-active,
141 | .nav-tab-active,
142 | .nav-tab-active:hover {
143 | border-bottom-color: #f3f3f3;
144 | }
145 |
146 | /********************** ADMIN MENU (SUB MENU) **********************/
147 | #adminmenu .wp-submenu,
148 | #adminmenu .wp-has-current-submenu .wp-submenu,
149 | #adminmenu .wp-has-current-submenu.opensub .wp-submenu,
150 | .folded #adminmenu .wp-has-current-submenu .wp-submenu,
151 | #adminmenu a.wp-has-current-submenu:focus + .wp-submenu {
152 | background: #26292c;
153 | }
154 | #adminmenu li.wp-has-submenu.wp-not-current-submenu.opensub:hover:after {
155 | border-right-color: #26292c;
156 | }
157 | #adminmenu .wp-submenu .wp-submenu-head {
158 | color: #c3c4c5;
159 | }
160 | #adminmenu .wp-submenu a,
161 | #adminmenu .wp-has-current-submenu .wp-submenu a,
162 | .folded #adminmenu .wp-has-current-submenu .wp-submenu a,
163 | #adminmenu a.wp-has-current-submenu:focus + .wp-submenu a,
164 | #adminmenu .wp-has-current-submenu.opensub .wp-submenu a {
165 | color: #c3c4c5;
166 | }
167 |
168 | /******************** ADMIN MENU (CURRENT MENU) ********************/
169 | #adminmenu .wp-submenu a:focus,
170 | #adminmenu .wp-has-current-submenu .wp-submenu a:focus,
171 | .folded #adminmenu .wp-has-current-submenu .wp-submenu a:focus,
172 | #adminmenu a.wp-has-current-submenu:focus + .wp-submenu a:focus,
173 | #adminmenu .wp-has-current-submenu.opensub .wp-submenu a:focus,
174 | #adminmenu .wp-submenu a:hover,
175 | #adminmenu .wp-has-current-submenu .wp-submenu a:hover,
176 | .folded #adminmenu .wp-has-current-submenu .wp-submenu a:hover,
177 | #adminmenu a.wp-has-current-submenu:focus + .wp-submenu a:hover,
178 | #adminmenu .wp-has-current-submenu.opensub .wp-submenu a:hover {
179 | color: #FF6600;
180 | }
181 | #adminmenu .wp-submenu li.current a,
182 | #adminmenu a.wp-has-current-submenu:focus + .wp-submenu li.current a,
183 | #adminmenu .wp-has-current-submenu.opensub .wp-submenu li.current a {
184 | color: #FFFFFF;
185 | }
186 | #adminmenu .wp-submenu li.current a:hover,
187 | #adminmenu a.wp-has-current-submenu:focus + .wp-submenu li.current a:hover,
188 | #adminmenu .wp-has-current-submenu.opensub .wp-submenu li.current a:hover,
189 | #adminmenu .wp-submenu li.current a:focus,
190 | #adminmenu a.wp-has-current-submenu:focus + .wp-submenu li.current a:focus,
191 | #adminmenu .wp-has-current-submenu.opensub .wp-submenu li.current a:focus {
192 | color: #FF6600;
193 | }
194 | ul#adminmenu a.wp-has-current-submenu:after,
195 | ul#adminmenu > li.current > a.current:after {
196 | border-right-color: #f3f3f3;
197 | }
198 | #adminmenu li.current a.menu-top,
199 | #adminmenu li.wp-has-current-submenu a.wp-has-current-submenu,
200 | #adminmenu li.wp-has-current-submenu .wp-submenu .wp-submenu-head,
201 | .folded #adminmenu li.current.menu-top {
202 | color: #FFFFFF;
203 | background: #FF6600;
204 | }
205 | #adminmenu li.wp-has-current-submenu div.wp-menu-image:before {
206 | color: #FFFFFF;
207 | }
208 | #adminmenu .awaiting-mod,
209 | #adminmenu .update-plugins {
210 | color: #FFFFFF;
211 | background: #25282b;
212 | }
213 | #adminmenu li.current a .awaiting-mod,
214 | #adminmenu li a.wp-has-current-submenu .update-plugins,
215 | #adminmenu li:hover a .awaiting-mod,
216 | #adminmenu li.menu-top:hover > a .update-plugins {
217 | color: #FFFFFF;
218 | background: #26292c;
219 | }
220 |
221 | /******************** ADMIN MENU (COLLAPSE MENU) *******************/
222 | #collapse-menu {
223 | color: #f1f2f3;
224 | }
225 | #collapse-menu:hover {
226 | color: #FFFFFF;
227 | }
228 | #collapse-button div:after {
229 | color: #f1f2f3;
230 | }
231 | #collapse-menu:hover #collapse-button div:after {
232 | color: #FFFFFF;
233 | }
234 |
235 | /**************************** ADMIN BAR ****************************/
236 | #wpadminbar {
237 | color: #FFFFFF;
238 | background: #FF6600;
239 | }
240 | #wpadminbar .ab-item,
241 | #wpadminbar a.ab-item,
242 | #wpadminbar > #wp-toolbar span.ab-label,
243 | #wpadminbar > #wp-toolbar span.noticon {
244 | color: #FFFFFF;
245 | }
246 | #wpadminbar .ab-icon,
247 | #wpadminbar .ab-icon:before,
248 | #wpadminbar .ab-item:before,
249 | #wpadminbar .ab-item:after {
250 | color: #f1f2f3;
251 | }
252 | #wpadminbar .ab-top-menu > li:hover > .ab-item,
253 | #wpadminbar .ab-top-menu > li.hover > .ab-item,
254 | #wpadminbar .ab-top-menu > li > .ab-item:focus,
255 | #wpadminbar.nojq .quicklinks .ab-top-menu > li > .ab-item:focus,
256 | #wpadminbar-nojs .ab-top-menu > li.menupop:hover > .ab-item,
257 | #wpadminbar .ab-top-menu > li.menupop.hover > .ab-item {
258 | color: #FFFFFF;
259 | background: #26292c;
260 | }
261 | #wpadminbar > #wp-toolbar li:hover span.ab-label,
262 | #wpadminbar > #wp-toolbar li.hover span.ab-label,
263 | #wpadminbar > #wp-toolbar a:focus span.ab-label {
264 | color: #FFFFFF;
265 | }
266 | #wpadminbar li:hover .ab-icon:before,
267 | #wpadminbar li:hover .ab-item:before,
268 | #wpadminbar li:hover .ab-item:after,
269 | #wpadminbar li:hover #adminbarsearch:before {
270 | color: #FFFFFF;
271 | }
272 |
273 | /*********************** ADMIN BAR (SUBMENU) ***********************/
274 | #wpadminbar .menupop .ab-sub-wrapper {
275 | background: #26292c;
276 | }
277 | #wpadminbar .quicklinks .menupop ul.ab-sub-secondary,
278 | #wpadminbar .quicklinks .menupop ul.ab-sub-secondary .ab-submenu {
279 | background: #4c4c4d;
280 | }
281 | #wpadminbar .ab-submenu .ab-item,
282 | #wpadminbar .quicklinks .menupop ul li a,
283 | #wpadminbar .quicklinks .menupop.hover ul li a,
284 | #wpadminbar-nojs .quicklinks .menupop:hover ul li a {
285 | color: #c3c4c5;
286 | }
287 | #wpadminbar .quicklinks li .blavatar,
288 | #wpadminbar .menupop .menupop > .ab-item:before {
289 | color: #f1f2f3;
290 | }
291 | #wpadminbar .quicklinks .menupop ul li a:hover,
292 | #wpadminbar .quicklinks .menupop ul li a:focus,
293 | #wpadminbar .quicklinks .menupop ul li a:hover strong,
294 | #wpadminbar .quicklinks .menupop ul li a:focus strong,
295 | #wpadminbar .quicklinks .menupop.hover ul li a:hover,
296 | #wpadminbar .quicklinks .menupop.hover ul li a:focus,
297 | #wpadminbar.nojs .quicklinks .menupop:hover ul li a:hover,
298 | #wpadminbar.nojs .quicklinks .menupop:hover ul li a:focus,
299 | #wpadminbar li:hover .ab-icon:before,
300 | #wpadminbar li:hover .ab-item:before,
301 | #wpadminbar li a:focus .ab-icon:before,
302 | #wpadminbar li .ab-item:focus:before,
303 | #wpadminbar li.hover .ab-icon:before,
304 | #wpadminbar li.hover .ab-item:before,
305 | #wpadminbar li:hover .ab-item:after,
306 | #wpadminbar li.hover .ab-item:after,
307 | #wpadminbar li:hover #adminbarsearch:before {
308 | color: #FFFFFF;
309 | }
310 | #wpadminbar .quicklinks li a:hover .blavatar,
311 | #wpadminbar .menupop .menupop > .ab-item:hover:before {
312 | color: #FFFFFF;
313 | }
314 |
315 | /*********************** ADMIN BAR (SEARCH) ************************/
316 | #wpadminbar #adminbarsearch:before {
317 | color: #f1f2f3;
318 | }
319 | #wpadminbar > #wp-toolbar > #wp-admin-bar-top-secondary > #wp-admin-bar-search #adminbarsearch input.adminbar-input:focus {
320 | color: #FFFFFF;
321 | background: #464d52;
322 | }
323 | #wpadminbar #adminbarsearch .adminbar-input::-webkit-input-placeholder {
324 | color: #FFFFFF;
325 | opacity: 0.7;
326 | }
327 | #wpadminbar #adminbarsearch .adminbar-input:-moz-placeholder {
328 | color: #FFFFFF;
329 | opacity: 0.7;
330 | }
331 | #wpadminbar #adminbarsearch .adminbar-input::-moz-placeholder {
332 | color: #FFFFFF;
333 | opacity: 0.7;
334 | }
335 | #wpadminbar #adminbarsearch .adminbar-input:-ms-input-placeholder {
336 | color: #FFFFFF;
337 | opacity: 0.7;
338 | }
339 |
340 | /********************* ADMIN BAR (MY ACCOUNT) **********************/
341 | #wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar > a img {
342 | border-color: #464d52;
343 | background-color: #464d52;
344 | }
345 | #wpadminbar #wp-admin-bar-user-info .display-name {
346 | color: #FFFFFF;
347 | }
348 | #wpadminbar #wp-admin-bar-user-info a:hover .display-name {
349 | color: #FF6600;
350 | }
351 | #wpadminbar #wp-admin-bar-user-info .username {
352 | color: #c3c4c5;
353 | }
354 |
355 | /***************************** POINTERS ****************************/
356 | .wp-pointer .wp-pointer-content h3 {
357 | background-color: #FF6600;
358 | }
359 | .wp-pointer .wp-pointer-content h3:before {
360 | color: #FF6600;
361 | }
362 | .wp-pointer.wp-pointer-top .wp-pointer-arrow,
363 | .wp-pointer.wp-pointer-undefined .wp-pointer-arrow {
364 | border-bottom-color: #FF6600;
365 | }
366 |
367 | /************************** MEDIA UPLOADER *************************/
368 | .media-item .bar,
369 | .media-progress-bar div {
370 | background-color: #FF6600;
371 | }
372 | .details.attachment {
373 | box-shadow: 0 0 0 1px #fff, 0 0 0 5px #FF6600;
374 | }
375 | .attachment.details .check {
376 | background-color: #FF6600;
377 | box-shadow: 0 0 0 1px #fff, 0 0 0 2px #FF6600;
378 | }
379 |
380 | /****************************** THEMES *****************************/
381 | .theme-browser .theme.active .theme-name,
382 | .theme-browser .theme.add-new-theme:hover:after {
383 | background: #FF6600;
384 | }
385 | .theme-browser .theme.add-new-theme:hover span:after {
386 | color: #FF6600;
387 | }
388 | .theme-overlay .theme-header .close:hover,
389 | .theme-overlay .theme-header .right:hover,
390 | .theme-overlay .theme-header .left:hover {
391 | background: #FF6600;
392 | }
393 |
394 | /************************* JQUERY UI SLIDER ************************/
395 | .wp-slider .ui-slider-handle,
396 | .wp-slider .ui-slider-handle.ui-state-hover,
397 | .wp-slider .ui-slider-handle.focus {
398 | background: #FF6600;
399 | border-color: #cc5200;
400 | -webkit-box-shadow: inset 0 1px 0 #ff944d, 0 1px 0 rgba(0, 0, 0, 0.15);
401 | box-shadow: inset 0 1px 0 #ff944d, 0 1px 0 rgba(0, 0, 0, 0.15);
402 | }
403 |
404 | /************************ RESPONSIVE TOGGLE ************************/
405 | div#wp-responsive-toggle a:before {
406 | color: #f1f2f3;
407 | }
408 | .wp-responsive-open div#wp-responsive-toggle a {
409 | border-color: transparent;
410 | background: #FF6600;
411 | }
412 | .star-rating .star {
413 | color: #FF6600;
414 | }
415 | .wp-responsive-open #wpadminbar #wp-admin-bar-menu-toggle a {
416 | background: #26292c;
417 | }
--------------------------------------------------------------------------------
/lib/alt-admin/css/admin-footer.css:
--------------------------------------------------------------------------------
1 | /**********************************************************************
2 |
3 | ADMIN FOOTER
4 |
5 | **********************************************************************/
6 |
7 | #wpfooter p {
8 | height: 30px;
9 | line-height: 30px;
10 | margin-bottom: 10px;
11 | }
12 |
13 | #wpfooter #footer-upgrade .alt-footer-logo {
14 | background-image: url('../images/altdesign.svg') !important;
15 | background-repeat: no-repeat;
16 | background-size: cover;
17 | display: block;
18 | height: 30px;
19 | outline: none;
20 | overflow: hidden;
21 | text-indent: 9999px;
22 | width: 51px;
23 | }
--------------------------------------------------------------------------------
/lib/alt-admin/css/admin-login.css:
--------------------------------------------------------------------------------
1 | /**********************************************************************
2 |
3 | ADMIN LOGIN
4 |
5 | **********************************************************************/
6 |
7 | h1 a {
8 | background-image: url('../images/admin-logo.png') !important;
9 | background-size: 320px 110px !important;
10 | height: 110px !important;
11 | width: 320px !important;
12 | }
13 |
14 | .login #nav a,
15 | .login #backtoblog a,
16 | .login .mysp-login-extras a {
17 | color: #333333 !important;
18 | text-shadow: 0 1px 0 #FFFFFF;
19 | }
20 |
21 | .login #nav a:hover,
22 | .login #backtoblog a:hover,
23 | .login .mysp-login-extras a:hover {
24 | color: #FF6600 !important;
25 | text-shadow: 0 1px 0 #FFFFFF;
26 | }
27 |
28 | .wp-core-ui .button-primary {
29 | background: #FF6600 !important;
30 | border-color: #E45A00 !important;
31 | -webkit-box-shadow: inset 0 1px 0 #FF9335, 0 1px 0 rgba(0, 0, 0, .15) !important;
32 | box-shadow: inset 0 1px 0 #FF9335, 0 1px 0 rgba(0, 0, 0, .15) !important;
33 | }
34 |
35 | .wp-core-ui .button-primary.focus,
36 | .wp-core-ui .button-primary.hover,
37 | .wp-core-ui .button-primary:focus,
38 | .wp-core-ui .button-primary:hover {
39 | background: #E45A00 !important;
40 | border-color: #E45A00 !important;
41 | -webkit-box-shadow: inset 0 1px 0 #EE8C45 !important;
42 | box-shadow: inset 0 1px 0 #EE8C45 !important;
43 | }
44 |
45 | #wp-submit {
46 | text-shadow: none !important;
47 | }
48 |
--------------------------------------------------------------------------------
/lib/alt-admin/images/admin-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alt-design/wp-spa-boilerplate/bde4eceabd4d064ef20c0a845a73b84003f086e9/lib/alt-admin/images/admin-logo.png
--------------------------------------------------------------------------------
/lib/alt-admin/images/altdesign.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
--------------------------------------------------------------------------------
/lib/endpoints/addonEndpoints.php:
--------------------------------------------------------------------------------
1 | 'GET',
13 | // 'callback' => ['SomeClass', 'someMethod'],
14 | // 'args' => [
15 | // 'some-param' => [
16 | // 'default' => false
17 | // ]
18 | // ]
19 | // ]);
20 | //});
--------------------------------------------------------------------------------
/lib/endpoints/themeEndpoints.php:
--------------------------------------------------------------------------------
1 | $tax['name'],
72 | 'terms' => wp_get_post_terms($id, $tax['name'], ['fields' => 'names'])
73 | ];
74 |
75 | }
76 |
77 | return $returnData;
78 | }
79 |
80 |
81 | /**
82 | * Return all posts from any post type
83 | *
84 | * @param $data
85 | *
86 | */
87 | public static function getArchiveData($data)
88 | {
89 |
90 | $endpoints = new AltThemeEndpoints();
91 | $posts = [];
92 | $taxonomies = [];
93 | $postIds = get_posts([
94 | 'fields' => 'ids',
95 | 'post_type' => $data['post_type'],
96 | 'showposts' => $data['amount']
97 | ]);
98 | $ignoredTaxonomies = [
99 | 'post_tag',
100 | 'post_format'
101 | ];
102 | $ignoredTerms = [
103 | // 'Uncategorized',
104 | // 'Uncategorised'
105 | ];
106 |
107 | foreach (get_object_taxonomies($data['post_type']) as $tax) {
108 |
109 | if (!in_array($tax, $ignoredTaxonomies)) {
110 |
111 | $terms = [];
112 |
113 | // exclude ignored terms
114 | foreach (get_terms(['fields' => 'names', 'taxonomy' => $tax]) as $term) {
115 | !in_array($term, $ignoredTerms) ? $terms[] = $term : null;
116 | }
117 |
118 | $taxonomies[] = [
119 | 'name' => $tax,
120 | 'terms' => $terms
121 | ];
122 |
123 | }
124 |
125 | }
126 |
127 | foreach ($postIds as $postId) {
128 | $posts[] = [
129 | 'title' => get_the_title($postId),
130 | 'date' => get_the_date('d F', $postId),
131 | 'link' => get_the_permalink($postId),
132 | 'img' => $endpoints->getFeaturedImg($postId),
133 | 'taxonomies' => AltThemeEndpoints::getPostTaxTerm($postId, $taxonomies),
134 | 'acf' => $data['acf'] ? get_field_objects($postId) : false
135 | ];
136 | }
137 |
138 | echo helpers::altJsonEncode(['taxonomies' => $taxonomies, 'posts' => $posts]);
139 |
140 | die();
141 | }
142 |
143 |
144 | /**
145 | * Return Featured Image Array
146 | *
147 | * @param $data
148 | *
149 | */
150 | public function returnFeaturedImg($data)
151 | {
152 |
153 | var_dump(AltThemeEndpoints::getFeaturedImg($data['id']));
154 | die();
155 | }
156 |
157 |
158 | /**
159 | * Collect and return the data for a post from is ID
160 | *
161 | * @param $id
162 | *
163 | * @return array|null|string|WP_Post
164 | */
165 | public function getPostData($id, $isPreview)
166 | {
167 | // Get WordPress post object or post revision object
168 | if (!$isPreview) {
169 | $returnData = get_post($id) ?? 'No post found with that ID';
170 | } else {
171 | // Get array of post revisions for this ID
172 | $revisions = wp_get_post_revisions($id);
173 |
174 | if (count($revisions)) {
175 | // Get the post data for the latest revision
176 | $revisionsValues = array_values($revisions);
177 | $returnData = array_shift($revisionsValues);
178 |
179 | // Use the latest revisions ID from now on
180 | $id = $returnData->ID;
181 | } else {
182 | $returnData = 'No post revisions found for that ID. Has the post type got revisions enabled?';
183 | }
184 | }
185 |
186 | // Get any custom fields for this post if ACF is installed
187 | $returnData->acf = (class_exists('acf') && get_field_objects($id)) ? get_field_objects($id) : false;
188 |
189 | // Get te featured image stuff
190 | $returnData->featured_image = AltThemeEndpoints::getFeaturedImg($id);
191 |
192 | // Get the breadcrumbs
193 | $returnData->breadcrumbs = $this->getBreadcrumbs($id);
194 |
195 | // List of post data not to be sent in response
196 | $noThanks = [
197 | 'post_modified_gmt',
198 | 'post_modified',
199 | 'post_author',
200 | 'pinged',
201 | 'post_modified',
202 | 'to_ping',
203 | 'post_status',
204 | 'post_password',
205 | 'post_mime_type',
206 | 'ping_status',
207 | 'filter',
208 | 'menu_order',
209 | 'guid',
210 | 'comment_status',
211 | 'comment_count',
212 | 'post_content_filtered',
213 | 'post_date_gmt'
214 | ];
215 |
216 | foreach ($noThanks as $goodBye) {
217 | unset($returnData->{$goodBye});
218 | }
219 |
220 | return $returnData;
221 |
222 | }
223 |
224 |
225 | /**
226 | * Get breadcrumbs from ID
227 | *
228 | * @param $id
229 | *
230 | * @return array
231 | */
232 | public function getBreadcrumbs($id)
233 | {
234 |
235 | $breadArr[] = ['title' => get_the_title($id), 'permalink' => get_permalink($id)];
236 |
237 | $parentId = wp_get_post_parent_id($id);
238 |
239 | // Only prepend to array is has parent post
240 | if ($parentId) {
241 |
242 | do {
243 | array_unshift($breadArr,
244 | ['title' => get_the_title($parentId), 'permalink' => get_permalink($parentId)]);
245 | $parentId = wp_get_post_parent_id($parentId);
246 | } while ($parentId != 0);
247 |
248 | }
249 |
250 | // Prepend post type if not page
251 | $postType = get_post_type($id);
252 | if ($postType !== 'page') {
253 | array_unshift($breadArr, ['title' => ucfirst($postType), 'permalink' => get_home_url() . '/' . $postType]);
254 | }
255 |
256 | array_unshift($breadArr, ['title' => 'Home', 'permalink' => get_home_url() . '/']);
257 |
258 | return $breadArr;
259 | }
260 |
261 |
262 | /**
263 | * Checks the slug passed against post types to see if it matches,
264 | * This means that when we search for the slug, we can check only
265 | * against the correct post type, allowing us to have posts in
266 | * different types with the same slug!
267 | *
268 | * @param $slug
269 | *
270 | * @return array
271 | */
272 | public function checkType($slug)
273 | {
274 | $matchingPostType = '';
275 |
276 | if ($slug[0] !== '/') {
277 | $slug = '/' . $slug;
278 | }
279 |
280 | // Retrieves a list of all post types
281 | $allPostTypes = get_post_types('', 'objects', '');
282 |
283 | // Explode the different parts of the slug and remove blank array parts
284 | $slugParts = array_diff(explode('/', $slug), ['']);
285 |
286 | // Check is second part to slug or if slug is just 1 deep
287 | if (isset($slugParts[2]) && !empty($slugParts[2])) {
288 |
289 | // For each post type
290 | foreach ($allPostTypes as $postType) {
291 | $matchTo = $postType->rewrite['slug'] ? $postType->rewrite['slug'] : $postType->name;
292 |
293 | // if the first part of the slug matches a post type rewrite url
294 | if ($slugParts[1] === $matchTo) {
295 | $matchingPostType = $postType->name;
296 | }
297 | }
298 |
299 | // if no matching post type, assume the slug multipart slug is because it's a child page, return the full slug
300 | if (!$matchingPostType) {
301 | $returnSlug = $slug;
302 | } else { // else return just the last part of the slug by stripping the post types rewrite url
303 | $returnSlug = str_replace('/', '', implode('', explode($matchingPostType, $slug, 2)));
304 | }
305 |
306 | // else if the slug has only one part, like a normal page
307 | } else {
308 | $returnSlug = $slug;
309 | }
310 |
311 | return [
312 | 'post_type' => $matchingPostType ? $matchingPostType : 'page',
313 | 'slug' => $returnSlug
314 | ];
315 | }
316 |
317 |
318 | /**
319 | * Returns an Object with everything you need in
320 | * Just pass it an ID or slug!
321 | *
322 | * @param $data
323 | *
324 | */
325 | public static function queryAll($data)
326 | {
327 | $AltThemeEndpoints = new AltThemeEndpoints();
328 | $isPreview = false;
329 |
330 | // Page Search Criteria
331 | $psc = $AltThemeEndpoints->checkType($data['slug']);
332 |
333 | // Handle previews and draft pages
334 | if (!empty($data['slug']) && isset($_GET['preview'])) {
335 | $isPreview = true;
336 |
337 | // Insert query params stug in slug into $_GET
338 | $urlParts = parse_url($data['slug']);
339 | parse_str($urlParts['query'], $params);
340 |
341 | foreach ($params as $key => $value) {
342 | if (!isset($_GET[$key])) {
343 | $_GET[$key] = $value;
344 | }
345 | }
346 |
347 | // Get the page ID from the preview_id param
348 | $id = $_GET['preview_id'] ?? $_GET['p'];
349 | }
350 | // If the slug is not empty and isn't "/" (Home page)
351 | elseif (!empty($data['slug'] && $data['slug'] !== '/')) {
352 | $id = get_page_by_path($psc['slug'], OBJECT, $psc['post_type'])->ID;
353 | } // If we have an ID instead of a slug
354 | elseif (!empty($data['id'])) {
355 | $id = $data['id'];
356 | } // Else, it will be the Home page
357 | else {
358 | $id = get_option('page_on_front');
359 | }
360 |
361 |
362 | // Get and return the content from our narrowed down ID
363 | // Ensures ID is an integer
364 | echo json_encode($AltThemeEndpoints->getPostData(intval($id), $isPreview));
365 | die();
366 | }
367 |
368 | public static function getArchives($type, $postType = false)
369 | {
370 | if ($type['type'] == 'date') {
371 | $y = 2009;
372 | $dateArr = [];
373 | $postType = $_GET['postType'];
374 | do{
375 | if(!empty(get_posts(['post_type' => $postType, 'date_query' => [['year' => $y]]]))){
376 | $dateArr[] = $y;
377 | }
378 | $y++;
379 | }while($y <= date('Y'));
380 | echo json_encode($dateArr);
381 | die();
382 | }
383 |
384 | if ($type['type'] == 'categories') {
385 | echo json_encode(get_terms(
386 | [
387 | 'taxonomy' => 'category',
388 | 'hide_empty' => false
389 | ]
390 | ));
391 | die();
392 | }
393 |
394 | if ($type['type'] == 'expertise') {
395 | echo json_encode(get_terms(
396 | [
397 | 'taxonomy' => 'expertise',
398 | 'hide_empty' => false
399 | ]
400 | ));
401 | die();
402 | }
403 |
404 | echo 'Type not specified. Please add either `date`, `categories` or `expertise`';
405 | die();
406 | }
407 | }
408 |
409 |
410 | /**
411 | * Endpoints are registered on rest_api_init
412 | */
413 | add_action('rest_api_init', function () {
414 |
415 | /**
416 | * Endpoint for getting post data by ID or slug
417 | */
418 | register_rest_route('alt/v1', '/all', [
419 | 'methods' => 'GET',
420 | 'callback' => ['AltThemeEndpoints', 'queryAll'],
421 | 'args' => [
422 | 'slug' => [
423 | 'default' => false // Pass a slug
424 | ],
425 | 'id' => [
426 | 'default' => false
427 | ]
428 | ],
429 | ]);
430 |
431 |
432 | /**
433 | * Endpoint for getting ACF options page data
434 | */
435 | register_rest_route('alt/v1', '/global-acf', [
436 | 'methods' => 'GET',
437 | 'callback' => ['AltThemeEndpoints', 'getACFGlobalOptions'],
438 | ]);
439 |
440 |
441 | /**
442 | * Endpoint for getting Featured Images
443 | */
444 | register_rest_route('alt/v1', '/featured-image', [
445 | 'methods' => 'GET',
446 | 'callback' => ['AltThemeEndpoints', 'returnFeaturedImg'],
447 | 'args' => [
448 | 'id' => [
449 | 'default' => false
450 | ]
451 | ]
452 | ]);
453 |
454 |
455 | /**
456 | * Endpoint for getting All posts of a certain post type
457 | */
458 | register_rest_route('alt/v1', '/archive', [
459 | 'methods' => 'GET',
460 | 'callback' => ['AltThemeEndpoints', 'getArchiveData'],
461 | 'args' => [
462 | 'post_type' => [
463 | 'default' => false
464 | ],
465 | 'amount' => [
466 | 'default' => -1
467 | ],
468 | 'acf' => [
469 | 'default' => false
470 | ]
471 | ]
472 | ]);
473 |
474 | /**
475 | * Endpoint for getting All posts of a certain post type
476 | */
477 | register_rest_route('alt/v1', '/get-archives', [
478 | 'methods' => 'GET',
479 | 'callback' => ['AltThemeEndpoints', 'getArchives'],
480 | 'args' => [
481 | 'type' => [
482 | 'default' => false // Or an ID
483 | ]
484 | ]
485 | ]);
486 | });
487 |
--------------------------------------------------------------------------------
/lib/helpers.php:
--------------------------------------------------------------------------------
1 | ' . __('Warning: ' . wp_get_theme() . ' requires the "WP API Menus". Install it here.') . '
';
35 | }
36 |
37 | if (!class_exists('acf')) {
38 | echo '' . __('Warning: ' . wp_get_theme() . ' requires the "Advanced Custom Fields PRO". Download it here.') . '
';
39 | }
40 |
41 | }
42 |
43 |
44 | /**
45 | * Tell WordPress to manage our page titles (JS will take over after initial load)
46 | *
47 | *
48 | */
49 | function theme_slug_setup()
50 | {
51 | add_theme_support('title-tag');
52 | }
53 |
54 |
55 | /**
56 | * Allow cross origin requests, necessary for our webpack dev server
57 | *
58 | *
59 | */
60 | function add_cors_http_header()
61 | {
62 | header("Access-Control-Allow-Origin: *");
63 | }
64 |
65 |
66 | /**
67 | * Register Custom Image Sizes and add theme support for post thumbnail
68 | *
69 | *
70 | */
71 | add_theme_support('post-thumbnails');
72 | set_post_thumbnail_size(672, 372, true);
73 | add_image_size('max', 1920, 1080, false);
74 | add_image_size('icon', 32, 32, false);
75 |
76 |
77 | /**
78 | * Register Navigation Menu's
79 | *
80 | *
81 | */
82 | register_nav_menus([
83 | 'main' => __('Main Navigation'),
84 | 'footer' => __('Footer Navigation'),
85 | ]);
86 |
87 |
88 | /**
89 | * Enqueue Front-end Scripts/Styles
90 | *
91 | *
92 | */
93 | function alt_enqueue_frontend_scripts_styles()
94 | {
95 | // wp_enqueue_script('jquery', null, null, null, true);
96 | wp_enqueue_script('MainJs', get_template_directory_uri() . '/dist/bundle.js', null, null, true);
97 | wp_enqueue_style('FontAwesome', '//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css',
98 | null, null, null);
99 | }
100 |
101 |
102 | /**
103 | * Register Global Options Page
104 | *
105 | *
106 | */
107 | if (function_exists('acf_add_options_page')) {
108 | acf_add_options_page('Global Options');
109 | }
110 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "build": "webpack",
4 | "watch": "webpack -w",
5 | "dev": "node server.js useDevMiddleware",
6 | "production": "webpack -p",
7 | "component": "node ./cli/scripts/componentGenerator.js"
8 | },
9 | "devDependencies": {
10 | "autoprefixer": "^6.7.6",
11 | "axios": "^0.15.3",
12 | "babel-core": "^6.20.0",
13 | "babel-loader": "^6.2.9",
14 | "babel-plugin-transform-decorators-legacy": "^1.3.4",
15 | "babel-plugin-transform-runtime": "^6.15.0",
16 | "babel-polyfill": "^6.23.0",
17 | "babel-preset-es2015": "^6.18.0",
18 | "babel-preset-stage-0": "^6.16.0",
19 | "babel-runtime": "^6.20.0",
20 | "css-loader": "^0.26.1",
21 | "dotenv": "^4.0.0",
22 | "eslint": "^3.15.0",
23 | "eslint-config-standard": "^6.2.1",
24 | "eslint-plugin-html": "^2.0.1",
25 | "eslint-plugin-promise": "^3.4.0",
26 | "eslint-plugin-standard": "^2.0.1",
27 | "express": "^4.14.0",
28 | "lodash": "^4.17.4",
29 | "node-sass": "^4.0.0",
30 | "postcss-loader": "^1.3.2",
31 | "postcss-scss": "^0.4.1",
32 | "sass-loader": "^4.0.2",
33 | "style-loader": "^0.13.1",
34 | "vue-hot-reload-api": "^2.0.6",
35 | "vue-html-loader": "^1.2.3",
36 | "vue-loader": "^10.0.2",
37 | "vue-style-loader": "^1.0.0",
38 | "vue-template-compiler": "^2.2.5",
39 | "webpack": "^2.2.1",
40 | "webpack-dev-middleware": "^1.8.4",
41 | "webpack-dev-server": "^1.16.2",
42 | "webpack-hot-middleware": "^2.13.2"
43 | },
44 | "dependencies": {
45 | "axios": "^0.15.3",
46 | "core-decorators": "^0.15.0",
47 | "jump.js": "^1.0.1",
48 | "normalize.css": "^7.0.0",
49 | "vue": "^2.2.5",
50 | "vue-router": "^2.3.0",
51 | "vuex": "^2.2.1"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | syntax: require('postcss-scss'),
3 | plugins: [
4 | require('autoprefixer')
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alt-design/wp-spa-boilerplate/bde4eceabd4d064ef20c0a845a73b84003f086e9/screenshot.jpg
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Imports
3 | */
4 | const env = require('dotenv').config()
5 | const http = require('http')
6 | const express = require('express')
7 | const app = express()
8 | const axios = require('axios')
9 | const fs = require('fs')
10 | const webpackDevMiddleware = require('webpack-dev-middleware')
11 | const webpack = require('webpack')
12 | const webpackConfig = require(process.env.WEBPACK_CONFIG ? process.env.WEBPACK_CONFIG : './webpack.config')
13 | const compiler = webpack(webpackConfig)
14 |
15 | /**
16 | * Server Plugins
17 | */
18 | app.use(webpackDevMiddleware(compiler, {noInfo: true, quiet: true, publicPath: webpackConfig.output.publicPath}))
19 | app.use(require('webpack-hot-middleware')(compiler))
20 |
21 | /**
22 | * Routes
23 | */
24 | // Just to stop logging of favicon requests
25 | app.get('/favicon.ico', () => {})
26 |
27 | // Handle all other requests
28 | app.get('*', (req, res) => {
29 | new Promise(resolve => {
30 | // Send the request to build webpackTemp.html
31 | axios.get(env.parsed.DEV_URL)
32 |
33 | // Once webpackTemp.html has been updated, resolve the promise
34 | fs.watch(__dirname + '/webpackTemp.html', () => {
35 | setTimeout(() => { resolve() }, 250)
36 | })
37 | }).then(() => {
38 | // Server webpackTemp.html
39 | res.sendFile(__dirname + '/webpackTemp.html')
40 | })
41 | })
42 |
43 | /**
44 | * Initiate the server
45 | */
46 | http.createServer(app).listen(env.parsed.PORT)
47 |
--------------------------------------------------------------------------------
/src/assets/scss/imports/_components.scss:
--------------------------------------------------------------------------------
1 | // Global Components SCSS, things like .container etc.
--------------------------------------------------------------------------------
/src/assets/scss/imports/_extends.scss:
--------------------------------------------------------------------------------
1 | // Reusable chunks of styling, this is to promote DRYNESS.
2 |
3 | // Font Colors
4 | %color--body {
5 | color: $color--body;
6 | }
7 |
8 | // Line Heights
9 | %line-height--body {
10 | line-height: $line-height--body;
11 | }
12 |
13 | %line-height--title {
14 | line-height: $line-height--title;
15 | }
16 |
17 | %line-height--compact {
18 | line-height: $line-height--compact;
19 | }
20 |
21 | // Hover Effects
22 | %hover--opacity {
23 | transition: .25s;
24 |
25 | &:hover {
26 | opacity: .75;
27 | }
28 | }
--------------------------------------------------------------------------------
/src/assets/scss/imports/_fonts.scss:
--------------------------------------------------------------------------------
1 | // Fonts
2 |
3 | h1, h2, h3, h4, h5, h6 {
4 | @extend %line-height--title;
5 | }
6 |
7 | a {
8 | text-decoration: inherit;
9 | color: inherit;
10 | }
--------------------------------------------------------------------------------
/src/assets/scss/imports/_forms.scss:
--------------------------------------------------------------------------------
1 | // Forms, inputs, buttons etc.
--------------------------------------------------------------------------------
/src/assets/scss/imports/_global.scss:
--------------------------------------------------------------------------------
1 | // General App Styles.
2 |
3 | body {
4 | @extend %line-height--body;
5 | @extend %color--body;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | font-size: 16px;
9 | }
10 |
--------------------------------------------------------------------------------
/src/assets/scss/imports/_mixins.scss:
--------------------------------------------------------------------------------
1 | // Mixins.
--------------------------------------------------------------------------------
/src/assets/scss/imports/_modifiers.scss:
--------------------------------------------------------------------------------
1 | // Place your modifier classes in here, e.g. .margin-0.
--------------------------------------------------------------------------------
/src/assets/scss/imports/_variables.scss:
--------------------------------------------------------------------------------
1 | // Variables.
2 |
3 | // Font Colors
4 | $color--body: #000;
5 |
6 | // Line Heights
7 | $line-height--body: 1.5;
8 | $line-height--title: 1.25;
9 | $line-height--compact: 1;
10 |
11 | // Breakpoints
12 | $breakpoint--xs: 576px;
13 | $breakpoint--s: 768px;
14 | $breakpoint--m: 992px;
15 | $breakpoint--l: 1200px;
16 |
--------------------------------------------------------------------------------
/src/assets/scss/main.scss:
--------------------------------------------------------------------------------
1 | /*
2 | Please don't write any styles in this file.
3 | */
4 | @import "~normalize.css";
5 | @import "imports/variables";
6 | @import "imports/mixins";
7 | @import "imports/extends";
8 | @import "imports/fonts";
9 | @import "imports/global";
10 | @import "imports/forms";
11 | @import "imports/components";
12 | @import "imports/modifiers";
13 |
--------------------------------------------------------------------------------
/src/components/App/App.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/components/App/App.component.js:
--------------------------------------------------------------------------------
1 | /**
2 | * App
3 | *
4 | * @template : ./App.component.html
5 | * @style : ./App.component.scss
6 | */
7 |
8 | export default {
9 | data () {
10 | return {
11 | message: 'App'
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/App/App.component.scss:
--------------------------------------------------------------------------------
1 | .app {
2 |
3 | &-enter-active, &-leave-active {
4 | transition: opacity .5s
5 | }
6 |
7 | &-enter, &-leave-active {
8 | opacity: 0
9 | }
10 | }
--------------------------------------------------------------------------------
/src/components/App/App.component.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/App/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "App.component.vue"
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/Breadcrumbs/Breadcrumbs.component.html:
--------------------------------------------------------------------------------
1 |
2 |
7 | {{breadcrumb.title}}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/components/Breadcrumbs/Breadcrumbs.component.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Breadcrumbs
3 | *
4 | * @template : ./Breadcrumbs.component.html
5 | * @style : ./Breadcrumbs.component.scss
6 | */
7 |
8 | export default {
9 | methods: {
10 | relativeLink (link) {
11 | if (link) return link.replace(this.$store.state.url, '')
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/Breadcrumbs/Breadcrumbs.component.scss:
--------------------------------------------------------------------------------
1 | .breadcrumbs {
2 |
3 | &__breadcrumb {
4 |
5 | }
6 |
7 | }
--------------------------------------------------------------------------------
/src/components/Breadcrumbs/Breadcrumbs.component.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/Breadcrumbs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "breadcrumbs",
3 | "version": "1.0.0",
4 | "main": "Breadcrumbs.component.vue"
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/Footer/Footer.component.html:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/components/Footer/Footer.component.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Footer
3 | *
4 | * @template : ./Footer.component.html
5 | * @style : ./Footer.component.scss
6 | */
7 |
8 | export default {
9 | data () {
10 | return {
11 | message: 'Footer'
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/Footer/Footer.component.scss:
--------------------------------------------------------------------------------
1 | .footer {
2 |
3 | }
--------------------------------------------------------------------------------
/src/components/Footer/Footer.component.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/Footer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "footer",
3 | "version": "1.0.0",
4 | "main": "Footer.component.vue"
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/Header/Header.component.html:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/components/Header/Header.component.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Header
3 | *
4 | * @template : ./Header.component.html
5 | * @style : ./Header.component.scss
6 | */
7 |
8 | export default {
9 | data () {
10 | return {
11 | message: 'Header'
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/Header/Header.component.scss:
--------------------------------------------------------------------------------
1 | .header {
2 |
3 | }
--------------------------------------------------------------------------------
/src/components/Header/Header.component.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/Header/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "header",
3 | "version": "1.0.0",
4 | "main": "Header.component.vue"
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/Menus/AppMenu/AppMenu.component.html:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/components/Menus/AppMenu/AppMenu.component.js:
--------------------------------------------------------------------------------
1 | /**
2 | * AppMenu
3 | *
4 | * @template : ./AppMenu.component.html
5 | * @style : ./AppMenu.component.scss
6 | */
7 |
8 | export default {
9 | props: {
10 | location: String,
11 | emitOnComplete: String
12 | },
13 | data () {
14 | return {
15 | items: Object,
16 | fetched: false
17 | }
18 | },
19 | created () {
20 | this.$http.get('/wp-json/wp-api-menus/v2/menu-locations/' + this.location).then(res => {
21 | if (res.data.length) {
22 | this.items = res.data
23 | } else {
24 | console.error(`Failed to retrieve menu ${this.location}, check the menu location is correct and the menu is not empty.`)
25 | }
26 | }).then(() => {
27 | this.fetched = true
28 | })
29 | },
30 | mounted () {
31 | let fetchInterval = setInterval(() => {
32 | if (this.fetched) {
33 | clearInterval(fetchInterval)
34 | this.emitOnComplete && this.$emit(this.emitOnComplete)
35 | }
36 | })
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/Menus/AppMenu/AppMenu.component.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/components/Menus/AppMenu/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app-menu",
3 | "version": "1.0.0",
4 | "main": "AppMenu.component.vue"
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/Menus/AppMenuItem/AppMenuItem.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
13 |
14 |
15 |
22 |
23 |
24 |
25 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/components/Menus/AppMenuItem/AppMenuItem.component.js:
--------------------------------------------------------------------------------
1 | /**
2 | * AppMenuItem
3 | *
4 | * @template : ./AppMenuItem.component.html
5 | * @style : ./AppMenuItem.component.scss
6 | */
7 |
8 | export default {
9 | props: {
10 | item: Object,
11 | location: String,
12 | depth: {
13 | type: Number,
14 | default: 0
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/Menus/AppMenuItem/AppMenuItem.component.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/components/Menus/AppMenuItem/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app-menu-item",
3 | "version": "1.0.0",
4 | "main": "AppMenuItem.component.vue"
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/PageBuilder/PageBuilder.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/components/PageBuilder/PageBuilder.component.js:
--------------------------------------------------------------------------------
1 | /**
2 | * PageBuilder
3 | *
4 | * @template : ./PageBuilder.component.html
5 | * @style : ./PageBuilder.component.scss
6 | */
7 |
8 | export default {}
9 |
--------------------------------------------------------------------------------
/src/components/PageBuilder/PageBuilder.component.scss:
--------------------------------------------------------------------------------
1 | .page-builder {
2 |
3 | }
--------------------------------------------------------------------------------
/src/components/PageBuilder/PageBuilder.component.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/PageBuilder/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "page-builder",
3 | "version": "1.0.0",
4 | "main": "PageBuilder.component.vue"
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/Views/Page/Page.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
![Logo]()
4 |
5 |
You are currently viewing the {{$store.state.post.post_title}} page. Edit this template -
6 | ~/src/components/Views/Page/Page.component.html
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/components/Views/Page/Page.component.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Page
3 | *
4 | * @template : ./Page.component.html
5 | * @style : ./Page.component.scss
6 | */
7 |
8 | export default {}
9 |
--------------------------------------------------------------------------------
/src/components/Views/Page/Page.component.scss:
--------------------------------------------------------------------------------
1 | .page {
2 |
3 | }
--------------------------------------------------------------------------------
/src/components/Views/Page/Page.component.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/Views/Page/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "page",
3 | "version": "1.0.0",
4 | "main": "Page.component.vue"
5 | }
6 |
--------------------------------------------------------------------------------
/src/imports/components.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | Vue.component('page-builder', require('../components/PageBuilder'))
4 | Vue.component('app-header', require('../components/Header'))
5 | Vue.component('app-footer', require('../components/Footer'))
6 | Vue.component('breadcrumbs', require('../components/Breadcrumbs'))
7 | Vue.component('app-menu', require('../components/Menus/AppMenu'))
8 | Vue.component('app-menu-item', require('../components/Menus/AppMenuItem'))
9 |
--------------------------------------------------------------------------------
/src/imports/functions.js:
--------------------------------------------------------------------------------
1 | import store from '../vuex/store'
2 |
3 | export default class Functions {
4 | static updateAdminBar () {
5 | const editPage = document.getElementById('wp-admin-bar-edit')
6 | const editPageLink = editPage ? editPage.querySelector('a') : false
7 | editPageLink && editPageLink.setAttribute('href', `${store.state.adminUrl}/post.php?post=${store.state.post.ID}&action=edit`)
8 | }
9 |
10 | static getUrlParams () {
11 | const queryString = window.location.href.split('?')[1]
12 | if (queryString) {
13 | const keyValuePairs = queryString.split('&')
14 | const params = {}
15 | let keyValue
16 |
17 | keyValuePairs.forEach((pair) => {
18 | keyValue = pair.split('=')
19 | params[keyValue[0]] = decodeURIComponent(keyValue[1]).replace('+', ' ')
20 | })
21 |
22 | return params
23 | }
24 | return false
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/imports/ready.js:
--------------------------------------------------------------------------------
1 | import store from '../vuex/store'
2 | import axios from 'axios'
3 |
4 | export default class Ready {
5 | constructor () {
6 | Ready.setGlobalOptions()
7 | Ready.setThemeDir()
8 | Ready.setSiteURL()
9 | Ready.setName()
10 | Ready.setAdminURL()
11 | }
12 |
13 | static setGlobalOptions () {
14 | axios.get(`${Ready.getSiteURL()}/wp-json/alt/v1/global-acf`).then(res => {
15 | store.commit('setGlobalOptions', res.data)
16 | })
17 | }
18 |
19 | static setAdminURL () {
20 | store.commit('setAdminURL', document.getElementById('a-url').innerHTML)
21 | }
22 |
23 | static setSiteURL () {
24 | store.commit('setSiteURL', Ready.getSiteURL())
25 | }
26 |
27 | static getSiteURL () {
28 | return document.getElementById('url').innerHTML
29 | }
30 |
31 | static setThemeDir () {
32 | store.commit('setThemeDir', document.getElementById('theme').innerHTML)
33 | }
34 |
35 | static setName () {
36 | store.commit('setName', document.getElementById('name').innerHTML)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import Vue from 'vue'
3 | import './assets/scss/main.scss'
4 | import App from './components/App'
5 | import './imports/components'
6 | import Ready from './imports/ready'
7 | import router from './router/router'
8 | import store from './vuex/store'
9 |
10 | /**
11 | * Vue Setup
12 | */
13 | Vue.prototype.$http = axios.create({
14 | baseURL: Ready.getSiteURL()
15 | })
16 |
17 | /**
18 | * Used to generate relative URL's from absolute
19 | * URL's for use with Vue Router
20 | */
21 | Vue.prototype.$relativeUrl = url => {
22 | return url.replace(store.state.url, '')
23 | }
24 |
25 | /**
26 | * Main Vue Instance
27 | */
28 | new Vue({
29 | el: '#app',
30 | store,
31 | router,
32 | mounted () {
33 | new Ready()
34 | },
35 | render: h => h(App)
36 | })
37 |
--------------------------------------------------------------------------------
/src/router/router.js:
--------------------------------------------------------------------------------
1 | import Jump from 'jump.js'
2 | import Vue from 'vue'
3 | import VueRouter from 'vue-router'
4 | import Functions from '../imports/Functions'
5 | import store from '../vuex/store'
6 | import routes from './Routes'
7 |
8 | Vue.use(VueRouter)
9 |
10 | // Main Router Instance
11 | const router = new VueRouter({
12 | routes,
13 | mode: 'history'
14 | })
15 |
16 | // Run before each route change
17 | router.beforeEach((to, from, next) => {
18 | Jump('html', {
19 | duration: window.pageYOffset > 10 ? 500 : 0,
20 | callback () {
21 | new Promise((resolve) => {
22 | store.commit('updatePost', (to.fullPath ? to.fullPath : 'home'))
23 | resolve()
24 | })
25 |
26 | next()
27 | }
28 | })
29 | })
30 |
31 | // Run after each route change
32 | // router.afterEach((to, from) => {
33 | // });
34 |
35 | export default router
36 |
--------------------------------------------------------------------------------
/src/router/routes.js:
--------------------------------------------------------------------------------
1 | import Page from '../components/Views/Page'
2 |
3 | const Routes = [
4 | {
5 | path: '/',
6 | component: Page,
7 | name: 'Home'
8 | },
9 | {
10 | path: '/:slug/',
11 | component: Page,
12 | name: 'Page'
13 | },
14 | {
15 | path: '/:slug/:childSlug/',
16 | component: Page,
17 | name: 'SubPage'
18 | }
19 | ]
20 |
21 | export default Routes
22 |
--------------------------------------------------------------------------------
/src/vuex/actions.js:
--------------------------------------------------------------------------------
1 | export default {
2 | // Actions
3 | }
4 |
--------------------------------------------------------------------------------
/src/vuex/getters.js:
--------------------------------------------------------------------------------
1 | export default {
2 | // Getters
3 | }
4 |
--------------------------------------------------------------------------------
/src/vuex/mutations.js:
--------------------------------------------------------------------------------
1 | import Http from 'axios'
2 | import Functions from '../imports/Functions'
3 |
4 | export default {
5 |
6 | setGlobalOptions (state, data) {
7 | state.global = data
8 | },
9 |
10 | setThemeDir (state, string) {
11 | state.theme = string
12 | },
13 |
14 | setSiteURL (state, string) {
15 | state.url = string
16 | },
17 |
18 | setAdminURL (state, string) {
19 | state.adminUrl = string
20 | },
21 |
22 | setName (state, name) {
23 | state.name = name
24 | },
25 |
26 | setTagLine (state, description) {
27 | state.tagLine = description
28 | },
29 |
30 | // This mutation is the life of the party
31 | updatePost (state, newPath) {
32 | Http.get(state.url + '/wp-json/alt/v1/all?slug=' + newPath).then(res => {
33 | state.post = res.data
34 | document.title = res.data.post_title + ' - ' + state.name
35 |
36 | Functions.updateAdminBar()
37 | }, res => {
38 | // Error
39 | console.error('Something went wrong with updatePost mutation, here it is:')
40 | console.error(res)
41 | })
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/vuex/state.js:
--------------------------------------------------------------------------------
1 | export default {
2 | adminUrl: '',
3 | global: {},
4 | theme: '',
5 | name: '',
6 | post: {},
7 | url: ''
8 | }
9 |
--------------------------------------------------------------------------------
/src/vuex/store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueX from 'vuex'
3 | import actions from './actions'
4 | import getters from './getters'
5 | import mutations from './mutations'
6 | import state from './state'
7 |
8 | Vue.use(VueX)
9 |
10 | export default new VueX.Store({
11 | state,
12 | mutations,
13 | actions,
14 | getters
15 | })
16 |
--------------------------------------------------------------------------------
/static/img/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | /*
2 | Theme Name: Vue.js / WP SPA Theme
3 | Author: Sam Mckay / Ben Harvey @ Alt Design
4 | Author URI: https://alt-design.net
5 | Version: I've heard so
6 | */
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const env = require('dotenv').config()
2 | const webpack = require('webpack')
3 |
4 | // User Settings
5 | const settings = {
6 | entry: [
7 | 'babel-polyfill', // Babel Polyfill is required by VueX for Promises in IE
8 | `./src/${env.parsed.ENTRY}`
9 | ],
10 | output: {
11 | JavaScript: {
12 | path: __dirname + '/dist/',
13 | publicPath: '/dist/',
14 | filename: env.parsed.OUTPUT
15 | }
16 | },
17 | scssLoaders: 'style-loader!css-loader!sass-loader!postcss-loader'
18 | }
19 |
20 | // If add the webpack-hot-middleware client for hot reloading if necessary.
21 | process.argv.includes('useDevMiddleware') && settings.entry.unshift('webpack-hot-middleware/client')
22 |
23 | // Webpack 2 Config
24 | module.exports = {
25 | entry: settings.entry,
26 | output: settings.output.JavaScript,
27 | resolve: {
28 | extensions: ['.js', '.vue']
29 | },
30 | module: {
31 | rules: [
32 | {
33 | test: /\.js$/,
34 | loader: 'babel-loader',
35 | exclude: /node_modules/
36 | },
37 | {
38 | test: /\.vue$/,
39 | loader: 'vue-loader',
40 | options: {
41 | loaders: {
42 | scss: settings.scssLoaders
43 | }
44 | }
45 | },
46 | {
47 | test: /\.scss$/,
48 | loader: settings.scssLoaders
49 | }
50 | ]
51 | },
52 | plugins: [
53 | new webpack.ProgressPlugin(),
54 | new webpack.optimize.OccurrenceOrderPlugin(),
55 | new webpack.HotModuleReplacementPlugin(),
56 | new webpack.NoEmitOnErrorsPlugin()
57 | ]
58 | }
59 |
--------------------------------------------------------------------------------