Add new PostFeedback and SocialShare components

Created the 'PostFeedback.astro' and 'SocialShare.astro' component files to enrich the UI interface. The PostFeedback component renders a user feedback section with a title and two responsive buttons. Whereas, the SocialShare component provides users with hyperlinks to popular social media platforms - it also enables copy-pasting of current page links using the ClipboardJS utility function and dropdown functionality from the Preline plugin.
This commit is contained in:
Emil Gulamov 2024-03-17 08:05:52 +04:00
parent b4f128b6e2
commit 991ec9a909
7 changed files with 369 additions and 0 deletions

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Html Stream
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,65 @@
export interface IDropdown {
options?: {};
open(): void;
close(isAnimated: boolean): void;
forceClearState(): void;
}
export interface IHTMLElementPopper extends HTMLElement {
_popper: any;
}
export interface IBasePlugin<O, E> {
el: E;
options?: O;
events?: {};
}
declare class HSBasePlugin<O, E = HTMLElement> implements IBasePlugin<O, E> {
el: E;
options: O;
events?: any;
constructor(el: E, options: O, events?: any);
createCollection(collection: any[], element: any): void;
fireEvent(evt: string, payload?: any): any;
on(evt: string, cb: Function): void;
}
export interface ICollectionItem<T> {
id: string | number;
element: T;
}
declare class HSDropdown extends HSBasePlugin<{}, IHTMLElementPopper> implements IDropdown {
private static history;
private readonly toggle;
menu: HTMLElement | null;
private eventMode;
private readonly closeMode;
private animationInProcess;
constructor(el: IHTMLElementPopper, options?: {}, events?: {});
private init;
resizeHandler(): void;
private onClickHandler;
private onMouseEnterHandler;
private onMouseLeaveHandler;
private destroyPopper;
private absoluteStrategyModifiers;
open(): boolean;
close(isAnimated?: boolean): boolean;
forceClearState(): void;
static getInstance(target: HTMLElement | string, isInstance?: boolean): ICollectionItem<HSDropdown> | IHTMLElementPopper;
static autoInit(): void;
static open(target: HTMLElement): void;
static close(target: HTMLElement): void;
static accessibility(evt: KeyboardEvent): void;
static onEscape(evt: KeyboardEvent): void;
static onEnter(evt: KeyboardEvent): void;
static onArrow(isArrowUp?: boolean): boolean;
static onStartEnd(isStart?: boolean): boolean;
static onFirstLetter(code: string): boolean;
static closeCurrentlyOpened(evtTarget?: HTMLElement | null, isAnimated?: boolean): void;
static on(evt: string, target: HTMLElement, cb: Function): void;
}
export {
HSDropdown as default,
};
export {};

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,15 @@
{
"name": "@preline/dropdown",
"version": "2.0.2",
"description": "Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.",
"main": "index.js",
"types": "index.d.ts",
"repository": "https://github.com/htmlstreamofficial/preline.git",
"homepage": "https://preline.co/plugins.html",
"keywords": ["preline", "html", "css", "next", "nuxt", "vue", "react", "angular", "javascript", "typescript", "tailwind", "tailwind components", "tailwind elements", "tailwind library", "tailwind sections", "tailwind css", "tailwind ui", "tailwind css react", "tailwind css vue", "tailwind css angular", "tailwind css laravel", "tailwindcss plugin", "tailwindcss plugins", "accordion"],
"author": "Htmlstream",
"license": "MIT",
"publishConfig": {
"access": "public"
}
}

View file

