IMPR: GraphQL page browsing with History API

This commit is contained in:
Tony Air 2021-02-16 13:45:57 +07:00
parent 85b77d3453
commit cd305e05cb
16 changed files with 196 additions and 381 deletions

2
dist/css/app.css vendored

File diff suppressed because one or more lines are too long

2
dist/index.html vendored
View File

@ -1,4 +1,4 @@
<!doctype html><html lang="en"><head><title>Webpack Bootstrap 4 UI Demo</title><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="description" content="UI Kit"/><meta name="author" content="Tony Air <tony@twma.pro>"/><meta name="ping" content="/Security/ping"/><script defer="defer" src="js/app.js"></script><link href="css/app.css" rel="stylesheet"></head><body data-default-lng="0" data-default-lat="0"><noscript><div class="main-bn">Please, enable javascript.</div></noscript><div id="PageTopAnchor"></div><div class="flyout-FlyoutUI"><i class="fas fa-times flyout-FlyoutUI__close"></i><h2 class="flyout-FlyoutUI__title"><i class="fas fa-asterisk"></i> Flyout Demo</h2><div class="flyout-FlyoutUI__content typography"><p>Lipsum .... .... ....</p></div></div><div class="wrapper"><header id="Header" class="bg-dark"><nav id="Navigation" class="fixed-top navbar navbar-expand-lg"><div class="container"><a class="navbar-brand" href="#">UI Kit</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#NavbarResponsive" aria-controls="NavbarResponsive" aria-expanded="false" aria-label="Toggle navigation"><i class="navbar-toggler-icon fa fas fa-bars"></i></button><div id="NavbarResponsive" class="collapse navbar-collapse"><ul class="navbar-nav"><li class="nav-item active"><a class="nav-link js-scrollTo" href="#PageTopAnchor">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link js-scrollTo" href="#Slider">Slider</a></li><li class="nav-item dropdown dropdown-hover"><a id="NavbarDropdown" class="nav-link dropdown-toggle-fl dropdown-toggle-notouch" href="#">Dropdown </a><button class="nav-link dropdown-toggle dropdown-toggle-sm dropdown-toggle-touch" role="button" data-bs-toggle="dropdown" aria-expanded="false" type="button">Dropdown</button><div class="dropdown-menu" aria-labelledby="NavbarDropdown"><a class="dropdown-item" href="#">Action</a> <a class="dropdown-item js-scrollTo" href="#AccordionList">Scroll to AccordionList</a><div class="dropdown-divider"></div><a class="dropdown-item" href="https://google.com" target="_blank" rel="nofollow">Go to Google.com</a></div></li><li class="nav-item"><a id="LoadGraphQL" class="nav-link js-scrollTo graphql" href="../src/_graphql/readPages.json">Load GraphQL</a></li><li class="nav-item"><a class="nav-link js-scrollTo" href="#List">List</a></li><li class="nav-item"><a class="nav-link js-scrollTo" href="#AccordionList">Accordion List</a></li><li><form class="form-inline my-2 my-lg-0"><input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search"/> <button class="btn btn-outline-success my-2 my-sm-0" type="submit"><i class="fas fa-search"></i> <span class="sr-only">Search</span></button></form></li></ul></div></div></nav></header><main id="MainContent" class="page-content"><div class="elemental-area"><section id="Slider" class="element site__elements__sliderelement"><div class="element-container container-fluid"><div id="Carousel{$ID}" class="carousel slide js-carousel d-none d-sm-block" data-indicators="true" data-arrows="true"><div class="carousel-inner"><div class="carousel-item active"><a href="$PageLink.Link" title="$PageLink.MenuTitle.XML" class="btn-primary"><img class="d-block w-100" src="https://placehold.it/1200x600" alt="$Headline"></a><div class="carousel-caption"><div class="carousel-caption-container"><h2 class="carousel-title">$Headline #1</h2><p class="carousel-content">$Description</p><p><a href="$PageLink.Link" title="$PageLink.MenuTitle.XML" class="btn btn-default btn-lg"><i class="fas fa-bars"></i> Learn More</a></p></div></div></div><div class="carousel-item"><a href="$PageLink.Link" title="$PageLink.MenuTitle.XML" class="btn-primary"><img class="d-block w-100" src="https://placehold.it/1200x600" alt="$Headline"></a><div class="carousel-caption"><div class="carousel-caption-container"><h2 class="carousel-title">$Headline #2</h2><p class="carousel-content">$Description</p><p><a href="$PageLink.Link" title="$PageLink.MenuTitle.XML" class="btn btn-default btn-lg"><i class="fas fa-bars"></i> Learn More</a></p></div></div></div><div class="carousel-item"><a href="$PageLink.Link" title="$PageLink.MenuTitle.XML" class="btn-primary"><img class="d-block w-100" src="https://placehold.it/1200x600" alt="$Headline"></a><div class="carousel-caption"><div class="carousel-caption-container"><h2 class="carousel-title">$Headline #3</h2><p class="carousel-content">$Description</p><p><a href="$PageLink.Link" title="$PageLink.MenuTitle.XML" class="btn btn-default btn-lg"><i class="fas fa-bars"></i> Learn More</a></p></div></div></div></div></div></div></section><section id="Lightbox" class="element dnadesign__elemental__models__elementcontent"><div class="element-container container"><div class="content-element__content"><h2 class="content-element__title">Lightbox Demo</h2><style>[data-toggle=lightbox]:focus,[data-toggle=lightbox]:hover{text-decoration:underline}</style><div id="MetaLightboxContainer"><div class="typography"><h2>Loading data</h2><p><a href="../src/img/photo1.png" data-toggle="lightbox" data-gallery="demo" data-title="That's first link">Load an Image</a><br/><a href="../src/test.json" data-toggle="lightbox">Load JSON</a></p><style>[data-toggle=lightbox]:focus,[data-toggle=lightbox]:hover{text-decoration:underline}</style><br/><a href="../src/test-pajax.html" data-toggle="lightbox">Load Partial AJAX HTML</a><br/><a href="../src/not-found.html" data-toggle="lightbox">Not Found test</a><p></p><h2>Embeds</h2><p><a href="https://www.youtube.com/watch?v=WYvZZYthDRI" data-toggle="lightbox" data-embed="true">Embed Youtube link</a><br/><a href="https://vimeo.com/26216129" data-toggle="lightbox" data-embed="true">Embed Vimeo link</a><br/><a href="https://soundcloud.com/littlenapoleon/led-zeppelin-vs-rolling-stones" data-toggle="lightbox" data-embed="true">Embed SoundCloud link</a><br/><a href="https://www.instagram.com/p/CKl5n87hf7R/" data-toggle="lightbox" data-embed="true">Embed Instagram</a></p><h2>Other</h2><p><a href="../src/img/photo2.jpg" data-toggle="lightbox" data-gallery="demo">Use [data-toggle="lightbox"] attribute to attach lightbox action and [href] to specify URL.</a></p><p><a href="../src/img/photo1.png" data-toggle="lightbox" data-gallery="demo" data-title="Use data-title attribute to specify lightbox title">Use [data-gallery="YOUR_GALLERY_NAME"] to group ligthboxes with next/prev arrows</a></p><p data-toggle="lightbox" data-href="https://youtu.be/GgnClrx8N2k" data-gallery="demo" data-title="Yes you can link vimeo and youtube videos as long as AJAX content">Use [data-toggle="lightbox"] + [data-href] attribute to toggle lightbox on regular elements. <b>Click me!</b></p></div><div id="MetaLightboxApp"></div></div></div></div></section><section id="Content" class="element dnadesign__elemental__models__elementcontent"><div class="element-container container"><div class="content-element__content"><h2 class="content-element__title">Content Demo</h2><div class="typography"><h3><i class="fas fa-search"></i> Quick start</h3><ol><li><p>Clone quick start repository</p><pre>git clone https://github.com/a2nt/webpack-bootstrap-ui-kit-quick-start.git</pre></li><li><p>Install npm packages</p><pre>
<!doctype html><html lang="en"><head><title>Webpack Bootstrap 4 UI Demo</title><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="description" content="UI Kit"/><meta name="author" content="Tony Air <tony@twma.pro>"/><meta name="ping" content="/Security/ping"/><meta name="api_url" content="http://127.0.0.1/graphql"/><script defer="defer" src="js/app.js"></script><link href="css/app.css" rel="stylesheet"></head><body data-default-lng="0" data-default-lat="0"><noscript><div class="main-bn">Please, enable javascript.</div></noscript><div id="PageTopAnchor"></div><div class="flyout-FlyoutUI"><i class="fas fa-times flyout-FlyoutUI__close"></i><h2 class="flyout-FlyoutUI__title"><i class="fas fa-asterisk"></i> Flyout Demo</h2><div class="flyout-FlyoutUI__content typography"><p>Lipsum .... .... ....</p></div></div><div class="wrapper"><header id="Header" class="bg-dark"><nav id="Navigation" class="fixed-top navbar navbar-expand-lg"><div class="container"><a class="navbar-brand" href="#">UI Kit</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#NavbarResponsive" aria-controls="NavbarResponsive" aria-expanded="false" aria-label="Toggle navigation"><i class="navbar-toggler-icon fa fas fa-bars"></i></button><div id="NavbarResponsive" class="collapse navbar-collapse"><ul class="navbar-nav"><li class="nav-item active"><a class="nav-link js-scrollTo" href="#PageTopAnchor">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link js-scrollTo" href="#Slider">Slider</a></li><li class="nav-item dropdown dropdown-hover"><a id="NavbarDropdown" class="nav-link dropdown-toggle-fl dropdown-toggle-notouch" href="#">Dropdown </a><button class="nav-link dropdown-toggle dropdown-toggle-sm dropdown-toggle-touch" role="button" data-bs-toggle="dropdown" aria-expanded="false" type="button">Dropdown</button><div class="dropdown-menu" aria-labelledby="NavbarDropdown"><a class="dropdown-item" href="#">Action</a> <a class="dropdown-item js-scrollTo" href="#AccordionList">Scroll to AccordionList</a><div class="dropdown-divider"></div><a class="dropdown-item" href="https://google.com" target="_blank" rel="nofollow">Go to Google.com</a></div></li><li class="nav-item"><a id="LoadGraphQL" class="nav-link graphql-page" href="/home">Load GraphQL</a></li><li class="nav-item"><a class="nav-link graphql-page" href="/aquascaping">Load GraphQL #2</a></li><li class="nav-item"><a class="nav-link js-scrollTo" href="#AccordionList">Accordion List</a></li><li><form class="form-inline my-2 my-lg-0"><input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search"/> <button class="btn btn-outline-success my-2 my-sm-0" type="submit"><i class="fas fa-search"></i> <span class="sr-only">Search</span></button></form></li></ul></div></div></nav></header><main id="MainContent" class="page-content"><div class="elemental-area"><section id="Slider" class="element site__elements__sliderelement"><div class="element-container container-fluid"><div id="Carousel{$ID}" class="carousel slide js-carousel d-none d-sm-block" data-indicators="true" data-arrows="true"><div class="carousel-inner"><div class="carousel-item active"><a href="$PageLink.Link" title="$PageLink.MenuTitle.XML" class="btn-primary"><img class="d-block w-100" src="https://placehold.it/1200x600" alt="$Headline"></a><div class="carousel-caption"><div class="carousel-caption-container"><h2 class="carousel-title">$Headline #1</h2><p class="carousel-content">$Description</p><p><a href="$PageLink.Link" title="$PageLink.MenuTitle.XML" class="btn btn-default btn-lg"><i class="fas fa-bars"></i> Learn More</a></p></div></div></div><div class="carousel-item"><a href="$PageLink.Link" title="$PageLink.MenuTitle.XML" class="btn-primary"><img class="d-block w-100" src="https://placehold.it/1200x600" alt="$Headline"></a><div class="carousel-caption"><div class="carousel-caption-container"><h2 class="carousel-title">$Headline #2</h2><p class="carousel-content">$Description</p><p><a href="$PageLink.Link" title="$PageLink.MenuTitle.XML" class="btn btn-default btn-lg"><i class="fas fa-bars"></i> Learn More</a></p></div></div></div><div class="carousel-item"><a href="$PageLink.Link" title="$PageLink.MenuTitle.XML" class="btn-primary"><img class="d-block w-100" src="https://placehold.it/1200x600" alt="$Headline"></a><div class="carousel-caption"><div class="carousel-caption-container"><h2 class="carousel-title">$Headline #3</h2><p class="carousel-content">$Description</p><p><a href="$PageLink.Link" title="$PageLink.MenuTitle.XML" class="btn btn-default btn-lg"><i class="fas fa-bars"></i> Learn More</a></p></div></div></div></div></div></div></section><section id="Lightbox" class="element dnadesign__elemental__models__elementcontent"><div class="element-container container"><div class="content-element__content"><h2 class="content-element__title">Lightbox Demo</h2><style>[data-toggle=lightbox]:focus,[data-toggle=lightbox]:hover{text-decoration:underline}</style><div id="MetaLightboxContainer"><div class="typography"><h2>Loading data</h2><p><a href="../src/img/photo1.png" data-toggle="lightbox" data-gallery="demo" data-title="That's first link">Load an Image</a><br/><a href="../src/test.json" data-toggle="lightbox">Load JSON</a></p><style>[data-toggle=lightbox]:focus,[data-toggle=lightbox]:hover{text-decoration:underline}</style><br/><a href="../src/test-pajax.html" data-toggle="lightbox">Load Partial AJAX HTML</a><br/><a href="../src/not-found.html" data-toggle="lightbox">Not Found test</a><p></p><h2>Embeds</h2><p><a href="https://www.youtube.com/watch?v=WYvZZYthDRI" data-toggle="lightbox" data-embed="true">Embed Youtube link</a><br/><a href="https://vimeo.com/26216129" data-toggle="lightbox" data-embed="true">Embed Vimeo link</a><br/><a href="https://soundcloud.com/littlenapoleon/led-zeppelin-vs-rolling-stones" data-toggle="lightbox" data-embed="true">Embed SoundCloud link</a><br/><a href="https://www.instagram.com/p/CKl5n87hf7R/" data-toggle="lightbox" data-embed="true">Embed Instagram</a></p><h2>Other</h2><p><a href="../src/img/photo2.jpg" data-toggle="lightbox" data-gallery="demo">Use [data-toggle="lightbox"] attribute to attach lightbox action and [href] to specify URL.</a></p><p><a href="../src/img/photo1.png" data-toggle="lightbox" data-gallery="demo" data-title="Use data-title attribute to specify lightbox title">Use [data-gallery="YOUR_GALLERY_NAME"] to group ligthboxes with next/prev arrows</a></p><p data-toggle="lightbox" data-href="https://youtu.be/GgnClrx8N2k" data-gallery="demo" data-title="Yes you can link vimeo and youtube videos as long as AJAX content">Use [data-toggle="lightbox"] + [data-href] attribute to toggle lightbox on regular elements. <b>Click me!</b></p></div><div id="MetaLightboxApp"></div></div></div></div></section><section id="Content" class="element dnadesign__elemental__models__elementcontent"><div class="element-container container"><div class="content-element__content"><h2 class="content-element__title">Content Demo</h2><div class="typography"><h3><i class="fas fa-search"></i> Quick start</h3><ol><li><p>Clone quick start repository</p><pre>git clone https://github.com/a2nt/webpack-bootstrap-ui-kit-quick-start.git</pre></li><li><p>Install npm packages</p><pre>
cd ./webpack-bootstrap-ui-kit-quick-start.git
npm install
</pre></li><li><p>Edit ./src files</p></li><li><p>Start development server at https://127.0.0.1:8001/:</p><pre>yarn start</pre><p>Compile:</p><pre>yarn build</pre></li></ol><h2><i class="fas fa-search"></i> Header #2 <i class="fas fa-search"></i></h2><p><img src="https://placehold.it/200x200" alt="Test Image" class="image right"/>Content Text Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p><ol><li>First<ul><li>First</li><li>Second<ol><li>First</li><li>Second</li><li>Third</li></ol><ul><li>First</li><li>Second</li><li>Third</li></ul></li><li><p>Content Text Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p></li></ul></li><li><p>{paragraph} Second</p><p>{paragraph} Second #2</p></li><li><p>Content Text Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p></li></ol><h2 class="text-center">Table #1: Default</h2><table><thead><tr><th>#1</th><th>#2</th><th>#3</th></tr></thead><tbody><tr><th>#1-1</th><td>#1-2</td><td>#1-3</td></tr><tr><td>#2-1</td><td>#2-2</td><td>#2-3</td></tr><tr><th>#3-1</th><td>#3-2</td><td>#3-3</td></tr></tbody></table><p class="text-justify">Content Text Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p><h2 class="text-center">Table #2: Border-less</h2><table class="table-none"><thead><tr><th>#1</th><th>#2</th><th>#3</th></tr></thead><tbody><tr><th>#1-1</th><td>#1-2</td><td>#1-3</td></tr><tr><td>#2-1</td><td>#2-2</td><td>#2-3</td></tr><tr><th>#3-1</th><td>#3-2</td><td>#3-3</td></tr></tbody></table></div></div></div></section><section id="List" class="element dnadesign__elementallist__model__elementlist bg-dark"><div class="element-container container"><h2 class="list-element__title">Content Header</h2><div class="typography">Some content ...</div><div class="list-element__container row" data-listelement-count="4"><div class="element dnadesign__elemental__models__elementcontent block col-md-3"><div class="element-container"><div class="content-element__content"><div class="typography"><img src="https://placehold.it/300x300" alt="Some kind image"/></div></div></div></div><div class="element dnadesign__elemental__models__elementcontent block col-md-3"><div class="element-container"><div class="content-element__content"><div class="typography"><img src="https://placehold.it/300x300" alt="Some kind image"/></div></div></div></div><div class="element dnadesign__elemental__models__elementcontent block col-md-3"><div class="element-container"><div class="content-element__content"><div class="typography"><img src="https://placehold.it/300x300" alt="Some kind image"/></div></div></div></div><div class="element dnadesign__elemental__models__elementcontent block col-md-3"><div class="element-container"><div class="content-element__content"><div class="typography"><img src="https://placehold.it/300x300" alt="Some kind image"/></div></div></div></div></div></div></section><section id="AccordionList" class="element site__elements__accordion"><div class="element-container container"><h2 class="list-element__title">Accordion demo</h2><div class="typography">Some content ...</div><div id="AccordionExample" class="list-element__container row accordion" data-listelement-count="4"><div class="element dnadesign__elemental__models__elementcontent block col-md-12 card"><div class="element-container"><div class="content-element__content"><div id="Heading1" class="card-header"><button data-target="#Collapse1" aria-controls="Collapse1" class="btn btn-link collapsed" type="button" data-toggle="collapse" aria-expanded="false"><h2 class="element__title">Content Header #1</h2></button></div><div id="Collapse1" aria-labelledby="Heading1" data-parent="#AccordionExample" class="collapse"><div class="card-body"><div class="typography"><p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p></div></div></div></div></div></div><div class="element dnadesign__elemental__models__elementcontent block col-md-12 card"><div class="element-container"><div class="content-element__content"><div class="card-header" id="Heading2"><button data-target="#Collapse2" aria-controls="Collapse2" class="btn btn-link collapsed" type="button" data-toggle="collapse" aria-expanded="false"><h2 class="element__title">Content Header #2</h2></button></div><div id="Collapse2" class="collapse" aria-labelledby="Heading2" data-parent="#AccordionExample"><div class="card-body"><div class="typography"><p>It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</p></div></div></div></div></div></div><div class="element dnadesign__elemental__models__elementcontent block col-md-12 card"><div class="element-container"><div class="content-element__content"><div class="card-header" id="Heading3"><button data-target="#Collapse3" aria-controls="Collapse3" class="btn btn-link collapsed" type="button" data-toggle="collapse" aria-expanded="false"><h2 class="element__title">Content Header #3</h2></button></div><div id="Collapse3" class="collapse" aria-labelledby="Heading3" data-parent="#AccordionExample"><div class="card-body"><div class="typography"><p>It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</p></div></div></div></div></div></div><div class="element dnadesign__elemental__models__elementcontent block col-md-12 card"><div class="element-container"><div class="content-element__content"><div id="Heading4" class="card-header"><button data-target="#Collapse4" aria-controls="Collapse4" class="btn btn-link collapsed" type="button" data-toggle="collapse" aria-expanded="false"><h2 class="element__title">Content Header #2</h2></button></div><div id="Collapse4" class="collapse" aria-labelledby="Heading4" data-parent="#AccordionExample"><div class="card-body"><div class="typography"><p>It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</p></div></div></div></div></div></div></div></div></section><section id="MapElement" class="element site__elements__mapelement"><div class="element-container container"><h2 class="content-element__title">Find Location</h2><div class="typography"><p>Use _consts.js to change Google Maps to Mapbox.</p></div><div class="mapAPI-map-container" data-map-zoom="12" data-key="" data-map-style="mapbox://styles/mapbox/streets-v9" data-geojson="{&quot;type&quot;:&quot;MarkerCollection&quot;,&quot;features&quot;:[{&quot;id&quot;:4,&quot;type&quot;:&quot;Feature&quot;,&quot;icon&quot;:&quot;&lt;i class=\&quot;fas fa-map-marker-alt\&quot;&gt;&lt;\/i&gt;&quot;,&quot;properties&quot;:{&quot;content&quot;:&quot;&lt;div id=\&quot;MapPin4\&quot; data-id=\&quot;4\&quot; class=\&quot;location\&quot;&gt;\n &lt;div class=\&quot;fn\&quot;&gt;Office #1&lt;\/div&gt;\n &lt;div class=\&quot;addr\&quot;&gt;17 Lakeside Drive&lt;\/div&gt;\n \n \n \n &lt;div class=\&quot;d-none\&quot;&gt; U&lt;\/div&gt;\n \n \n \n &lt;div class=\&quot;dir-link\&quot;&gt;\n &lt;a href=\&quot;https:\/\/www.google.com\/maps\/dir\/Current+Location\/40.8827743,-74.4276612\&quot; target=\&quot;_blank\&quot;&gt;Get Directions &amp;raquo;&lt;\/a&gt;\n &lt;\/div&gt;\n&lt;\/div&gt;\n&quot;},&quot;geometry&quot;:{&quot;type&quot;:&quot;Point&quot;,&quot;coordinates&quot;:[-74.4276612,40.8827743]}},{&quot;id&quot;:3,&quot;type&quot;:&quot;Feature&quot;,&quot;icon&quot;:&quot;&lt;i class=\&quot;fas fa-map-marker-alt\&quot;&gt;&lt;\/i&gt;&quot;,&quot;properties&quot;:{&quot;content&quot;:&quot;&lt;div id=\&quot;MapPin3\&quot; data-id=\&quot;3\&quot; class=\&quot;location\&quot;&gt;\n &lt;div class=\&quot;fn\&quot;&gt;Office #2&lt;\/div&gt;\n &lt;div class=\&quot;addr\&quot;&gt;Flower Hill Cemetery&lt;\/div&gt;\n \n \n \n &lt;div class=\&quot;d-none\&quot;&gt; N&lt;\/div&gt;\n \n \n \n &lt;div class=\&quot;dir-link\&quot;&gt;\n &lt;a href=\&quot;https:\/\/www.google.com\/maps\/dir\/Current+Location\/40.7884708,-74.0249253\&quot; target=\&quot;_blank\&quot;&gt;Get Directions &amp;raquo;&lt;\/a&gt;\n &lt;\/div&gt;\n&lt;\/div&gt;\n&quot;},&quot;geometry&quot;:{&quot;type&quot;:&quot;Point&quot;,&quot;coordinates&quot;:[-74.0249253,40.7884708]}},{&quot;id&quot;:2,&quot;type&quot;:&quot;Feature&quot;,&quot;icon&quot;:&quot;&lt;i class=\&quot;fas fa-map-marker-alt\&quot;&gt;&lt;\/i&gt;&quot;,&quot;properties&quot;:{&quot;content&quot;:&quot;&lt;div id=\&quot;MapPin2\&quot; data-id=\&quot;2\&quot; class=\&quot;location\&quot;&gt;\n &lt;div class=\&quot;fn\&quot;&gt;Office #3&lt;\/div&gt;\n &lt;div class=\&quot;addr\&quot;&gt;555 Phoenix Road&lt;\/div&gt;\n \n \n \n &lt;div class=\&quot;d-none\&quot;&gt; U&lt;\/div&gt;\n \n \n \n &lt;div class=\&quot;dir-link\&quot;&gt;\n &lt;a href=\&quot;https:\/\/www.google.com\/maps\/dir\/Current+Location\/40.8527479,-78.2475576\&quot; target=\&quot;_blank\&quot;&gt;Get Directions &amp;raquo;&lt;\/a&gt;\n &lt;\/div&gt;\n&lt;\/div&gt;\n&quot;},&quot;geometry&quot;:{&quot;type&quot;:&quot;Point&quot;,&quot;coordinates&quot;:[-78.2475576,40.8527479]}},{&quot;id&quot;:1,&quot;type&quot;:&quot;Feature&quot;,&quot;icon&quot;:&quot;&lt;i class=\&quot;fas fa-map-marker-alt\&quot;&gt;&lt;\/i&gt;&quot;,&quot;properties&quot;:{&quot;content&quot;:&quot;&lt;div id=\&quot;MapPin1\&quot; data-id=\&quot;1\&quot; class=\&quot;location\&quot;&gt;\n &lt;div class=\&quot;fn\&quot;&gt;Office #4&lt;\/div&gt;\n &lt;div class=\&quot;addr\&quot;&gt;15 East Hadley Road&lt;\/div&gt;\n \n \n \n &lt;div class=\&quot;d-none\&quot;&gt; U&lt;\/div&gt;\n \n \n \n &lt;div class=\&quot;dir-link\&quot;&gt;\n &lt;a href=\&quot;https:\/\/www.google.com\/maps\/dir\/Current+Location\/42.3297023,-72.5552186\&quot; target=\&quot;_blank\&quot;&gt;Get Directions &amp;raquo;&lt;\/a&gt;\n &lt;\/div&gt;\n&lt;\/div&gt;\n&quot;},&quot;geometry&quot;:{&quot;type&quot;:&quot;Point&quot;,&quot;coordinates&quot;:[-72.5552186,42.3297023]}}]}" data-fly-to-marker="true" data-fly-to-bounds="false"><div class="mapAPI-map"></div></div><div class="locations"><div class="row"><div class="col-sm-3"><div id="MapPin4" data-id="4" class="location"><div class="fn">Office #1</div><div class="addr">17 Lakeside Drive</div><div class="d-none">U</div><div class="dir-link"><a href="https://www.google.com/maps/dir/Current+Location/40.8827743,-74.4276612" target="_blank">Get Directions &raquo;</a></div></div></div><div class="col-sm-3"><div id="MapPin3" data-id="3" class="location"><div class="fn">Office #2</div><div class="addr">Flower Hill Cemetery</div><div class="d-none">N</div><div class="dir-link"><a href="https://www.google.com/maps/dir/Current+Location/40.7884708,-74.0249253" target="_blank">Get Directions &raquo;</a></div></div></div><div class="col-sm-3"><div id="MapPin2" data-id="2" class="location"><div class="fn">Office #3</div><div class="addr">555 Phoenix Road</div><div class="d-none">U</div><div class="dir-link"><a href="https://www.google.com/maps/dir/Current+Location/40.8527479,-78.2475576" target="_blank">Get Directions &raquo;</a></div></div></div><div class="col-sm-3"><div id="MapPin1" data-id="1" class="location"><div class="fn">Office #4</div><div class="addr">15 East Hadley Road</div><div class="d-none">U</div><div class="dir-link"><a href="https://www.google.com/maps/dir/Current+Location/42.3297023,-72.5552186" target="_blank">Get Directions &raquo;</a></div></div></div></div></div></div></section></div></main></div><footer id="Footer" class="footer site-footer"><div class="wrapper"><div class="container"><div class="row"><div class="col-sm-3">Col #1</div><div class="col-sm-3">Col #2</div><div class="col-sm-3">Col #3</div><div class="col-sm-3">Col #4</div></div></div></div><div class="footer"><div class="container"><div class="row"><div class="col-sm-8 d-flex"><div class="copyright">Copyright &copy; 2019 - Tony Air (A2nt) Webpack Bootstrap 4 UI Kit</div><nav class="footer-nav"><ul class="list-inline d-flex"><li><a href="#">Sitemap</a></li><li><a href="#">Privacy Policy</a></li></ul></nav></div><div class="col-sm-4 text-right"><div class="credits footer__credits"><a href="https://github.com/a2nt" target="_blank" rel="nofollow"><span>Developed by</span> Tony Air</a></div></div></div></div></div></footer><script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script><script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.12.0/css/all.css" media="all"/></body></html>

