public inbox for [email protected]
 help / color / mirror / Atom feed
From: Muhammad Rizki <[email protected]>
To: Ammar Faizi <[email protected]>
Cc: Muhammad Rizki <[email protected]>,
	Alviro Iskandar Setiawan <[email protected]>,
	GNU/Weeb Mailing List <[email protected]>
Subject: [PATCH v1 09/17] feat(ui): Add popover and dialog UI component
Date: Wed,  5 Mar 2025 21:40:08 +0700	[thread overview]
Message-ID: <[email protected]> (raw)
In-Reply-To: <[email protected]>

Added popover and dialog UI, this commit include updates for bits-ui
version.

Signed-off-by: Muhammad Rizki <[email protected]>
---
 package-lock.json                             |  8 ++--
 package.json                                  |  2 +-
 .../ui/dialog/dialog-content.svelte           | 38 +++++++++++++++++++
 .../ui/dialog/dialog-description.svelte       | 16 ++++++++
 .../components/ui/dialog/dialog-footer.svelte | 20 ++++++++++
 .../components/ui/dialog/dialog-header.svelte | 20 ++++++++++
 .../ui/dialog/dialog-overlay.svelte           | 19 ++++++++++
 .../components/ui/dialog/dialog-title.svelte  | 16 ++++++++
 src/lib/components/ui/dialog/index.ts         | 37 ++++++++++++++++++
 src/lib/components/ui/popover/index.ts        | 17 +++++++++
 .../ui/popover/popover-content.svelte         | 28 ++++++++++++++
 11 files changed, 216 insertions(+), 5 deletions(-)
 create mode 100644 src/lib/components/ui/dialog/dialog-content.svelte
 create mode 100644 src/lib/components/ui/dialog/dialog-description.svelte
 create mode 100644 src/lib/components/ui/dialog/dialog-footer.svelte
 create mode 100644 src/lib/components/ui/dialog/dialog-header.svelte
 create mode 100644 src/lib/components/ui/dialog/dialog-overlay.svelte
 create mode 100644 src/lib/components/ui/dialog/dialog-title.svelte
 create mode 100644 src/lib/components/ui/dialog/index.ts
 create mode 100644 src/lib/components/ui/popover/index.ts
 create mode 100644 src/lib/components/ui/popover/popover-content.svelte

diff --git a/package-lock.json b/package-lock.json
index b9d8ba9..525092e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,7 +20,7 @@
         "@sveltejs/kit": "^2.0.0",
         "@sveltejs/vite-plugin-svelte": "^4.0.0",
         "autoprefixer": "^10.4.20",
-        "bits-ui": "^1.3.2",
+        "bits-ui": "^1.3.5",
         "clsx": "^2.1.1",
         "eslint": "^9.7.0",
         "eslint-config-prettier": "^9.1.0",
@@ -1898,9 +1898,9 @@
       }
     },
     "node_modules/bits-ui": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-1.3.2.tgz",
-      "integrity": "sha512-27fg/O71yqYmDaDf/opInylQYJjBNlFw3Ari0mXKLA6UrQpRvY62EsAid8s+ci/wYrGZMvHpzNJ69t15ycBHTw==",
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-1.3.5.tgz",
+      "integrity": "sha512-pfd8MK5Hp7bOvsW25LJrWVABmBIeAOH3g0pFPJLBIKlcqsaalEdBYejQmlSwrynEDX589aW3hTAWbhmWqRjjrQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
diff --git a/package.json b/package.json
index c323150..52b0e4c 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,7 @@
     "@sveltejs/kit": "^2.0.0",
     "@sveltejs/vite-plugin-svelte": "^4.0.0",
     "autoprefixer": "^10.4.20",
-    "bits-ui": "^1.3.2",
+    "bits-ui": "^1.3.5",
     "clsx": "^2.1.1",
     "eslint": "^9.7.0",
     "eslint-config-prettier": "^9.1.0",
