Skip to content

Instantly share code, notes, and snippets.

@mike183
Last active August 17, 2020 18:15
Show Gist options
  • Save mike183/ec667248ae703707db4e2157709eae4c to your computer and use it in GitHub Desktop.
Save mike183/ec667248ae703707db4e2157709eae4c to your computer and use it in GitHub Desktop.
LexasCMS Blog Example
// ===========================================================
// ===========================================================
//
// This is an example of a simple blog website which has been
// created using the following technologies:
//
// - LexasCMS (https://www.lexascms.com)
// - Ember.js (https://emberjs.com/)
// - TailwindCSS (https://tailwindcss.com/)
//
// Source Code: https://github.com/LexasCMS/example-ember-blog
// Author: Michael Donaldson (Twitter: @mike_183)
//
// ===========================================================
// ===========================================================
import LexascmsAdapter from 'ember-data-lexascms/adapters/lexascms';
export default class ApplicationAdapter extends LexascmsAdapter {
}
<div class="grid lg:grid-cols-5 gap-4 md:gap-6">
<LinkTo @route="post" @model={{@post.slug}} class="block lg:col-span-3">
<img src="{{@post.coverImage.url}}" class="w-full object-cover rounded" />
</LinkTo>
<div class="lg:col-span-2">
<h2 class="text-3xl leading-10 font-bold mb-3">
<LinkTo @route="post" @model={{@post.slug}} class="block hover:text-gray-700">{{@post.title}}</LinkTo>
</h2>
<div class="flex font-medium text-gray-900 mb-3">
{{moment-format @post.publishedAt "MMMM D, YYYY"}}
</div>
<div class="text-gray-700 mb-4">
<p>{{@post.excerpt}}</p>
</div>
<LinkTo @route="post" @model={{@post.slug}} class="text-gray-900 font-medium hover:text-gray-700">Read More &rarr;</LinkTo>
</div>
</div>
<div>
<LinkTo @route="post" @model={{@post.slug}} class="block h-56 sm:h-48 mb-4">
<img src="{{@post.coverImage.url}}" class="w-full h-full object-cover rounded" />
</LinkTo>
<h2 class="text-2xl leading-8 font-bold mb-3">
<LinkTo @route="post" @model={{@post.slug}} class="block hover:text-gray-700">{{@post.title}}</LinkTo>
</h2>
<div class="flex font-medium text-gray-900 mb-3">
{{moment-format @post.publishedAt "MMMM D, YYYY"}}
</div>
<div class="text-gray-700 mb-4">
{{@post.excerpt}}
</div>
<div class="flex justify-between">
<LinkTo @route="post" @model={{@post.slug}} class="text-gray-900 font-medium hover:text-gray-700">Read More &rarr;</LinkTo>
</div>
</div>
<div class="flex justify-between border-t border-gray-300 pt-6 mt-12">
<div>
{{#if @prevPage}}
<LinkTo @route="page" @model={{@prevPage}} class="text-gray-900 font-medium hover:text-gray-700">&larr; Newer Posts</LinkTo>
{{/if}}
</div>
<div>
{{#if @nextPage}}
<LinkTo @route="page" @model={{@nextPage}} class="text-gray-900 font-medium hover:text-gray-700">Older Posts &rarr;</LinkTo>
{{/if}}
</div>
</div>
import Controller from '@ember/controller';
export default class ApplicationController extends Controller {
appName = 'Ember Twiddle';
}
import Model, { attr } from '@ember-data/model';
export default class AuthorModel extends Model {
@attr name;
}
import Model, { attr, belongsTo } from '@ember-data/model';
export default class BlogPostModel extends Model {
@attr slug;
@attr title;
@attr publishedAt;
@attr excerpt;
@attr mainContent;
@belongsTo('author') author;
@belongsTo('core-image') coverImage;
}
import EmberRouter from '@ember/routing/router';
import config from './config/environment';
const Router = EmberRouter.extend({
location: 'none',
rootURL: config.rootURL
});
Router.map(function() {
this.route('page', { path: '/page/:page_num' });
this.route('post', { path: '/:post_slug' });
});
export default Router;
import Route from '@ember/routing/route';
import { action } from '@ember/object';
export default class ApplicationRoute extends Route {
@action
didTransition() {
// Reset scroll position when route changes
window.scrollTo(0, 0);
}
}
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default class IndexRoute extends Route {
@service store;
async model() {
// Define page size
const pageSize = 10;
// Retrieve blog posts from LexasCMS
const blogPosts = await this.store.query('blog-post', {
page: { limit: pageSize },
sort: '-publishedAt',
include: 'coverImage'
});
// Return
return {
featured: blogPosts.firstObject,
blogPosts: blogPosts.slice(1),
hasNextPage: blogPosts.meta.total > pageSize
}
}
}
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default class PageRoute extends Route {
@service store;
beforeModel() {
// Get page number
const { page_num } = this.paramsFor('page');
// Redirect is this is page 1
if (page_num === '1') {
this.transitionTo('index');
}
}
async model({ page_num }) {
// Define variables
const currentPage = parseInt(page_num, 10);
const postsOnFirstPage = 10;
const postsPerPage = 9;
// Retrieve blog posts from LexasCMS
const blogPosts = await this.store.query('blog-post', {
page: {
limit: postsPerPage,
skip: (postsPerPage * (currentPage - 2)) + postsOnFirstPage
},
sort: '-publishedAt',
include: 'coverImage'
});
// Calculate total pages
const totalPages = Math.ceil((blogPosts.meta.total - postsOnFirstPage) / postsPerPage) + 1;
// Return
return {
blogPosts,
nextPage: currentPage < totalPages ? currentPage + 1 : undefined,
prevPage: currentPage - 1
};
}
}
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default class PostRoute extends Route {
@service store;
async model({ post_slug }) {
// Query for blog post by slug
const blogPost = await this.store.query('blog-post', {
page: { limit: 1 },
filter: {
slug: { _eq: post_slug }
},
include: 'author,coverImage'
});
// Return blog post
return blogPost.firstObject;
}
}
import LexascmsSerializer from 'ember-data-lexascms/serializers/lexascms';
export default class ApplicationSerializer extends LexascmsSerializer {
}
<div class="px-5">
<div class="max-w-screen-lg mx-auto py-16">
{{outlet}}
</div>
</div>
<h1 class="text-6xl font-bold mb-10">Blog</h1>
<div class="border-b border-gray-300 pb-12 lg:pb-16 mb-12 lg:mb-16">
<BlogPost::Large @post={{@model.featured}} />
</div>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-5 row-gap-12 sm:row-gap-16">
{{#each @model.blogPosts as |blogPost|}}
<BlogPost::Small @post={{blogPost}} />
{{/each}}
</div>
{{#if @model.hasNextPage}}
<Pagination @nextPage={{2}} />
{{/if}}
<h1 class="text-6xl font-bold mb-10">Blog</h1>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-5 row-gap-12 sm:row-gap-16">
{{#each @model.blogPosts as |blogPost|}}
<BlogPost::Small @post={{blogPost}} />
{{/each}}
</div>
<Pagination @prevPage={{@model.prevPage}} @nextPage={{@model.nextPage}} />
<h1 class="text-4xl sm:text-5xl leading-10 sm:leading-14 font-bold mb-8 sm:text-center">{{@model.title}}</h1>
<div class="text-lg font-medium text-gray-900 sm:text-center mb-10">
Published on {{moment-format @model.publishedAt "MMMM D, YYYY"}} by {{@model.author.name}}
</div>
<div class="max-w-screen-md mx-auto">
<img src="{{@model.coverImage.url}}" class="w-full object-cover rounded mb-6" />
<div class="text-gray-700 leading-8 space-y-4 mb-8">
{{markdown-to-html @model.mainContent tagName=""}}
</div>
<LinkTo @route="index" class="text-gray-900 font-medium hover:text-gray-700">&larr; Back to Posts</LinkTo>
</div>
{
"version": "0.17.1",
"EmberENV": {
"FEATURES": {},
"_TEMPLATE_ONLY_GLIMMER_COMPONENTS": true,
"_APPLICATION_TEMPLATE_WRAPPER": false,
"_JQUERY_INTEGRATION": false
},
"ENV": {
"lexascms": {
"spaceId": "7feec553-ece1-44c2-b81a-53e69625b54a"
}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"ember": "3.18.1",
"ember-template-compiler": "3.18.1",
"ember-testing": "3.18.1",
"tailwindcss": "https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css"
},
"addons": {
"@glimmer/component": "1.0.0",
"ember-cli-showdown": "4.5.0",
"ember-data": "3.18.0",
"ember-data-lexascms": "0.1.2",
"ember-moment": "8.0.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment