diff --git a/data.json b/data.json index 7b0eb67..69a1ac2 100644 --- a/data.json +++ b/data.json @@ -101,7 +101,7 @@ "menubar" : "w-full p-10 box-border flex items-center justify-center", "navigation-menu" : "w-full p-10 box-border flex items-center justify-center", "pagination" : "w-full flex items-end justify-center", - "popover" : "w-full p-10 box-border flex items-center justify-center", + "popover" : "w-full p-10 box-border h-full flex items-center justify-center", "progress" : "w-full max-w-sm mx-auto p-10 box-border flex items-center justify-center", "quotes" : "w-full p-10 box-border flex items-center justify-center", "radio-group" : "w-full p-10 box-border flex items-center justify-center", diff --git a/elements/accordion.json b/elements/accordion.json index 464304f..339b6ce 100644 --- a/elements/accordion.json +++ b/elements/accordion.json @@ -7,6 +7,6 @@ "explanation" : "The accordion UI element on a website is like a digital filing cabinet, revealing and concealing sections of information with a click, granting users the power to navigate the web's vastness while maintaining order and harmony.", "alert_notification" : { "title" : "Not seeing the animation?", - "description" : "We are using the AlpineJS collapse plug-in in this example. Simply, include it in your project and you will see the collapse plug-in in action." + "description" : "We are using the AlpineJS collapse plugin in this example. Simply, include it in your project and you will see the collapse plugin in action." } } \ No newline at end of file diff --git a/elements/full-screen-menu.html b/elements/full-screen-menu.html index bdd0267..167a765 100644 --- a/elements/full-screen-menu.html +++ b/elements/full-screen-menu.html @@ -16,7 +16,7 @@
-
+
Home diff --git a/elements/hover-card.html b/elements/hover-card.html index 5dbd4e3..49223c2 100644 --- a/elements/hover-card.html +++ b/elements/hover-card.html @@ -2,7 +2,7 @@ hoverCardHovered: false, hoverCardTimout: null, hoverCardLeaveTimeout: null, - hoverCardDelay: 1000, + hoverCardDelay: 600, hoverCardLeaveDelay: 500, hoverCardEnter () { clearTimeout(this.hoverCardLeaveTimeout); diff --git a/elements/popover.html b/elements/popover.html index ef0dabd..c43e470 100644 --- a/elements/popover.html +++ b/elements/popover.html @@ -16,7 +16,6 @@ }); }, popoverPositionCalculate(){ - console.log(this.$refs.popoverButton.getBoundingClientRect().top) if(window.innerHeight < (this.$refs.popoverButton.getBoundingClientRect().top + this.$refs.popoverButton.offsetHeight + this.popoverOffset + this.popoverHeight)){ this.popoverPosition = 'top'; } else { diff --git a/elements/select.html b/elements/select.html index ff40aab..f450e0a 100644 --- a/elements/select.html +++ b/elements/select.html @@ -108,7 +108,6 @@ } } - console.log(this.selectItemsFindBestMath()); if(this.selectKeydownValue != ''){ clearTimeout(this.selectKeydownClearTimeout); this.selectKeydownClearTimeout = setTimeout(() => { @@ -138,11 +137,6 @@ } else { this.selectDropdownPosition = 'bottom'; } - console.log(selectDropdownBottomPos); - //console.log(this.$refs.selectButton.getBoundingClientRect().top + this.$refs.selectButton.offsetHeight); - //console.log(this.$refs.selectableItemsList.getBoundingClientRect().top); - //console.log(parseInt(window.getComputedStyle(this.$refs.selectableItemsList).marginTop)); - // console.log(window.getComputedStyle(this.$refs.selectableItemsList).maxHeight); } }" x-init=" diff --git a/elements/switch-examples/example-01.html b/elements/switch-examples/example-01.html index c6024d9..6f5ba51 100644 --- a/elements/switch-examples/example-01.html +++ b/elements/switch-examples/example-01.html @@ -6,9 +6,9 @@ type="button" @click="switchOn = ! switchOn" :class="switchOn ? 'bg-neutral-900' : 'bg-neutral-200'" - class="relative inline-flex h-4 py-0.5 ml-4 rounded-full focus:outline-none w-7" + class="relative inline-flex h-4 py-0.5 ml-4 rounded-full focus:outline-none w-6" x-cloak> - +
+ +

Creating an Alpine Plugin is very simple and will allow you to create re-usable components. Alpine has some great documentation on extending functionality into your own plugin; however, it will be beneficial to give you a specific example. + +

Lets learn how to extract the tooltip element so that it can be used as a custom Alpine directive, like so:

+ +
<div x-data x-tooltip="Your tooltip text here">
+    hover me
+</div>
+ +

Below is the code for the Alpine Tooltip Element: + +

<div 
+    x-data="{
+        tooltipVisible: false,
+        tooltipText: 'Tooltip text',
+        tooltipArrow: true,
+        tooltipPosition: 'top',
+    }"
+    x-init="$refs.content.addEventListener('mouseenter', () => { tooltipVisible = true; }); $refs.content.addEventListener('mouseleave', () => { tooltipVisible = false; });"
+    class="relative">
+    
+    <div x-ref="tooltip" x-show="tooltipVisible" :class="{ 'top-0 left-1/2 -translate-x-1/2 -mt-0.5 -translate-y-full' : tooltipPosition == 'top', 'top-1/2 -translate-y-1/2 -ml-0.5 left-0 -translate-x-full' : tooltipPosition == 'left', 'bottom-0 left-1/2 -translate-x-1/2 -mb-0.5 translate-y-full' : tooltipPosition == 'bottom', 'top-1/2 -translate-y-1/2 -mr-0.5 right-0 translate-x-full' : tooltipPosition == 'right' }" class="absolute w-auto text-sm" x-cloak>
+        <div x-show="tooltipVisible" x-transition class="relative px-2 py-1 text-white bg-black rounded bg-opacity-90">
+            <p x-text="tooltipText" class="flex-shrink-0 block text-xs whitespace-nowrap"></p>
+            <div x-ref="tooltipArrow" x-show="tooltipArrow" :class="{ 'bottom-0 -translate-x-1/2 left-1/2 w-2.5 translate-y-full' : tooltipPosition == 'top', 'right-0 -translate-y-1/2 top-1/2 h-2.5 -mt-px translate-x-full' : tooltipPosition == 'left', 'top-0 -translate-x-1/2 left-1/2 w-2.5 -translate-y-full' : tooltipPosition == 'bottom', 'left-0 -translate-y-1/2 top-1/2 h-2.5 -mt-px -translate-x-full' : tooltipPosition == 'right' }" class="absolute inline-flex items-center justify-center overflow-hidden">
+                <div :class="{ 'origin-top-left -rotate-45' : tooltipPosition == 'top', 'origin-top-left rotate-45' : tooltipPosition == 'left', 'origin-bottom-left rotate-45' : tooltipPosition == 'bottom', 'origin-top-right -rotate-45' : tooltipPosition == 'right' }" class="w-1.5 h-1.5 transform bg-black bg-opacity-90"></div>
+            </div>
+        </div>
+    </div>
+    
+    <div x-ref="content" class="px-3 py-1 text-xs rounded-full cursor-pointer text-neutral-500 bg-neutral-100">hover me</div>
+
+</div>
+ + + +

To make this tooltip functionality re-useable we could extract it into its own plugin, like so:

+ +
<script>
+    document.addEventListener('alpine:init', () => {
+        
+        Alpine.directive('tooltip', (el, { modifiers, expression }, { cleanup }) => {
+            let tooltipText = expression;
+            let tooltipArrow = modifiers.includes('noarrow') ? false : true;
+            let tooltipPosition = 'top';
+            let tooltipId = 'tooltip-' + Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
+            let positions = ['top', 'bottom', 'left', 'right'];
+            let elementPosition = getComputedStyle(el).position;
+
+            for (let position of positions) {
+                if (modifiers.includes(position)) {
+                    tooltipPosition = position;
+                    break;
+                }
+            }
+
+            if(!elementPosition.includes(['relative', 'absolute', 'fixed'])){
+                el.style.position='relative';
+            }
+            
+            let tooltipHTML = `
+                <div id="${tooltipId}" x-data="{ tooltipVisible: false, tooltipText: '${tooltipText}', tooltipArrow: ${tooltipArrow}, tooltipPosition: '${tooltipPosition}' }" x-ref="tooltip" x-init="setTimeout(function(){ tooltipVisible = true; }, 1);" x-show="tooltipVisible" :class="{ 'top-0 left-1/2 -translate-x-1/2 -mt-0.5 -translate-y-full' : tooltipPosition == 'top', 'top-1/2 -translate-y-1/2 -ml-1.5 left-0 -translate-x-full' : tooltipPosition == 'left', 'bottom-0 left-1/2 -translate-x-1/2 -mb-0.5 translate-y-full' : tooltipPosition == 'bottom', 'top-1/2 -translate-y-1/2 -mr-1.5 right-0 translate-x-full' : tooltipPosition == 'right' }" class="absolute w-auto text-sm" x-cloak>
+                    <div x-show="tooltipVisible" x-transition class="relative px-2 py-1 text-white bg-black rounded bg-opacity-90">
+                        <p x-text="tooltipText" class="flex-shrink-0 block text-xs whitespace-nowrap"></p>
+                        <div x-ref="tooltipArrow" x-show="tooltipArrow" :class="{ 'bottom-0 -translate-x-1/2 left-1/2 w-2.5 translate-y-full' : tooltipPosition == 'top', 'right-0 -translate-y-1/2 top-1/2 h-2.5 -mt-px translate-x-full' : tooltipPosition == 'left', 'top-0 -translate-x-1/2 left-1/2 w-2.5 -translate-y-full' : tooltipPosition == 'bottom', 'left-0 -translate-y-1/2 top-1/2 h-2.5 -mt-px -translate-x-full' : tooltipPosition == 'right' }" class="absolute inline-flex items-center justify-center overflow-hidden">
+                            <div :class="{ 'origin-top-left -rotate-45' : tooltipPosition == 'top', 'origin-top-left rotate-45' : tooltipPosition == 'left', 'origin-bottom-left rotate-45' : tooltipPosition == 'bottom', 'origin-top-right -rotate-45' : tooltipPosition == 'right' }" class="w-1.5 h-1.5 transform bg-black bg-opacity-90"></div>
+                        </div>
+                    </div>
+                </div>
+            `;
+            
+            el.dataset.tooltip = tooltipId;
+
+            let mouseEnter = function(event){ 
+                el.innerHTML += tooltipHTML;
+            };
+
+            let mouseLeave = function(event){
+                document.getElementById(event.target.dataset.tooltip).remove();
+            };
+            
+            el.addEventListener('mouseenter', mouseEnter);
+            el.addEventListener('mouseleave', mouseLeave);
+
+            cleanup(() => {
+                el.removeEventListener('mouseenter', mouseEnter);
+                el.removeEventListener('mouseleave', mouseLeave);
+            })
+        })
+        
+    })
+
+</script>
+
+<!-- Then, include a tooltip anwhere in your code -->
+<div class="w-screen h-screen flex items-center justify-center">
+    <div x-data x-tooltip.top="hello">
+        hover me
+    </div>
+</div>
+ +
+ +
+ +

Now, you will be able to use the x-tooltip directive anywhere you want to include a tooltip.

+ + + +
+ +

Understanding how to build the plugin

+ +

First, we will want to create the directive. We can do so with the following line:

+ +
Alpine.directive('tooltip', (el, { modifiers, expression }, { cleanup }) => {
+ +

This will give us the ability to use the x-tooltip directive. Then, we want to define all our variables:

+ +
// The text that will be displayed in the tooltip
+let tooltipText = expression;
+// Hide the arrow if the directive has a .noarrow modifier
+let tooltipArrow = modifiers.includes('noarrow') ? false : true;
+// The default tooltip position will be set to 'top'
+let tooltipPosition = 'top';
+// We need to create a dynamic ID so that way we can reference the tooltip element when we want to add or remove it
+let tooltipId = 'tooltip-' + Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
+// These are all the possible positions for the tooltip
+let positions = ['top', 'bottom', 'left', 'right'];
+// We need to check the position of the element that the tooltip is attached to, if it is not relative, absolute, or fixed, then we will set it to relative
+let elementPosition = getComputedStyle(el).position;
+ +

Next we will loop through all the positions to see if the directive has any of the following modifiers `.top, .left, .bottom, .right`.

+ +
for (let position of positions) {
+    if (modifiers.includes(position)) {
+        tooltipPosition = position;
+        break;
+    }
+}
+ +

This will allow modifiers to specify the position of the tooltip:

+ + + +

Because our tooltip has an absolute position we need the parent element to have a position of relative, absolute, or fixed. If it is not we will set the position to relative:

+ +
if(!elementPosition.includes(['relative', 'absolute', 'fixed'])){
+    el.style.position='relative';
+}
+ +

Then, we store the HTML of our tooltip so we can append it to the element when it's hovered.

+ +
let tooltipHTML = `
+        <div id="${tooltipId}" x-data="{ tooltipVisible: false, tooltipText: '${tooltipText}', tooltipArrow: ${tooltipArrow}, tooltipPosition: '${tooltipPosition}' }" x-ref="tooltip" x-init="setTimeout(function(){ tooltipVisible = true; }, 1);" x-show="tooltipVisible" :class="{ 'top-0 left-1/2 -translate-x-1/2 -mt-0.5 -translate-y-full' : tooltipPosition == 'top', 'top-1/2 -translate-y-1/2 -ml-1.5 left-0 -translate-x-full' : tooltipPosition == 'left', 'bottom-0 left-1/2 -translate-x-1/2 -mb-0.5 translate-y-full' : tooltipPosition == 'bottom', 'top-1/2 -translate-y-1/2 -mr-1.5 right-0 translate-x-full' : tooltipPosition == 'right' }" class="absolute w-auto text-sm" x-cloak>
+            <div x-show="tooltipVisible" x-transition class="relative px-2 py-1 text-white bg-black rounded bg-opacity-90">
+                <p x-text="tooltipText" class="flex-shrink-0 block text-xs whitespace-nowrap"></p>
+                <div x-ref="tooltipArrow" x-show="tooltipArrow" :class="{ 'bottom-0 -translate-x-1/2 left-1/2 w-2.5 translate-y-full' : tooltipPosition == 'top', 'right-0 -translate-y-1/2 top-1/2 h-2.5 -mt-px translate-x-full' : tooltipPosition == 'left', 'top-0 -translate-x-1/2 left-1/2 w-2.5 -translate-y-full' : tooltipPosition == 'bottom', 'left-0 -translate-y-1/2 top-1/2 h-2.5 -mt-px -translate-x-full' : tooltipPosition == 'right' }" class="absolute inline-flex items-center justify-center overflow-hidden">
+                    <div :class="{ 'origin-top-left -rotate-45' : tooltipPosition == 'top', 'origin-top-left rotate-45' : tooltipPosition == 'left', 'origin-bottom-left rotate-45' : tooltipPosition == 'bottom', 'origin-top-right -rotate-45' : tooltipPosition == 'right' }" class="w-1.5 h-1.5 transform bg-black bg-opacity-90"></div>
+                </div>
+            </div>
+        </div>
+    `;
+ +

Next, we need to set the tooltip ID to the element's dataset so that way we can reference it later on.

+ +
el.dataset.tooltip = tooltipId;
+ +

When the user hovers the element we want to append the tooltip HTML, and when their mouse leaves the element we want to remove it:

+ +
let mouseEnter = function(event){ 
+    el.innerHTML += tooltipHTML;
+};
+
+let mouseLeave = function(event){
+    document.getElementById(event.target.dataset.tooltip).remove();
+};
+
+el.addEventListener('mouseenter', mouseEnter);
+el.addEventListener('mouseleave', mouseLeave);
+ +

Finally, the last peice of code is using a helper callback function called cleanup() that is provided by AlpineJS. It will allow us to perform any cleanup functionality, such as removing event listeners, whenever the parent element is removed from the DOM:

+ +
cleanup(() => {
+    el.removeEventListener('mouseenter', mouseEnter);
+    el.removeEventListener('mouseleave', mouseLeave);
+})
+ +

And that's how you would go about creating a simple Alpine plugin that can be re-used throughout your application.

+ + + +
+ +

Why not create Pines as a separate library

+ +

We wanted to build a set of UI elements that are highly customizable and flexible. These elements will allow developers to easily integrate them with any Tallstack application. This will let you to use these elements and possibly even build your own UI library 😉 If you only use a handful of these elements, you can use them separately and you won't end up including a ton of functionality you don't even use.

+ +

It's possible that this could change down the road or we might keep it the same and build a ton more copy-paste components. Be sure to give us a Star on Github and contribute if you want to help steer the direction of this project.

\ No newline at end of file diff --git a/getting-started/contribution.html b/getting-started/contribution.html index 9f8ade2..d576936 100644 --- a/getting-started/contribution.html +++ b/getting-started/contribution.html @@ -1,8 +1,8 @@

Contribution

-

You may contribute to this project by visiting the Github Repo here. We currently do not have issues open, so you'll need to submit a PR with you contribution and we'll get to the PR as soon as we can.

-

If you wish to discuss functionality or any issues you have with Pines, you can visit the DevDojo Discussions/Questions section.

+

You may contribute to this project by visiting the Github Repo here. We currently do not have issues open, so you'll need to submit a PR with your contribution and we'll get to the PR as soon as we can.

+

If you wish to discuss functionality or any issues you have with Pines, you can visit the DevDojo Discussions/Questions section.

diff --git a/getting-started/how-to-use.html b/getting-started/how-to-use.html index 603721e..2415c5e 100644 --- a/getting-started/how-to-use.html +++ b/getting-started/how-to-use.html @@ -1,7 +1,7 @@

How To Use

-

Pines, is super easy to use. If your application uses AlpineJS and TailwindCSS, you can copy and paste an element into your page an it will work flawlessly. If you want to test an element on a single HTML page, you can copy/paste this snippet below into any `.html` file:

+

Pines, is super easy to use. If your application uses Alpine and Tailwind, you can copy and paste an element into your page an it will work flawlessly. If you want to test an element on a single HTML page, you can copy/paste this snippet below into any `.html` file:

<!DOCTYPE html>
 <html lang="en">
@@ -34,3 +34,18 @@
 

The Pines Playground

You may also open up the and edit any element or design your own. Keep in mind that the data entered into the playground will not be saved, so if you leave the page you may lose any changes. However, it may be the easiest way to make a few modifications to an element or create your own.

+ +

Re-usability

+ +

Pines isn't a standalone library, but rather a collection of elements for Alpine and Tailwind projects. This will make it a delight to integrate with your TALL stack applications; however, some of these elements you may want to make re-usable. One such element is the tooltip element, which you may want to extract it into it's own Alpine plugin, allowing you to do something like this:

+ +
<div x-tooltip="Your tooltip text here">
+    hover me
+</div>
+ + + +

In the next section you will learn how simple it can be to extract an element into its own plugin.

diff --git a/main.js b/main.js index e908881..af96e79 100644 --- a/main.js +++ b/main.js @@ -18,7 +18,6 @@ document.addEventListener('DOMContentLoaded', function() { if(element.includes('-examples')){ element = element.replace('-examples', '').split('/')[0]; } - console.log(element); document.getElementById('content').className = data.container[element]; } else { document.getElementById('content').className = 'max-w-3xl p-10 prose';