├── .gitignore
├── README.md
├── package.json
└── src
├── assets
├── favicon.ico
├── icons
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── apple-touch-icon.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ └── mstile-150x150.png
└── images
│ └── splash-image.jpg
├── components
├── app.js
└── header
│ ├── index.js
│ └── style.css
├── constants.js
├── index.js
├── manifest.json
├── routes
├── 404
│ ├── index.js
│ └── style.css
├── blog
│ ├── post.js
│ ├── posts.js
│ └── style.css
├── home
│ ├── index.js
│ └── style.css
└── profile
│ ├── index.js
│ └── style.css
└── style
└── index.css
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | .DS_Store
61 |
62 | # lock files
63 | yarn.lock
64 | package-lock.json
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # preact material app
2 |
3 | Super performant `Material` app for preact world using [preact-material-components](https://github.com/prateekbh/preact-material-components)
4 |
5 | ## CLI Commands
6 |
7 | ``` bash
8 | # install dependencies
9 | npm install
10 |
11 | # serve with hot reload at localhost:8080
12 | npm run dev
13 |
14 | # build for production with minification
15 | npm run build
16 |
17 | # test the production build locally
18 | npm run serve
19 | ```
20 |
21 | For detailed explanation on how things work, checkout the [CLI Readme](https://github.com/developit/preact-cli/blob/master/README.md).
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "preact-wordpress",
4 | "version": "0.0.0",
5 | "license": "MIT",
6 | "scripts": {
7 | "start": "if-env NODE_ENV=production && npm run -s serve || npm run -s dev",
8 | "build": "preact build",
9 | "serve": "preact build && preact serve",
10 | "dev": "preact watch",
11 | "lint": "eslint src"
12 | },
13 | "eslintConfig": {
14 | "extends": "eslint-config-synacor",
15 | "rules": {
16 | "no-unused-vars": "warn",
17 | "react/sort-comp": "off",
18 | "lines-around-comment": "off",
19 | "react/prefer-stateless-function": "off"
20 | }
21 | },
22 | "eslintIgnore": [
23 | "build/*"
24 | ],
25 | "devDependencies": {
26 | "eslint": "^4.5.0",
27 | "eslint-config-synacor": "^1.1.0",
28 | "if-env": "^1.0.0",
29 | "preact-cli": "^2.0.0"
30 | },
31 | "dependencies": {
32 | "npm": "^6.8.0",
33 | "preact": "^8.2.1",
34 | "preact-compat": "^3.17.0",
35 | "preact-helmet": "^4.0.0-alpha-3",
36 | "preact-markup": "^1.6.0",
37 | "preact-material-components": "^1.4.3",
38 | "preact-router": "^2.5.5"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Michael-Brooks/preact-wp/2e3f17750d50409fee9941e942ec17faad573a11/src/assets/favicon.ico
--------------------------------------------------------------------------------
/src/assets/icons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Michael-Brooks/preact-wp/2e3f17750d50409fee9941e942ec17faad573a11/src/assets/icons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/src/assets/icons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Michael-Brooks/preact-wp/2e3f17750d50409fee9941e942ec17faad573a11/src/assets/icons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/src/assets/icons/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Michael-Brooks/preact-wp/2e3f17750d50409fee9941e942ec17faad573a11/src/assets/icons/apple-touch-icon.png
--------------------------------------------------------------------------------
/src/assets/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Michael-Brooks/preact-wp/2e3f17750d50409fee9941e942ec17faad573a11/src/assets/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/src/assets/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Michael-Brooks/preact-wp/2e3f17750d50409fee9941e942ec17faad573a11/src/assets/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/src/assets/icons/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Michael-Brooks/preact-wp/2e3f17750d50409fee9941e942ec17faad573a11/src/assets/icons/mstile-150x150.png
--------------------------------------------------------------------------------
/src/assets/images/splash-image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Michael-Brooks/preact-wp/2e3f17750d50409fee9941e942ec17faad573a11/src/assets/images/splash-image.jpg
--------------------------------------------------------------------------------
/src/components/app.js:
--------------------------------------------------------------------------------
1 | import { h, Component } from 'preact';
2 | import { Router } from 'preact-router';
3 |
4 | import Header from './header';
5 | import Home from '../routes/home';
6 | import Posts from '../routes/blog/posts';
7 | import Post from '../routes/blog/post';
8 | import NotFound from '../routes/404';
9 |
10 | export default class App extends Component {
11 | /** Gets fired when the route changes.
12 | * @param {Object} event "change" event from [preact-router](http://git.io/preact-router)
13 | * @param {string} event.url The newly routed URL
14 | */
15 | handleRoute = e => {
16 | this.setState({
17 | currentUrl: e.url
18 | });
19 | };
20 |
21 | render() {
22 | return (
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/header/index.js:
--------------------------------------------------------------------------------
1 | import { h, Component } from 'preact';
2 | import { route } from 'preact-router';
3 | import TopAppBar from 'preact-material-components/TopAppBar';
4 | import Drawer from 'preact-material-components/Drawer';
5 | import List from 'preact-material-components/List';
6 | import { appName } from '../../constants';
7 | import 'preact-material-components/Switch/style.css';
8 | import 'preact-material-components/Dialog/style.css';
9 | import 'preact-material-components/Drawer/style.css';
10 | import 'preact-material-components/List/style.css';
11 | import 'preact-material-components/TopAppBar/style.css';
12 | import style from './style';
13 |
14 | export default class Header extends Component {
15 | closeDrawer() {
16 | this.drawer.MDComponent.open = false;
17 | }
18 |
19 | openDrawer = () => (this.drawer.MDComponent.open = true);
20 |
21 | drawerRef = drawer => (this.drawer = drawer);
22 |
23 | linkTo = path => () => {
24 | route(path);
25 | this.closeDrawer();
26 | };
27 |
28 | goHome = this.linkTo('/');
29 | goToBlog = this.linkTo('/blog');
30 | nonBlogLinks = [
31 | '/',
32 | '/blog'
33 | ];
34 |
35 | render(props) {
36 | return (
37 |
38 |
39 |
40 |
41 | {appName}
42 |
43 |
44 |
45 | menu
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | home
54 | Home
55 |
56 |
59 | toc
60 | Blog
61 |
62 |
63 |
64 |
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/components/header/style.css:
--------------------------------------------------------------------------------
1 | a {
2 | cursor: pointer;
3 | }
4 |
5 | .topbar {
6 | top: 0;
7 | background-color: #fff;
8 | color: #000;
9 | -webkit-box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.2), 0 0 2px 0 rgba(0, 0, 0, 0.14), 0 0 2px 0 rgba(0, 0, 0, 0.12);
10 | box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.2), 0 0 10px 0 rgba(0, 0, 0, 0.14), 0 0 10px 0 rgba(0, 0, 0, 0.12);
11 | }
12 |
13 | .topbar__row {
14 | max-width: 100%;
15 | width: 1250px;
16 | margin: 0 auto;
17 | }
18 |
19 | .mdc-drawer--modal {
20 | top: 0;
21 | background-color: #000;
22 | }
--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
1 | const protocol = 'https';
2 | const baseUrl = 'www.codeinwp.com/wp-json/wp/v2/';
3 | const appName = 'Michael Brooks';
4 | const homeTitle = `${appName} | Website Developer | Newton Abbot`;
5 | const blogTitle = `Blog | ${appName}`;
6 |
7 | module.exports = {
8 | baseUrl,
9 | protocol,
10 | appName,
11 | homeTitle,
12 | blogTitle
13 | };
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import './style';
2 | import App from './components/app';
3 |
4 | export default App;
5 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "preact-wordpress",
3 | "short_name": "preact-wordpress",
4 | "start_url": "/",
5 | "display": "standalone",
6 | "orientation": "portrait",
7 | "background_color": "#fff",
8 | "theme_color": "#673ab8",
9 | "icons": [
10 | {
11 | "src": "/assets/icons/android-chrome-192x192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "/assets/icons/android-chrome-512x512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/src/routes/404/index.js:
--------------------------------------------------------------------------------
1 | import { h, Component } from 'preact';
2 | import Card from 'preact-material-components/Card';
3 | import 'preact-material-components/Card/style.css';
4 | import style from './style';
5 |
6 | export default class NotFound extends Component {
7 | render() {
8 | return (
9 |
10 |
11 |
14 |
15 | Looks like the page you are trying to access, doesn't exist.
16 |
17 |
18 |
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/routes/404/style.css:
--------------------------------------------------------------------------------
1 | .home {
2 | padding: 20px;
3 | min-height: 100%;
4 | width: 100%;
5 | }
6 |
7 | .cardHeader {
8 | padding: 16px;
9 | }
10 |
11 | .cardBody {
12 | padding: 16px;
13 | }
14 |
--------------------------------------------------------------------------------
/src/routes/blog/post.js:
--------------------------------------------------------------------------------
1 | import { h, Component } from 'preact';
2 | import Markup from 'preact-markup';
3 | import Helmet from 'preact-helmet';
4 | import Card from 'preact-material-components/Card';
5 | import { protocol, baseUrl } from '../../constants';
6 | import 'preact-material-components/Card/style.css';
7 | import 'preact-material-components/Button/style.css';
8 | import style from './style';
9 |
10 | export default class Post extends Component {
11 | constructor() {
12 | super();
13 | this.state = {
14 | loading: true,
15 | posts: []
16 | };
17 | }
18 |
19 | componentDidMount() {
20 | fetch(`${protocol}://${baseUrl}posts?slug=${this.props.slug}`)
21 | .then(response => response.json())
22 | .then((data) => {
23 | let posts = data.map((post) => {
24 | return (
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 | );
35 | });
36 | this.setState({ loading: false, posts });
37 | })
38 | .catch(error => {
39 | this.setState({ loading: false });
40 | });
41 | }
42 |
43 | loadPosts() {
44 | if (this.state.loading) {
45 | return (
46 |
49 | );
50 | }
51 |
52 | if (this.state.loading === false && this.state.posts.length === 0) {
53 | return (
54 |
57 | );
58 | }
59 |
60 | return (
61 | this.state.posts
62 | );
63 | }
64 |
65 | render() {
66 | return (
67 |
68 | {this.loadPosts()}
69 |
70 | );
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/routes/blog/posts.js:
--------------------------------------------------------------------------------
1 | import { h, Component } from 'preact';
2 | import Markup from 'preact-markup';
3 | import Helmet from 'preact-helmet';
4 | import Card from 'preact-material-components/Card';
5 | import { protocol, baseUrl, blogTitle } from '../../constants';
6 | import 'preact-material-components/Card/style.css';
7 | import 'preact-material-components/Button/style.css';
8 | import style from './style';
9 |
10 | export default class Posts extends Component {
11 | constructor() {
12 | super();
13 | this.state = {
14 | loading: true,
15 | posts: []
16 | };
17 | }
18 |
19 | componentDidMount() {
20 | fetch(`${protocol}://${baseUrl}posts?page=${this.props.page || 1}`)
21 | .then(response => response.json())
22 | .then((data) => {
23 | let posts = data.map((post) => {
24 | const renderExcerpt = `
25 | ${post.excerpt.rendered.split(' ').splice(0, 54).join(' ')}...
26 | `;
27 |
28 | return (
29 |
30 |
31 |
34 |
38 |
39 | );
40 | });
41 | this.setState({ loading: false, posts });
42 | })
43 | .catch(error => {
44 | this.setState({ loading: false });
45 | });
46 | }
47 |
48 | loadPosts() {
49 | if (this.state.loading) {
50 | return (
51 |
54 | );
55 | }
56 |
57 | if (this.state.loading === false && this.state.posts.length === 0) {
58 | return (
59 |
62 | );
63 | }
64 |
65 | return (
66 | this.state.posts
67 | );
68 | }
69 |
70 | render(props) {
71 | return (
72 |
73 |
Blog {props.page && ` - Page ${props.page}`}
74 | {this.loadPosts()}
75 |
76 | );
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/routes/blog/style.css:
--------------------------------------------------------------------------------
1 | .blog {
2 | min-height: 100%;
3 | max-width: 1080px;
4 | width: 90%;
5 | background: #fff;
6 | margin: 72px 15px 0;
7 | padding: 15px;
8 | }
9 |
10 | #app .page {
11 | padding-top: 0;
12 | }
13 |
14 | .cardHeader {
15 | padding: 16px;
16 | }
17 |
18 | .cardBody {
19 | padding: 16px;
20 | }
21 |
22 | .mdcCard {
23 | margin: 50px 0;
24 | }
25 |
26 | .screen-reader-text {
27 | border: 0;
28 | clip: rect(1px, 1px, 1px, 1px);
29 | -webkit-clip-path: inset(50%);
30 | clip-path: inset(50%);
31 | height: 1px;
32 | margin: -1px;
33 | overflow: hidden;
34 | padding: 0;
35 | position: absolute !important;
36 | width: 1px;
37 | word-wrap: normal !important;
38 | }
--------------------------------------------------------------------------------
/src/routes/home/index.js:
--------------------------------------------------------------------------------
1 | import { h, Component } from 'preact';
2 | import Helmet from 'preact-helmet';
3 | import { homeTitle } from '../../constants';
4 | import 'preact-material-components/Card/style.css';
5 | import 'preact-material-components/Button/style.css';
6 | import style from './style';
7 |
8 | export default class Home extends Component {
9 | render() {
10 | return (
11 |
12 |
13 |

14 |
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/routes/home/style.css:
--------------------------------------------------------------------------------
1 | .home {
2 | margin-top: 56px;
3 | min-height: 100%;
4 | width: 100%;
5 | }
6 |
7 | .cardHeader {
8 | padding: 16px;
9 | }
10 |
11 | .cardBody {
12 | padding: 16px;
13 | }
14 |
15 | img {
16 | width: 100%;
17 | }
--------------------------------------------------------------------------------
/src/routes/profile/index.js:
--------------------------------------------------------------------------------
1 | import { h, Component } from 'preact';
2 | import Button from 'preact-material-components/Button';
3 | import 'preact-material-components/Button/style.css';
4 | import style from './style';
5 |
6 | export default class Profile extends Component {
7 | state = {
8 | time: Date.now(),
9 | count: 10
10 | };
11 |
12 | // gets called when this route is navigated to
13 | componentDidMount() {
14 | // start a timer for the clock:
15 | this.timer = setInterval(this.updateTime, 1000);
16 | }
17 |
18 | // gets called just before navigating away from the route
19 | componentWillUnmount() {
20 | clearInterval(this.timer);
21 | }
22 |
23 | // update the current time
24 | updateTime = () => {
25 | this.setState({ time: Date.now() });
26 | };
27 |
28 | increment = () => {
29 | this.setState({ count: this.state.count+1 });
30 | };
31 |
32 | // Note: `user` comes from the URL, courtesy of our router
33 | render({ user }, { time, count }) {
34 | return (
35 |
36 |
Profile: {user}
37 |
This is the user profile for a user named { user }.
38 |
39 |
Current time: {new Date(time).toLocaleString()}
40 |
41 |
42 |
43 | {' '}
44 | Clicked {count} times.
45 |
46 |
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/routes/profile/style.css:
--------------------------------------------------------------------------------
1 | .profile {
2 | padding: 20px;
3 | min-height: 100%;
4 | width: 100%;
5 | }
6 |
--------------------------------------------------------------------------------
/src/style/index.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | width: 100%;
3 | padding: 0;
4 | margin: 0;
5 | background: #FAFAFA;
6 | font-family: 'Helvetica Neue', arial, sans-serif;
7 | font-weight: 400;
8 | color: #444;
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | }
12 |
13 | /* fallback */
14 | @font-face {
15 | font-family: 'Material Icons';
16 | font-style: normal;
17 | font-weight: 400;
18 | src: url(https://fonts.gstatic.com/s/materialicons/v29/2fcrYFNaTjcS6g4U3t-Y5UEw0lE80llgEseQY3FEmqw.woff2) format('woff2');
19 | }
20 |
21 | .material-icons {
22 | font-family: 'Material Icons';
23 | font-weight: normal;
24 | font-style: normal;
25 | font-size: 24px;
26 | line-height: 1;
27 | letter-spacing: normal;
28 | text-transform: none;
29 | display: inline-block;
30 | white-space: nowrap;
31 | word-wrap: normal;
32 | direction: ltr;
33 | -webkit-font-feature-settings: 'liga';
34 | -webkit-font-smoothing: antialiased;
35 | }
36 |
37 | * {
38 | box-sizing: border-box;
39 | }
40 |
41 | .mdc-theme--dark {
42 | background-color: #333;
43 | color: #fff;
44 | }
45 |
46 | .mdc-theme--dark .mdc-card {
47 | color: #444;
48 | }
--------------------------------------------------------------------------------