mirror of
https://github.com/bigskysoftware/htmx.git
synced 2026-03-19 09:25:40 +00:00
Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
@@ -1420,7 +1420,7 @@ return (function () {
|
||||
}
|
||||
|
||||
function restoreHistory(path) {
|
||||
saveHistory(currentPathForHistory);
|
||||
saveHistory();
|
||||
path = path || location.pathname+location.search;
|
||||
triggerEvent(getDocument().body, "htmx:historyRestore", {path:path});
|
||||
var cached = getCachedHistory(path);
|
||||
|
||||
@@ -22,8 +22,11 @@ You can copy and paste them and then adjust them for your needs.
|
||||
| [Progress Bar](/examples/progress-bar) | Demonstrates a job-runner like progress bar
|
||||
| [Value Select](/examples/value-select) | Demonstrates making the values of a select dependent on another select
|
||||
| [Animations](/examples/animations) | Demonstrates various animation techniques
|
||||
| [File Upload](/examples/file-upload) | Demonstrates how to upload a file via ajax with a progress bar
|
||||
| [Dialogs - Browser](/examples/dialogs) | Demonstrates the prompt and confirm dialogs
|
||||
| [Dialogs - UIKIt](/examples/modal-uikit) | Demonstrates modal dialogs using UIKit
|
||||
| [Dialogs - Bootstrap](/examples/modal-bootstrap) | Demonstrates modal dialogs using Bootstrap
|
||||
| [File Upload](/examples/file-upload) | Demonstrates how to upload a file via ajax with a progress bar
|
||||
| [Dialogs - Custom](/examples/modal-custom) | Demonstrates modal dialogs from scratch
|
||||
| [Tabs (Using HATEOAS)](/examples/tabs-hateoas) | Demonstrates how to display and select tabs using HATEOAS principles
|
||||
| [Tabs (Using Hyperscript)](/examples/tabs-hyperscript) | Demonstrates how to display and select tabs using Hyperscript
|
||||
|
||||
|
||||
247
www/examples/modal-custom.md
Normal file
247
www/examples/modal-custom.md
Normal file
@@ -0,0 +1,247 @@
|
||||
---
|
||||
layout: demo_layout.njk
|
||||
---
|
||||
|
||||
## Custom Modal Dialogs
|
||||
|
||||
While htmx works great with dialogs built into CSS frameworks (like [Bootstrap](../modal-bootstrap) and [UIKit](../modal-uikit)), htmx also makes it easy to build modal dialogs from scratch. Here is a quick example of one way to build them.
|
||||
|
||||
Click here to see a demo of the final result:
|
||||
|
||||
<button hx-get="/modal" hx-target="body" hx-swap="beforeend">Open a Modal</button>
|
||||
|
||||
### High Level Plan
|
||||
|
||||
We're going to make a button that loads remote content from the server, then displays it in a modal dialog. The modal content will be added to the end of the `<body>` element, in a div named `#modal`.
|
||||
|
||||
We'll define some nice animations in CSS, and use some Hyperscript events (or alternatively, Javascript) to remove the modals from the DOM when the user is done. This requires you to add a minimal amount of extra markup around your modal HTML.
|
||||
|
||||
|
||||
### Main Page HTML
|
||||
|
||||
```html
|
||||
<button hx-get="/modal" hx-target="body" hx-swap="beforeend">Open a Modal</button>
|
||||
```
|
||||
|
||||
### Modal HTML Fragment
|
||||
```html
|
||||
<div id="modal" _="on closeModal add .closing then wait 150ms then remove me">
|
||||
<div class="modal-underlay" _="on click trigger closeModal"></div>
|
||||
<div class="modal-content">
|
||||
<h1>Modal Dialog</h1>
|
||||
This is the modal content.
|
||||
You can put anything here, like text, or a form, or an image.
|
||||
<br>
|
||||
<br>
|
||||
<button _="on click trigger closeModal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Custom Stylesheet
|
||||
```css
|
||||
/***** MODAL DIALOG ****/
|
||||
#modal {
|
||||
/* Underlay covers entire screen. */
|
||||
position: fixed;
|
||||
top:0px;
|
||||
bottom: 0px;
|
||||
left:0px;
|
||||
right:0px;
|
||||
background-color:rgba(0,0,0,0.5);
|
||||
z-index:1000;
|
||||
|
||||
/* Flexbox centers the .modal-content vertically and horizontally */
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
align-items:center;
|
||||
|
||||
/* Animate when opening */
|
||||
animation-name: fadeIn;
|
||||
animation-duration:150ms;
|
||||
animation-timing-function: ease;
|
||||
}
|
||||
|
||||
#modal > .modal-underlay {
|
||||
/* underlay takes up the entire viewport. This is only
|
||||
required if you want to click to dismiss the popup */
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top:0px;
|
||||
bottom:0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
#modal > .modal-content {
|
||||
/* Position visible dialog near the top of the window */
|
||||
margin-top:10vh;
|
||||
|
||||
/* Sizing for visible dialog */
|
||||
width:80%;
|
||||
max-width:600px;
|
||||
|
||||
/* Display properties for visible dialog*/
|
||||
border:solid 1px #999;
|
||||
border-radius:8px;
|
||||
box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.3);
|
||||
background-color:white;
|
||||
padding:20px;
|
||||
|
||||
/* Animate when opening */
|
||||
animation-name:zoomIn;
|
||||
animation-duration:150ms;
|
||||
animation-timing-function: ease;
|
||||
}
|
||||
|
||||
#modal.closing {
|
||||
/* Animate when closing */
|
||||
animation-name: fadeOut;
|
||||
animation-duration:150ms;
|
||||
animation-timing-function: ease;
|
||||
}
|
||||
|
||||
#modal.closing > .modal-content {
|
||||
/* Aniate when closing */
|
||||
animation-name: zoomOut;
|
||||
animation-duration:150ms;
|
||||
animation-timing-function: ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {opacity: 0;}
|
||||
100% {opacity: 1;}
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
0% {opacity: 1;}
|
||||
100% {opacity: 0;}
|
||||
}
|
||||
|
||||
@keyframes zoomIn {
|
||||
0% {transform: scale(0.9);}
|
||||
100% {transform: scale(1);}
|
||||
}
|
||||
|
||||
@keyframes zoomOut {
|
||||
0% {transform: scale(1);}
|
||||
100% {transform: scale(0.9);}
|
||||
}
|
||||
```
|
||||
|
||||
<script src="https://unpkg.com/htmx.org"></script>
|
||||
<script src="https://unpkg.com/hyperscript.org"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
//=========================================================================
|
||||
// Fake Server Side Code
|
||||
//=========================================================================
|
||||
|
||||
// routes
|
||||
init("/modal", function(request){
|
||||
return `
|
||||
<div id="modal" _="on closeModal add .closing then wait 150ms then remove me">
|
||||
<div class="modal-underlay" _="on click trigger closeModal"></div>
|
||||
<div class="modal-content">
|
||||
<h1>Modal Dialog</h1>
|
||||
This is the modal content.
|
||||
You can put anything here, like text, or a form, or an image.
|
||||
<br>
|
||||
<br>
|
||||
<button _="on click trigger closeModal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/***** MODAL DIALOG ****/
|
||||
|
||||
#modal {
|
||||
/* Underlay covers entire screen. */
|
||||
position: fixed;
|
||||
top:0px;
|
||||
bottom: 0px;
|
||||
left:0px;
|
||||
right:0px;
|
||||
background-color:rgba(0,0,0,0.5);
|
||||
z-index:1000;
|
||||
|
||||
/* Flexbox centers the .modal-content vertically and horizontally */
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
align-items:center;
|
||||
|
||||
/* Animate when opening */
|
||||
animation-name: fadeIn;
|
||||
animation-duration:150ms;
|
||||
animation-timing-function: ease;
|
||||
}
|
||||
|
||||
#modal > .modal-underlay {
|
||||
/* underlay takes up the entire viewport. This is only
|
||||
required if you want to click to dismiss the popup */
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top:0px;
|
||||
bottom:0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
#modal > .modal-content {
|
||||
/* Position visible dialog near the top of the window */
|
||||
margin-top:10vh;
|
||||
|
||||
/* Sizing for visible dialog */
|
||||
width:80%;
|
||||
max-width:600px;
|
||||
|
||||
/* Display properties for visible dialog*/
|
||||
border:solid 1px #999;
|
||||
border-radius:8px;
|
||||
box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.3);
|
||||
background-color:white;
|
||||
padding:20px;
|
||||
|
||||
/* Animate when opening */
|
||||
animation-name:zoomIn;
|
||||
animation-duration:150ms;
|
||||
animation-timing-function: ease;
|
||||
}
|
||||
|
||||
#modal.closing {
|
||||
/* Animate when closing */
|
||||
animation-name: fadeOut;
|
||||
animation-duration:150ms;
|
||||
animation-timing-function: ease;
|
||||
}
|
||||
|
||||
#modal.closing > .modal-content {
|
||||
/* Aniate when closing */
|
||||
animation-name: zoomOut;
|
||||
animation-duration:150ms;
|
||||
animation-timing-function: ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {opacity: 0;}
|
||||
100% {opacity: 1;}
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
0% {opacity: 1;}
|
||||
100% {opacity: 0;}
|
||||
}
|
||||
|
||||
@keyframes zoomIn {
|
||||
0% {transform: scale(0.9);}
|
||||
100% {transform: scale(1);}
|
||||
}
|
||||
|
||||
@keyframes zoomOut {
|
||||
0% {transform: scale(1);}
|
||||
100% {transform: scale(0.9);}
|
||||
}
|
||||
</style>
|
||||
119
www/examples/tabs-hateoas.md
Normal file
119
www/examples/tabs-hateoas.md
Normal file
@@ -0,0 +1,119 @@
|
||||
---
|
||||
layout: demo_layout.njk
|
||||
---
|
||||
|
||||
## Tabs (Using HATEOAS)
|
||||
|
||||
This example shows how easy it is to implement tabs using htmx. Following the principle of [Hypertext As The Engine Of Application State](https://en.wikipedia.org/wiki/HATEOAS), the selected tab is a part of the application state. Therefore, to display and select tabs in your application, simply include the tab markup in the returned HTML. If this does not suit your application server design, you can also use a little bit of [Javascript to select tabs instead](../tabs-javascript).
|
||||
|
||||
### Example Code (Main Page)
|
||||
The main page simply includes the following HTML to load the initial tab into the DOM.
|
||||
```html
|
||||
<div id="tabs" hx-get="/tab1" hx-trigger="load after:100ms" hx-target="#tabs" hx-swap="innerHTML"></div>
|
||||
```
|
||||
|
||||
### Example Code (Each Tab)
|
||||
Subsequent tab pages display all tabs and highlight the selected one accordingly.
|
||||
|
||||
```html
|
||||
<div class="tab-list">
|
||||
<a hx-get="/tab1" class="selected">Tab 1</a>
|
||||
<a hx-get="/tab2">Tab 2</a>
|
||||
<a hx-get="/tab3">Tab 3</a>
|
||||
</div>
|
||||
|
||||
<div class="tab-content">
|
||||
Commodo normcore truffaut VHS duis gluten-free keffiyeh iPhone taxidermy godard ramps anim pour-over.
|
||||
Pitchfork vegan mollit umami quinoa aute aliquip kinfolk eiusmod live-edge cardigan ipsum locavore.
|
||||
Polaroid duis occaecat narwhal small batch food truck.
|
||||
PBR&B venmo shaman small batch you probably haven't heard of them hot chicken readymade.
|
||||
Enim tousled cliche woke, typewriter single-origin coffee hella culpa.
|
||||
Art party readymade 90's, asymmetrical hell of fingerstache ipsum.
|
||||
</div>
|
||||
```
|
||||
|
||||
{% include demo_ui.html.liquid %}
|
||||
|
||||
<div id="tabs" hx-get="/tab1" hx-trigger="load after:100ms" hx-target="#tabs" hx-swap="innerHTML"></div>
|
||||
|
||||
|
||||
<script>
|
||||
onGet("/tab1", function() {
|
||||
return `
|
||||
<div class="tab-list">
|
||||
<a hx-get="/tab1" class="selected">Tab 1</a>
|
||||
<a hx-get="/tab2">Tab 2</a>
|
||||
<a hx-get="/tab3">Tab 3</a>
|
||||
</div>
|
||||
|
||||
<div class="tab-content">
|
||||
Commodo normcore truffaut VHS duis gluten-free keffiyeh iPhone taxidermy godard ramps anim pour-over.
|
||||
Pitchfork vegan mollit umami quinoa aute aliquip kinfolk eiusmod live-edge cardigan ipsum locavore.
|
||||
Polaroid duis occaecat narwhal small batch food truck.
|
||||
PBR&B venmo shaman small batch you probably haven't heard of them hot chicken readymade.
|
||||
Enim tousled cliche woke, typewriter single-origin coffee hella culpa.
|
||||
Art party readymade 90's, asymmetrical hell of fingerstache ipsum.
|
||||
</div>`
|
||||
})
|
||||
|
||||
onGet("/tab2", function() {
|
||||
return `
|
||||
<div class="tab-list">
|
||||
<a hx-get="/tab1">Tab 1</a>
|
||||
<a hx-get="/tab2" class="selected">Tab 2</a>
|
||||
<a hx-get="/tab3">Tab 3</a>
|
||||
</div>
|
||||
|
||||
<div class="tab-content">
|
||||
Kitsch fanny pack yr, farm-to-table cardigan cillum commodo reprehenderit plaid dolore cronut meditation.
|
||||
Tattooed polaroid veniam, anim id cornhole hashtag sed forage.
|
||||
Microdosing pug kitsch enim, kombucha pour-over sed irony forage live-edge.
|
||||
Vexillologist eu nulla trust fund, street art blue bottle selvage raw denim.
|
||||
Dolore nulla do readymade, est subway tile affogato hammock 8-bit.
|
||||
Godard elit offal pariatur you probably haven't heard of them post-ironic.
|
||||
Prism street art cray salvia.
|
||||
</div>`
|
||||
})
|
||||
|
||||
onGet("/tab3", function() {
|
||||
return `
|
||||
<div class="tab-list">
|
||||
<a hx-get="/tab1">Tab 1</a>
|
||||
<a hx-get="/tab2">Tab 2</a>
|
||||
<a hx-get="/tab3" class="selected">Tab 3</a>
|
||||
</div>
|
||||
|
||||
<div class="tab-content">
|
||||
Aute chia marfa echo park tote bag hammock mollit artisan listicle direct trade.
|
||||
Raw denim flexitarian eu godard etsy.
|
||||
Poke tbh la croix put a bird on it fixie polaroid aute cred air plant four loko gastropub swag non brunch.
|
||||
Iceland fanny pack tumeric magna activated charcoal bitters palo santo laboris quis consectetur cupidatat portland aliquip venmo.
|
||||
</div>`
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#demo-canvas {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#tabs > .tab-list {
|
||||
border-bottom: solid 3px #eee;
|
||||
}
|
||||
|
||||
#tabs > .tab-list a {
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
#tabs > .tab-list a.selected {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
#tabs > .tab-content {
|
||||
padding:10px;
|
||||
margin-bottom:100px;
|
||||
}
|
||||
</style>
|
||||
93
www/examples/tabs-hyperscript.md
Normal file
93
www/examples/tabs-hyperscript.md
Normal file
@@ -0,0 +1,93 @@
|
||||
---
|
||||
layout: demo_layout.njk
|
||||
---
|
||||
|
||||
## Tabs (Using Hyperscript)
|
||||
|
||||
This example shows how to load tab contents using htmx, and to select the "active" tab using Javascript. This reduces some duplication by offloading some of the work of re-rendering the tab HTML from your application server to your clients' browsers.
|
||||
|
||||
You may also consider [a more idiomatic approach](../tabs-hateoas) that follows the principle of [Hypertext As The Engine Of Application State](https://en.wikipedia.org/wiki/HATEOAS)..
|
||||
|
||||
### Example Code
|
||||
|
||||
The HTML below displays a list of tabs, with added HTMX to dynamically load each tab pane from the server. A simple [hyperscript](https://hyperscript.org) event handler uses the [`take` command](https://hyperscript.org/commands/take/) to switch the selected tab when the content is swapped into the DOM. Alternatively, this could be accomplished with a slightly longer Javascript event handler.
|
||||
|
||||
```html
|
||||
<div id="tabs" hx-target="#tab-contents" _="on htmx:afterOnLoad take .selected for event.target">
|
||||
<a hx-get="/tab1" class="selected">Tab 1</a>
|
||||
<a hx-get="/tab2">Tab 2</a>
|
||||
<a hx-get="/tab3">Tab 3</a>
|
||||
</div>
|
||||
|
||||
<div id="tab-contents" hx-get="/tab1" hx-trigger="load"></div>
|
||||
```
|
||||
|
||||
{% include demo_ui.html.liquid %}
|
||||
|
||||
<div id="tabs" hx-target="#tab-contents" _="on click take .selected for event.target">
|
||||
<a hx-get="/tab1" class="selected">Tab 1</a>
|
||||
<a hx-get="/tab2">Tab 2</a>
|
||||
<a hx-get="/tab3">Tab 3</a>
|
||||
</div>
|
||||
|
||||
<div id="tab-contents" hx-get="/tab1" hx-trigger="load"></div>
|
||||
|
||||
<script src="https://unpkg.com/hyperscript.org"></script>
|
||||
<script>
|
||||
onGet("/tab1", function() {
|
||||
return `
|
||||
<p>Commodo normcore truffaut VHS duis gluten-free keffiyeh iPhone taxidermy godard ramps anim pour-over.
|
||||
Pitchfork vegan mollit umami quinoa aute aliquip kinfolk eiusmod live-edge cardigan ipsum locavore.
|
||||
Polaroid duis occaecat narwhal small batch food truck.
|
||||
PBR&B venmo shaman small batch you probably haven't heard of them hot chicken readymade.
|
||||
Enim tousled cliche woke, typewriter single-origin coffee hella culpa.
|
||||
Art party readymade 90's, asymmetrical hell of fingerstache ipsum.</p>
|
||||
`});
|
||||
|
||||
onGet("/tab2", function() {
|
||||
return `
|
||||
<p>Kitsch fanny pack yr, farm-to-table cardigan cillum commodo reprehenderit plaid dolore cronut meditation.
|
||||
Tattooed polaroid veniam, anim id cornhole hashtag sed forage.
|
||||
Microdosing pug kitsch enim, kombucha pour-over sed irony forage live-edge.
|
||||
Vexillologist eu nulla trust fund, street art blue bottle selvage raw denim.
|
||||
Dolore nulla do readymade, est subway tile affogato hammock 8-bit.
|
||||
Godard elit offal pariatur you probably haven't heard of them post-ironic.
|
||||
Prism street art cray salvia.</p>
|
||||
`
|
||||
});
|
||||
|
||||
onGet("/tab3", function() {
|
||||
return `
|
||||
<p>Aute chia marfa echo park tote bag hammock mollit artisan listicle direct trade.
|
||||
Raw denim flexitarian eu godard etsy.
|
||||
Poke tbh la croix put a bird on it fixie polaroid aute cred air plant four loko gastropub swag non brunch.
|
||||
Iceland fanny pack tumeric magna activated charcoal bitters palo santo laboris quis consectetur cupidatat portland aliquip venmo.</p>
|
||||
`
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
#demo-canvas {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
border-bottom: solid 3px #eee;
|
||||
}
|
||||
|
||||
#tabs > a {
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
#tabs > a.selected {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
#tab-contents {
|
||||
padding:10px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user