@ -0,0 +1,189 @@
---
// Destructure the properties from Astro.props
const { pageTitle } = Astro.props;
// Define TypeScript interface for the properties
interface Props {
pageTitle: string;
}
type SocialPlatform = {
name: string;
url: string;
svg: string;
};
const socialPlatforms: SocialPlatform[] = [
{
name: "Facebook",
url: `https://www.facebook.com/share.php?u=${Astro.url}&title=${pageTitle}`,
svg: `<svg
role="img"
viewBox="0 0 24 24"
stroke="currentColor"
class="size-4 flex-shrink-0 fill-current"
><title>Facebook</title><path
d="M9.101 23.691v-7.98H6.627v-3.667h2.474v-1.58c0-4.085 1.848-5.978 5.858-5.978.401 0 .955.042 1.468.103a8.68 8.68 0 0 1 1.141.195v3.325a8.623 8.623 0 0 0-.653-.036 26.805 26.805 0 0 0-.733-.009c-.707 0-1.259.096-1.675.309a1.686 1.686 0 0 0-.679.622c-.258.42-.374.995-.374 1.752v1.297h3.919l-.386 2.103-.287 1.564h-3.246v8.245C19.396 23.238 24 18.179 24 12.044c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.628 3.874 10.35 9.101 11.647Z"
></path></svg
>`,
},
{
name: "X",
url: `https://twitter.com/home/?status=${pageTitle}${Astro.url}`,
svg: `<svg
role="img"
viewBox="0 0 24 24"
stroke="currentColor"
class="size-4 flex-shrink-0 fill-current"
><title>X</title><path
d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z"
></path></svg
>`,
},
{
name: "LinkedIn",
url: `https://www.linkedin.com/shareArticle?mini=true&url=${Astro.url}&title=${pageTitle}`,
svg: `<svg
role="img"
viewBox="0 0 24 24"
stroke="currentColor"
class="size-4 flex-shrink-0 fill-current"
><title>LinkedIn</title><path
d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"
></path></svg
>`,
},
];
---
<div
class="hs-dropdown relative inline-flex [--auto-close:inside] [--placement:top-left]"
>
<button
id="hs-dropup"
type="button"
class="hs-dropdown-toggle inline-flex items-center gap-x-2 rounded-lg px-4 py-3 text-sm font-medium text-neutral-600 outline-none ring-zinc-500 transition duration-300 hover:bg-neutral-100 hover:text-neutral-700 focus-visible:ring dark:text-neutral-400 dark:ring-zinc-200 dark:hover:bg-neutral-700 dark:hover:text-neutral-300 dark:focus:outline-none"
>
<svg
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="h-4 w-4 group-hover:text-neutral-700"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M7.217 10.907a2.25 2.25 0 1 0 0 2.186m0-2.186c.18.324.283.696.283 1.093s-.103.77-.283 1.093m0-2.186 9.566-5.314m-9.566 7.5 9.566 5.314m0 0a2.25 2.25 0 1 0 3.935 2.186 2.25 2.25 0 0 0-3.935-2.186Zm0-12.814a2.25 2.25 0 1 0 3.933-2.185 2.25 2.25 0 0 0-3.933 2.185Z"
></path>
</svg>
Share
</button>
<div
class="hs-dropdown-menu duration z-10 hidden w-72 divide-y divide-neutral-200 rounded-lg bg-neutral-50 p-2 opacity-0 shadow-md transition-[opacity,margin] hs-dropdown-open:opacity-100 dark:divide-neutral-700 dark:border dark:border-neutral-700 dark:bg-neutral-800"
aria-labelledby="hs-dropup"
>
<div class="py-2 first:pt-0 last:pb-0">
{
socialPlatforms.map((platform) => (
<a
class="flex items-center gap-x-3.5 rounded-lg px-3 py-2 text-sm text-neutral-700 hover:bg-neutral-200 focus:bg-neutral-100 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-700 dark:hover:text-neutral-300 dark:focus:bg-neutral-700 "
href={platform.url}
>
<Fragment set:html={platform.svg} />
Share on {platform.name}
</a>
))
}
</div>
<div class="py-2 first:pt-0 last:pb-0">
<button
type="button"
class="js-clipboard hover:text-dark focus-visible:ring-secondary group inline-flex w-full items-center gap-x-3.5 rounded-lg px-3 py-2 text-sm text-neutral-700 hover:bg-neutral-200 focus:bg-neutral-100 focus:outline-none focus-visible:outline-none focus-visible:ring-1 dark:text-neutral-300 dark:hover:bg-neutral-700 dark:hover:text-neutral-300 dark:focus:bg-neutral-700"
data-clipboard-success-text="Copied"
>
<svg
class="js-clipboard-default h-4 w-4 transition group-hover:rotate-6"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="8" height="4" x="8" y="2" rx="1" ry="1"></rect>
<path
d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"
></path>
</svg>
<svg
class="js-clipboard-success text-primary-accent-light dark:text-primary-accent-dark hidden h-4 w-4"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
<span class="js-clipboard-success-text">Copy link</span>
</button>
</div>
</div>
</div>
<!--Import the necessary Dropdown and Clipboard plugins-->
<!--https://preline.co/plugins/html/dropdown.html-->
<script is:inline src="/scripts/vendor/preline/dropdown/index.js"></script>
<!-- https://clipboardjs.com/ -->
<script is:inline src="/scripts/vendor/clipboard.min.js"></script>
<script is:inline>
// Initialization of Clipboard
(function () {
window.addEventListener("load", () => {
const $clipboards = document.querySelectorAll(".js-clipboard");
$clipboards.forEach((el) => {
const clipboard = new ClipboardJS(el, {
text: () => {
return window.location.href;
},
});
clipboard.on("success", () => {
const $default = el.querySelector(".js-clipboard-default");
const $success = el.querySelector(".js-clipboard-success");
const $successText = el.querySelector(".js-clipboard-success-text");
const successText = el.dataset.clipboardSuccessText || "";
let oldSuccessText;
if ($successText) {
oldSuccessText = $successText.textContent;
$successText.textContent = successText;
}
if ($default && $success) {
$default.style.display = "none";
$success.style.display = "block";
}
setTimeout(function () {
if ($successText && oldSuccessText)
$successText.textContent = oldSuccessText;
if ($default && $success) {
$success.style.display = "";
$default.style.display = "";
}
}, 800);
});
});
});
})();
</script>

