aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorDhravya <[email protected]>2024-06-29 00:11:28 -0500
committerDhravya <[email protected]>2024-06-29 00:11:28 -0500
commitdfcc1ea7f7db341843d9d60c5c70f682f08dc4a8 (patch)
tree2157ea4afea117dcfd19228e4f202d9cd1172e31 /packages
parentallow pointer events (diff)
downloadsupermemory-dfcc1ea7f7db341843d9d60c5c70f682f08dc4a8.tar.xz
supermemory-dfcc1ea7f7db341843d9d60c5c70f682f08dc4a8.zip
new filter
Diffstat (limited to 'packages')
-rw-r--r--packages/tailwind-config/globals.css6
-rw-r--r--packages/tailwind-config/tailwind.config.ts1
-rw-r--r--packages/ui/icons/add.svg2
-rw-r--r--packages/ui/icons/arrowright.svg2
-rw-r--r--packages/ui/icons/canvas.svg2
-rw-r--r--packages/ui/icons/explore.svg2
-rw-r--r--packages/ui/icons/memories.svg2
-rw-r--r--packages/ui/shadcn/button.tsx4
-rw-r--r--packages/ui/shadcn/combobox.tsx572
-rw-r--r--packages/ui/shadcn/command.tsx6
-rw-r--r--packages/ui/shadcn/dialog.tsx4
-rw-r--r--packages/ui/shadcn/select.tsx2
12 files changed, 77 insertions, 528 deletions
diff --git a/packages/tailwind-config/globals.css b/packages/tailwind-config/globals.css
index d09b120b..0b12258c 100644
--- a/packages/tailwind-config/globals.css
+++ b/packages/tailwind-config/globals.css
@@ -5,10 +5,10 @@
:root {
--foreground: rgba(179, 188, 197, 1);
--foreground-menu: rgba(106, 115, 125, 1);
- --background: rgba(23, 27, 31, 1);
- --secondary: rgba(31, 36, 40, 1);
+ --background: rgba(31, 36, 40, 1);
+ --secondary: rgb(45, 51, 58);
--primary: rgba(54, 157, 253, 1);
- --border: rgba(51, 57, 67, 1);
+ --border: rgba(62, 68, 73, 1);
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
diff --git a/packages/tailwind-config/tailwind.config.ts b/packages/tailwind-config/tailwind.config.ts
index bd3f26b1..7de0393c 100644
--- a/packages/tailwind-config/tailwind.config.ts
+++ b/packages/tailwind-config/tailwind.config.ts
@@ -8,6 +8,7 @@ const config = {
"./app/**/*.{ts,tsx}",
"./src/**/*.{ts,tsx}",
"../../packages/ui/**/*.{js,ts,jsx,tsx}", // Add the ui package
+ "../../packages/ui/shadcn/**/*.{js,ts,jsx,tsx}", // Add the ui package
],
prefix: "",
theme: {
diff --git a/packages/ui/icons/add.svg b/packages/ui/icons/add.svg
index 970a7c95..ede03f81 100644
--- a/packages/ui/icons/add.svg
+++ b/packages/ui/icons/add.svg
@@ -1,3 +1,3 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M10 4.25V16.75M16.25 10.5H3.75" stroke="#989EA4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M10 4.25V16.75M16.25 10.5H3.75" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg> \ No newline at end of file
diff --git a/packages/ui/icons/arrowright.svg b/packages/ui/icons/arrowright.svg
index d794b094..759472c6 100644
--- a/packages/ui/icons/arrowright.svg
+++ b/packages/ui/icons/arrowright.svg
@@ -1,3 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M11.25 16.25L17.5 10M17.5 10L11.25 3.75M17.5 10H2.5" stroke="#369DFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M11.25 16.25L17.5 10M17.5 10L11.25 3.75M17.5 10H2.5" stroke="#989EA4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg> \ No newline at end of file
diff --git a/packages/ui/icons/canvas.svg b/packages/ui/icons/canvas.svg
index 529c75de..141d4801 100644
--- a/packages/ui/icons/canvas.svg
+++ b/packages/ui/icons/canvas.svg
@@ -1,3 +1,3 @@
<svg width="22" height="20" viewBox="0 0 22 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M1.25 0.25C1.05109 0.25 0.860322 0.329018 0.71967 0.46967C0.579018 0.610322 0.5 0.801088 0.5 1C0.5 1.19891 0.579018 1.38968 0.71967 1.53033C0.860322 1.67098 1.05109 1.75 1.25 1.75H2V12.25C2 13.0456 2.31607 13.8087 2.87868 14.3713C3.44129 14.9339 4.20435 15.25 5 15.25H6.21L5.038 18.763C4.97514 18.9518 4.98988 19.1579 5.07896 19.3359C5.16804 19.5138 5.32417 19.6491 5.513 19.712C5.70183 19.7749 5.9079 19.7601 6.08588 19.671C6.26385 19.582 6.39914 19.4258 6.462 19.237L6.791 18.25H15.209L15.539 19.237C15.6073 19.4186 15.7433 19.5666 15.9184 19.6501C16.0935 19.7335 16.2941 19.7459 16.4782 19.6845C16.6622 19.6232 16.8153 19.4929 16.9053 19.3211C16.9954 19.1493 17.0153 18.9492 16.961 18.763L15.791 15.25H17C17.7956 15.25 18.5587 14.9339 19.1213 14.3713C19.6839 13.8087 20 13.0456 20 12.25V1.75H20.75C20.9489 1.75 21.1397 1.67098 21.2803 1.53033C21.421 1.38968 21.5 1.19891 21.5 1C21.5 0.801088 21.421 0.610322 21.2803 0.46967C21.1397 0.329018 20.9489 0.25 20.75 0.25H1.25ZM7.79 15.25H14.21L14.71 16.75H7.29L7.79 15.25ZM15.875 6.255C15.961 6.20611 16.0364 6.1407 16.097 6.06253C16.1577 5.98436 16.2022 5.89497 16.2281 5.79952C16.254 5.70406 16.2608 5.60443 16.2481 5.50634C16.2353 5.40826 16.2033 5.31366 16.1538 5.228C16.1044 5.14235 16.0385 5.06732 15.9599 5.00724C15.8813 4.94715 15.7916 4.90321 15.696 4.87793C15.6004 4.85264 15.5007 4.84653 15.4027 4.85993C15.3047 4.87333 15.2103 4.90598 15.125 4.956C13.7615 5.74523 12.5554 6.7792 11.567 8.006L10.03 6.47C9.88937 6.32955 9.69875 6.25066 9.5 6.25066C9.30125 6.25066 9.11063 6.32955 8.97 6.47L5.97 9.47C5.89631 9.53866 5.83721 9.62146 5.79622 9.71346C5.75523 9.80546 5.73319 9.90477 5.73141 10.0055C5.72963 10.1062 5.74816 10.2062 5.78588 10.2996C5.8236 10.393 5.87974 10.4778 5.95096 10.549C6.02218 10.6203 6.10701 10.6764 6.2004 10.7141C6.29379 10.7518 6.39382 10.7704 6.49452 10.7686C6.59523 10.7668 6.69454 10.7448 6.78654 10.7038C6.87854 10.6628 6.96134 10.6037 7.03 10.53L9.5 8.06L11.117 9.678C11.1946 9.75558 11.2882 9.81519 11.3913 9.85264C11.4945 9.89008 11.6046 9.90445 11.7138 9.89472C11.8231 9.885 11.9289 9.85142 12.0238 9.79635C12.1187 9.74128 12.2003 9.66606 12.263 9.576C13.2095 8.21806 14.4425 7.0844 15.875 6.255Z" fill="#6A737D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M1.25 0.25C1.05109 0.25 0.860322 0.329018 0.71967 0.46967C0.579018 0.610322 0.5 0.801088 0.5 1C0.5 1.19891 0.579018 1.38968 0.71967 1.53033C0.860322 1.67098 1.05109 1.75 1.25 1.75H2V12.25C2 13.0456 2.31607 13.8087 2.87868 14.3713C3.44129 14.9339 4.20435 15.25 5 15.25H6.21L5.038 18.763C4.97514 18.9518 4.98988 19.1579 5.07896 19.3359C5.16804 19.5138 5.32417 19.6491 5.513 19.712C5.70183 19.7749 5.9079 19.7601 6.08588 19.671C6.26385 19.582 6.39914 19.4258 6.462 19.237L6.791 18.25H15.209L15.539 19.237C15.6073 19.4186 15.7433 19.5666 15.9184 19.6501C16.0935 19.7335 16.2941 19.7459 16.4782 19.6845C16.6622 19.6232 16.8153 19.4929 16.9053 19.3211C16.9954 19.1493 17.0153 18.9492 16.961 18.763L15.791 15.25H17C17.7956 15.25 18.5587 14.9339 19.1213 14.3713C19.6839 13.8087 20 13.0456 20 12.25V1.75H20.75C20.9489 1.75 21.1397 1.67098 21.2803 1.53033C21.421 1.38968 21.5 1.19891 21.5 1C21.5 0.801088 21.421 0.610322 21.2803 0.46967C21.1397 0.329018 20.9489 0.25 20.75 0.25H1.25ZM7.79 15.25H14.21L14.71 16.75H7.29L7.79 15.25ZM15.875 6.255C15.961 6.20611 16.0364 6.1407 16.097 6.06253C16.1577 5.98436 16.2022 5.89497 16.2281 5.79952C16.254 5.70406 16.2608 5.60443 16.2481 5.50634C16.2353 5.40826 16.2033 5.31366 16.1538 5.228C16.1044 5.14235 16.0385 5.06732 15.9599 5.00724C15.8813 4.94715 15.7916 4.90321 15.696 4.87793C15.6004 4.85264 15.5007 4.84653 15.4027 4.85993C15.3047 4.87333 15.2103 4.90598 15.125 4.956C13.7615 5.74523 12.5554 6.7792 11.567 8.006L10.03 6.47C9.88937 6.32955 9.69875 6.25066 9.5 6.25066C9.30125 6.25066 9.11063 6.32955 8.97 6.47L5.97 9.47C5.89631 9.53866 5.83721 9.62146 5.79622 9.71346C5.75523 9.80546 5.73319 9.90477 5.73141 10.0055C5.72963 10.1062 5.74816 10.2062 5.78588 10.2996C5.8236 10.393 5.87974 10.4778 5.95096 10.549C6.02218 10.6203 6.10701 10.6764 6.2004 10.7141C6.29379 10.7518 6.39382 10.7704 6.49452 10.7686C6.59523 10.7668 6.69454 10.7448 6.78654 10.7038C6.87854 10.6628 6.96134 10.6037 7.03 10.53L9.5 8.06L11.117 9.678C11.1946 9.75558 11.2882 9.81519 11.3913 9.85264C11.4945 9.89008 11.6046 9.90445 11.7138 9.89472C11.8231 9.885 11.9289 9.85142 12.0238 9.79635C12.1187 9.74128 12.2003 9.66606 12.263 9.576C13.2095 8.21806 14.4425 7.0844 15.875 6.255Z" fill="#fff"/>
</svg>
diff --git a/packages/ui/icons/explore.svg b/packages/ui/icons/explore.svg
index 98785c14..aa056cbc 100644
--- a/packages/ui/icons/explore.svg
+++ b/packages/ui/icons/explore.svg
@@ -1,3 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M6.5 17.5L14 14L17.5 6.5L10 10L6.5 17.5ZM12 13C11.7167 13 11.4793 12.904 11.288 12.712C11.0967 12.52 11.0007 12.2827 11 12C10.9993 11.7173 11.0953 11.48 11.288 11.288C11.4807 11.096 11.718 11 12 11C12.282 11 12.5197 11.096 12.713 11.288C12.9063 11.48 13.002 11.7173 13 12C12.998 12.2827 12.902 12.5203 12.712 12.713C12.522 12.9057 12.2847 13.0013 12 13ZM12 22C10.6167 22 9.31667 21.7373 8.1 21.212C6.88334 20.6867 5.825 19.9743 4.925 19.075C4.025 18.1757 3.31267 17.1173 2.788 15.9C2.26333 14.6827 2.00067 13.3827 2 12C1.99933 10.6173 2.262 9.31733 2.788 8.1C3.314 6.88267 4.02633 5.82433 4.925 4.925C5.82367 4.02567 6.882 3.31333 8.1 2.788C9.318 2.26267 10.618 2 12 2C13.382 2 14.682 2.26267 15.9 2.788C17.118 3.31333 18.1763 4.02567 19.075 4.925C19.9737 5.82433 20.6863 6.88267 21.213 8.1C21.7397 9.31733 22.002 10.6173 22 12C21.998 13.3827 21.7353 14.6827 21.212 15.9C20.6887 17.1173 19.9763 18.1757 19.075 19.075C18.1737 19.9743 17.1153 20.687 15.9 21.213C14.6847 21.739 13.3847 22.0013 12 22Z" fill="#777E87"/>
+<path d="M6.5 17.5L14 14L17.5 6.5L10 10L6.5 17.5ZM12 13C11.7167 13 11.4793 12.904 11.288 12.712C11.0967 12.52 11.0007 12.2827 11 12C10.9993 11.7173 11.0953 11.48 11.288 11.288C11.4807 11.096 11.718 11 12 11C12.282 11 12.5197 11.096 12.713 11.288C12.9063 11.48 13.002 11.7173 13 12C12.998 12.2827 12.902 12.5203 12.712 12.713C12.522 12.9057 12.2847 13.0013 12 13ZM12 22C10.6167 22 9.31667 21.7373 8.1 21.212C6.88334 20.6867 5.825 19.9743 4.925 19.075C4.025 18.1757 3.31267 17.1173 2.788 15.9C2.26333 14.6827 2.00067 13.3827 2 12C1.99933 10.6173 2.262 9.31733 2.788 8.1C3.314 6.88267 4.02633 5.82433 4.925 4.925C5.82367 4.02567 6.882 3.31333 8.1 2.788C9.318 2.26267 10.618 2 12 2C13.382 2 14.682 2.26267 15.9 2.788C17.118 3.31333 18.1763 4.02567 19.075 4.925C19.9737 5.82433 20.6863 6.88267 21.213 8.1C21.7397 9.31733 22.002 10.6173 22 12C21.998 13.3827 21.7353 14.6827 21.212 15.9C20.6887 17.1173 19.9763 18.1757 19.075 19.075C18.1737 19.9743 17.1153 20.687 15.9 21.213C14.6847 21.739 13.3847 22.0013 12 22Z" fill="#fff"/>
</svg> \ No newline at end of file
diff --git a/packages/ui/icons/memories.svg b/packages/ui/icons/memories.svg
index 069b4db8..425bd583 100644
--- a/packages/ui/icons/memories.svg
+++ b/packages/ui/icons/memories.svg
@@ -1,3 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M5.566 4.657C5.95195 4.55235 6.35011 4.49955 6.75 4.5H17.25C17.66 4.5 18.056 4.555 18.433 4.657C18.1837 4.15898 17.8006 3.7402 17.3268 3.44754C16.8529 3.15489 16.3069 2.99993 15.75 3H8.25C7.69288 2.99974 7.1467 3.15462 6.67265 3.44728C6.1986 3.73994 5.8154 4.15883 5.566 4.657ZM2.25 12C2.25 11.2044 2.56607 10.4413 3.12868 9.87868C3.69129 9.31607 4.45435 9 5.25 9H18.75C19.5456 9 20.3087 9.31607 20.8713 9.87868C21.4339 10.4413 21.75 11.2044 21.75 12V18C21.75 18.7956 21.4339 19.5587 20.8713 20.1213C20.3087 20.6839 19.5456 21 18.75 21H5.25C4.45435 21 3.69129 20.6839 3.12868 20.1213C2.56607 19.5587 2.25 18.7956 2.25 18V12ZM5.25 7.5C4.84 7.5 4.444 7.555 4.066 7.657C4.3154 7.15883 4.6986 6.73994 5.17265 6.44728C5.6467 6.15462 6.19288 5.99974 6.75 6H17.25C17.8069 5.99993 18.3529 6.15489 18.8268 6.44754C19.3006 6.7402 19.6837 7.15898 19.933 7.657C19.5474 7.55244 19.1496 7.49964 18.75 7.5H5.25Z" fill="#777E87"/>
+<path d="M5.566 4.657C5.95195 4.55235 6.35011 4.49955 6.75 4.5H17.25C17.66 4.5 18.056 4.555 18.433 4.657C18.1837 4.15898 17.8006 3.7402 17.3268 3.44754C16.8529 3.15489 16.3069 2.99993 15.75 3H8.25C7.69288 2.99974 7.1467 3.15462 6.67265 3.44728C6.1986 3.73994 5.8154 4.15883 5.566 4.657ZM2.25 12C2.25 11.2044 2.56607 10.4413 3.12868 9.87868C3.69129 9.31607 4.45435 9 5.25 9H18.75C19.5456 9 20.3087 9.31607 20.8713 9.87868C21.4339 10.4413 21.75 11.2044 21.75 12V18C21.75 18.7956 21.4339 19.5587 20.8713 20.1213C20.3087 20.6839 19.5456 21 18.75 21H5.25C4.45435 21 3.69129 20.6839 3.12868 20.1213C2.56607 19.5587 2.25 18.7956 2.25 18V12ZM5.25 7.5C4.84 7.5 4.444 7.555 4.066 7.657C4.3154 7.15883 4.6986 6.73994 5.17265 6.44728C5.6467 6.15462 6.19288 5.99974 6.75 6H17.25C17.8069 5.99993 18.3529 6.15489 18.8268 6.44754C19.3006 6.7402 19.6837 7.15898 19.933 7.657C19.5474 7.55244 19.1496 7.49964 18.75 7.5H5.25Z" fill="#fff"/>
</svg> \ No newline at end of file
diff --git a/packages/ui/shadcn/button.tsx b/packages/ui/shadcn/button.tsx
index 90c4fa46..a91ff896 100644
--- a/packages/ui/shadcn/button.tsx
+++ b/packages/ui/shadcn/button.tsx
@@ -1,8 +1,8 @@
+import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@repo/ui/lib/utils";
-import React from "react";
+import { cn } from "../lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
diff --git a/packages/ui/shadcn/combobox.tsx b/packages/ui/shadcn/combobox.tsx
index 0d0fd714..9eb62cf8 100644
--- a/packages/ui/shadcn/combobox.tsx
+++ b/packages/ui/shadcn/combobox.tsx
@@ -1,528 +1,76 @@
"use client";
-import * as React from "react";
-import { forwardRef, useEffect } from "react";
-import { Command as CommandPrimitive, useCommandState } from "cmdk";
-import { X } from "lucide-react";
-
+import React, { useState, useEffect } from "react";
+import { Check, ChevronsUpDown } from "lucide-react";
import { cn } from "../lib/utils";
-import { Badge } from "./badge";
-import { Command, CommandGroup, CommandItem, CommandList } from "./command";
-
-export interface Option {
+import { Button } from "./button";
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "./command";
+
+interface Option {
value: string;
label: string;
- disable?: boolean;
- /** fixed option that can't be removed. */
- fixed?: boolean;
- /** Group the options by providing key. */
- [key: string]: string | boolean | undefined;
-}
-interface GroupOption {
- [key: string]: Option[];
}
-interface MultipleSelectorProps {
- value?: Option[];
- defaultOptions?: Option[];
- /** manually controlled options */
- options?: Option[];
+interface ComboboxWithCreateProps {
+ options: Option[];
+ onSelect: (value: string) => void;
+ onSubmit: () => void;
placeholder?: string;
- /** Loading component. */
- loadingIndicator?: React.ReactNode;
- /** Empty component. */
- emptyIndicator?: React.ReactNode;
- /** Debounce time for async search. Only work with `onSearch`. */
- delay?: number;
- /**
- * Only work with `onSearch` prop. Trigger search when `onFocus`.
- * For example, when user click on the input, it will trigger the search to get initial options.
- **/
- triggerSearchOnFocus?: boolean;
- /** async search */
- onSearch?: (value: string) => Promise<Option[]>;
- onChange?: (options: Option[]) => void;
- /** Limit the maximum number of selected options. */
- maxSelected?: number;
- /** When the number of selected options exceeds the limit, the onMaxSelected will be called. */
- onMaxSelected?: (maxLimit: number) => void;
- /** Hide the placeholder when there are options selected. */
- hidePlaceholderWhenSelected?: boolean;
- disabled?: boolean;
- /** Group the options base on provided key. */
- groupBy?: string;
- className?: string;
- badgeClassName?: string;
- /**
- * First item selected is a default behavior by cmdk. That is why the default is true.
- * This is a workaround solution by add a dummy item.
- *
- * @reference: https://github.com/pacocoursey/cmdk/issues/171
- */
- selectFirstItem?: boolean;
- /** Allow user to create option when there is no option matched. */
- creatable?: boolean;
- /** Props of `Command` */
- commandProps?: React.ComponentPropsWithoutRef<typeof Command>;
- /** Props of `CommandInput` */
- inputProps?: Omit<
- React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>,
- "value" | "placeholder" | "disabled"
- >;
+ emptyMessage?: string;
+ createNewMessage?: string;
}
-export interface MultipleSelectorRef {
- selectedValue: Option[];
- input: HTMLInputElement;
-}
-
-export function useDebounce<T>(value: T, delay?: number): T {
- const [debouncedValue, setDebouncedValue] = React.useState<T>(value);
+const ComboboxWithCreate: React.FC<ComboboxWithCreateProps> = ({
+ options: initialOptions,
+ onSelect,
+ onSubmit,
+ placeholder = "Select an option",
+ emptyMessage = "No option found.",
+ createNewMessage = "Create",
+}) => {
+ const [open, setOpen] = useState(false);
+ const [value, setValue] = useState("");
+ const [options, setOptions] = useState<Option[]>(initialOptions);
+ const [inputValue, setInputValue] = useState("");
useEffect(() => {
- const timer = setTimeout(() => setDebouncedValue(value), delay || 500);
-
- return () => {
- clearTimeout(timer);
- };
- }, [value, delay]);
-
- return debouncedValue;
-}
-
-function transToGroupOption(options: Option[], groupBy?: string) {
- if (options.length === 0) {
- return {};
- }
- if (!groupBy) {
- return {
- "": options,
- };
- }
-
- const groupOption: GroupOption = {};
- options.forEach((option) => {
- const key = (option[groupBy] as string) || "";
- if (!groupOption[key]) {
- groupOption[key] = [];
- }
- groupOption[key]?.push(option);
- });
- return groupOption;
-}
-
-function removePickedOption(groupOption: GroupOption, picked: Option[]) {
- const cloneOption = JSON.parse(JSON.stringify(groupOption)) as GroupOption;
-
- for (const [key, value] of Object.entries(cloneOption)) {
- cloneOption[key] = value.filter(
- (val) => !picked.find((p) => p.value === val.value),
- );
- }
- return cloneOption;
-}
-
-function isOptionsExist(groupOption: GroupOption, targetOption: Option[]) {
- for (const [key, value] of Object.entries(groupOption)) {
- if (
- value.some((option) => targetOption.find((p) => p.value === option.value))
- ) {
- return true;
- }
- }
- return false;
-}
-
-/**
- * The `CommandEmpty` of shadcn/ui will cause the cmdk empty not rendering correctly.
- * So we create one and copy the `Empty` implementation from `cmdk`.
- *
- * @reference: https://github.com/hsuanyi-chou/shadcn-ui-expansions/issues/34#issuecomment-1949561607
- **/
-const CommandEmpty = forwardRef<
- HTMLDivElement,
- React.ComponentProps<typeof CommandPrimitive.Empty>
->(({ className, ...props }, forwardedRef) => {
- const render = useCommandState((state) => state.filtered.count === 0);
-
- if (!render) return null;
+ setOptions(initialOptions);
+ }, [initialOptions]);
return (
- <div
- ref={forwardedRef}
- className={cn("py-6 text-center text-sm", className)}
- cmdk-empty=""
- role="presentation"
- {...props}
- />
- );
-});
-
-CommandEmpty.displayName = "CommandEmpty";
-
-const MultipleSelector = React.forwardRef<
- MultipleSelectorRef,
- MultipleSelectorProps
->(
- (
- {
- value,
- onChange,
- placeholder,
- defaultOptions: arrayDefaultOptions = [],
- options: arrayOptions,
- delay,
- onSearch,
- loadingIndicator,
- emptyIndicator,
- maxSelected = Number.MAX_SAFE_INTEGER,
- onMaxSelected,
- hidePlaceholderWhenSelected,
- disabled,
- groupBy,
- className,
- badgeClassName,
- selectFirstItem = true,
- creatable = false,
- triggerSearchOnFocus = false,
- commandProps,
- inputProps,
- }: MultipleSelectorProps,
- ref: React.Ref<MultipleSelectorRef>,
- ) => {
- const inputRef = React.useRef<HTMLInputElement>(null);
- const [open, setOpen] = React.useState(false);
- const [isLoading, setIsLoading] = React.useState(false);
-
- const [selected, setSelected] = React.useState<Option[]>(value || []);
- const [options, setOptions] = React.useState<GroupOption>(
- transToGroupOption(arrayDefaultOptions, groupBy),
- );
- const [inputValue, setInputValue] = React.useState("");
- const debouncedSearchTerm = useDebounce(inputValue, delay || 500);
-
- React.useImperativeHandle(
- ref,
- () => ({
- selectedValue: [...selected],
- input: inputRef.current as HTMLInputElement,
- focus: () => inputRef.current?.focus(),
- }),
- [selected],
- );
-
- const handleUnselect = React.useCallback(
- (option: Option) => {
- const newOptions = selected.filter((s) => s.value !== option.value);
- setSelected(newOptions);
- onChange?.(newOptions);
- },
- [onChange, selected],
- );
-
- const handleKeyDown = React.useCallback(
- (e: React.KeyboardEvent<HTMLDivElement>) => {
- const input = inputRef.current;
- if (input) {
- if (e.key === "Delete" || e.key === "Backspace") {
- if (input.value === "" && selected.length > 0) {
- handleUnselect(selected[selected.length - 1]!);
- }
- }
- // This is not a default behavior of the <input /> field
- if (e.key === "Escape") {
- input.blur();
- }
- }
- },
- [handleUnselect, selected],
- );
-
- useEffect(() => {
- if (value) {
- setSelected(value);
- }
- }, [value]);
-
- useEffect(() => {
- /** If `onSearch` is provided, do not trigger options updated. */
- if (!arrayOptions || onSearch) {
- return;
- }
- const newOption = transToGroupOption(arrayOptions || [], groupBy);
- if (JSON.stringify(newOption) !== JSON.stringify(options)) {
- setOptions(newOption);
- }
- }, [arrayDefaultOptions, arrayOptions, groupBy, onSearch, options]);
-
- useEffect(() => {
- const doSearch = async () => {
- setIsLoading(true);
- const res = await onSearch?.(debouncedSearchTerm);
- setOptions(transToGroupOption(res || [], groupBy));
- setIsLoading(false);
- };
-
- const exec = async () => {
- if (!onSearch || !open) return;
-
- if (triggerSearchOnFocus) {
- await doSearch();
- }
-
- if (debouncedSearchTerm) {
- await doSearch();
- }
- };
-
- void exec();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [debouncedSearchTerm, groupBy, open, triggerSearchOnFocus]);
-
- const CreatableItem = () => {
- if (!creatable) return undefined;
- if (
- isOptionsExist(options, [{ value: inputValue, label: inputValue }]) ||
- selected.find((s) => s.value === inputValue)
- ) {
- return undefined;
- }
-
- const Item = (
- <CommandItem
- value={inputValue}
- className="cursor-pointer"
- onMouseDown={(e) => {
- e.preventDefault();
- e.stopPropagation();
- }}
- onSelect={(value: string) => {
- if (selected.length >= maxSelected) {
- onMaxSelected?.(selected.length);
- return;
- }
- setInputValue("");
- const newOptions = [...selected, { value, label: value }];
- setSelected(newOptions);
- onChange?.(newOptions);
- }}
+ <Command className="w-40 group">
+ <CommandInput
+ onChangeCapture={(e) => setInputValue(e.currentTarget.value)}
+ placeholder={placeholder}
+ value={inputValue}
+ />
+ <CommandList className="z-10 translate-y-12 translate-x-5 opacity-0 absolute group-focus-within:opacity-100 bg-secondary p-2 rounded-b-xl max-w-64">
+ <CommandEmpty>
+ {createNewMessage} "{inputValue}"
+ </CommandEmpty>
+ <CommandGroup
+ className="hidden group-focus-within:block"
+ heading="Spaces"
>
- {`Create "${inputValue}"`}
- </CommandItem>
- );
-
- // For normal creatable
- if (!onSearch && inputValue.length > 0) {
- return Item;
- }
-
- // For async search creatable. avoid showing creatable item before loading at first.
- if (onSearch && debouncedSearchTerm.length > 0 && !isLoading) {
- return Item;
- }
-
- return undefined;
- };
-
- const EmptyItem = React.useCallback(() => {
- if (!emptyIndicator) return undefined;
-
- // For async search that showing emptyIndicator
- if (onSearch && !creatable && Object.keys(options).length === 0) {
- return (
- <CommandItem value="-" disabled>
- {emptyIndicator}
- </CommandItem>
- );
- }
-
- return <CommandEmpty>{emptyIndicator}</CommandEmpty>;
- }, [creatable, emptyIndicator, onSearch, options]);
-
- const selectables = React.useMemo<GroupOption>(
- () => removePickedOption(options, selected),
- [options, selected],
- );
-
- /** Avoid Creatable Selector freezing or lagging when paste a long string. */
- const commandFilter = React.useCallback(() => {
- if (commandProps?.filter) {
- return commandProps.filter;
- }
-
- if (creatable) {
- return (value: string, search: string) => {
- return value.toLowerCase().includes(search.toLowerCase()) ? 1 : -1;
- };
- }
- // Using default filter in `cmdk`. We don't have to provide it.
- return undefined;
- }, [creatable, commandProps?.filter]);
-
- return (
- <Command
- {...commandProps}
- onKeyDown={(e) => {
- handleKeyDown(e);
- commandProps?.onKeyDown?.(e);
- }}
- className={cn(
- "h-auto overflow-visible bg-transparent z-10",
- commandProps?.className,
- )}
- shouldFilter={
- commandProps?.shouldFilter !== undefined
- ? commandProps.shouldFilter
- : !onSearch
- } // When onSearch is provided, we don't want to filter the options. You can still override it.
- filter={commandFilter()}
- >
- <div
- className={cn(
- "min-h-10 rounded-xl text-sm ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
- {
- "px-3 py-2": selected.length !== 0,
- "cursor-text": !disabled && selected.length !== 0,
- },
- className,
- )}
- onClick={() => {
- if (disabled) return;
- inputRef.current?.focus();
- }}
- >
- <div className="flex flex-wrap gap-1">
- {selected.map((option) => {
- return (
- <Badge
- key={option.value}
- className={cn(
- "data-[disabled]:bg-muted-foreground data-[disabled]:text-muted data-[disabled]:hover:bg-muted-foreground",
- "data-[fixed]:bg-muted-foreground data-[fixed]:text-muted data-[fixed]:hover:bg-muted-foreground",
- badgeClassName,
- )}
- data-fixed={option.fixed}
- data-disabled={disabled || undefined}
- >
- {option.label}
- <button
- className={cn(
- "ml-1 rounded-full outline-none ring-offset-background focus:ring-2 focus:ring-ring focus:ring-offset-2",
- (disabled || option.fixed) && "hidden",
- )}
- onKeyDown={(e) => {
- if (e.key === "Enter") {
- handleUnselect(option);
- }
- }}
- onMouseDown={(e) => {
- e.preventDefault();
- e.stopPropagation();
- }}
- onClick={() => handleUnselect(option)}
- >
- <X className="h-3 w-3 text-muted-foreground hover:text-foreground" />
- </button>
- </Badge>
- );
- })}
- {/* Avoid having the "Search" Icon */}
- <CommandPrimitive.Input
- {...inputProps}
- ref={inputRef}
- value={inputValue}
- disabled={disabled}
- onValueChange={(value) => {
- setInputValue(value);
- inputProps?.onValueChange?.(value);
- }}
- onBlur={(event) => {
- setOpen(false);
- inputProps?.onBlur?.(event);
- }}
- onFocus={(event) => {
- setOpen(true);
- triggerSearchOnFocus && onSearch?.(debouncedSearchTerm);
- inputProps?.onFocus?.(event);
- }}
- placeholder={
- hidePlaceholderWhenSelected && selected.length !== 0
- ? ""
- : placeholder
- }
- className={cn(
- "flex-1 bg-transparent outline-none placeholder:text-muted-foreground",
- {
- "w-full": hidePlaceholderWhenSelected,
- "px-3 py-2": selected.length === 0,
- "ml-1": selected.length !== 0,
- },
- inputProps?.className,
- )}
- />
- </div>
- </div>
- <div className="relative">
- {open && (
- <CommandList className="absolute top-1 z-10 w-full rounded-md border bg-popover text-popover-foreground shadow-md outline-none animate-in">
- {isLoading ? (
- <>{loadingIndicator}</>
- ) : (
- <>
- {EmptyItem()}
- {CreatableItem()}
- {!selectFirstItem && (
- <CommandItem value="-" className="hidden" />
- )}
- {Object.entries(selectables).map(([key, dropdowns]) => (
- <CommandGroup
- key={key}
- heading={key}
- className="h-full overflow-auto"
- >
- <>
- {dropdowns.map((option) => {
- return (
- <CommandItem
- key={option.value}
- value={option.value}
- disabled={option.disable}
- onMouseDown={(e) => {
- e.preventDefault();
- e.stopPropagation();
- }}
- onSelect={() => {
- if (selected.length >= maxSelected) {
- onMaxSelected?.(selected.length);
- return;
- }
- setInputValue("");
- const newOptions = [...selected, option];
- setSelected(newOptions);
- onChange?.(newOptions);
- }}
- className={cn(
- "cursor-pointer",
- option.disable &&
- "cursor-default text-muted-foreground",
- )}
- >
- {option.label}
- </CommandItem>
- );
- })}
- </>
- </CommandGroup>
- ))}
- </>
- )}
- </CommandList>
- )}
- </div>
- </Command>
- );
- },
-);
+ {options.map((option, idx) => (
+ <CommandItem
+ key={`opt-${idx}`}
+ onSelect={() => onSelect(option.value)}
+ >
+ {option.label}
+ </CommandItem>
+ ))}
+ </CommandGroup>
+ </CommandList>
+ </Command>
+ );
+};
-MultipleSelector.displayName = "MultipleSelector";
-export { MultipleSelector };
+export default ComboboxWithCreate;
diff --git a/packages/ui/shadcn/command.tsx b/packages/ui/shadcn/command.tsx
index f81763f2..802dfb11 100644
--- a/packages/ui/shadcn/command.tsx
+++ b/packages/ui/shadcn/command.tsx
@@ -15,7 +15,7 @@ const Command = React.forwardRef<
<CommandPrimitive
ref={ref}
className={cn(
- "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
+ "flex h-full w-full flex-col overflow-hidden rounded-bl-3xl bg-[#3C464D]",
className,
)}
{...props}
@@ -41,7 +41,7 @@ const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => (
- <div className="flex items-center border-b px-3" cmdk-input-wrapper="">
+ <div className="flex items-center px-3" cmdk-input-wrapper="">
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
<CommandPrimitive.Input
ref={ref}
@@ -89,7 +89,7 @@ const CommandGroup = React.forwardRef<
<CommandPrimitive.Group
ref={ref}
className={cn(
- "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
+ "overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
className,
)}
{...props}
diff --git a/packages/ui/shadcn/dialog.tsx b/packages/ui/shadcn/dialog.tsx
index f73becfe..f162360f 100644
--- a/packages/ui/shadcn/dialog.tsx
+++ b/packages/ui/shadcn/dialog.tsx
@@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={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",
+ "fixed inset-0 z-50 bg-black/30 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
{...props}
@@ -38,7 +38,7 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={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",
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border-2 border-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%] rounded-lg",
className,
)}
{...props}
diff --git a/packages/ui/shadcn/select.tsx b/packages/ui/shadcn/select.tsx
index 8abe27c1..ea2cb088 100644
--- a/packages/ui/shadcn/select.tsx
+++ b/packages/ui/shadcn/select.tsx
@@ -19,7 +19,7 @@ const SelectTrigger = React.forwardRef<
<SelectPrimitive.Trigger
ref={ref}
className={cn(
- "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
+ "flex h-10 w-full items-center justify-between rounded-md border border-white/10 bg-background px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className,
)}
{...props}