2
dist/js/app.js vendored

File diff suppressed because one or more lines are too long

4
dist/records.json vendored
View File

@ -169,7 +169,7 @@
"./node_modules/.pnpm/babel-loader@8.2.2_dcac3d309cb98ce30193ac990b30c3c3/node_modules/babel-loader/lib/index.js??ruleSet[1].rules[0].use!./node_modules/.pnpm/tslib@2.1.0/node_modules/tslib/tslib.es6.js": 7775,
"./node_modules/.pnpm/babel-loader@8.2.2_dcac3d309cb98ce30193ac990b30c3c3/node_modules/babel-loader/lib/index.js??ruleSet[1].rules[0].use!./node_modules/.pnpm/zen-observable@0.8.15/node_modules/zen-observable/index.js": 6169,
"./node_modules/.pnpm/babel-loader@8.2.2_dcac3d309cb98ce30193ac990b30c3c3/node_modules/babel-loader/lib/index.js??ruleSet[1].rules[0].use!./node_modules/.pnpm/zen-observable@0.8.15/node_modules/zen-observable/lib/Observable.js": 1243,
"./node_modules/.pnpm/babel-loader@8.2.2_dcac3d309cb98ce30193ac990b30c3c3/node_modules/babel-loader/lib/index.js??ruleSet[1].rules[0].use!./src/js/app.js|2151b630eb873fd086d666959fe67b82": 6907,
"./node_modules/.pnpm/babel-loader@8.2.2_dcac3d309cb98ce30193ac990b30c3c3/node_modules/babel-loader/lib/index.js??ruleSet[1].rules[0].use!./src/js/app.js|f25cb5a31d1956240425a0d16eeb46a3": 9567,
"./node_modules/.pnpm/file-loader@6.2.0_webpack@5.21.2/node_modules/file-loader/dist/cjs.js??ruleSet[1].rules[3].use[0]!./node_modules/.pnpm/img-optimize-loader@1.0.7_file-loader@6.2.0+webpack@5.21.2/node_modules/img-optimize-loader/dist/index.js??ruleSet[1].rules[4].use[0]!./node_modules/.pnpm/image-minimizer-webpack-plugin@2.2.0_webpack@5.21.2/node_modules/image-minimizer-webpack-plugin/dist/loader.js??ruleSet[1].rules[5]!./src/img/photo3.svg": 3609,
"./node_modules/.pnpm/graphql@15.5.0/node_modules/graphql/language/visitor.mjs|3169b6159575a7c1144bcfbea4ed01af": 3210,
"./node_modules/.pnpm/img-optimize-loader@1.0.7_file-loader@6.2.0+webpack@5.21.2/node_modules/img-optimize-loader/dist/index.js??ruleSet[1].rules[4].use[0]!./node_modules/.pnpm/image-minimizer-webpack-plugin@2.2.0_webpack@5.21.2/node_modules/image-minimizer-webpack-plugin/dist/loader.js??ruleSet[1].rules[5]!./src/img/bg.png": 7665,
@ -248,7 +248,6 @@
6730,
6785,
6794,
6907,
7090,
7121,
7219,
@ -273,6 +272,7 @@
9311,
9389,
9502,
9567,
9578,
9863,
9923