diff --git a/src/lib/components/ui/dialog/dialog-content.svelte b/src/lib/components/ui/dialog/dialog-content.svelte
new file mode 100644
index 0000000..e0f8d9c
--- /dev/null
+++ b/src/lib/components/ui/dialog/dialog-content.svelte
@@ -0,0 +1,38 @@
+<script lang="ts">
+  import { Dialog as DialogPrimitive, type WithoutChildrenOrChild } from "bits-ui";
+  import X from "lucide-svelte/icons/x";
+  import type { Snippet } from "svelte";
+  import * as Dialog from "./index";
+  import { cn } from "$utils";
+
+  let {
+    ref = $bindable(null),
+    class: className,
+    portalProps,
+    children,
+    ...restProps
+  }: WithoutChildrenOrChild<DialogPrimitive.ContentProps> & {
+    portalProps?: DialogPrimitive.PortalProps;
+    children: Snippet;
+  } = $props();
+</script>
+
+<Dialog.Portal {...portalProps}>
+  <Dialog.Overlay />
+  <DialogPrimitive.Content
+    bind:ref
+    class={cn(
+      "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
+      className
+    )}
+    {...restProps}
+  >
+    {@render children?.()}
+    <DialogPrimitive.Close
+      class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
+    >
+      <X class="size-4" />
+      <span class="sr-only">Close</span>
+    </DialogPrimitive.Close>
+  </DialogPrimitive.Content>
+</Dialog.Portal>
diff --git a/src/lib/components/ui/dialog/dialog-description.svelte b/src/lib/components/ui/dialog/dialog-description.svelte
new file mode 100644
index 0000000..adb4f44
--- /dev/null
+++ b/src/lib/components/ui/dialog/dialog-description.svelte
@@ -0,0 +1,16 @@
+<script lang="ts">
+  import { Dialog as DialogPrimitive } from "bits-ui";
+  import { cn } from "$utils";
+
+  let {
+    ref = $bindable(null),
+    class: className,
+    ...restProps
+  }: DialogPrimitive.DescriptionProps = $props();
+</script>
+
+<DialogPrimitive.Description
+  bind:ref
+  class={cn("text-sm text-muted-foreground", className)}
+  {...restProps}
+/>
diff --git a/src/lib/components/ui/dialog/dialog-footer.svelte b/src/lib/components/ui/dialog/dialog-footer.svelte
new file mode 100644
index 0000000..7695cd2
--- /dev/null
+++ b/src/lib/components/ui/dialog/dialog-footer.svelte
@@ -0,0 +1,20 @@
+<script lang="ts">
+  import type { WithElementRef } from "bits-ui";
+  import type { HTMLAttributes } from "svelte/elements";
+  import { cn } from "$utils";
+
+  let {
+    ref = $bindable(null),
+    class: className,
+    children,
+    ...restProps
+  }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
+</script>
+
+<div
+  bind:this={ref}
+  class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
+  {...restProps}
+>
+  {@render children?.()}
+</div>
diff --git a/src/lib/components/ui/dialog/dialog-header.svelte b/src/lib/components/ui/dialog/dialog-header.svelte
new file mode 100644
index 0000000..ec60291
--- /dev/null
+++ b/src/lib/components/ui/dialog/dialog-header.svelte
@@ -0,0 +1,20 @@
+<script lang="ts">
+  import type { HTMLAttributes } from "svelte/elements";
+  import type { WithElementRef } from "bits-ui";
+  import { cn } from "$utils";
+
+  let {
+    ref = $bindable(null),
+    class: className,
+    children,
+    ...restProps
+  }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
+</script>
+
+<div
+  bind:this={ref}
+  class={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)}
+  {...restProps}
+>
+  {@render children?.()}
+</div>
diff --git a/src/lib/components/ui/dialog/dialog-overlay.svelte b/src/lib/components/ui/dialog/dialog-overlay.svelte
new file mode 100644
index 0000000..b9b2cae
--- /dev/null
+++ b/src/lib/components/ui/dialog/dialog-overlay.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+  import { Dialog as DialogPrimitive } from "bits-ui";
+  import { cn } from "$utils";
+
+  let {
+    ref = $bindable(null),
+    class: className,
+    ...restProps
+  }: DialogPrimitive.OverlayProps = $props();
+</script>
+
+<DialogPrimitive.Overlay
+  bind:ref
+  class={cn(
+    "fixed inset-0 z-50 bg-black/80  data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
+    className
+  )}
+  {...restProps}
+/>
diff --git a/src/lib/components/ui/dialog/dialog-title.svelte b/src/lib/components/ui/dialog/dialog-title.svelte
new file mode 100644
index 0000000..15eae58
--- /dev/null
+++ b/src/lib/components/ui/dialog/dialog-title.svelte
@@ -0,0 +1,16 @@
+<script lang="ts">
+  import { Dialog as DialogPrimitive } from "bits-ui";
+  import { cn } from "$utils";
+
+  let {
+    ref = $bindable(null),
+    class: className,
+    ...restProps
+  }: DialogPrimitive.TitleProps = $props();
+</script>
+
+<DialogPrimitive.Title
+  bind:ref
+  class={cn("text-lg font-semibold leading-none tracking-tight", className)}
+  {...restProps}
+/>
diff --git a/src/lib/components/ui/dialog/index.ts b/src/lib/components/ui/dialog/index.ts
new file mode 100644
index 0000000..5126e26
--- /dev/null
+++ b/src/lib/components/ui/dialog/index.ts
@@ -0,0 +1,37 @@
+import { Dialog as DialogPrimitive } from "bits-ui";
+
+import Title from "./dialog-title.svelte";
+import Footer from "./dialog-footer.svelte";
+import Header from "./dialog-header.svelte";
+import Overlay from "./dialog-overlay.svelte";
+import Content from "./dialog-content.svelte";
+import Description from "./dialog-description.svelte";
+
+const Root: typeof DialogPrimitive.Root = DialogPrimitive.Root;
+const Trigger: typeof DialogPrimitive.Trigger = DialogPrimitive.Trigger;
+const Close: typeof DialogPrimitive.Close = DialogPrimitive.Close;
+const Portal: typeof DialogPrimitive.Portal = DialogPrimitive.Portal;
+
+export {
+  Root,
+  Title,
+  Portal,
+  Footer,
+  Header,
+  Trigger,
+  Overlay,
+  Content,
+  Description,
+  Close,
+  //
+  Root as Dialog,
+  Title as DialogTitle,
+  Portal as DialogPortal,
+  Footer as DialogFooter,
+  Header as DialogHeader,
+  Trigger as DialogTrigger,
+  Overlay as DialogOverlay,
+  Content as DialogContent,
+  Description as DialogDescription,
+  Close as DialogClose
+};
diff --git a/src/lib/components/ui/popover/index.ts b/src/lib/components/ui/popover/index.ts
new file mode 100644
index 0000000..e5a8bc6
--- /dev/null
+++ b/src/lib/components/ui/popover/index.ts
@@ -0,0 +1,17 @@
+import { Popover as PopoverPrimitive } from "bits-ui";
+import Content from "./popover-content.svelte";
+const Root = PopoverPrimitive.Root;
+const Trigger = PopoverPrimitive.Trigger;
+const Close = PopoverPrimitive.Close;
+
+export {
+  Root,
+  Content,
+  Trigger,
+  Close,
+  //
+  Root as Popover,
+  Content as PopoverContent,
+  Trigger as PopoverTrigger,
+  Close as PopoverClose
+};
diff --git a/src/lib/components/ui/popover/popover-content.svelte b/src/lib/components/ui/popover/popover-content.svelte
new file mode 100644
index 0000000..11cf914
--- /dev/null
+++ b/src/lib/components/ui/popover/popover-content.svelte
@@ -0,0 +1,28 @@
+<script lang="ts">
+  import { cn } from "$utils";
+  import { Popover as PopoverPrimitive } from "bits-ui";
+
+  let {
+    ref = $bindable(null),
+    class: className,
+    align = "center",
+    sideOffset = 4,
+    portalProps,
+    ...restProps
+  }: PopoverPrimitive.ContentProps & {
+    portalProps?: PopoverPrimitive.PortalProps;
+  } = $props();
+</script>
+
+<PopoverPrimitive.Portal {...portalProps}>
+  <PopoverPrimitive.Content
+    bind:ref
+    {align}
+    {sideOffset}
+    class={cn(
+      "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
+      className
+    )}
+    {...restProps}
+  />
+</PopoverPrimitive.Portal>
-- 
Muhammad Rizki


  parent reply	other threads:[~2025-03-05 14:40 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-03-05 14:39 [PATCH v1 00/17] Profile Page, SEO, Fixed API structure, Docs Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 01/17] fix(typing): add user_info type prop Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 02/17] refactor: optimize icon imports to reduce bundle size Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 03/17] chore(change-pwd): adjust change password heading styling Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 04/17] chore(settings/layout): use prose: for " Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 05/17] fix(profile): fix edit avatar button position Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 06/17] fix(breadcrumb): Move settingsNav to settings items navigations Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 07/17] chore(responsive): adjust styling Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 08/17] chore(navigations): Replace index /settings url Muhammad Rizki
