├── .babelrc
├── .gitignore
├── .travis.yml
├── README.md
├── SUMMARY.md
├── book.json
├── build
└── gitbook.css
├── dist
├── preact-layout.min.js
└── preact-layout.umd.js
├── docs
├── README.md
├── api
│ ├── Layout.md
│ ├── README.md
│ ├── Section.md
│ └── contribution-functions.md
├── contributing-guide
│ └── README.md
└── getting-started
│ ├── Examples.md
│ ├── README.md
│ ├── Setup.md
│ └── Usage-guide.md
├── examples
├── README.md
├── buildAll.js
├── header-footer
│ ├── .babelrc
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── components
│ │ │ ├── HomePage.jsx
│ │ │ └── Layouts.jsx
│ │ └── index.jsx
│ └── webpack.config.js
└── testAll.js
├── package.json
├── preact-layout.png
├── src
└── index.jsx
├── test
└── Layout.spec.jsx
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | ["transform-es2015-template-literals", { "loose": true }],
4 | "transform-es2015-literals",
5 | "transform-es2015-function-name",
6 | "transform-es2015-arrow-functions",
7 | "transform-es2015-block-scoped-functions",
8 | ["transform-es2015-classes", { "loose": true }],
9 | "transform-es2015-object-super",
10 | "transform-es2015-shorthand-properties",
11 | ["transform-es2015-computed-properties", { "loose": true }],
12 | ["transform-es2015-for-of", { "loose": true }],
13 | "transform-es2015-sticky-regex",
14 | "transform-es2015-unicode-regex",
15 | "check-es2015-constants",
16 | ["transform-es2015-spread", { "loose": true }],
17 | "transform-es2015-parameters",
18 | ["transform-es2015-destructuring", { "loose": true }],
19 | "transform-es2015-block-scoping",
20 | "transform-object-rest-spread",
21 | "transform-es3-member-expression-literals",
22 | "transform-es3-property-literals",
23 | ["transform-react-jsx", { "pragma": "h" }]
24 | ],
25 | "env": {
26 | "commonjs": {
27 | "plugins": [
28 | ["transform-es2015-modules-commonjs", { "loose": true }],
29 | ["transform-react-jsx", { "pragma": "h" }]
30 | ]
31 | },
32 | "es": {
33 | "plugins": [
34 | ["transform-react-jsx", { "pragma": "h" }]
35 | ]
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.log
3 | node_modules
4 | lib
5 | es
6 | coverage
7 | _book
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "4"
4 | - "5"
5 | - "6"
6 | script:
7 | - npm run test
8 | - npm run build
9 | - npm run check:examples
10 | branches:
11 | only:
12 | - master
13 | - /^greenkeeper-.*$/
14 | cache:
15 | directories:
16 | - $HOME/.npm
17 | - examples/header-footer/node_modules
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # preact-layout
2 | #### Small and simple layout library for Preact.
3 |
4 | [](https://npmjs.com/package/preact-layout)
5 | [](https://creativecommons.org/licenses/by/4.0/)
6 | [](https://travis-ci.org/Download/preact-layout)
7 | [](https://greenkeeper.io/)
8 | 
9 |
10 | 
11 |
12 | ### Think out of the box!
13 |
14 |
15 |
16 |
17 |
18 |
19 | [Preact](https://preactjs.com/) is beautiful and pure. Literally, because with
20 | Preact we mostly write pure components that take properties and render markup
21 | possibly including child components that we control via their props. Information
22 | flows one way and everything is good.
23 |
24 | But what if a component needs to render some data in another section of the page?
25 | In the header for example? Do we really have to make the root component aware of
26 | the title of each page? What if we need something in the footer as well? Or in
27 | some sidebar? We want our components to be able to contribute content to other
28 | sections of the page, even if those sections are higher up the tree...
29 | Is it even possible?
30 |
31 | preact-layout does not only make this possible, it makes it **simple**!
32 |
33 | ## Getting Started
34 | Getting started with preact-layout:
35 | * Play with the [Preact Layout Kickstart](http://codepen.io/StijnDeWitt/pen/rrzJEA?editors=0010) CodePen.
36 | * [Setup](https://download.github.io/preact-layout/docs/getting-started/Setup.html) - Add preact-layout to your project
37 | * [Basic Usage](https://download.github.io/preact-layout/docs/getting-started/Basic-usage.html) - Using Layout, Section and contribution functions
38 | * [Examples](https://download.github.io/preact-layout/docs/getting-started/Examples.html) - Learn by example!
39 |
40 | ## Why
41 | Preact Layout elegantly solves some common layout challenges you will undoubtedly
42 | encounter when using Preact, due to the hierarchical nature of the component
43 | tree and the one-way data flow. Preact Layout allows you to think out of the box.
44 |
45 | ### Simple but powerful API
46 | With just 2 components, the API is very simple to learn, yet powerful.
47 | * [Layout](docs/api/Layout.md) to define layouts
48 | * [Section](docs/api/Section.md) to divide the layout into multiple sections
49 |
50 | ### Lightweight
51 | preact-layout is microscopically small. The sources are just over 2 Kb
52 | and the minified and gzipped distribution file weighs only 1 kB.
53 |
54 | ### Well documented
55 | A good library needs plenty of good docs. In the case of preact-layout, the
56 | docs are way bigger than the library itself! We have a
57 | [Usage guide](https://download.github.io/preact-layout/docs/getting-started/Basic-usage.html),
58 | [API docs](https://download.github.io/preact-layout/docs/api/),
59 | [Examples](https://download.github.io/preact-layout/docs/getting-started/Examples.html) and a
60 | [Preact Layout Kickstart CodePen](http://codepen.io/StijnDeWitt/pen/rrzJEA?editors=0010) to learn from.
61 |
62 | ### Well tested
63 | Good tests ensure not only that the code works, but that it keeps working! We use [Travis
64 | CI](https://travis-ci.org/Download/preact-layout) to test every push to master so we
65 | catch any bugs before they make it into a release.
66 |
67 | ### Open Source, Open Community
68 | preact-layout is Open Source software with a
69 | [public code repository](https://github.com/download/preact-layout) and
70 | [public issue tracker](https://github.com/download/preact-layout/issues).
71 |
72 | ## Using preact-layout
73 | * Define contribution functions
74 | * Create a layout
75 | * Use the contribution functions in your components
76 | * Nest the component inside the layout
77 |
78 | ### Define contribution functions
79 | preact-layout allows you to define [contribution functions](https://download.github.io/preact-layout/docs/api/contribution-functions.html)
80 | that are used as JSX tags that signal that the components contained in those tags
81 | should be contributed to *another section* of the parent layout.
82 |
83 | For example, for a simple layout with a header and a footer around some main
84 | content block, we might define these functions:
85 |
86 | ```js
87 | function Header(){}
88 | function Footer(){}
89 | ```
90 |
91 | ### Create a layout
92 | Create a layout with sections for the Header and Footer:
93 |
94 | ```js
95 | function MyLayout({children}) {return (
96 |
97 |
108 |
109 | )}
110 | ```
111 |
112 | ### Use the contribution functions in your components
113 | Now, we can build components that use the contribution functions to contribute
114 | components to the related sections of the layout:
115 |
116 | ```js
117 | function MyPage(){return (
118 |
119 |
my page
120 | main article
121 |
122 |
123 | )}
124 | ```
125 |
126 | ### Nest the component inside the layout
127 | Finally, render the component nested within the layout:
128 |
129 | ```js
130 | render(
131 |
132 |
133 |
134 | ,
135 | document.getElementsByTagName('body')[0]
136 | )
137 | ```
138 |
139 | This will result in:
140 |
141 | ```html
142 |
143 |
144 |
my page
145 |
146 |
147 | main article
148 |
149 |
152 |
153 | ```
154 | Read more in the [Usage Guide](https://download.github.io/preact-layout/docs/getting-started/Usage-guide.html).
155 |
156 | ## Component Reference
157 | * [Layout](https://download.github.io/preact-layout/docs/api/Layout.html) - Create layouts using `Layout`
158 | * [Section](https://download.github.io/preact-layout/docs/api/Section.html) - Divide your layout into named `Section`s
159 | * [contribution functions](https://download.github.io/preact-layout/docs/api/contribution-functions.html) - Defining conribution functions
160 |
161 | ## Performance considerations
162 | When collecting contributions, preact-layout peeks ahead at the elements that
163 | the child components will produce by rendering those child components. The
164 | resulting elements could itself again contain components, which would need to
165 | be rendered as well, leading to recursive rendering that of course comes at a
166 | performance cost. You can tune this cost by setting the
167 | [recurse](https://download.github.io/preact-layout/docs/api/Layout.html#recurse)
168 | attribute on the `Layout` to some positive number. It defaults to `9`.
169 |
170 | ## Issues
171 | Add an issue in this project's [issue tracker](https://github.com/download/preact-layout/issues)
172 | to let me know of any problems you find, or questions you may have.
173 |
174 | ## Contributing
175 | This project welcomes contributions from the community!
176 | Learn more in the [contributing guide](https://download.github.io/preact-layout/docs/contributing-guide/).
177 |
178 | ## Copyright
179 | Copyright 2016 by [Stijn de Witt](http://StijnDeWitt.com). Some rights reserved.
180 |
181 | ## License
182 | Licensed under the [Creative Commons Attribution 4.0 International (CC-BY-4.0)](https://creativecommons.org/licenses/by/4.0/) Open Source license.
183 |
184 |
--------------------------------------------------------------------------------
/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Table of Contents
2 |
3 | * [Getting Started](/docs/getting-started/README.md)
4 | * [Setup](/docs/getting-started/Setup.md)
5 | * [Usage Guide](/docs/getting-started/Usage-guide.md)
6 | * [Examples](/docs/getting-started/Examples.md)
7 | * [Component Reference](/docs/api/README.md)
8 | * [Layout](/docs/api/Layout.md)
9 | * [Section](/docs/api/Section.md)
10 | * [contribution functions](/docs/api/contribution-functions.md)
11 | * [Contributing Guide](/docs/contributing-guide/README.md)
12 |
--------------------------------------------------------------------------------
/book.json:
--------------------------------------------------------------------------------
1 | {
2 | "gitbook": "3.x.x",
3 | "title": "preact-layout",
4 | "plugins": ["prism", "-highlight", "github", "anchorjs"],
5 | "pluginsConfig": {
6 | "github": {
7 | "url": "https://github.com/Download/preact-layout/"
8 | },
9 | "theme-default": {
10 | "showLevel": true,
11 | "styles": {
12 | "website": "build/gitbook.css"
13 | }
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/build/gitbook.css:
--------------------------------------------------------------------------------
1 | .book-summary ul.summary li span {
2 | cursor: not-allowed;
3 | opacity: 0.3;
4 | }
5 |
6 | .book-summary ul.summary li a:hover {
7 | color: #008cff;
8 | text-decoration: none;
9 | }
10 |
--------------------------------------------------------------------------------
/dist/preact-layout.min.js:
--------------------------------------------------------------------------------
1 | !function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t(require("preact"));else if("function"==typeof define&&define.amd)define(["preact"],t);else{var n=t("object"==typeof exports?require("preact"):e.preact);for(var r in n)("object"==typeof exports?exports:e)[r]=n[r]}}(this,function(e){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){"use strict";function r(e,t){var n={};for(var r in e)t.indexOf(r)<0&&Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function o(e,t){var n=e.className,o=e.recurse,i=e.children,u=(r(e,["className","recurse","children"]),a(i)),d=u.main,f=u.sections;return c(d,f,s({},t),o),i&&1===i.length?i[0]:(0,p.h)("div",{className:n||"Layout"},i)}function i(e,t){var n=e.children,o=r(e,["type","children"]);return n&&1===n.length?n[0]:(0,p.h)("div",o,n)}function a(e,t){t||(t={sections:[]}),e.nodeName===i&&(e.attributes&&e.attributes.type?t.sections.push(e):t.main=e);var n=Array.isArray(e)?e:e.children;return n&&n.forEach(function(e){a(e,t)}),t}function c(e,t,n,r,o,i){var a=[],p=!i;return n=n||{},void 0===r&&(r=9),i=i||{},t.forEach(function(e){return i[e.attributes.type]=i[e.attributes.type]||e.children||[]}),e&&e.children&&e.children.forEach(function(e){if(u(e,t))return i[e.nodeName]||(i[e.nodeName]=[]),void(e.attributes&&e.attributes.append?i[e.nodeName].push.apply(i[e.nodeName],e.children||[]):e.attributes&&e.attributes.prepend?i[e.nodeName].unshift.apply(i[e.nodeName],e.children||[]):i[e.nodeName]=e.children||[]);if(a.push(e),"function"==typeof e.nodeName&&r){var p=s({},e.nodeName.defaultProps,e.attributes,{children:e.children});if(e.nodeName.prototype&&"function"==typeof e.nodeName.prototype.render){var d=new e.nodeName(p,n);d.props=p,d.context=n,d.componentWillMount&&d.componentWillMount(),e=d.render(d.props,d.state,d.context),d.getChildContext&&(n=s({},n,d.getChildContext()))}else e=e.nodeName(p,n);r--}c(e,t,n,r,o,i)}),o||(e.children&&(e.children=a),p&&t.forEach(function(e){return e.children=i[e.attributes.type]})),i}function u(e,t){return t.filter(function(t){return e.nodeName===t.attributes.type}).length>0}t.__esModule=!0,t.processNode=t.isContribution=t.getSections=t.Section=t.Layout=void 0;var s=Object.assign||function(e){for(var t=1;arguments.length>t;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},p=n(1);t.Layout=o,t.Section=i,t.getSections=a,t.isContribution=u,t.processNode=c},function(t,n){t.exports=e}])});
--------------------------------------------------------------------------------
/dist/preact-layout.umd.js:
--------------------------------------------------------------------------------
1 | (function webpackUniversalModuleDefinition(root, factory) {
2 | if(typeof exports === 'object' && typeof module === 'object')
3 | module.exports = factory(require("preact"));
4 | else if(typeof define === 'function' && define.amd)
5 | define(["preact"], factory);
6 | else {
7 | var a = typeof exports === 'object' ? factory(require("preact")) : factory(root["preact"]);
8 | for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
9 | }
10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__) {
11 | return /******/ (function(modules) { // webpackBootstrap
12 | /******/ // The module cache
13 | /******/ var installedModules = {};
14 |
15 | /******/ // The require function
16 | /******/ function __webpack_require__(moduleId) {
17 |
18 | /******/ // Check if module is in cache
19 | /******/ if(installedModules[moduleId])
20 | /******/ return installedModules[moduleId].exports;
21 |
22 | /******/ // Create a new module (and put it into the cache)
23 | /******/ var module = installedModules[moduleId] = {
24 | /******/ exports: {},
25 | /******/ id: moduleId,
26 | /******/ loaded: false
27 | /******/ };
28 |
29 | /******/ // Execute the module function
30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31 |
32 | /******/ // Flag the module as loaded
33 | /******/ module.loaded = true;
34 |
35 | /******/ // Return the exports of the module
36 | /******/ return module.exports;
37 | /******/ }
38 |
39 |
40 | /******/ // expose the modules object (__webpack_modules__)
41 | /******/ __webpack_require__.m = modules;
42 |
43 | /******/ // expose the module cache
44 | /******/ __webpack_require__.c = installedModules;
45 |
46 | /******/ // __webpack_public_path__
47 | /******/ __webpack_require__.p = "";
48 |
49 | /******/ // Load entry module and return exports
50 | /******/ return __webpack_require__(0);
51 | /******/ })
52 | /************************************************************************/
53 | /******/ ([
54 | /* 0 */
55 | /***/ function(module, exports, __webpack_require__) {
56 |
57 | 'use strict';
58 |
59 | exports.__esModule = true;
60 | exports.processNode = exports.isContribution = exports.getSections = exports.Section = exports.Layout = undefined;
61 |
62 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
63 |
64 | var _preact = __webpack_require__(1);
65 |
66 | function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
67 |
68 | function Layout(_ref, context) {
69 | var className = _ref.className;
70 | var recurse = _ref.recurse;
71 | var children = _ref.children;
72 |
73 | var props = _objectWithoutProperties(_ref, ['className', 'recurse', 'children']);
74 |
75 | var _getSections = getSections(children);
76 |
77 | var main = _getSections.main;
78 | var sections = _getSections.sections;
79 |
80 | processNode(main, sections, _extends({}, context), recurse);
81 | return children && children.length === 1 ? children[0] : (0, _preact.h)(
82 | 'div',
83 | { className: className || 'Layout' },
84 | children
85 | );
86 | }
87 |
88 | function Section(_ref2, context) {
89 | var type = _ref2.type;
90 | var children = _ref2.children;
91 |
92 | var props = _objectWithoutProperties(_ref2, ['type', 'children']);
93 |
94 | return children && children.length === 1 ? children[0] : (0, _preact.h)(
95 | 'div',
96 | props,
97 | children
98 | );
99 | }
100 |
101 | function getSections(n, result) {
102 | if (!result) result = { sections: [] };
103 | if (n.nodeName === Section) {
104 | if (n.attributes && n.attributes.type) result.sections.push(n);else result.main = n;
105 | }
106 | var children = Array.isArray(n) ? n : n.children;
107 | children && children.forEach(function (c) {
108 | getSections(c, result);
109 | });
110 | return result;
111 | }
112 |
113 | function processNode(node, sections, context, recurse, collectOnly, results) {
114 | var leftovers = [],
115 | postProcess = !results;
116 | context = context || {};
117 | if (recurse === undefined) recurse = 9;
118 | results = results || {};
119 | sections.forEach(function (s) {
120 | return results[s.attributes.type] = results[s.attributes.type] || s.children || [];
121 | });
122 | node && node.children && node.children.forEach(function (n) {
123 | if (isContribution(n, sections)) {
124 | if (!results[n.nodeName]) results[n.nodeName] = [];
125 | if (n.attributes && n.attributes.append) results[n.nodeName].push.apply(results[n.nodeName], n.children || []);else if (n.attributes && n.attributes.prepend) results[n.nodeName].unshift.apply(results[n.nodeName], n.children || []);else results[n.nodeName] = n.children || [];
126 | return; // continue
127 | }
128 | leftovers.push(n);
129 | if (typeof n.nodeName == 'function' && recurse) {
130 | var props = _extends({}, n.nodeName.defaultProps, n.attributes, { children: n.children });
131 | if (n.nodeName.prototype && typeof n.nodeName.prototype.render == 'function') {
132 | var rn = void 0,
133 | c = new n.nodeName(props, context);
134 | c.props = props;
135 | c.context = context;
136 | if (c.componentWillMount) c.componentWillMount();
137 | n = c.render(c.props, c.state, c.context);
138 | if (c.getChildContext) context = _extends({}, context, c.getChildContext());
139 | } else n = n.nodeName(props, context);
140 | recurse--;
141 | }
142 | processNode(n, sections, context, recurse, collectOnly, results);
143 | });
144 | if (!collectOnly) {
145 | if (node.children) node.children = leftovers;
146 | if (postProcess) sections.forEach(function (s) {
147 | return s.children = results[s.attributes.type];
148 | });
149 | }
150 | return results;
151 | }
152 |
153 | function isContribution(n, sections) {
154 | return sections.filter(function (s) {
155 | return n.nodeName === s.attributes.type;
156 | }).length > 0;
157 | }
158 |
159 | exports.Layout = Layout;
160 | exports.Section = Section;
161 | exports.getSections = getSections;
162 | exports.isContribution = isContribution;
163 | exports.processNode = processNode;
164 |
165 | /***/ },
166 | /* 1 */
167 | /***/ function(module, exports) {
168 |
169 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__;
170 |
171 | /***/ }
172 | /******/ ])
173 | });
174 | ;
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Table of Contents
2 |
3 | * [Getting Started](/docs/getting-started/README.md)
4 | * [Setup](/docs/getting-started/Setup.md)
5 | * [Usage Guide](/docs/getting-started/Usage-guide.md)
6 | * [Examples](/docs/getting-started/Examples.md)
7 | * [Component Reference](/docs/api/README.md)
8 | * [Layout](/docs/api/Layout.md)
9 | * [Section](/docs/api/Section.md)
10 | * [contribution functions](/docs/api/contribution-functions.md)
11 | * [Contributing Guide](/docs/contributing-guide/README.md)
12 |
--------------------------------------------------------------------------------
/docs/api/Layout.md:
--------------------------------------------------------------------------------
1 | # Layout
2 |
3 | A preact-layout `Layout`:
4 | * Defines a layout with exactly one 'main' section and zero or more sections that are associated with a contribution function.
5 | * Collects layout *contributions* from the children of the main section and renders those contributions in the associated other sections of the layout
6 | * Recursively renders children of the main section to discover contributions.
7 | * By default recurses 9 levels deep
8 | * Allows the recursion level to be specified as a component attribute
9 |
10 | ## Attributes
11 |
12 | ### recurse
13 | The maximum recursion depth. Optional. Number.
14 | Defaults to `9`
15 |
16 | ## See also
17 | * [Section](Section.md)
18 | * [contribution functions](contribution-functions.md)
19 |
--------------------------------------------------------------------------------
/docs/api/README.md:
--------------------------------------------------------------------------------
1 | ## Component Reference
2 |
3 | * [Layout](Layout.md) - Create layouts using `Layout`
4 | * [Section](Section.md) - Divide your layout into named `Section`s
5 | * [contribution functions](contribution-functions.md) - Are the mutual contract between layouts and components
6 |
7 |
--------------------------------------------------------------------------------
/docs/api/Section.md:
--------------------------------------------------------------------------------
1 | # Section
2 |
3 | A preact-layout `Section`:
4 | * Defines where components will be rendered within a parent `Layout`
5 | * Has a `type` attribute to associate it with a contribution function, or
6 | * Is a 'main' section (no `type` attribute)
7 |
8 | ## Attributes
9 | ### type
10 | The contribution function this section should be associated with
11 | Optional. Function. Defaults to `undefined`
12 |
13 | ## Example
14 | ```js
15 |
16 | This markup will be used when no contributions are found
17 |
18 | ```
19 |
20 | ## See also
21 | * [Layout](Layout.md)
22 | * [contribution functions](contribution-functions.md)
23 |
--------------------------------------------------------------------------------
/docs/api/contribution-functions.md:
--------------------------------------------------------------------------------
1 | # Contribution functions
2 |
3 | Contribution functions act a bit like ES6 symbols, or 'tagging interfaces' in
4 | Java speak. They don't do anything themselves, but allow a component to interact
5 | with a layout without being tighly coupled with it.
6 |
7 | In the most basic scenario, we define the contribution functions right along
8 | our layout and also import them from there in our components:
9 |
10 | *Layout.js*
11 | ```js
12 | function Header(){}
13 | function Footer(){}
14 |
15 | function MyLayout(){
16 | // use Header and Footer here
17 | }
18 |
19 | export {
20 | Header,
21 | Footer,
22 | MyLayout
23 | }
24 | ```
25 |
26 | *Component.js*
27 | ```js
28 | import { Header, Footer } from './Layout'
29 |
30 | function MyComponent(){
31 | // use Header and Footer here
32 | }
33 | ```
34 |
35 | If our site has many layouts and many components contributing to those
36 | layouts, it will probably pay off to place the contribution functions in a
37 | separate file and reuse them where needed:
38 |
39 | *Sections.js*
40 | ```js
41 | function Header(){}
42 | function Footer(){}
43 |
44 | export {
45 | Header,
46 | Footer
47 | }
48 | ```
49 |
50 | *Layout.js*
51 | ```js
52 | import { Header, Footer } from './Sections'
53 |
54 | function MyLayout(){
55 | // use Header and Footer here
56 | }
57 | export {
58 | MyLayout
59 | }
60 | ```
61 |
62 | *Component.js*
63 | ```js
64 | import { Header, Footer } from './Sections'
65 |
66 | function MyComponent(){
67 | // use Header and Footer here
68 | }
69 | ```
70 |
71 | ## Attributes
72 | ### append
73 | When set, any contributions found will be `appended` to any existing
74 | default elements / prior contributions, instead of replacing them (default)
75 |
76 | ### prepend
77 | When set, any contributions found will be `prepended` before any existing
78 | default elements / prior contributions, instead of replacing them (default)
79 |
80 | ## Example
81 | *(assuming `Header` is a contribution function)*
82 | ```js
83 | function MyComponent(){return (
84 |
85 |
86 | This will be appended to the header section instead of replacing it
87 |
88 |
89 |
Stuff here ends up in the main section
90 |
91 | )}
92 | ```
93 |
94 | ## See also
95 | * [Layout](Layout.md)
96 | * [Section](Section.md)
97 |
--------------------------------------------------------------------------------
/docs/contributing-guide/README.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thanks for considering to contribute to preact-layout! For small contributions,
4 | go right ahead and create a PR following the instructions below. For bigger
5 | contributions, consider [creating an issue](https://github.com/download/preact-layout/issues/new)
6 | first, so we can discuss.
7 |
8 | ## Preparing a Pull Request
9 | Below are the general steps to create a PR:
10 |
11 | ### Fork
12 | Fork this repo on Github, then clone the fork to your local machine and install
13 | the dependencies:
14 |
15 | ```sh
16 | git clone https://github.com/your-username/preact-layout.git
17 | cd preact-layout
18 | npm install
19 | ```
20 |
21 | ### Build and test
22 | Before we start changing stuff, let's build everything and run the tests to make
23 | sure that everything is still fine.
24 |
25 | #### Build all
26 | Running the `build` task will create both a CommonJS and a UMD build.
27 | ```sh
28 | npm run build
29 | ```
30 |
31 | #### Build only CommonJS
32 | To create just a CommonJS build:
33 | ```sh
34 | npm run build:lib
35 | ```
36 | The result will be in the `lib` folder.
37 |
38 | #### Build only UMD
39 | To create just a UMD build:
40 | ```sh
41 | npm run build:umd
42 | npm run build:umd:min
43 | ```
44 | The result will be in the `dist` folder.
45 |
46 | #### Run the tests
47 | ```sh
48 | npm run test
49 | ```
50 | To continuously watch and run tests, run the following:
51 | ```sh
52 | npm run test:watch
53 | ```
54 |
55 | ### Create and switch to a new branch
56 | Before starting work, create a new branch based on `master` and switch to it.
57 | This will greatly simplify merging the PR later.
58 |
59 | ### Write tests
60 | It's good practice to write a (couple of) test(s) that puts your new code through the motions.
61 |
62 | ### Write the code
63 | With your new tests in place, write the code that makes them pass.
64 |
65 | ### Write documentation
66 | Remember, if it's not documented, it might as well not exist. Any new features
67 | should get a section in the documentation explaining it.
68 |
69 | #### Installing Gitbook
70 | To install the latest version of `gitbook` and prepare to build the documentation, run the following:
71 | ```sh
72 | npm run docs:prepare
73 | ```
74 |
75 | #### Building the Docs
76 | To build the documentation, run the following:
77 | ```sh
78 | npm run docs:build
79 | ```
80 | To watch and rebuild documentation when changes occur, run the following:
81 | ```sh
82 | npm run docs:watch
83 | ```
84 | The docs will be served at [http://localhost:4000](http://localhost:4000).
85 |
86 | #### Publishing the Docs
87 | To publish the documentation, run the following:
88 | ```sh
89 | npm run docs:publish
90 | ```
91 |
92 | #### Cleaning the Docs
93 | To remove previously built documentation, run the following:
94 | ```sh
95 | npm run docs:clean
96 | ```
97 |
98 | ### Examples
99 | preact-layout comes with [official examples](http://download.github.io/preact-layout/docs/introduction/Examples.html) to demonstrate various concepts and best practices.
100 |
101 | When adding a new example, please adhere to the style and format of the existing examples, and try to reuse as much code as possible. For example, `index.html`, `server.js`, and `webpack.config.js` can typically be reused.
102 |
103 | #### Building and Testing the Examples
104 | To build and test the official preact-layout examples, run the following:
105 | ```sh
106 | npm run build:examples
107 | npm run test:examples
108 | ```
109 |
110 | Please visit the [Examples page](http://download.github.io/preact-layout/docs/introduction/Examples.html) for information on running individual examples.
111 |
112 | Thanks for contributing!
113 |
--------------------------------------------------------------------------------
/docs/getting-started/Examples.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | preact-layout contains a few [examples](https://github.com/download/preact-layout/tree/master/examples).
4 |
5 | ## Header/Footer Example
6 |
7 | Learn how to create a layout with a header, a main body and a footer. What site can do without it?!
8 | [Header/Footer](https://github.com/download/preact-layout/tree/master/examples/header-footer)
9 |
10 | ```sh
11 | git clone https://github.com/download/preact-layout.git
12 |
13 | cd preact-layout/examples/header-footer
14 | npm install
15 | npm start
16 | ```
17 | open [http://localhost:8888/](http://localhost:8888/)
18 |
--------------------------------------------------------------------------------
/docs/getting-started/README.md:
--------------------------------------------------------------------------------
1 | ## Getting Started
2 |
3 | Getting started with preact-layout!
4 |
5 | * Play with the [Preact Layout Kickstart CodePen](http://codepen.io/StijnDeWitt/pen/rrzJEA?editors=0010).
6 | * [Setup](Setup.md) - Add preact-layout to your project
7 | * [Usage guide](Usage-guide.md) - Using Layout, Section and contribution functions
8 | * [Examples](Examples.md) - Learn by example!
9 |
10 |
--------------------------------------------------------------------------------
/docs/getting-started/Setup.md:
--------------------------------------------------------------------------------
1 | # Setup
2 |
3 | ## Install
4 | ```sh
5 | npm install --save preact-layout
6 | ```
7 |
8 | ## Require
9 | ```js
10 | var Layout = require('preact-layout').Layout
11 | var Section = require('preact-layout').Section
12 | ```
13 |
14 | ## Import
15 | ```js
16 | import { Layout, Section } from 'preact-layout'
17 | ```
18 |
19 | ## AMD
20 | ```js
21 | define(['preact-layout'], function(preactLayout){
22 | const { Layout, Section } = preactLayout
23 | })
24 | ```
25 |
26 | ## Script tag
27 | ```html
28 |
29 | ```
30 |
--------------------------------------------------------------------------------
/docs/getting-started/Usage-guide.md:
--------------------------------------------------------------------------------
1 | # Usage Guide
2 |
3 | Using preact-layout requires you to:
4 | * Define contribution functions
5 | * Create a layout referncing these contribution functions
6 | * Make components that use the contribution functions
7 | * Learn about caveats
8 |
9 | ## Define contribution functions
10 | Define *contribution functions* to represent the different sections
11 | of your website / app:
12 | ```js
13 | function Header(){}
14 | function Footer(){}
15 | ```
16 | These functions's don't do anything in and of themselves, but they serve as
17 | symbols that can be used by both layouts and regular components.
18 |
19 | ## Create a layout
20 | Use `Layout` to create a layout which is subdivided in sections. Use `Section`
21 | to create those sections. There should be exactly one main section (not associated
22 | with a contribution function) and any `children` should be rendered inside this main section.
23 | In addition, layouts can have zero or more extra sections, each of which must be
24 | associated with a different contribution function, using the `type` attribute.
25 |
26 | Have a look at this example:
27 |
28 | ```js
29 | function MyLayout({children}) {
30 | return (
31 |
32 |
33 |
Default title
34 |
35 |
36 | {children}
37 |
38 |
41 |
42 | )
43 | }
44 | ```
45 |
46 | Much simpler than it sounded right? But here's the cool trick: Any children
47 | of the main section will be rendered before the sections themselves and if they
48 | produce *layout contributions*, those contributions will be moved to the correct
49 | section based on the associated contribution function.
50 |
51 |
52 | ## Make components that use the contribution functions
53 | Again have a look at an example:
54 |
55 | ```js
56 | function HomePage({children}) {
57 | return (
58 |
59 |
This is the homepage!
60 |
61 |
And thats how preact-layout works. It takes the contributions made by the
62 | children and shows them in the correct region of the layout.
63 |
64 |
65 |
66 | )
67 | }
68 | ```
69 | As you can see, in our components, we wrap the content that we want to
70 | contribute to other sections of the layout in JSX tags created from our
71 | contribution functions.
72 |
73 | The magic happens when we combine the layout and the page we just created:
74 |
75 | ```js
76 | function Website({children, ...props}) {
77 | return (
78 |
79 |
80 |
81 | )
82 | }
83 | ```
84 |
85 | `Website` will render this output:
86 |
87 | ```html
88 |
89 |
90 |
This is the homepage!
91 |
92 |
93 |
And thats how preact-layout works. It takes the contributions made by the
94 | children and shows them in the correct region of the layout.
95 |
96 |
99 |
100 | ```
101 |
102 | Because we separated the layout concerns from the `HomePage` component, we
103 | can easily make an alternative layout and use that instead:
104 |
105 | ```js
106 | function ReversedLayout({children}) {
107 | return (
108 |
109 |
112 |
113 | {children}
114 |
115 |
116 |
Default title
117 |
118 |
119 | )
120 | }
121 | ```
122 |
123 | Change `Website` to use the alternative layout and you're done:
124 |
125 | ```js
126 | function Website({children, ...props}) {
127 | return (
128 |
129 |
130 |
131 | )
132 | }
133 | ```
134 |
135 | The contributions made by `HomePage` will automatically land in the correct place:
136 |
137 | ```html
138 |
139 |
142 |
143 |
And thats how preact-layout works. It takes the contributions made by the
144 | children and shows them in the correct region of the layout.
145 |
146 |
147 |
This is the homepage!
148 |
149 |
150 | ```
151 |
152 | ## Caveats
153 | In the examples above we used stateless functional components.
154 | But preact-layout can be used with class components just as well:
155 |
156 | ```js
157 | class HomePage extends Component {
158 | render({children}) {return (
159 |
160 |
This is the homepage!
161 |
162 |
And thats how preact-layout works. It takes the contributions made by the
163 | children and shows them in the correct region of the layout.
164 |
165 |
166 |
167 | )}
168 | }
169 | ```
170 |
171 | This works just as great. But things become a bit more tricky when
172 | we start using *stateful* class components. Consider this example:
173 |
174 | ```js
175 | class HomePage extends Component {
176 | state = {text: 'Hello, world!'};
177 |
178 | render({children}, {text}) {return (
179 |
180 |
{text}
181 |
182 |
This paragraph is in the main section
183 |
184 |
185 |
186 |
187 | )}
188 | }
189 | ```
190 |
191 | This will render fine and appears to work, until we change the text
192 | in the input. The changes will not be reflected in the header. The reason
193 | for this is that the children of the `` tag are actually moved
194 | outside of the `HomePage` component by the layout, so Preact does not
195 | re-render them in response to state changes.
196 |
197 | The children of the `HomePage` that are not contributed to other sections
198 | will respond to state changes as expected. Also, you are free to
199 | contribute stateful components. It's just that inside the component
200 | making the contributions, local state is not accessible in the
201 | contributed sections.
202 |
203 | So either make components that contribute markup to the layout stateless,
204 | or take care to only make use of state in the main section of the
205 | component and not in those sections that are contributed to the layout.
206 |
207 | The [Preact Layout Kickstart CodePen](http://codepen.io/StijnDeWitt/pen/rrzJEA) actually demonstrates this.
208 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | Read the descriptions for every example on the [Examples](https://download.github.io/preact-layout/docs/getting-started/Examples.html) documentation page.
4 |
--------------------------------------------------------------------------------
/examples/buildAll.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs')
2 | var path = require('path')
3 | var { spawnSync } = require('child_process')
4 |
5 | var exampleDirs = fs.readdirSync(__dirname).filter((file) => {
6 | return fs.statSync(path.join(__dirname, file)).isDirectory()
7 | })
8 |
9 | // Ordering is important here. `npm install` must come first.
10 | var cmdArgs = [
11 | { cmd: 'npm', args: [ 'install' ] },
12 | { cmd: 'webpack', args: [ 'index.js' ] }
13 | ]
14 |
15 | for (const dir of exampleDirs) {
16 | for (const cmdArg of cmdArgs) {
17 | // declare opts in this scope to avoid https://github.com/joyent/node/issues/9158
18 | const opts = {
19 | cwd: path.join(__dirname, dir),
20 | stdio: 'inherit'
21 | }
22 | let result = {}
23 | if (process.platform === 'win32') {
24 | result = spawnSync(cmdArg.cmd + '.cmd', cmdArg.args, opts)
25 | } else {
26 | result = spawnSync(cmdArg.cmd, cmdArg.args, opts)
27 | }
28 | if (result.status !== 0) {
29 | throw new Error('Building examples exited with non-zero')
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/examples/header-footer/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "stage-2"
5 | ],
6 | "plugins": [
7 | ["transform-react-jsx", { "pragma": "h" }]
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/examples/header-footer/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 |
6 | # production
7 | build
8 |
9 | # misc
10 | .DS_Store
11 | npm-debug.log
12 |
--------------------------------------------------------------------------------
/examples/header-footer/README.md:
--------------------------------------------------------------------------------
1 | # Header / Footer Example
2 |
3 | This example demonstrates a layout with a header and a footer that are contributed by the children.
4 |
5 | ## Running the example
6 |
7 | In the project folder:
8 |
9 | ```sh
10 | $ npm start
11 | ```
12 |
13 | Runs the app in development mode.
14 |
15 | Open [http://localhost:8888](http://localhost:8888) to view it in the browser.
16 |
17 |
--------------------------------------------------------------------------------
/examples/header-footer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "header-footer",
3 | "version": "0.1.0",
4 | "private": true,
5 | "devDependencies": {
6 | "babel-core": "^6.16.0",
7 | "babel-loader": "^6.2.5",
8 | "babel-plugin-transform-react-jsx": "^6.8.0",
9 | "babel-preset-es2015": "^6.16.0",
10 | "babel-preset-stage-2": "^6.16.0",
11 | "webpack": "^1.13.2",
12 | "webpack-dev-server": "^1.16.1"
13 | },
14 | "dependencies": {
15 | "preact": "^6.1.0",
16 | "preact-layout": "^0.2.0"
17 | },
18 | "scripts": {
19 | "start": "webpack-dev-server --output-path public --output-filename bundle.js \"./src\" --content-base public --port 8888"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/header-footer/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Header/Footer Example - preact-layout
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/header-footer/src/components/HomePage.jsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact'
2 | import { Header, Footer } from './Layouts'
3 |
4 | function HomePage({children}) {return (
5 |
6 |
7 |
HomePage
8 |
9 |
10 |
This is the homepage. Isnt it cool?
11 |
12 |
15 |
16 | )}
17 |
18 | export default HomePage
19 |
--------------------------------------------------------------------------------
/examples/header-footer/src/components/Layouts.jsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact'
2 | import { Layout, Section } from 'preact-layout'
3 |
4 | function Header(){}
5 |
6 | function Footer(){}
7 |
8 | function MyLayout({children}) {return (
9 |
10 |
11 |