4
dist/report.html vendored

File diff suppressed because one or more lines are too long

View File

@ -5,4 +5,3 @@
/>
<meta name="description" content="UI Kit" />
<meta name="author" content="Tony Air <tony@twma.pro>" />
<meta name="ping" content="/Security/ping" />

View File

@ -63,13 +63,15 @@
<li class="nav-item">
<a
id="LoadGraphQL"
class="nav-link js-scrollTo graphql"
href="../src/_graphql/readPages.json"
class="nav-link graphql-page"
href="/home"
>Load GraphQL</a
>
</li>
<li class="nav-item">
<a class="nav-link js-scrollTo" href="#List">List</a>
<a class="nav-link graphql-page" href="/aquascaping"
>Load GraphQL #2</a
>
</li>
<li class="nav-item">
<a class="nav-link js-scrollTo" href="#AccordionList"

View File

@ -3,6 +3,8 @@
<head>
<title>Webpack Bootstrap 4 UI Demo</title>
<%= require('html-loader!./html/Head.html') %>
<meta name="ping" content="/Security/ping" />
<meta name="api_url" content="<%= GRAPHQL_URL %>" />
</head>
<body data-default-lng="0" data-default-lat="0">
<%= require('html-loader!./html/First.html') %> <%=

View File

@ -1,26 +1,21 @@
import Events from '../_events';
import { cache } from './_apollo.cache';
import {
from,
ApolloClient,
HttpLink,
ApolloLink,
InMemoryCache,
concat,
} from '@apollo/client';
//import { IonicStorageModule } from '@ionic/storage';
//import { persistCache, IonicStorageWrapper } from 'apollo3-cache-persist';
import { persistCacheSync, LocalStorageWrapper } from 'apollo3-cache-persist';
import { onError } from '@apollo/client/link/error';
const NAME = '_appolo';
const cache = new InMemoryCache();
// await before instantiating ApolloClient, else queries might run before the cache is persisted
//await persistCache({
persistCacheSync({
cache,
storage: new LocalStorageWrapper(window.localStorage),
key: 'web-persist',
maxSize: 1048576, // 1Mb
//new IonicStorageWrapper(IonicStorageModule),
});
const API_META = document.querySelector('meta[name="api_url"]');
const API_URL = API_META
? API_META.getAttribute('content')
: `${window.location.protocol }//${ window.location.host }/graphql`;
const authMiddleware = new ApolloLink((operation, forward) => {
// add the authorization to the headers
@ -33,18 +28,75 @@ const authMiddleware = new ApolloLink((operation, forward) => {
return forward(operation);
});
const link = new HttpLink({
uri: 'http://127.0.0.1/graphql',
console.info(`%cAPI: ${API_URL}`, 'color:green;font-size:10px');
// Use explicit `window.fetch` so tha outgoing requests
// are captured and deferred until the Service Worker is ready.
fetch: (...args) => fetch(...args),
credentials: 'same-origin', //'include',
});
const link = from([
authMiddleware,
new ApolloLink((operation, forward) => {
operation.setContext({ start: new Date() });
return forward(operation);
}),
onError(({ operation, response, graphQLErrors, networkError, forward }) => {
if (operation.operationName === 'IgnoreErrorsQuery') {
response.errors = null;
return;
}
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path }) =>
console.error(
`${NAME}: [GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
),
);
if (networkError) {
/*let msg = '';
switch (networkError.statusCode) {
case 404:
msg = 'Not Found.';
break;
case 500:
msg = 'Server issue, please try again latter.';
break;
default:
msg = 'Something went wrong.';
break;
}*/
console.error(`${NAME}: [Network error] ${networkError.statusCode}`);
window.dispatchEvent(new Event(Events.OFFLINE));
}
}),
new ApolloLink((operation, forward) => {
return forward(operation).map((data) => {
// data from a previous link
const time = new Date() - operation.getContext().start;
console.log(
`${NAME}: operation ${operation.operationName} took ${time} ms to complete`,
);
window.dispatchEvent(new Event(Events.ONLINE));
return data;
});
}),
new HttpLink({
uri: API_URL,
// Use explicit `window.fetch` so tha outgoing requests
// are captured and deferred until the Service Worker is ready.
fetch: (...args) => fetch(...args),
credentials: 'same-origin', //'include',
connectToDevTools: process.env.NODE_ENV === 'development' ? true : false,
}),
]);
// Isolate Apollo client so it could be reused
// in both application runtime and tests.
export const client = new ApolloClient({
const client = new ApolloClient({
cache,
link: concat(authMiddleware, link),
link,
});
export { client };

View File

@ -1,17 +1,17 @@
/**
* Add your global events here
*/
import Events from '../_events';
import Consts from '../_consts';
import Page from './_page.jsx';
const axios = require('axios');
import './_main.visibility';
import './_main.touch';
import './_main.online';
import './_main.css-screen-size';
import './_main.links';
const MainUI = ((W) => {
const NAME = 'MainUI';
const NAME = '_main';
const D = document;
const BODY = document.body;
console.clear();
const BODY = D.body;
console.info(
`%cUI Kit ${UINAME} ${UIVERSION}`,
@ -42,284 +42,38 @@ const MainUI = ((W) => {
console.groupCollapsed('Init');
console.time('init');
// update visibility state
// get browser window visibility preferences
// Opera 12.10, Firefox >=18, Chrome >=31, IE11
const HiddenName = 'hidden';
const VisibilityChangeEvent = 'visibilitychange';
D.addEventListener(VisibilityChangeEvent, () => {
if (D.visibilityState === HiddenName) {
console.log(`${NAME}: Tab: hidden`);
BODY.classList.add('is-hidden');
BODY.classList.remove('is-focused');
W.dispatchEvent(new Event(Events.TABHIDDEN));
} else {
console.log(`${NAME}: Tab: focused`);
BODY.classList.add('is-focused');
BODY.classList.remove('is-hidden');
W.dispatchEvent(new Event(Events.TABFOCUSED));
}
});
let pingInterval;
const PING_META = document.querySelector('meta[name="ping"]');
let update_online_status_lock = false;
const UPDATE_ONLINE_STATUS = (online) => {
if (update_online_status_lock) {
return;
}
update_online_status_lock = true;
if (online) {
if (BODY.classList.contains('is-offline')) {
console.log(`${NAME}: back Online`);
W.dispatchEvent(new Event(Events.BACKONLINE));
} else {
console.log(`${NAME}: Online`);
W.dispatchEvent(new Event(Events.ONLINE));
}
BODY.classList.add('is-online');
BODY.classList.remove('is-offline');
if (PING_META && !pingInterval) {
console.log(`${NAME}: SESSION_PING is active`);
pingInterval = setInterval(SESSION_PING, 300000); // 5 min in ms
}
} else {
console.log(`${NAME}: Offline`);
BODY.classList.add('is-offline');
BODY.classList.remove('is-online');
clearInterval(pingInterval);
pingInterval = null;
W.dispatchEvent(new Event(Events.OFFLINE));
}
update_online_status_lock = false;
};
// session ping
let session_ping_lock = false;
const SESSION_PING = () => {
if (session_ping_lock || BODY.classList.contains('is-offline')) {
return;
}
const PING_URL = PING_META.getAttribute('content');
console.log(`${NAME}: session ping`);
session_ping_lock = true;
axios
.post(PING_URL, {})
.then((resp) => {
session_ping_lock = false;
UPDATE_ONLINE_STATUS(true);
})
.catch((error) => {
console.error(error);
console.warn(`${NAME}: SESSION_PING failed`);
session_ping_lock = false;
UPDATE_ONLINE_STATUS(false);
});
};
// current browser online state
if (typeof navigator.onLine !== 'undefined') {
if (!navigator.onLine) {
UPDATE_ONLINE_STATUS(false);
} else {
UPDATE_ONLINE_STATUS(true);
}
}
W.addEventListener(`${Events.OFFLINE}`, () => {
UPDATE_ONLINE_STATUS(false);
});
W.addEventListener(`${Events.ONLINE}`, () => {
UPDATE_ONLINE_STATUS(true);
});
// touch/mouse detection
let prev_touch_event_name;
let touch_timeout;
const SET_TOUCH_SCREEN = (bool, event_name) => {
if (touch_timeout || event_name === prev_touch_event_name) {
return;
}
if (bool) {
console.log(`${NAME}: Touch screen enabled`);
BODY.classList.add('is-touch');
BODY.classList.remove('is-mouse');
W.dispatchEvent(new Event(Events.TOUCHENABLE));
} else {
console.log(`${NAME}: Touch screen disabled`);
BODY.classList.add('is-mouse');
BODY.classList.remove('is-touch');
W.dispatchEvent(new Event(Events.TOUCHDISABLED));
}
prev_touch_event_name = event_name;
// prevent firing touch and mouse events together
if (!touch_timeout) {
touch_timeout = setTimeout(() => {
clearTimeout(touch_timeout);
touch_timeout = null;
}, 500);
}
};
SET_TOUCH_SCREEN(
'ontouchstart' in W ||
navigator.MaxTouchPoints > 0 ||
navigator.msMaxTouchPoints > 0 ||
window.matchMedia('(hover: none)').matches,
'init',
);
D.addEventListener('touchend', (e) => {
let touch = false;
if (e.type !== 'click') {
touch = true;
}
SET_TOUCH_SCREEN(touch, 'click-touchend');
});
// disable touch on mouse events
D.addEventListener('click', (e) => {
let touch = false;
if (e.type !== 'click') {
touch = true;
}
SET_TOUCH_SCREEN(touch, 'click-touchend');
});
class MainUI {
// first time the website initialization
static init() {
const ui = this;
console.log(`${NAME}: init`);
ui.detectCSSScreenSize();
W.addEventListener(`${Events.RESIZE}`, () => {
ui.detectCSSScreenSize();
});
// store landing page state
window.history.replaceState(
{ landing: window.location.href },
document.title,
window.location.href,
W.history.replaceState(
{ landing: W.location.href },
D.title,
W.location.href,
);
//
ui.ajax();
console.groupEnd('init');
ui.loaded();
}
static ajax() {
// init AJAX components
static loaded() {
const ui = this;
console.log(`${NAME}: ajax`);
document.querySelectorAll('.graphql').forEach((el) => {
el.addEventListener('click', (e) => {
e.preventDefault();
const GraphPage = ReactDOM.render(
<Page />,
document.getElementById('MainContent'),
);
const el = e.currentTarget;
const link = el.getAttribute('href') || el.getAttribute('data-href');
GraphPage.state.current = el;
GraphPage.load(link);
window.addEventListener('popstate', (e) => {
if (e.state && e.state.page) {
console.log(`${NAME} popstate: load`);
GraphPage.setState(JSON.parse(e.state.page));
} else if (e.state && e.state.landing) {
console.log(`${NAME} popstate: go to landing`);
window.location.href = e.state.landing;
} else {
console.log(`${NAME} popstate: missing`);
console.log(e);
}
});
});
});
}
static detectCSSScreenSize() {
const el = D.createElement('div');
el.className = 'env-test';
BODY.appendChild(el);
const envs = [...Consts.ENVS].reverse();
let curEnv = envs.shift();
BODY.classList.remove(...envs);
for (let i = 0; i < envs.length; ++i) {
const env = envs[i];
el.classList.add(`d-${env}-none`);
if (W.getComputedStyle(el).display === 'none') {
curEnv = env;
BODY.classList.add(`${curEnv}`);
break;
}
}
let landscape = true;
if (W.innerWidth > W.innerHeight) {
BODY.classList.add('landscape');
BODY.classList.remove('portrait');
} else {
landscape = false;
BODY.classList.add('portrait');
BODY.classList.remove('landscape');
}
console.log(
`${NAME}: screen size detected ${curEnv} | landscape ${landscape}`,
);
return curEnv;
console.log(`${NAME}: loaded`);
}
}
W.addEventListener(`${Events.LOADED}`, () => {
console.log(`${NAME}: Loaded`);
MainUI.init();
UPDATE_ONLINE_STATUS(true);
console.groupEnd('init');
console.timeEnd('init');
});
W.addEventListener(`${Events.AJAX}`, () => {
MainUI.ajax();
UPDATE_ONLINE_STATUS(true);
W.addEventListener(`${Events.AJAX}`, () => {
MainUI.loaded();
});
W.MainUI = MainUI;

View File

@ -1,24 +1,28 @@
/*
* Lightbox window
* page #MainContent area
*/
import { Component } from 'react';
import Events from '../_events';
import { useQuery, gql } from '@apollo/client';
import { client } from './_apollo';
import { gql } from '@apollo/client';
import { cache } from './_apollo.cache';
const D = document;
const BODY = document.body;
class Page extends Component {
state = {
type: [],
shown: false,
loading: false,
Title: 'Loading ...',
loading: true,
error: false,
current: null,
ID: null,
URLSegment: null,
ClassName: 'Page',
CSSClass: null,
Title: null,
Summary: null,
Link: null,
URL: null,
@ -31,14 +35,6 @@ class Page extends Component {
if (ui.state.Title) {
document.title = ui.state.Title;
if (ui.state.URL) {
window.history.pushState(
{ page: JSON.stringify(ui.state) },
ui.state.Title,
ui.state.URL,
);
}
}
if (ui.state.Elements.length) {
@ -50,30 +46,25 @@ class Page extends Component {
super(props);
const ui = this;
ui.name = ui.constructor.name;
ui.empty_state = ui.state;
console.log(`${ui.name}: init`);
}
reset = () => {
const ui = this;
ui.setState({
type: [],
shown: false,
loading: false,
error: false,
ID: null,
Title: null,
URL: null,
Elements: [],
});
isOnline = () => {
return BODY.classList.contains('is-online');
};
load = (link) => {
const ui = this;
const query = gql(`
const url_segment = link.split('/').pop();
return new Promise((resolve, reject) => {
const query = gql(`
query Pages {
readPages(URLSegment: "home", limit: 1, offset: 0) {
readPages(URLSegment: "${url_segment}", limit: 1, offset: 0) {
edges {
node {
__typename
@ -112,61 +103,71 @@ class Page extends Component {
}
`);
ui.reset();
ui.setState({
Title: 'Loading ...',
loading: true,
});
console.log(client.readQuery({ query }));
client
.query({
query: query,
})
.then((resp) => {
const page = resp.data.readPages.edges[0].node;
if (!ui.isOnline()) {
const resp = client.readQuery({ query });
// write to cache
client.writeQuery({ query, data: { resp } });
console.log(client.readQuery({ query }));
ui.setState({
ID: page.ID,
Title: page.Title,
ClassName: page.ClassName,
URLSegment: page.URLSegment,
CSSClass: page.CSSClass,
Summary: page.Summary,
Link: page.Link,
Elements: page.Elements.edges,
URL: page.Link || link,
loading: false,
});
})
.catch((error) => {
console.error(error);
let msg = '';
if (error.response) {
switch (error.response.status) {
case 404:
msg = 'Not Found.';
break;
case 500:
msg = 'Server issue, please try again latter.';
break;
default:
msg = 'Something went wrong.';
break;
}
} else if (error.request) {
msg = 'No response received';
if (ui.processResponse(resp)) {
console.log(`${ui.name}: Offline cached response`);
resolve(resp);
} else {
console.warn('Error', error.message);
console.log(`${ui.name}: No offline response`);
reject();
}
} else {
if (!ui.state.loading) {
ui.setState(ui.empty_state);
}
ui.setState({ error: msg });
client
.query({
query: query,
fetchPolicy: ui.isOnline() ? 'no-cache' : 'cache-first',
})
.then((resp) => {
// write to cache
client.writeQuery({ query, data: { resp } });
if (ui.processResponse(resp.data)) {
console.log(`${ui.name}: got the server response`);
resolve(resp.data);
} else {
console.log(`${ui.name}: not found`);
reject();
}
});
}
});
};
processResponse = (data) => {
const ui = this;
if (!data.readPages.edges.length) {
console.log(`${ui.name}: not found`);
ui.setState({
Title: 'Not Found',
CSSClass: 'graphql__not-found',
Summary: 'Not Found',
loading: false,
});
return false;
}
const page = data.readPages.edges[0].node;
ui.setState({
ID: page.ID,
Title: page.Title,
ClassName: page.ClassName,
URLSegment: page.URLSegment,
CSSClass: page.CSSClass,
Summary: page.Summary,
Link: page.Link,
Elements: page.Elements.edges,
loading: false,
});
return true;
};
getHtml = (html) => {
@ -189,10 +190,12 @@ class Page extends Component {
let html = '';
if (ui.state.Elements.length) {
console.log(`${ui.name}: render`);
ui.state.Elements.map((el) => {
html += el.node.Render;
});
} else {
console.log(`${ui.name}: loading`);
html += '<div class="loading">Loading ...</div>';
}

View File

@ -39,7 +39,7 @@ export const handlers = [
{
node: {
ID: '1',
Title: 'Home',
Title: 'Home-Mocked',
ClassName: 'Site\\Pages\\HomePage',
CSSClass: 'Site-Pages-HomePage',
Summary:

View File

@ -190,7 +190,7 @@ button.nav-link {
}
}
@media (hover: none) {
@media not all and (hover: none) {
> .dropdown-toggle-touch {
display: inherit;
}

View File

@ -145,6 +145,7 @@ if (filesystem.existsSync(indexPath)) {
template: path.join(conf.APPDIR, conf.SRC, 'index.html'),
templateParameters: {
NODE_ENV: NODE_ENV,
GRAPHQL_URL: conf['GRAPHQL_URL'],
REACT_SCRIPTS:
NODE_ENV === 'production'
? '<script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script><script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>'

View File

@ -153,6 +153,7 @@ const config = merge(common, {
template: path.join(conf.APPDIR, conf.SRC, 'index.html'),
templateParameters: {
NODE_ENV: NODE_ENV,
GRAPHQL_URL: conf['GRAPHQL_URL'],
REACT_SCRIPTS:
NODE_ENV === 'production'
? '<script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script><script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>'

View File

@ -13,4 +13,5 @@ Site\Templates\WebpackTemplateProvider:
TYPESSCSS: src/scss/types
NODE_ENV: production #production,development
webp: false
GRAPHQL_URL: 'http://127.0.0.1/graphql'
GRAPHQL_API_KEY: 'ApKrRSjySO4JAmX3E2Z7HvrBD0m8xdDlAiXyh4IbH3w'