pict-section-recordset 1.0.64 → 1.0.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ /*!
2
+ Pure v3.0.0
3
+ Copyright 2013 Yahoo!
4
+ Licensed under the BSD License.
5
+ https://github.com/pure-css/pure/blob/master/LICENSE
6
+ */
7
+ /*!
8
+ normalize.css v | MIT License | https://necolas.github.io/normalize.css/
9
+ Copyright (c) Nicolas Gallagher and Jonathan Neal
10
+ */
11
+ /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}html{font-family:sans-serif}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{display:flex;flex-flow:row wrap;align-content:flex-start}.pure-u{display:inline-block;vertical-align:top}.pure-u-1,.pure-u-1-1,.pure-u-1-12,.pure-u-1-2,.pure-u-1-24,.pure-u-1-3,.pure-u-1-4,.pure-u-1-5,.pure-u-1-6,.pure-u-1-8,.pure-u-10-24,.pure-u-11-12,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-2-24,.pure-u-2-3,.pure-u-2-5,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24,.pure-u-3-24,.pure-u-3-4,.pure-u-3-5,.pure-u-3-8,.pure-u-4-24,.pure-u-4-5,.pure-u-5-12,.pure-u-5-24,.pure-u-5-5,.pure-u-5-6,.pure-u-5-8,.pure-u-6-24,.pure-u-7-12,.pure-u-7-24,.pure-u-7-8,.pure-u-8-24,.pure-u-9-24{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%}.pure-u-1-12,.pure-u-2-24{width:8.3333%}.pure-u-1-8,.pure-u-3-24{width:12.5%}.pure-u-1-6,.pure-u-4-24{width:16.6667%}.pure-u-1-5{width:20%}.pure-u-5-24{width:20.8333%}.pure-u-1-4,.pure-u-6-24{width:25%}.pure-u-7-24{width:29.1667%}.pure-u-1-3,.pure-u-8-24{width:33.3333%}.pure-u-3-8,.pure-u-9-24{width:37.5%}.pure-u-2-5{width:40%}.pure-u-10-24,.pure-u-5-12{width:41.6667%}.pure-u-11-24{width:45.8333%}.pure-u-1-2,.pure-u-12-24{width:50%}.pure-u-13-24{width:54.1667%}.pure-u-14-24,.pure-u-7-12{width:58.3333%}.pure-u-3-5{width:60%}.pure-u-15-24,.pure-u-5-8{width:62.5%}.pure-u-16-24,.pure-u-2-3{width:66.6667%}.pure-u-17-24{width:70.8333%}.pure-u-18-24,.pure-u-3-4{width:75%}.pure-u-19-24{width:79.1667%}.pure-u-4-5{width:80%}.pure-u-20-24,.pure-u-5-6{width:83.3333%}.pure-u-21-24,.pure-u-7-8{width:87.5%}.pure-u-11-12,.pure-u-22-24{width:91.6667%}.pure-u-23-24{width:95.8333%}.pure-u-1,.pure-u-1-1,.pure-u-24-24,.pure-u-5-5{width:100%}.pure-button{display:inline-block;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;user-select:none;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-group{letter-spacing:-.31em;text-rendering:optimizespeed}.opera-only :-o-prefocus,.pure-button-group{word-spacing:-0.43em}.pure-button-group .pure-button{letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:rgba(0,0,0,.8);border:none transparent;background-color:#e6e6e6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:focus,.pure-button:hover{background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000}.pure-button-disabled,.pure-button-disabled:active,.pure-button-disabled:focus,.pure-button-disabled:hover,.pure-button[disabled]{border:none;background-image:none;opacity:.4;cursor:not-allowed;box-shadow:none;pointer-events:none}.pure-button-hidden{display:none}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-button-group .pure-button{margin:0;border-radius:0;border-right:1px solid rgba(0,0,0,.2)}.pure-button-group .pure-button:first-child{border-top-left-radius:2px;border-bottom-left-radius:2px}.pure-button-group .pure-button:last-child{border-top-right-radius:2px;border-bottom-right-radius:2px;border-right:none}.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=color]:focus,.pure-form input[type=date]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=email]:focus,.pure-form input[type=month]:focus,.pure-form input[type=number]:focus,.pure-form input[type=password]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=text]:focus,.pure-form input[type=time]:focus,.pure-form input[type=url]:focus,.pure-form input[type=week]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129fea}.pure-form input:not([type]):focus{outline:0;border-color:#129fea}.pure-form input[type=checkbox]:focus,.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=color][disabled],.pure-form input[type=date][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=email][disabled],.pure-form input[type=month][disabled],.pure-form input[type=number][disabled],.pure-form input[type=password][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=text][disabled],.pure-form input[type=time][disabled],.pure-form input[type=url][disabled],.pure-form input[type=week][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form select:focus:invalid,.pure-form textarea:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=checkbox]:focus:invalid:focus,.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=color],.pure-form-stacked input[type=date],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=email],.pure-form-stacked input[type=file],.pure-form-stacked input[type=month],.pure-form-stacked input[type=number],.pure-form-stacked input[type=password],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=text],.pure-form-stacked input[type=time],.pure-form-stacked input[type=url],.pure-form-stacked input[type=week],.pure-form-stacked label,.pure-form-stacked select,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned select,.pure-form-aligned textarea,.pure-form-message-inline{display:inline-block;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form .pure-input-rounded,.pure-form input.pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-3-4{width:75%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=color],.pure-group input[type=date],.pure-group input[type=datetime-local],.pure-group input[type=datetime],.pure-group input[type=email],.pure-group input[type=month],.pure-group input[type=number],.pure-group input[type=password],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=text],.pure-group input[type=time],.pure-group input[type=url],.pure-group input[type=week]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0 0}.pure-form-message,.pure-form-message-inline{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-item,.pure-menu-list{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-heading,.pure-menu-link{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-separator{display:inline-block;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-active>.pure-menu-children,.pure-menu-allow-hover:hover>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;padding:.5em 0}.pure-menu-horizontal .pure-menu-children .pure-menu-separator,.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-horizontal .pure-menu-children .pure-menu-separator{display:block;width:auto}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-heading,.pure-menu-link{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent;cursor:default}.pure-menu-active>.pure-menu-link,.pure-menu-link:focus,.pure-menu-link:hover{background-color:#eee}.pure-menu-selected>.pure-menu-link,.pure-menu-selected>.pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0}
@@ -0,0 +1,46 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7
+ <meta name="description" content="Bookstore - pict-section-recordset example">
8
+
9
+ <title>Bookstore</title>
10
+
11
+ <!-- Simple purecss framework https://purecss.io/ -->
12
+ <link href="css/pure.min.css" rel="stylesheet">
13
+ <link href="css/bookstore.css" rel="stylesheet">
14
+ <!-- PICT Dynamic View CSS Container -->
15
+ <style id="PICT-CSS"></style>
16
+
17
+ <!-- Load the PICT library -->
18
+ <script src="./js/pict.min.js" type="text/javascript"></script>
19
+ <!-- Load the Application -->
20
+ <script type="text/javascript">
21
+ //<![CDATA[
22
+ Pict.safeOnDocumentReady(() => { Pict.safeLoadPictApplication(BookstoreApplication, 2)});
23
+ //]]>
24
+ </script>
25
+ </head>
26
+ <body>
27
+ <!-- ===== Login Screen (visible when unauthenticated) ===== -->
28
+ <div id="Bookstore-Login-Container">
29
+ <div class="bookstore-login-header">
30
+ <h1>Bookstore</h1>
31
+ <p>pict-section-recordset + pict-section-login</p>
32
+ </div>
33
+ <!-- pict-section-login renders its login card into this container -->
34
+ <div id="Bookstore-Login-Form-Container"></div>
35
+ <div class="bookstore-login-hint">
36
+ Use any valid user (e.g. <code>admin</code>, <code>jdoe</code>, <code>bsmith</code>) with any password.
37
+ </div>
38
+ </div>
39
+
40
+ <!-- ===== Protected App (hidden until authenticated) ===== -->
41
+ <div id="Bookstore-App-Container" style="display:none"></div>
42
+
43
+ <!-- Load the Bookstore PICT Application -->
44
+ <script src="./bookstore_application.min.js" type="text/javascript"></script>
45
+ </body>
46
+ </html>
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "bookstore_application",
3
+ "version": "1.0.0",
4
+ "description": "A bookstore application using pict-section-recordset with pict-login authentication against retold-harness endpoints.",
5
+ "main": "Bookstore-Application.js",
6
+ "scripts": {
7
+ "start": "node Bookstore-Application.js",
8
+ "build": "npx quack build && npx quack copy"
9
+ },
10
+ "author": "steven velozo <steven@velozo.com>",
11
+ "license": "MIT",
12
+ "devDependencies": {
13
+ "pict-router": "^1.0.8",
14
+ "pict-section-login": "^0.0.1",
15
+ "pict-template-preprocessor": "^0.0.2"
16
+ },
17
+ "copyFilesSettings": {
18
+ "whenFileExists": "overwrite"
19
+ },
20
+ "copyFiles": [
21
+ {
22
+ "from": "./html/*",
23
+ "to": "./dist/"
24
+ },
25
+ {
26
+ "from": "./css/**",
27
+ "to": "./dist/css/"
28
+ },
29
+ {
30
+ "from": "../../node_modules/pict/dist/*",
31
+ "to": "./dist/js/"
32
+ }
33
+ ]
34
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "ProviderIdentifier": "Pict-Router",
3
+ "AutoInitialize": true,
4
+ "AutoInitializeOrdinal": 0,
5
+ "SkipRouteResolveOnAdd": true,
6
+ "Routes": [
7
+ {
8
+ "path": "/Dashboard",
9
+ "template": "{~LV:Pict.PictApplication.showView(`Bookstore-Dashboard`)~}"
10
+ },
11
+ {
12
+ "path": "/Books",
13
+ "template": "{~LV:Pict.PictApplication.showBookList()~}"
14
+ },
15
+ {
16
+ "path": "/Authors",
17
+ "template": "{~LV:Pict.PictApplication.showAuthorList()~}"
18
+ },
19
+ {
20
+ "path": "/BookStores",
21
+ "template": "{~LV:Pict.PictApplication.showBookStoreList()~}"
22
+ },
23
+ {
24
+ "path": "/About",
25
+ "template": "{~LV:Pict.PictApplication.showView(`Bookstore-About-View`)~}"
26
+ },
27
+ {
28
+ "path": "/Legal",
29
+ "template": "{~LV:Pict.PictApplication.showView(`Bookstore-Legal-View`)~}"
30
+ }
31
+ ]
32
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "ViewIdentifier": "Bookstore-About-View",
3
+
4
+ "DefaultRenderable": "Bookstore-About",
5
+ "DefaultDestinationAddress": "#Bookstore-Content-Container",
6
+
7
+ "AutoRender": false,
8
+
9
+ "Templates": [
10
+ {
11
+ "Hash": "Bookstore-About-Content",
12
+ "Template": "<div class=\"header\"><h1>About the Bookstore</h1><h2>A demonstration application for the Retold ecosystem.</h2></div> <div class=\"content\"><h2 class=\"content-subhead\">What Is This?</h2><p>The Bookstore application showcases how <strong>pict-section-recordset</strong> integrates with <strong>retold-harness</strong> Meadow endpoints to provide a complete CRUD management experience. It combines authentication via <strong>pict-section-login</strong> with route-based navigation and dynamic recordset views.</p> <h2 class=\"content-subhead\">Architecture</h2><p>This application is built on the Retold module ecosystem. The backend is powered by <strong>retold-harness</strong>, which auto-generates RESTful CRUD endpoints from Meadow schema definitions. The harness serves a bookstore schema with entities for Books, Authors, BookStores, Reviews, Pricing, and Inventory.</p> <p>On the frontend, <strong>Pict</strong> provides the MVC framework. Views are registered with the application and rendered into designated DOM containers. The <strong>pict-section-recordset</strong> metacontroller manages configuration-driven record list, read, create, and dashboard views with auto-generated filtering from the Meadow schema.</p> <h2 class=\"content-subhead\">The Retold Ecosystem</h2><p>Retold is a suite of 60+ JavaScript/Node.js modules for building web applications and APIs. The ecosystem follows a service provider pattern where modules register with a Fable instance and gain access to logging, configuration, and other services through dependency injection.</p> <p>Key module groups include <strong>Fable</strong> (core DI and configuration), <strong>Meadow</strong> (data access and ORM), <strong>Orator</strong> (API server), <strong>Pict</strong> (MVC views and templates), and <strong>Utility</strong> (build tools and manifests).</p></div>"
13
+ }
14
+ ],
15
+ "Renderables": [
16
+ {
17
+ "RenderableHash": "Bookstore-About",
18
+ "TemplateHash": "Bookstore-About-Content"
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "ViewIdentifier": "Bookstore-Legal-View",
3
+
4
+ "DefaultRenderable": "Bookstore-Legal",
5
+ "DefaultDestinationAddress": "#Bookstore-Content-Container",
6
+
7
+ "AutoRender": false,
8
+
9
+ "Templates": [
10
+ {
11
+ "Hash": "Bookstore-Legal-Content",
12
+ "Template": "<div class=\"header\"><h1>Terms &amp; Legal</h1><h2>The fine print you never read but always agree to.</h2></div> <div class=\"content\"> <h2 class=\"content-subhead\">Terms of Use</h2><p>By accessing and using this bookstore application, you agree to abide by the following terms and conditions. This application is provided as a demonstration of the Retold ecosystem and pict-section-recordset module capabilities. All data presented is sample data and does not represent real books, authors, or bookstores.</p> <h2 class=\"content-subhead\">Intellectual Property</h2><p>All content, including but not limited to text, graphics, logos, and software, provided on this application is the property of the Retold project contributors and is protected under the MIT License. You are free to use, copy, modify, and distribute the software in accordance with the terms of the MIT License.</p> <h2 class=\"content-subhead\">Data &amp; Privacy</h2><p>This demonstration application stores session data locally via cookies. No personal information is transmitted to third parties. Session data is used solely for authentication purposes within the application. All data in this application is sample data generated for demonstration purposes.</p> <h2 class=\"content-subhead\">Limitation of Liability</h2><p>This software is provided \"as is\", without warranty of any kind, express or implied. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability arising from the use of this software.</p> <h2 class=\"content-subhead\">Open Source License</h2><p>This application and all Retold ecosystem modules are released under the MIT License. You are encouraged to explore, modify, and build upon this codebase for your own projects. Contributions to the ecosystem are welcome via GitHub.</p></div>"
13
+ }
14
+ ],
15
+ "Renderables": [
16
+ {
17
+ "RenderableHash": "Bookstore-Legal",
18
+ "TemplateHash": "Bookstore-Legal-Content"
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,147 @@
1
+ const libPictView = require('pict-view');
2
+
3
+ const _ViewConfiguration =
4
+ {
5
+ ViewIdentifier: "Bookstore-Dashboard",
6
+
7
+ DefaultRenderable: "Bookstore-Dashboard-Content",
8
+ DefaultDestinationAddress: "#Bookstore-Content-Container",
9
+
10
+ AutoRender: false,
11
+
12
+ CSS: /*css*/`
13
+ .bookstore-dashboard-session-info
14
+ {
15
+ background: #264653;
16
+ color: #FAEDCD;
17
+ border-radius: 6px;
18
+ padding: 0.8rem 1rem;
19
+ font-size: 0.85rem;
20
+ margin-bottom: 1.5rem;
21
+ display: flex;
22
+ align-items: center;
23
+ gap: 0.5rem;
24
+ }
25
+ .bookstore-dashboard-session-info .badge
26
+ {
27
+ background: #E76F51;
28
+ color: #fff;
29
+ font-size: 0.7rem;
30
+ font-weight: 700;
31
+ padding: 0.15rem 0.5rem;
32
+ border-radius: 9999px;
33
+ }
34
+ .bookstore-dashboard-cards
35
+ {
36
+ display: flex;
37
+ gap: 1.25rem;
38
+ flex-wrap: wrap;
39
+ }
40
+ .bookstore-dashboard-card
41
+ {
42
+ flex: 1;
43
+ min-width: 200px;
44
+ background: #fff;
45
+ border: 1px solid #D4A373;
46
+ border-top: 4px solid #E76F51;
47
+ border-radius: 6px;
48
+ padding: 1.25rem;
49
+ cursor: pointer;
50
+ transition: box-shadow 0.15s, transform 0.15s;
51
+ box-shadow: 0 2px 8px rgba(38,70,83,0.08);
52
+ }
53
+ .bookstore-dashboard-card:hover
54
+ {
55
+ box-shadow: 0 4px 16px rgba(38,70,83,0.15);
56
+ transform: translateY(-2px);
57
+ }
58
+ .bookstore-dashboard-card h3
59
+ {
60
+ margin: 0 0 0.5rem;
61
+ color: #E76F51;
62
+ font-size: 1.05rem;
63
+ }
64
+ .bookstore-dashboard-card p
65
+ {
66
+ margin: 0;
67
+ font-size: 0.9rem;
68
+ color: #666;
69
+ }
70
+ `,
71
+
72
+ Templates:
73
+ [
74
+ {
75
+ Hash: "Bookstore-Dashboard-Template",
76
+ Template: /*html*/`
77
+ <div class="header">
78
+ <h1>Bookstore Dashboard</h1>
79
+ <h2>Manage your books, authors, and store inventory.</h2>
80
+ </div>
81
+ <div class="content">
82
+ <div class="bookstore-dashboard-session-info" id="Bookstore-Dashboard-SessionInfo"></div>
83
+ <h2 class="content-subhead">Quick Navigation</h2>
84
+ <div class="bookstore-dashboard-cards">
85
+ <div class="bookstore-dashboard-card" onclick="{~P~}.PictApplication.navigateTo('/Books')">
86
+ <h3>Books</h3>
87
+ <p>Browse, filter, and manage the full book catalog.</p>
88
+ </div>
89
+ <div class="bookstore-dashboard-card" onclick="{~P~}.PictApplication.navigateTo('/Authors')">
90
+ <h3>Authors</h3>
91
+ <p>View and manage author records.</p>
92
+ </div>
93
+ <div class="bookstore-dashboard-card" onclick="{~P~}.PictApplication.navigateTo('/BookStores')">
94
+ <h3>Stores</h3>
95
+ <p>Manage bookstore locations and inventory.</p>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ `
100
+ }
101
+ ],
102
+
103
+ Renderables:
104
+ [
105
+ {
106
+ RenderableHash: "Bookstore-Dashboard-Content",
107
+ TemplateHash: "Bookstore-Dashboard-Template",
108
+ DestinationAddress: "#Bookstore-Content-Container",
109
+ RenderMethod: "replace"
110
+ }
111
+ ]
112
+ };
113
+
114
+ class BookstoreDashboardView extends libPictView
115
+ {
116
+ constructor(pFable, pOptions, pServiceHash)
117
+ {
118
+ super(pFable, pOptions, pServiceHash);
119
+ }
120
+
121
+ onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)
122
+ {
123
+ // Populate session info banner
124
+ let tmpSession = this.pict.AppData.Session;
125
+ let tmpInfoElements = this.services.ContentAssignment.getElement('#Bookstore-Dashboard-SessionInfo');
126
+ if (tmpInfoElements && tmpInfoElements.length > 0 && tmpSession && tmpSession.UserRecord)
127
+ {
128
+ let tmpUser = tmpSession.UserRecord;
129
+ let tmpHTML = 'Logged in as <strong>' + (tmpUser.FullName || tmpUser.LoginID || '') + '</strong>';
130
+ if (tmpUser.IDUser)
131
+ {
132
+ tmpHTML += ' <span class="badge">ID ' + tmpUser.IDUser + '</span>';
133
+ }
134
+ if (tmpUser.Email)
135
+ {
136
+ tmpHTML += '&nbsp;&nbsp;' + tmpUser.Email;
137
+ }
138
+ tmpInfoElements[0].innerHTML = tmpHTML;
139
+ }
140
+
141
+ return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
142
+ }
143
+ }
144
+
145
+ module.exports = BookstoreDashboardView;
146
+
147
+ module.exports.default_configuration = _ViewConfiguration;
@@ -0,0 +1,85 @@
1
+ const libPictView = require('pict-view');
2
+
3
+ const _ViewConfiguration =
4
+ {
5
+ ViewIdentifier: "Bookstore-Layout",
6
+
7
+ DefaultRenderable: "Bookstore-Layout-Shell",
8
+ DefaultDestinationAddress: "#Bookstore-App-Container",
9
+
10
+ AutoRender: false,
11
+
12
+ Templates:
13
+ [
14
+ {
15
+ Hash: "Bookstore-Layout-Shell-Template",
16
+ Template: /*html*/`
17
+ <div id="layout">
18
+ <a href="#menu" id="menuLink" class="menu-link"><span></span></a>
19
+ <div id="menu">
20
+ <div id="Bookstore-Navigation-Container" class="pure-menu"></div>
21
+ </div>
22
+ <div id="Bookstore-Content-Container"></div>
23
+ </div>
24
+ `
25
+ }
26
+ ],
27
+
28
+ Renderables:
29
+ [
30
+ {
31
+ RenderableHash: "Bookstore-Layout-Shell",
32
+ TemplateHash: "Bookstore-Layout-Shell-Template",
33
+ DestinationAddress: "#Bookstore-App-Container",
34
+ RenderMethod: "replace"
35
+ }
36
+ ]
37
+ };
38
+
39
+ class BookstoreLayoutView extends libPictView
40
+ {
41
+ constructor(pFable, pOptions, pServiceHash)
42
+ {
43
+ super(pFable, pOptions, pServiceHash);
44
+ }
45
+
46
+ onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)
47
+ {
48
+ // Render the navigation sidebar
49
+ this.pict.views['Bookstore-Navigation'].render();
50
+
51
+ // Inject all CSS
52
+ this.pict.CSSMap.injectCSS();
53
+
54
+ // Wire up the hamburger menu toggle for responsive
55
+ this._initMenuToggle();
56
+
57
+ // Try to navigate to current URL hash route, or fall back to dashboard
58
+ if (this.pict.providers.PictRouter && !this.pict.providers.PictRouter.navigateCurrent())
59
+ {
60
+ this.pict.PictApplication.showView('Bookstore-Dashboard');
61
+ this.pict.providers.PictRouter.resolve();
62
+ }
63
+
64
+ return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
65
+ }
66
+
67
+ _initMenuToggle()
68
+ {
69
+ let tmpMenuLink = document.getElementById('menuLink');
70
+ let tmpLayout = document.getElementById('layout');
71
+
72
+ if (tmpMenuLink && tmpLayout)
73
+ {
74
+ tmpMenuLink.onclick = function (pEvent)
75
+ {
76
+ pEvent.preventDefault();
77
+ tmpLayout.classList.toggle('active');
78
+ };
79
+ }
80
+ }
81
+ }
82
+
83
+ module.exports = BookstoreLayoutView;
84
+
85
+ module.exports.default_configuration = _ViewConfiguration;
@@ -0,0 +1,58 @@
1
+ const libPictSectionLogin = require('pict-section-login');
2
+
3
+ const _ViewConfiguration =
4
+ {
5
+ ViewIdentifier: "Bookstore-Login",
6
+
7
+ DefaultRenderable: "Login-Container",
8
+ DefaultDestinationAddress: "#Bookstore-Login-Form-Container",
9
+ TargetElementAddress: "#Bookstore-Login-Container",
10
+
11
+ AutoRender: false,
12
+
13
+ // The application controls when to check the session,
14
+ // so we disable the auto-check built into the section.
15
+ CheckSessionOnLoad: false,
16
+ ShowOAuthProviders: false,
17
+
18
+ // orator-authentication endpoints (defaults match retold-harness)
19
+ LoginEndpoint: "/1.0/Authenticate",
20
+ LoginMethod: "POST",
21
+ LogoutEndpoint: "/1.0/Deauthenticate",
22
+ CheckSessionEndpoint: "/1.0/CheckSession",
23
+
24
+ SessionDataAddress: "AppData.Session"
25
+ };
26
+
27
+ class BookstoreLoginView extends libPictSectionLogin
28
+ {
29
+ constructor(pFable, pOptions, pServiceHash)
30
+ {
31
+ super(pFable, pOptions, pServiceHash);
32
+ }
33
+
34
+ onLoginSuccess(pSessionData)
35
+ {
36
+ this.log.info('Login succeeded, switching to protected app.');
37
+ if (this.pict.PictApplication && typeof (this.pict.PictApplication.showProtectedApp) === 'function')
38
+ {
39
+ this.pict.PictApplication.showProtectedApp();
40
+ }
41
+ }
42
+
43
+ onSessionChecked(pSessionData)
44
+ {
45
+ if (pSessionData && pSessionData.LoggedIn)
46
+ {
47
+ this.log.info('Existing session found, switching to protected app.');
48
+ if (this.pict.PictApplication && typeof (this.pict.PictApplication.showProtectedApp) === 'function')
49
+ {
50
+ this.pict.PictApplication.showProtectedApp();
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ module.exports = BookstoreLoginView;
57
+
58
+ module.exports.default_configuration = _ViewConfiguration;