View file

@ -0,0 +1,55 @@
---
// Define props from Astro
const { title, firstChoice, secondChoice } = Astro.props;
// Define TypeScript interface for props
interface Props {
title: string;
firstChoice: string;
secondChoice: string;
}
---
<div class="mt-12 flex items-center justify-center gap-x-2">
<h3 class="text-neutral-700 dark:text-neutral-300">{title}</h3>
<button
type="button"
class="group inline-flex items-center gap-x-2 rounded-lg border border-neutral-400 px-3 py-2 text-sm font-medium text-neutral-800 hover:border-yellow-500 hover:bg-yellow-500 hover:shadow-2xl hover:shadow-yellow-500 dark:border-neutral-500 dark:text-neutral-50 dark:hover:bg-yellow-500 dark:hover:text-neutral-800 dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-neutral-600"
>
<svg
class="size-4 flex-shrink-0 transition duration-300 group-hover:-translate-y-1 group-focus-visible:-translate-y-1"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><path d="M7 10v12"></path><path
d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2h0a3.13 3.13 0 0 1 3 3.88Z"
></path></svg
>
{firstChoice}
</button>
<button
type="button"
class="group inline-flex items-center gap-x-2 rounded-lg border border-neutral-400 px-3 py-2 text-sm font-medium text-neutral-800 hover:bg-neutral-400 dark:border-neutral-500 dark:text-neutral-50 dark:hover:bg-neutral-500 dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-neutral-600"
>
<svg
class="size-4 flex-shrink-0 transition duration-300 group-hover:translate-y-1 group-focus-visible:translate-y-1"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><path d="M17 14V2"></path><path
d="M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22h0a3.13 3.13 0 0 1-3-3.88Z"
></path></svg
>
{secondChoice}
</button>
</div>