@js-empire/emperor-ui 1.2.3 → 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/code-conventions.mdc +50 -0
- package/README.md +222 -31
- package/dist/emperor-ui.js +119 -71
- package/dist/emperor-ui.umd.cjs +27 -13
- package/dist/globals.css +1 -1
- package/dist/index-BXtdEByK.js +5 -0
- package/dist/index-CDB93OLO.js +55965 -0
- package/dist/index-CYORMghp.js +290 -0
- package/dist/index.d.ts +334 -33
- package/dist/src-UW24ZMRV-C1Pn8-w8.js +5 -0
- package/package.json +32 -2
- package/src/animations/blink.ts +26 -0
- package/src/animations/floating.ts +12 -0
- package/src/animations/index.ts +2 -0
- package/src/components/atoms/brand/brand.tsx +1 -1
- package/src/components/atoms/color-picker/color-picker.tsx +13 -0
- package/src/components/atoms/color-picker/free-color-picker.tsx +60 -0
- package/src/components/atoms/color-picker/index.ts +3 -0
- package/src/components/atoms/color-picker/preset-color-picker.tsx +64 -0
- package/src/components/atoms/color-picker/stories/color-picker.stories.tsx +49 -0
- package/src/components/atoms/color-picker/styles/color-picker.css +23 -0
- package/src/components/atoms/copy-button/copy-button.tsx +73 -0
- package/src/components/atoms/copy-button/index.ts +1 -0
- package/src/components/atoms/copy-button/stories/copy-button.stories.tsx +21 -0
- package/src/components/atoms/field/field.stories.tsx +27 -0
- package/src/components/atoms/field/field.tsx +11 -0
- package/src/components/atoms/field/index.ts +1 -0
- package/src/components/atoms/field/styles/classes.ts +9 -0
- package/src/components/atoms/field/styles/index.ts +1 -0
- package/src/components/atoms/filter/filter.tsx +92 -0
- package/src/components/atoms/filter/index.ts +3 -0
- package/src/components/atoms/filter/stories/filter.stories.tsx +97 -0
- package/src/components/atoms/filter/styles/classes.ts +20 -0
- package/src/components/atoms/filter/styles/index.ts +1 -0
- package/src/components/atoms/filter/units/autocomplete-filter.tsx +39 -0
- package/src/components/atoms/filter/units/checkbox-filter.tsx +32 -0
- package/src/components/atoms/filter/units/checkbox-group-filter.tsx +37 -0
- package/src/components/atoms/filter/units/date-filter.tsx +50 -0
- package/src/components/atoms/filter/units/index.ts +9 -0
- package/src/components/atoms/filter/units/numeric-filter.tsx +36 -0
- package/src/components/atoms/filter/units/range-filter.tsx +36 -0
- package/src/components/atoms/filter/units/search-filter.tsx +52 -0
- package/src/components/atoms/filter/units/select-filter.tsx +49 -0
- package/src/components/atoms/filter/units/switch-filter.tsx +33 -0
- package/src/components/atoms/index.ts +5 -0
- package/src/components/atoms/theme-switch/index.ts +1 -0
- package/src/components/atoms/theme-switch/styles/classes.ts +16 -0
- package/src/components/atoms/theme-switch/styles/index.ts +1 -0
- package/src/components/atoms/theme-switch/theme-switch.stories.tsx +26 -0
- package/src/components/atoms/theme-switch/theme-switch.tsx +54 -0
- package/src/components/atoms/uploader/avatar-label.tsx +3 -1
- package/src/components/atoms/uploader/stories/uploader.stories.tsx +1 -1
- package/src/components/atoms/uploader/upload-file-error-box.tsx +1 -1
- package/src/components/atoms/uploader/upload-file-input.tsx +1 -1
- package/src/components/atoms/uploader/upload-file-label.tsx +2 -1
- package/src/components/atoms/uploader/upload-file-listing.tsx +2 -1
- package/src/components/atoms/uploader/view-image-modal.tsx +2 -1
- package/src/components/molecules/index.ts +0 -1
- package/src/components/molecules/item-card/index.ts +6 -0
- package/src/components/molecules/item-card/item-actions-dropdown.tsx +57 -0
- package/src/components/molecules/item-card/item-banner.tsx +22 -0
- package/src/components/molecules/item-card/item-card-body.tsx +68 -0
- package/src/components/molecules/item-card/item-card-footer.tsx +55 -0
- package/src/components/molecules/item-card/item-card-header.tsx +61 -0
- package/src/components/molecules/item-card/item-card.tsx +83 -3
- package/src/components/molecules/item-card/loading-item.tsx +88 -0
- package/src/components/molecules/item-card/stories/item-card.stories.tsx +182 -0
- package/src/components/molecules/item-card/styles/classes.ts +138 -0
- package/src/components/molecules/item-card/styles/index.ts +1 -0
- package/src/components/molecules/nav-bar/sub-items-box.tsx +2 -1
- package/src/components/molecules/scaffold/index.ts +1 -0
- package/src/components/molecules/scaffold/scaffold.tsx +4 -17
- package/src/components/molecules/scaffold/styles/index.ts +1 -0
- package/src/components/molecules/scaffold/styles/scaffold-classes.ts +10 -0
- package/src/components/molecules/side-bar/compact-side-bar.tsx +3 -1
- package/src/components/molecules/side-bar/side-bar-drawer.tsx +6 -17
- package/src/components/molecules/side-bar/side-bar.stories.tsx +1 -1
- package/src/components/organisms/filters/filters.stories.tsx +32 -0
- package/src/components/organisms/filters/filters.tsx +36 -0
- package/src/components/organisms/filters/index.ts +1 -0
- package/src/components/organisms/filters/styles/classes.ts +9 -0
- package/src/components/organisms/filters/styles/index.ts +1 -0
- package/src/components/organisms/footer/copy-rights-box.tsx +1 -1
- package/src/components/organisms/footer/footer.tsx +1 -1
- package/src/components/organisms/footer/policies-box.tsx +2 -1
- package/src/components/organisms/footer/quick-links-box.tsx +2 -1
- package/src/components/organisms/footer/social-links-box.tsx +2 -1
- package/src/components/organisms/footer/stories/footer.stories.tsx +1 -1
- package/src/components/organisms/header/header.tsx +1 -8
- package/src/components/organisms/index.ts +1 -0
- package/src/components/organisms/listings/empty-listings.tsx +80 -0
- package/src/components/organisms/listings/index.ts +2 -0
- package/src/components/organisms/listings/listings.tsx +90 -9
- package/src/components/organisms/listings/stories/grid-listings.stories.tsx +153 -0
- package/src/components/organisms/listings/stories/list-listings.stories.tsx +171 -0
- package/src/components/organisms/listings/styles/classes.ts +41 -3
- package/src/constants/animations.ts +14 -0
- package/src/constants/card.tsx +26 -0
- package/src/constants/defaults.ts +1 -16
- package/src/constants/index.ts +2 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/use-filters.ts +20 -0
- package/src/hooks/use-search-params-handler.tsx +186 -0
- package/src/hooks/use-uploader.tsx +1 -1
- package/src/hooks/use-window-size.tsx +53 -0
- package/src/i18n/locales/atoms/ar.ts +3 -0
- package/src/i18n/locales/atoms/en.ts +3 -0
- package/src/i18n/locales/organisms/ar.ts +7 -1
- package/src/i18n/locales/organisms/en.ts +7 -1
- package/src/mocks/constants.ts +103 -0
- package/src/mocks/index.ts +2 -0
- package/src/mocks/listings.tsx +154 -0
- package/src/mocks/types.ts +64 -0
- package/src/providers/config-provider.tsx +0 -8
- package/src/providers/emperor-ui-provider.tsx +16 -5
- package/src/providers/index.ts +1 -0
- package/src/providers/theme-provider.tsx +16 -0
- package/src/providers/uploader-provider.tsx +1 -1
- package/src/styles/hero.ts +1 -1
- package/src/styles/index.css +23 -0
- package/src/types/components/atoms/color-picker/color-picker.ts +12 -0
- package/src/types/components/atoms/color-picker/index.ts +1 -0
- package/src/types/components/atoms/field/field.ts +9 -0
- package/src/types/components/atoms/field/index.ts +1 -0
- package/src/types/components/atoms/filter/filter.ts +43 -0
- package/src/types/components/atoms/filter/index.ts +2 -0
- package/src/types/components/atoms/filter/select-filter.ts +8 -0
- package/src/types/components/atoms/index.ts +3 -0
- package/src/types/components/atoms/uploader.ts +1 -1
- package/src/types/components/index.ts +1 -0
- package/src/types/components/molecules/index.ts +1 -1
- package/src/types/components/molecules/item-card/item-card.ts +50 -0
- package/src/types/components/molecules/listings/listings.ts +21 -5
- package/src/types/components/molecules/side-bar/side-bar.ts +1 -1
- package/src/types/components/molecules/theme-switch/index.ts +1 -0
- package/src/types/components/molecules/theme-switch/theme-switch.ts +9 -0
- package/src/types/components/organisms/filters/filters.ts +11 -0
- package/src/types/components/organisms/filters/index.ts +1 -0
- package/src/types/components/organisms/index.ts +1 -0
- package/src/types/context/config.ts +3 -4
- package/src/types/context/index.ts +0 -1
- package/src/types/context/localization.ts +1 -0
- package/src/types/shared/components.ts +3 -0
- package/src/utils/uploader.ts +1 -1
- package/dist/features-animation-w9dWMd15.js +0 -1938
- package/dist/index-BY47HgaP.js +0 -26533
- package/dist/index-CN4cJ1N7.js +0 -1630
- package/dist/index-Cr1mc-d4.js +0 -5
- package/dist/src-UW24ZMRV-nsR4cpiy.js +0 -5
- package/src/components/molecules/filter/filter.tsx +0 -6
- package/src/components/molecules/filter/index.ts +0 -1
- package/src/components/organisms/listings/stories/listings.stories.tsx +0 -30
- package/src/main.tsx +0 -3
- package/src/mocks/listings.ts +0 -200
- package/src/types/components/molecules/filter/filter.ts +0 -9
- package/src/types/components/molecules/filter/index.ts +0 -1
- package/src/types/context/theme.ts +0 -17
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { FilterProps } from "@/types";
|
|
4
|
+
import { filterClasses } from "@/components";
|
|
5
|
+
import { Checkbox } from "@heroui/checkbox";
|
|
6
|
+
import { cn } from "@/utils";
|
|
7
|
+
import { useSearchParamsHandler } from "@/hooks";
|
|
8
|
+
|
|
9
|
+
export function CheckboxFilter({
|
|
10
|
+
classNames,
|
|
11
|
+
checkboxProps,
|
|
12
|
+
paramKey,
|
|
13
|
+
...props
|
|
14
|
+
}: Pick<FilterProps, "classNames" | "checkboxProps" | "paramKey">) {
|
|
15
|
+
const { getParam, setParams } = useSearchParamsHandler();
|
|
16
|
+
const value = getParam(paramKey);
|
|
17
|
+
const isSelected = value === "true" || value === "1" || value === "yes";
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Checkbox
|
|
21
|
+
isSelected={isSelected}
|
|
22
|
+
onValueChange={(selected) =>
|
|
23
|
+
setParams({
|
|
24
|
+
params: { [paramKey]: selected ? "true" : undefined },
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
{...checkboxProps}
|
|
28
|
+
{...props}
|
|
29
|
+
className={cn(filterClasses({ type: "checkbox" }), classNames?.field)}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { FilterProps } from "@/types";
|
|
4
|
+
import { filterClasses } from "@/components";
|
|
5
|
+
import { CheckboxGroup } from "@heroui/checkbox";
|
|
6
|
+
import { cn } from "@/utils";
|
|
7
|
+
import { useSearchParamsHandler } from "@/hooks";
|
|
8
|
+
|
|
9
|
+
export function CheckboxGroupFilter({
|
|
10
|
+
classNames,
|
|
11
|
+
checkboxGroupProps,
|
|
12
|
+
paramKey,
|
|
13
|
+
...props
|
|
14
|
+
}: Pick<FilterProps, "classNames" | "checkboxGroupProps" | "paramKey">) {
|
|
15
|
+
const { getParam, setParams } = useSearchParamsHandler();
|
|
16
|
+
const valueStr = getParam(paramKey);
|
|
17
|
+
const value = valueStr ? valueStr.split(",").filter(Boolean) : [];
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<CheckboxGroup
|
|
21
|
+
value={value}
|
|
22
|
+
onValueChange={(values) =>
|
|
23
|
+
setParams({
|
|
24
|
+
params: {
|
|
25
|
+
[paramKey]: values.length > 0 ? values.join(",") : undefined,
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
{...checkboxGroupProps}
|
|
30
|
+
{...props}
|
|
31
|
+
className={cn(
|
|
32
|
+
filterClasses({ type: "checkboxGroup" }),
|
|
33
|
+
classNames?.field,
|
|
34
|
+
)}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { FilterProps } from "@/types";
|
|
4
|
+
import { filterClasses } from "@/components";
|
|
5
|
+
import { DatePicker } from "@heroui/date-picker";
|
|
6
|
+
import { cn } from "@/utils";
|
|
7
|
+
import { useSearchParamsHandler } from "@/hooks";
|
|
8
|
+
import { parseDate, type DateValue } from "@internationalized/date";
|
|
9
|
+
import { useMemo } from "react";
|
|
10
|
+
|
|
11
|
+
export function DateFilter({
|
|
12
|
+
classNames,
|
|
13
|
+
dateProps,
|
|
14
|
+
paramKey,
|
|
15
|
+
...props
|
|
16
|
+
}: Pick<FilterProps, "classNames" | "dateProps" | "paramKey">) {
|
|
17
|
+
const { getParam, setParams } = useSearchParamsHandler();
|
|
18
|
+
const valueStr = getParam(paramKey);
|
|
19
|
+
|
|
20
|
+
const value = useMemo(() => {
|
|
21
|
+
if (!valueStr) return null;
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
return parseDate(valueStr);
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}, [valueStr]);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<DatePicker
|
|
32
|
+
labelPlacement="outside-top"
|
|
33
|
+
variant="faded"
|
|
34
|
+
radius="sm"
|
|
35
|
+
value={value}
|
|
36
|
+
onChange={(date: DateValue | null) =>
|
|
37
|
+
setParams({
|
|
38
|
+
params: {
|
|
39
|
+
[paramKey]: date
|
|
40
|
+
? `${date.year}-${String(date.month).padStart(2, "0")}-${String(date.day).padStart(2, "0")}`
|
|
41
|
+
: undefined,
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
{...dateProps}
|
|
46
|
+
{...props}
|
|
47
|
+
className={cn(filterClasses({ type: "date" }), classNames?.field)}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./search-filter";
|
|
2
|
+
export * from "./select-filter";
|
|
3
|
+
export * from "./autocomplete-filter";
|
|
4
|
+
export * from "./date-filter";
|
|
5
|
+
export * from "./numeric-filter";
|
|
6
|
+
export * from "./checkbox-filter";
|
|
7
|
+
export * from "./checkbox-group-filter";
|
|
8
|
+
export * from "./switch-filter";
|
|
9
|
+
export * from "./range-filter";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { FilterProps } from "@/types";
|
|
4
|
+
import { filterClasses } from "@/components";
|
|
5
|
+
import { Input } from "@heroui/input";
|
|
6
|
+
import { cn } from "@/utils";
|
|
7
|
+
import { useSearchParamsHandler } from "@/hooks";
|
|
8
|
+
|
|
9
|
+
export function NumericFilter({
|
|
10
|
+
classNames,
|
|
11
|
+
numericProps,
|
|
12
|
+
paramKey,
|
|
13
|
+
...props
|
|
14
|
+
}: Pick<FilterProps, "classNames" | "numericProps" | "paramKey">) {
|
|
15
|
+
const { getParam, setParams } = useSearchParamsHandler();
|
|
16
|
+
|
|
17
|
+
const value = getParam(paramKey);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Input
|
|
21
|
+
type="number"
|
|
22
|
+
labelPlacement="outside-top"
|
|
23
|
+
variant="faded"
|
|
24
|
+
radius="sm"
|
|
25
|
+
value={value ?? ""}
|
|
26
|
+
onValueChange={(v) =>
|
|
27
|
+
setParams({
|
|
28
|
+
params: { [paramKey]: v || undefined },
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
{...numericProps}
|
|
32
|
+
{...props}
|
|
33
|
+
className={cn(filterClasses({ type: "numeric" }), classNames?.field)}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { FilterProps } from "@/types";
|
|
4
|
+
import { filterClasses } from "@/components";
|
|
5
|
+
import { Slider } from "@heroui/slider";
|
|
6
|
+
import { cn } from "@/utils";
|
|
7
|
+
import { useSearchParamsHandler } from "@/hooks";
|
|
8
|
+
|
|
9
|
+
export function RangeFilter({
|
|
10
|
+
classNames,
|
|
11
|
+
rangeProps,
|
|
12
|
+
paramKey,
|
|
13
|
+
...props
|
|
14
|
+
}: Pick<FilterProps, "classNames" | "rangeProps" | "paramKey">) {
|
|
15
|
+
const { getParam, setParams } = useSearchParamsHandler();
|
|
16
|
+
|
|
17
|
+
const valueStr = getParam(paramKey);
|
|
18
|
+
const value = valueStr != null ? Number(valueStr) : undefined;
|
|
19
|
+
const numValue = value != null && !Number.isNaN(value) ? value : undefined;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Slider
|
|
23
|
+
value={numValue}
|
|
24
|
+
onChange={(v) =>
|
|
25
|
+
setParams({
|
|
26
|
+
params: {
|
|
27
|
+
[paramKey]: v != null ? String(v) : undefined,
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
{...rangeProps}
|
|
32
|
+
{...props}
|
|
33
|
+
className={cn(filterClasses({ type: "range" }), classNames?.field)}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { FilterProps } from "@/types";
|
|
4
|
+
import { filterClasses } from "@/components";
|
|
5
|
+
import { Input } from "@heroui/input";
|
|
6
|
+
import { cn } from "@/utils";
|
|
7
|
+
import { useEffect, useState } from "react";
|
|
8
|
+
import { useDebounce } from "use-debounce";
|
|
9
|
+
import { Search } from "lucide-react";
|
|
10
|
+
import { useSearchParamsHandler } from "@/hooks";
|
|
11
|
+
|
|
12
|
+
export function SearchFilter({
|
|
13
|
+
classNames,
|
|
14
|
+
searchProps,
|
|
15
|
+
paramKey,
|
|
16
|
+
...props
|
|
17
|
+
}: Pick<FilterProps, "classNames" | "searchProps" | "paramKey">) {
|
|
18
|
+
const [searchValue, setSearchValue] = useState(
|
|
19
|
+
searchProps?.defaultValue || "",
|
|
20
|
+
);
|
|
21
|
+
const { setParams } = useSearchParamsHandler();
|
|
22
|
+
|
|
23
|
+
const [debouncedSearchValue] = useDebounce(searchValue, 700);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
setParams({
|
|
27
|
+
params: {
|
|
28
|
+
[paramKey]: debouncedSearchValue,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
32
|
+
}, [debouncedSearchValue]);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Input
|
|
36
|
+
labelPlacement="outside-top"
|
|
37
|
+
variant="faded"
|
|
38
|
+
radius="sm"
|
|
39
|
+
endContent={<Search className="size-4" />}
|
|
40
|
+
{...searchProps}
|
|
41
|
+
{...props}
|
|
42
|
+
value={searchValue}
|
|
43
|
+
onValueChange={setSearchValue}
|
|
44
|
+
className={cn(filterClasses({ type: "search" }), classNames?.field)}
|
|
45
|
+
classNames={{
|
|
46
|
+
input: "min-w-40",
|
|
47
|
+
label: "font-semibold",
|
|
48
|
+
...searchProps?.classNames,
|
|
49
|
+
}}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { FilterProps } from "@/types";
|
|
4
|
+
import { filterClasses } from "@/components";
|
|
5
|
+
import { Select, SelectItem } from "@heroui/select";
|
|
6
|
+
import { cn } from "@/utils";
|
|
7
|
+
import { useSearchParamsHandler } from "@/hooks";
|
|
8
|
+
|
|
9
|
+
export function SelectFilter({
|
|
10
|
+
classNames,
|
|
11
|
+
selectProps,
|
|
12
|
+
paramKey,
|
|
13
|
+
options,
|
|
14
|
+
...props
|
|
15
|
+
}: Pick<FilterProps, "classNames" | "selectProps" | "paramKey" | "options">) {
|
|
16
|
+
const { getParam, setParams } = useSearchParamsHandler();
|
|
17
|
+
const key = paramKey ?? "select";
|
|
18
|
+
const value = getParam(key);
|
|
19
|
+
|
|
20
|
+
if (!options?.length)
|
|
21
|
+
throw new Error("Filter with type 'select' must have 'options' property.");
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Select
|
|
25
|
+
labelPlacement="outside-top"
|
|
26
|
+
variant="faded"
|
|
27
|
+
radius="sm"
|
|
28
|
+
selectedKeys={value ? [value] : []}
|
|
29
|
+
onSelectionChange={(keys) => {
|
|
30
|
+
const selectedKey = Array.from(keys)[0];
|
|
31
|
+
setParams({
|
|
32
|
+
params: { [key]: selectedKey ? String(selectedKey) : undefined },
|
|
33
|
+
});
|
|
34
|
+
}}
|
|
35
|
+
{...selectProps}
|
|
36
|
+
{...props}
|
|
37
|
+
className={cn(filterClasses({ type: "select" }), classNames?.field)}
|
|
38
|
+
classNames={{
|
|
39
|
+
trigger: "min-w-40",
|
|
40
|
+
label: "font-semibold",
|
|
41
|
+
...selectProps?.classNames,
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
{options.map((option) => (
|
|
45
|
+
<SelectItem key={option.key}>{option.label}</SelectItem>
|
|
46
|
+
))}
|
|
47
|
+
</Select>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { FilterProps } from "@/types";
|
|
4
|
+
import { filterClasses } from "@/components";
|
|
5
|
+
import { Switch } from "@heroui/switch";
|
|
6
|
+
import { cn } from "@/utils";
|
|
7
|
+
import { useSearchParamsHandler } from "@/hooks";
|
|
8
|
+
|
|
9
|
+
export function SwitchFilter({
|
|
10
|
+
classNames,
|
|
11
|
+
switchProps,
|
|
12
|
+
paramKey,
|
|
13
|
+
...props
|
|
14
|
+
}: Pick<FilterProps, "classNames" | "switchProps" | "paramKey">) {
|
|
15
|
+
const { getParam, setParams } = useSearchParamsHandler();
|
|
16
|
+
|
|
17
|
+
const value = getParam(paramKey);
|
|
18
|
+
const isSelected = value === "true" || value === "1" || value === "yes";
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Switch
|
|
22
|
+
isSelected={isSelected}
|
|
23
|
+
onValueChange={(selected) =>
|
|
24
|
+
setParams({
|
|
25
|
+
params: { [paramKey]: selected ? "true" : undefined },
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
{...switchProps}
|
|
29
|
+
{...props}
|
|
30
|
+
className={cn(filterClasses({ type: "switch" }), classNames?.field)}
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -4,3 +4,8 @@ export * from "./container";
|
|
|
4
4
|
export * from "./row";
|
|
5
5
|
export * from "./portal";
|
|
6
6
|
export * from "./uploader";
|
|
7
|
+
export * from "./filter";
|
|
8
|
+
export * from "./field";
|
|
9
|
+
export * from "./theme-switch";
|
|
10
|
+
export * from "./color-picker";
|
|
11
|
+
export * from "./copy-button";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./theme-switch";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { cva } from "class-variance-authority";
|
|
2
|
+
|
|
3
|
+
export const themeSwitcherClasses = cva(
|
|
4
|
+
[
|
|
5
|
+
"flex items-center justify-center size-9 rounded-lg transition-all duration-300 text-foreground/80 cursor-pointer",
|
|
6
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 ",
|
|
7
|
+
"hover:bg-default-100 hover:text-foreground",
|
|
8
|
+
],
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {},
|
|
12
|
+
},
|
|
13
|
+
defaultVariants: {},
|
|
14
|
+
compoundVariants: [],
|
|
15
|
+
},
|
|
16
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./classes";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { ThemeSwitch } from "@/components";
|
|
3
|
+
import { getStorybookDecorators } from "@/utils";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof ThemeSwitch> = {
|
|
6
|
+
title: "Atoms/ThemeSwitch",
|
|
7
|
+
component: ThemeSwitch,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: "fullscreen",
|
|
10
|
+
},
|
|
11
|
+
tags: ["autodocs"],
|
|
12
|
+
decorators: getStorybookDecorators({}),
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default meta;
|
|
16
|
+
|
|
17
|
+
type Story = StoryObj<typeof meta>;
|
|
18
|
+
|
|
19
|
+
export const Default: Story = {
|
|
20
|
+
args: {},
|
|
21
|
+
render: () => (
|
|
22
|
+
<div className="flex justify-center items-center w-screen h-screen">
|
|
23
|
+
<ThemeSwitch className="m-auto" />
|
|
24
|
+
</div>
|
|
25
|
+
),
|
|
26
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { cn } from "@/utils";
|
|
2
|
+
import { themeSwitcherClasses } from "./styles";
|
|
3
|
+
import { ThemeSwitchProps } from "@/types";
|
|
4
|
+
import { useTheme } from "next-themes";
|
|
5
|
+
import { MoonIcon, SunIcon } from "lucide-react";
|
|
6
|
+
import { motion, AnimatePresence } from "motion/react";
|
|
7
|
+
|
|
8
|
+
export function ThemeSwitch({
|
|
9
|
+
className,
|
|
10
|
+
classNames,
|
|
11
|
+
...props
|
|
12
|
+
}: ThemeSwitchProps) {
|
|
13
|
+
const { setTheme, resolvedTheme } = useTheme();
|
|
14
|
+
|
|
15
|
+
const handleThemeToggle = () => {
|
|
16
|
+
setTheme(resolvedTheme === "dark" ? "light" : "dark");
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<motion.button
|
|
21
|
+
data-slot="emperor-ui-theme-knob"
|
|
22
|
+
className={cn(themeSwitcherClasses(), className, classNames?.base)}
|
|
23
|
+
onClick={handleThemeToggle}
|
|
24
|
+
initial={false}
|
|
25
|
+
whileHover={{ scale: 1.08 }}
|
|
26
|
+
whileTap={{ scale: 0.92 }}
|
|
27
|
+
transition={{
|
|
28
|
+
type: "spring" as const,
|
|
29
|
+
stiffness: 500,
|
|
30
|
+
damping: 28,
|
|
31
|
+
}}
|
|
32
|
+
{...props}
|
|
33
|
+
>
|
|
34
|
+
<span className="relative flex size-5 items-center justify-center">
|
|
35
|
+
<AnimatePresence mode="wait">
|
|
36
|
+
<motion.span
|
|
37
|
+
key={resolvedTheme ?? "loading"}
|
|
38
|
+
initial={{ opacity: 0 }}
|
|
39
|
+
animate={{ opacity: 1 }}
|
|
40
|
+
exit={{ opacity: 0 }}
|
|
41
|
+
transition={{ duration: 0.2 }}
|
|
42
|
+
className="absolute inset-0 flex items-center justify-center"
|
|
43
|
+
>
|
|
44
|
+
{resolvedTheme === "dark" ? (
|
|
45
|
+
<SunIcon className="size-5" />
|
|
46
|
+
) : (
|
|
47
|
+
<MoonIcon className="size-5" />
|
|
48
|
+
)}
|
|
49
|
+
</motion.span>
|
|
50
|
+
</AnimatePresence>
|
|
51
|
+
</span>
|
|
52
|
+
</motion.button>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { Placeholders } from "@/enums";
|
|
4
|
-
import { Avatar
|
|
4
|
+
import { Avatar } from "@heroui/avatar";
|
|
5
|
+
import { Spinner } from "@heroui/spinner";
|
|
6
|
+
import { cn } from "@/utils";
|
|
5
7
|
import { useEmperorUI, useUploaderContext } from "@/hooks";
|
|
6
8
|
import { useState } from "react";
|
|
7
9
|
|
|
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
|
2
2
|
import { Uploader } from "@/components";
|
|
3
3
|
import { getStorybookDecorators } from "@/utils";
|
|
4
4
|
import { useUploader } from "@/hooks";
|
|
5
|
-
import { useDisclosure } from "@heroui/
|
|
5
|
+
import { useDisclosure } from "@heroui/modal";
|
|
6
6
|
import { LangKey } from "@/i18n";
|
|
7
7
|
|
|
8
8
|
const meta: Meta<typeof Uploader> = {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { Spinner
|
|
3
|
+
import { Spinner } from "@heroui/spinner";
|
|
4
|
+
import { cn } from "@/utils";
|
|
4
5
|
import { useEmperorUI, useUploaderContext } from "@/hooks";
|
|
5
6
|
import { UploadCloud } from "lucide-react";
|
|
6
7
|
import { useState } from "react";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { Image
|
|
3
|
+
import { Image } from "@heroui/image";
|
|
4
|
+
import { Modal, ModalBody, ModalContent } from "@heroui/modal";
|
|
4
5
|
import { useEmperorUI, useUploaderContext } from "@/hooks";
|
|
5
6
|
|
|
6
7
|
export function ViewImageModal() {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { ItemCardProps } from "@/types";
|
|
4
|
+
import { cn } from "@/utils";
|
|
5
|
+
import { Button } from "@heroui/button";
|
|
6
|
+
import {
|
|
7
|
+
Dropdown,
|
|
8
|
+
DropdownTrigger,
|
|
9
|
+
DropdownMenu,
|
|
10
|
+
DropdownItem,
|
|
11
|
+
} from "@heroui/dropdown";
|
|
12
|
+
import { EllipsisVertical } from "lucide-react";
|
|
13
|
+
|
|
14
|
+
export function ItemActionsDropdown({
|
|
15
|
+
actions,
|
|
16
|
+
classNames,
|
|
17
|
+
onActionClick,
|
|
18
|
+
}: Pick<ItemCardProps, "actions" | "classNames" | "onActionClick">) {
|
|
19
|
+
if (!actions || actions?.length === 0) return null;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Dropdown placement="bottom-end">
|
|
23
|
+
<DropdownTrigger data-slot="emperor-ui-item-card-actions-dropdown-trigger">
|
|
24
|
+
<Button
|
|
25
|
+
isIconOnly
|
|
26
|
+
size="sm"
|
|
27
|
+
variant="flat"
|
|
28
|
+
radius="full"
|
|
29
|
+
className={cn(
|
|
30
|
+
"absolute right-2 top-2 z-10 size-7 min-w-7 backdrop-blur-lg",
|
|
31
|
+
"border border-default-300",
|
|
32
|
+
classNames?.dropdown,
|
|
33
|
+
)}
|
|
34
|
+
>
|
|
35
|
+
<EllipsisVertical className="size-4" />
|
|
36
|
+
</Button>
|
|
37
|
+
</DropdownTrigger>
|
|
38
|
+
|
|
39
|
+
<DropdownMenu
|
|
40
|
+
data-slot="emperor-ui-item-card-actions-dropdown-menu"
|
|
41
|
+
aria-label="Card actions"
|
|
42
|
+
onAction={(key) => onActionClick?.(String(key))}
|
|
43
|
+
items={actions}
|
|
44
|
+
>
|
|
45
|
+
{({ key, label, ...props }) => (
|
|
46
|
+
<DropdownItem
|
|
47
|
+
key={key}
|
|
48
|
+
data-slot="emperor-ui-item-card-actions-dropdown-item"
|
|
49
|
+
textValue={label}
|
|
50
|
+
title={label}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
)}
|
|
54
|
+
</DropdownMenu>
|
|
55
|
+
</Dropdown>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { ItemCardProps } from "@/types";
|
|
4
|
+
import { cn } from "@/utils";
|
|
5
|
+
import { itemBannerClasses } from "./styles";
|
|
6
|
+
|
|
7
|
+
export function ItemBanner({
|
|
8
|
+
item,
|
|
9
|
+
orientation,
|
|
10
|
+
classNames,
|
|
11
|
+
}: Pick<ItemCardProps, "item" | "orientation" | "classNames">) {
|
|
12
|
+
if (!item?.banner) return null;
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
data-slot="emperor-ui-item-card-banner"
|
|
17
|
+
className={cn(itemBannerClasses({ orientation }), classNames?.banner)}
|
|
18
|
+
>
|
|
19
|
+
{item?.banner}
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
}
|