# Getting Started ## Prerequisites You need the [application-prototype](https://labs.sgapps.io/open-source/application-prototype) framework served next to your site -- its two script tags and its `constructors/` folder (the built-in modules the modules of this repository depend on: `extensions/prototype`, `uri-load`, `request`, `custom-elements`, `async`). ```html ``` The modules are plain ES5 files with no build step; they are fetched by the framework's module loader over HTTP, so the repository must be reachable from the page (a static server, not `file://`). ## Installation Clone the repository next to your site. The modules live directly at the repository root (there is no `modules/` folder): ``` my-site/ application-prototype/ <-- the framework (ApplicationPrototype.js, ApplicationBuilder.js, constructors/) application-prototype-modules/ <-- this repository lib.js <-- registers the 10 module names custom-elements/ lib.js, scroller.js + scroller/style.css elements/ popup.js, notify.js, notify-service.js, context-menu.js, crop-sections.js, tree-node.js (+ one folder each) gadgets/ wrapper.js, grid-panel.js + grid-panel/{block,grid}.js application-patterns/ routing.js + routing/{components,resources,routes}.js docs/ this documentation and the playground ``` ```sh git clone git@labs.sgapps.io:open-source/application-prototype-modules.git ``` No `npm install` is needed to use the modules. `npm start` serves the landing page, the documentation and the playground locally (see [Development](#development)). ## Registering All Modules `lib.js` registers every module path with the framework. Require it by **root-relative path or absolute URL** (module names are resolved relative to the folder `lib.js` was loaded from) and call the exported function once during startup: ```js var App = new ApplicationBuilder({ onready: function () { var App = this; App.modulePath('/application-prototype/constructors'); App.require(['extensions/prototype', 'lib'], function (libs) { // register the framework's built-in modules by name libs.lib(); // register the modules of this repository by name App.require('/application-prototype-modules/lib.js').then(function (loader) { loader(); console.log('modules registered'); }); }); } }); ``` `loader()` runs `Application.moduleRegister(__dirname, [...])` for the list below; after that every module is available through `Application.require()` (or `App.require()`) by its short name: | Name | File | |---|---| | `custom-elements/lib` | `custom-elements/lib.js` | | `custom-elements/scroller` | `custom-elements/scroller.js` | | `elements/context-menu` | `elements/context-menu.js` | | `elements/crop-sections` | `elements/crop-sections.js` | | `elements/tree-node` | `elements/tree-node.js` | | `elements/popup` | `elements/popup.js` | | `elements/notify` | `elements/notify.js` | | `elements/notify-service` | `elements/notify-service.js` | | `gadgets/wrapper` | `gadgets/wrapper.js` | | `gadgets/grid-panel` | `gadgets/grid-panel.js` | The playground pages register the same way: `App.moduleRegister('/vendor/modules', ['lib#?module=libModules'])` (`/vendor/modules` is a placeholder the playground resolves to the repository root or to the CDN copy) gives the file a name, `App.require('libModules')` loads it, `lib.libModules()` registers the list. ## Loading a Module ### Single module ```js Application.require('elements/notify').then(function (NotifyManager) { var notify = NotifyManager(); document.body.appendChild(notify.node()); notify.info('Hello', 'The notification manager is ready'); }); ``` ### Multiple modules with aliases ```js Application.require([ "popup :: elements/popup", "menu :: elements/context-menu", "panels :: elements/crop-sections" ]).then(function (libs) { libs.popup.alert('Title', 'Text'); var menu = libs.menu(); var panels = libs.panels(); }); ``` ## How Modules Work ### Factories that return an `ApplicationPrototype` `elements/context-menu`, `elements/crop-sections`, `elements/tree-node`, `elements/notify`, `elements/notify-service`, `gadgets/wrapper` and `gadgets/grid-panel` export a **factory**: calling it builds one instance -- an `ApplicationPrototype` with methods attached through `.bind()`: ```js var app = new ApplicationPrototype(); app.bind("node", function () { return config.node; }); app.bind("show", function (pos) { /* ... */ return app; }); module.exports = function () { /* ... */ return app; }; ``` Every bound method is observable: `instance.on('beforeShow', fn)` runs before the call (return `false` to veto), `'onShow'` with it and `'afterShow'` on the next tick. Methods bound with an empty configuration string (`app.bind("node", fn, "")`) are plain functions without hooks -- the module pages say which. ### Static objects `elements/popup` exports a plain object of functions (`popup`, `alert`, `prompt`, `confirm`); `custom-elements/lib` exports one function; `custom-elements/scroller` exports `true` once the tag is registered; `application-patterns/routing` exports `{ components, resources }`. ### Stylesheets and templates A module that needs CSS links its own stylesheet when it loads, through the framework's `uri-load` and `module.resourceUrl('style.css')` (the URL is relative to the module file, so the folder structure must stay as it is). `elements/crop-sections` also fetches its two HTML templates with `request`. Nothing is inlined into your page and nothing is loaded from a CDN. The dialogs (`elements/popup`), the notifications (`elements/notify`) and the context menu (`elements/context-menu`) are designed for the SGApps **RegalRoyal** theme and use its class names (`rr-button`, `rr-button-danger`, `rr-block-space`, `rr-menu rr-menu-vertical`, `regal-royal--dark-theme`, the `websymbols` icon font, the `animated` / `bounceIn` / `fadeIn` animation classes). With the theme on the page they take its look; without it they render with the browser's defaults and the module's own `style.css` (positioning, overlay, colours of the notification types). The theme is available at `https://sgapps.io/addons/RegalRoyal/styles/less/style.less.cache.css` -- the playground examples of those three modules link it. ### Custom elements `` is not a `customElements.define()` element. `custom-elements/lib` hands the tag -> module map to the framework's `custom-elements` module (`lazyLoadModules`), which watches `document.body` with a `MutationObserver` and requires `custom-elements/scroller` the first time the tag is seen; the scroller module then calls `registerMethods('sgapps-scroller', { __onInit, ... })` and every element gets its `attrdata.SGAppsScroller` API. Options are declared with `attrb-*` attributes (JSON-parsed by `extensions/prototype`'s `attrdata`). ## Development ```sh npm start # landing page + docs + playground at http://localhost:3000/ and /docs/ npm run docs-site-published # serves docs/ alone, the layout of a published docs-only site npm test # lints the documentation: links, sidebar, playground registry, landing page, lib.js coverage ``` The playground (`docs/playground.html`) loads the framework from the SGApps CDN (`https://cdn.sgapps.io/components/application-prototype`, 1.31.0, CORS enabled) and the modules from the repository root (one level above `docs/`) when it is served, so every example runs on your working copy; when only `docs/` is served (the published site) it falls back to the CDN copy at `https://cdn.sgapps.io/components/application-prototype-modules`. Append `#lib=` or `#modules=` to the playground URL to run the examples on another checkout (a local framework checkout served by the same server, another version, ...). ## Next Steps - [Popup](elements/popup.md) -- dialogs: `alert`, `confirm`, `prompt`, custom content - [Notify](elements/notify.md) and [Notify Service](elements/notify-service.md) -- toast notifications - [Context Menu](elements/context-menu.md) -- right-click menus with sub-lists - [Crop Sections](elements/crop-sections.md) -- collapsible side-by-side panels - [Grid Panel](gadgets/grid-panel.md) -- a dashboard layout editor - [sgapps-scroller](custom-elements/scroller.md) -- transform-based scrolling - [Routing](application-patterns/routing.md) -- the static routing pattern