2025-03-05 14:40 ` Muhammad Rizki [this message]
2025-03-05 14:40 ` [PATCH v1 10/17] feat(http): Use PUBLIC_BASE_URL for each environment Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 11/17] feat(icons): Add social icons Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 12/17] feat(typing/enum): add Gender and IsActive enum Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 13/17] refactor!:feat: update API response structure, update profile page Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 14/17] chore(meta): rename favicon.png to favicon.ico Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 15/17] feat(seo): add SEO for site metadata Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 16/17] chore(login): use $derived() instead of function based Muhammad Rizki
2025-03-05 14:40 ` [PATCH v1 17/17] docs: update README.md Muhammad Rizki
2025-03-05 16:54 ` [PATCH v1 00/17] Profile Page, SEO, Fixed API structure, Docs Ammar Faizi
2025-03-05 16:57   ` Alviro Iskandar Setiawan
2025-03-06  7:01     ` Muhammad Rizki
2025-03-06  7:02       ` Alviro Iskandar Setiawan
2025-03-06  7:04         ` Muhammad Rizki
2025-03-06  2:02   ` Muhammad Rizki
2025-03-06  3:37     ` Ammar Faizi
2025-03-05 17:04 ` Ammar Faizi
2025-03-05 18:14 ` Alviro Iskandar Setiawan
2025-03-06  1:59   ` Muhammad Rizki
2025-03-06  3:35     ` Ammar Faizi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    [email protected] \
    [email protected] \
    [email protected] \
    [email protected] \
    [email protected] \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox