pixedi 1.0.0 → 1.0.2
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/README.md +65 -60
- package/README.npm.md +80 -0
- package/dist/lib/index.js +1602 -0
- package/dist/lib/index.umd.js +28 -0
- package/package.json +9 -4
- package/.github/workflows/deploy.yml +0 -54
- package/.prettierr +0 -9
- package/.storybook/main.ts +0 -16
- package/.storybook/preview.ts +0 -53
- package/.vscode/extensions.json +0 -6
- package/eslint.config.js +0 -22
- package/index.html +0 -13
- package/pnpm-workspace.yaml +0 -2
- package/public/favicon.svg +0 -1
- package/src/app/App.tsx +0 -10
- package/src/app/entries/index.ts +0 -145
- package/src/app/providers/RouterProvider.tsx +0 -6
- package/src/app/providers/index.ts +0 -1
- package/src/app/router/AppRouter.tsx +0 -12
- package/src/app/router/index.ts +0 -0
- package/src/app/styles/index.css +0 -4
- package/src/features/gallery/GalleryPage.tsx +0 -33
- package/src/features/gallery/ImageEditPage.tsx +0 -37
- package/src/features/gallery/index.ts +0 -2
- package/src/main.tsx +0 -10
- package/src/shared/components/ImageCard/ImageCard.tsx +0 -54
- package/src/shared/components/ImageCard/index.ts +0 -1
- package/src/shared/components/ImageEditor/ImageEditor.tsx +0 -48
- package/src/shared/components/ImageEditor/assets/icons.tsx +0 -114
- package/src/shared/components/ImageEditor/constants.ts +0 -118
- package/src/shared/components/ImageEditor/crop/Crop.tsx +0 -60
- package/src/shared/components/ImageEditor/crop/CropBorders.tsx +0 -28
- package/src/shared/components/ImageEditor/crop/CropLines.tsx +0 -8
- package/src/shared/components/ImageEditor/crop/CropPointer.tsx +0 -21
- package/src/shared/components/ImageEditor/crop/CropPointers.tsx +0 -44
- package/src/shared/components/ImageEditor/crop/CropTools.tsx +0 -138
- package/src/shared/components/ImageEditor/crop/crop.css +0 -180
- package/src/shared/components/ImageEditor/crop/index.ts +0 -8
- package/src/shared/components/ImageEditor/crop/useCropInteraction.ts +0 -230
- package/src/shared/components/ImageEditor/eventBus.ts +0 -8
- package/src/shared/components/ImageEditor/frame/Frame.tsx +0 -48
- package/src/shared/components/ImageEditor/frame/frame.css +0 -40
- package/src/shared/components/ImageEditor/frame/index.ts +0 -1
- package/src/shared/components/ImageEditor/header/Header.tsx +0 -81
- package/src/shared/components/ImageEditor/header/header.css +0 -27
- package/src/shared/components/ImageEditor/header/index.ts +0 -1
- package/src/shared/components/ImageEditor/hooks/index.ts +0 -5
- package/src/shared/components/ImageEditor/hooks/useAbove.tsx +0 -18
- package/src/shared/components/ImageEditor/hooks/useBellow.tsx +0 -18
- package/src/shared/components/ImageEditor/hooks/useImageLoader.tsx +0 -110
- package/src/shared/components/ImageEditor/hooks/useImageProcessor.test.tsx +0 -147
- package/src/shared/components/ImageEditor/hooks/useImageProcessor.ts +0 -225
- package/src/shared/components/ImageEditor/hooks/useMobile.tsx +0 -33
- package/src/shared/components/ImageEditor/index.css +0 -145
- package/src/shared/components/ImageEditor/index.ts +0 -1
- package/src/shared/components/ImageEditor/provider/ImageEditorProvider.test.tsx +0 -126
- package/src/shared/components/ImageEditor/provider/ImageEditorProvider.tsx +0 -107
- package/src/shared/components/ImageEditor/provider/StoreContext.tsx +0 -20
- package/src/shared/components/ImageEditor/provider/initialState.ts +0 -30
- package/src/shared/components/ImageEditor/provider/useImageEditorContext.tsx +0 -12
- package/src/shared/components/ImageEditor/resize/ResizeTools.tsx +0 -183
- package/src/shared/components/ImageEditor/resize/index.ts +0 -1
- package/src/shared/components/ImageEditor/resize/resize.css +0 -56
- package/src/shared/components/ImageEditor/sidebar/Sidebar.tsx +0 -54
- package/src/shared/components/ImageEditor/sidebar/SidebarCrop.tsx +0 -57
- package/src/shared/components/ImageEditor/sidebar/SidebarPresets.tsx +0 -54
- package/src/shared/components/ImageEditor/sidebar/SidebarResize.tsx +0 -26
- package/src/shared/components/ImageEditor/sidebar/index.ts +0 -1
- package/src/shared/components/ImageEditor/sidebar/sidebar.css +0 -101
- package/src/shared/components/ImageEditor/types.ts +0 -59
- package/src/shared/components/ImageEditor/ui/button/Button.stories.tsx +0 -42
- package/src/shared/components/ImageEditor/ui/button/Button.tsx +0 -21
- package/src/shared/components/ImageEditor/ui/button/button.css +0 -69
- package/src/shared/components/ImageEditor/ui/button-crop/ButtonCrop.stories.tsx +0 -26
- package/src/shared/components/ImageEditor/ui/button-crop/ButtonCrop.tsx +0 -29
- package/src/shared/components/ImageEditor/ui/button-crop/button-crop.css +0 -38
- package/src/shared/components/ImageEditor/ui/index.ts +0 -6
- package/src/shared/components/ImageEditor/ui/input/Input.stories.tsx +0 -45
- package/src/shared/components/ImageEditor/ui/input/Input.tsx +0 -20
- package/src/shared/components/ImageEditor/ui/input/input.css +0 -48
- package/src/shared/components/ImageEditor/ui/input-pixel/InputPixel.stories.tsx +0 -33
- package/src/shared/components/ImageEditor/ui/input-pixel/InputPixel.tsx +0 -23
- package/src/shared/components/ImageEditor/ui/input-pixel/input-pixel.css +0 -24
- package/src/shared/components/ImageEditor/ui/select/Select.stories.tsx +0 -67
- package/src/shared/components/ImageEditor/ui/select/Select.tsx +0 -161
- package/src/shared/components/ImageEditor/ui/select/select.css +0 -147
- package/src/shared/components/ImageEditor/ui/separator/Separator.stories.tsx +0 -37
- package/src/shared/components/ImageEditor/ui/separator/Separator.tsx +0 -27
- package/src/shared/components/ImageEditor/ui/separator/separator.css +0 -19
- package/src/shared/components/ImageEditor/utils.test.ts +0 -136
- package/src/shared/components/ImageEditor/utils.ts +0 -497
- package/src/shared/components/ui/index.ts +0 -1
- package/src/shared/components/ui/preloader.tsx +0 -78
- package/src/shared/config/index.ts +0 -3
- package/src/shared/config/routes.ts +0 -4
- package/src/shared/features/gallery/GalleryPage.tsx +0 -33
- package/src/shared/features/gallery/ImageEditPage.tsx +0 -37
- package/src/shared/features/gallery/index.ts +0 -2
- package/src/shared/hooks/index.ts +0 -1
- package/src/shared/hooks/useImagePreload.tsx +0 -49
- package/src/shared/types/index.ts +0 -11
- package/src/test/setup.ts +0 -14
- package/src/virtual-css-injected-by-js.d.ts +0 -8
- package/src/widget.tsx +0 -119
- package/tsconfig.app.json +0 -29
- package/tsconfig.json +0 -12
- package/tsconfig.lib.json +0 -18
- package/tsconfig.node.json +0 -23
- package/vite-env.d.ts +0 -1
- package/vite.config.ts +0 -24
- package/vite.lib.config.ts +0 -33
- package/vite.widget.config.ts +0 -39
- package/wrangler.json +0 -5
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
.input {
|
|
2
|
-
display: block;
|
|
3
|
-
width: 100%;
|
|
4
|
-
height: 32px;
|
|
5
|
-
padding-left: 8px;
|
|
6
|
-
padding-right: 8px;
|
|
7
|
-
font-family: inherit;
|
|
8
|
-
font-size: 14px;
|
|
9
|
-
line-height: normal;
|
|
10
|
-
color: var(--foreground);
|
|
11
|
-
background-color: var(--background);
|
|
12
|
-
border: 1px solid var(--input);
|
|
13
|
-
border-radius: 4px;
|
|
14
|
-
box-sizing: border-box;
|
|
15
|
-
outline: none;
|
|
16
|
-
transition:
|
|
17
|
-
border-color 0.2s,
|
|
18
|
-
box-shadow 0.2s;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.input:hover:not(:disabled):not(:focus) {
|
|
22
|
-
border-color: var(--muted-foreground);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.input:focus {
|
|
26
|
-
box-shadow: 0 0 0 1px var(--ring);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
.input:disabled {
|
|
30
|
-
cursor: not-allowed;
|
|
31
|
-
opacity: 0.5;
|
|
32
|
-
background-color: var(--secondary);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.input::placeholder {
|
|
36
|
-
color: var(--muted-foreground);
|
|
37
|
-
opacity: 1;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.input-number-clean::-webkit-outer-spin-button,
|
|
41
|
-
.input-number-clean::-webkit-inner-spin-button {
|
|
42
|
-
-webkit-appearance: none;
|
|
43
|
-
margin: 0;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.input-number-clean[type="number"] {
|
|
47
|
-
-moz-appearance: textfield;
|
|
48
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { fn } from "@storybook/test";
|
|
3
|
-
import { InputPixel } from "./InputPixel";
|
|
4
|
-
|
|
5
|
-
const meta = {
|
|
6
|
-
title: "ImageEditor/UI/InputPixel",
|
|
7
|
-
component: InputPixel,
|
|
8
|
-
tags: ["autodocs"],
|
|
9
|
-
args: {
|
|
10
|
-
label: "Width",
|
|
11
|
-
value: 120,
|
|
12
|
-
onChange: fn(),
|
|
13
|
-
},
|
|
14
|
-
decorators: [
|
|
15
|
-
(Story) => (
|
|
16
|
-
<div style={{ maxWidth: 320 }}>
|
|
17
|
-
<Story />
|
|
18
|
-
</div>
|
|
19
|
-
),
|
|
20
|
-
],
|
|
21
|
-
} satisfies Meta<typeof InputPixel>;
|
|
22
|
-
|
|
23
|
-
export default meta;
|
|
24
|
-
type Story = StoryObj<typeof meta>;
|
|
25
|
-
|
|
26
|
-
export const Default: Story = {};
|
|
27
|
-
|
|
28
|
-
export const WithValue: Story = {
|
|
29
|
-
args: {
|
|
30
|
-
label: "Height",
|
|
31
|
-
value: 240,
|
|
32
|
-
},
|
|
33
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { forwardRef } from "react";
|
|
2
|
-
import "./input-pixel.css";
|
|
3
|
-
import { Input } from "../../ui";
|
|
4
|
-
|
|
5
|
-
interface InputPixelProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
6
|
-
label: string;
|
|
7
|
-
className?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const InputPixel = forwardRef<HTMLInputElement, InputPixelProps>(
|
|
11
|
-
({ label, className = "", style, ...props }, ref) => {
|
|
12
|
-
return (
|
|
13
|
-
<div
|
|
14
|
-
className={`input-pixel-container ${className}`.trim()}
|
|
15
|
-
style={style}
|
|
16
|
-
>
|
|
17
|
-
<span className="input-pixel-legend">{label}</span>
|
|
18
|
-
<Input ref={ref} type="number" {...props} />
|
|
19
|
-
<span className="input-pixel-suffix">px</span>
|
|
20
|
-
</div>
|
|
21
|
-
);
|
|
22
|
-
},
|
|
23
|
-
);
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
.input-pixel-container {
|
|
2
|
-
position: relative;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
.input-pixel-legend {
|
|
6
|
-
position: absolute;
|
|
7
|
-
top: -9px;
|
|
8
|
-
font-size: 12px;
|
|
9
|
-
color: var(--muted-foreground);
|
|
10
|
-
padding-left: 4px;
|
|
11
|
-
padding-right: 4px;
|
|
12
|
-
margin-left: 8px;
|
|
13
|
-
background: var(--background);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.input-pixel-suffix {
|
|
17
|
-
position: absolute;
|
|
18
|
-
right: 8px;
|
|
19
|
-
bottom: 2px;
|
|
20
|
-
font-size: 14px;
|
|
21
|
-
color: var(--muted-foreground);
|
|
22
|
-
pointer-events: none;
|
|
23
|
-
user-select: none;
|
|
24
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { fn } from "@storybook/test";
|
|
3
|
-
import { Select, type SelectOption } from "./Select";
|
|
4
|
-
|
|
5
|
-
const items = [
|
|
6
|
-
{ value: "1", label: "Small" },
|
|
7
|
-
{ value: "2", label: "Medium" },
|
|
8
|
-
{ value: "3", label: "Large" },
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
const groupedItems = [
|
|
12
|
-
{
|
|
13
|
-
label: "Sizes",
|
|
14
|
-
options: [
|
|
15
|
-
{ value: "s", label: "Small", rightLabel: "S" },
|
|
16
|
-
{ value: "m", label: "Medium", rightLabel: "M" },
|
|
17
|
-
{ value: "l", label: "Large", rightLabel: "L" },
|
|
18
|
-
],
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
label: "Formats",
|
|
22
|
-
options: [
|
|
23
|
-
{ value: "jpg", label: "JPEG" },
|
|
24
|
-
{ value: "png", label: "PNG" },
|
|
25
|
-
{ value: "webp", label: "WebP" },
|
|
26
|
-
],
|
|
27
|
-
},
|
|
28
|
-
] as SelectOption[];
|
|
29
|
-
|
|
30
|
-
const meta = {
|
|
31
|
-
title: "ImageEditor/UI/Select",
|
|
32
|
-
component: Select,
|
|
33
|
-
tags: ["autodocs"],
|
|
34
|
-
args: {
|
|
35
|
-
items,
|
|
36
|
-
value: "1",
|
|
37
|
-
placeholder: "Select an option",
|
|
38
|
-
onChange: fn(),
|
|
39
|
-
},
|
|
40
|
-
decorators: [
|
|
41
|
-
(Story) => (
|
|
42
|
-
<div style={{ minHeight: 300, maxWidth: 320 }}>
|
|
43
|
-
<Story />
|
|
44
|
-
</div>
|
|
45
|
-
),
|
|
46
|
-
],
|
|
47
|
-
} satisfies Meta<typeof Select>;
|
|
48
|
-
|
|
49
|
-
export default meta;
|
|
50
|
-
type Story = StoryObj<typeof meta>;
|
|
51
|
-
|
|
52
|
-
export const SingleItems: Story = {};
|
|
53
|
-
|
|
54
|
-
export const GroupedItems: Story = {
|
|
55
|
-
args: {
|
|
56
|
-
items: groupedItems,
|
|
57
|
-
value: "m",
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export const CustomRenderOption: Story = {
|
|
62
|
-
args: {
|
|
63
|
-
renderOption: (option) => (
|
|
64
|
-
<strong style={{ color: "#60a5fa" }}>{option.label}</strong>
|
|
65
|
-
),
|
|
66
|
-
},
|
|
67
|
-
};
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useState, useMemo, useLayoutEffect } from "react";
|
|
2
|
-
import { ChevronDown, Check } from "../../assets/icons";
|
|
3
|
-
import "./select.css";
|
|
4
|
-
|
|
5
|
-
export interface SelectOption {
|
|
6
|
-
value: string;
|
|
7
|
-
label: string;
|
|
8
|
-
options?: SelectOption[];
|
|
9
|
-
rightLabel?: string;
|
|
10
|
-
fullName?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface SelectProps {
|
|
14
|
-
items: SelectOption[];
|
|
15
|
-
value: string;
|
|
16
|
-
onChange: (value: string) => void;
|
|
17
|
-
placeholder?: string;
|
|
18
|
-
className?: string;
|
|
19
|
-
renderOption?: (option: SelectOption) => React.ReactNode;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const Select: React.FC<SelectProps> = ({
|
|
23
|
-
items,
|
|
24
|
-
value,
|
|
25
|
-
onChange,
|
|
26
|
-
placeholder = "Select an option",
|
|
27
|
-
className = "",
|
|
28
|
-
renderOption,
|
|
29
|
-
}) => {
|
|
30
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
31
|
-
|
|
32
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
33
|
-
const triggerRef = useRef<HTMLButtonElement>(null);
|
|
34
|
-
const contentRef = useRef<HTMLDivElement>(null);
|
|
35
|
-
|
|
36
|
-
const allOptions = useMemo(() => {
|
|
37
|
-
const flatten = (list: SelectOption[]): SelectOption[] => {
|
|
38
|
-
return list.reduce<SelectOption[]>((acc, item) => {
|
|
39
|
-
if (item.options) {
|
|
40
|
-
return [
|
|
41
|
-
...acc,
|
|
42
|
-
...flatten(
|
|
43
|
-
item.options.map((option) => ({
|
|
44
|
-
...option,
|
|
45
|
-
fullName: `${item.label} ${option.label}`,
|
|
46
|
-
})),
|
|
47
|
-
),
|
|
48
|
-
];
|
|
49
|
-
}
|
|
50
|
-
return [...acc, item];
|
|
51
|
-
}, []);
|
|
52
|
-
};
|
|
53
|
-
return flatten(items);
|
|
54
|
-
}, [items]);
|
|
55
|
-
|
|
56
|
-
const selectedOption = allOptions.find((opt) => opt.value === value);
|
|
57
|
-
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
60
|
-
if (
|
|
61
|
-
containerRef.current &&
|
|
62
|
-
!containerRef.current.contains(event.target as Node)
|
|
63
|
-
) {
|
|
64
|
-
setIsOpen(false);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
68
|
-
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
69
|
-
}, []);
|
|
70
|
-
|
|
71
|
-
useLayoutEffect(() => {
|
|
72
|
-
if (!isOpen || !triggerRef.current || !contentRef.current) return;
|
|
73
|
-
|
|
74
|
-
const editorRect = containerRef.current
|
|
75
|
-
?.closest<HTMLElement>(".picedi.main")
|
|
76
|
-
?.getBoundingClientRect();
|
|
77
|
-
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
78
|
-
const contentRect = contentRef.current.getBoundingClientRect();
|
|
79
|
-
const availableBottom = (editorRect?.bottom ?? window.innerHeight) - 16;
|
|
80
|
-
const contentBottom = triggerRect.bottom + contentRect.height + 4;
|
|
81
|
-
|
|
82
|
-
let contentTop = "calc(100% + 4px)";
|
|
83
|
-
if (contentBottom > availableBottom) {
|
|
84
|
-
const diff = contentBottom - availableBottom;
|
|
85
|
-
contentTop = `calc(100% + 4px - ${diff}px)`;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
contentRef.current.style.top = contentTop;
|
|
89
|
-
}, [isOpen, items]);
|
|
90
|
-
|
|
91
|
-
const handleSelectItem = (val: string) => {
|
|
92
|
-
onChange(val);
|
|
93
|
-
setIsOpen(false);
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
return (
|
|
97
|
-
<div
|
|
98
|
-
ref={containerRef}
|
|
99
|
-
className={`select-wrapper ${className}`.trim()}
|
|
100
|
-
data-state={isOpen ? "open" : "closed"}
|
|
101
|
-
>
|
|
102
|
-
<button
|
|
103
|
-
ref={triggerRef}
|
|
104
|
-
type="button"
|
|
105
|
-
className="select-trigger"
|
|
106
|
-
onClick={() => setIsOpen(!isOpen)}
|
|
107
|
-
>
|
|
108
|
-
<span>{selectedOption ? selectedOption.fullName : placeholder}</span>
|
|
109
|
-
<ChevronDown className="select-trigger-arrow" />
|
|
110
|
-
</button>
|
|
111
|
-
|
|
112
|
-
<div ref={contentRef} className="select-content">
|
|
113
|
-
{items.map((item, index) => {
|
|
114
|
-
if (item.options) {
|
|
115
|
-
return (
|
|
116
|
-
<div key={`group-${index}`}>
|
|
117
|
-
{/* Group Name */}
|
|
118
|
-
<div className="select-group-label semibold">{item.label}</div>
|
|
119
|
-
{item.options.map((option) => (
|
|
120
|
-
<div
|
|
121
|
-
key={option.value}
|
|
122
|
-
className="select-item"
|
|
123
|
-
onClick={() => handleSelectItem(option.value)}
|
|
124
|
-
>
|
|
125
|
-
{renderOption ? (
|
|
126
|
-
renderOption(option)
|
|
127
|
-
) : (
|
|
128
|
-
<span className="select-item-left">{option.label}</span>
|
|
129
|
-
)}
|
|
130
|
-
<div className="select-item-addon">
|
|
131
|
-
{option.rightLabel && <span>{option.rightLabel}</span>}
|
|
132
|
-
{value === option.value && (
|
|
133
|
-
<Check className="select-item-check" />
|
|
134
|
-
)}
|
|
135
|
-
{value !== option.value && <b />}
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
))}
|
|
139
|
-
</div>
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return (
|
|
144
|
-
<div
|
|
145
|
-
key={item.value}
|
|
146
|
-
className="select-item"
|
|
147
|
-
onClick={() => handleSelectItem(item.value)}
|
|
148
|
-
>
|
|
149
|
-
{renderOption ? (
|
|
150
|
-
renderOption(item)
|
|
151
|
-
) : (
|
|
152
|
-
<span className="select-item-left">{item.label}1</span>
|
|
153
|
-
)}
|
|
154
|
-
{value === item.value && <Check className="select-item-check" />}
|
|
155
|
-
</div>
|
|
156
|
-
);
|
|
157
|
-
})}
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
);
|
|
161
|
-
};
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
.select-wrapper {
|
|
2
|
-
position: relative;
|
|
3
|
-
width: 100%;
|
|
4
|
-
box-sizing: border-box;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
.select-trigger {
|
|
8
|
-
width: 100%;
|
|
9
|
-
height: 32px;
|
|
10
|
-
padding-left: 10px;
|
|
11
|
-
padding-right: 8px;
|
|
12
|
-
font-family: inherit;
|
|
13
|
-
font-size: 14px;
|
|
14
|
-
color: var(--foreground);
|
|
15
|
-
background-color: var(--background);
|
|
16
|
-
border: 1px solid var(--border);
|
|
17
|
-
border-radius: 4px;
|
|
18
|
-
cursor: pointer;
|
|
19
|
-
box-sizing: border-box;
|
|
20
|
-
display: flex;
|
|
21
|
-
align-items: center;
|
|
22
|
-
justify-content: space-between;
|
|
23
|
-
outline: none;
|
|
24
|
-
user-select: none;
|
|
25
|
-
transition: border-color 0.2s;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.select-trigger:hover,
|
|
29
|
-
.select-trigger:focus {
|
|
30
|
-
border-color: var(--muted-foreground);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.select-trigger-arrow {
|
|
34
|
-
width: 16px;
|
|
35
|
-
height: 16px;
|
|
36
|
-
color: var(--muted-foreground);
|
|
37
|
-
transition: transform 0.2s;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.select-wrapper[data-state="open"] .select-trigger-arrow {
|
|
41
|
-
transform: rotate(180deg);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.select-content {
|
|
45
|
-
position: absolute;
|
|
46
|
-
top: calc(100% + 4px);
|
|
47
|
-
left: 0;
|
|
48
|
-
width: 100%;
|
|
49
|
-
max-height: 282px;
|
|
50
|
-
overflow-y: auto;
|
|
51
|
-
background-color: var(--background);
|
|
52
|
-
border: 1px solid var(--border);
|
|
53
|
-
border-radius: 4px;
|
|
54
|
-
box-shadow:
|
|
55
|
-
0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
|
56
|
-
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
57
|
-
z-index: 50;
|
|
58
|
-
box-sizing: border-box;
|
|
59
|
-
padding: 4px;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.select-content::-webkit-scrollbar {
|
|
63
|
-
width: 4px;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.select-content::-webkit-scrollbar-track {
|
|
67
|
-
background: transparent;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.select-content::-webkit-scrollbar-thumb {
|
|
71
|
-
background-color: var(--border);
|
|
72
|
-
border-radius: 10px;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
.select-wrapper[data-state="closed"] .select-content {
|
|
76
|
-
display: none;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.select-group-label {
|
|
80
|
-
font-size: 12px;
|
|
81
|
-
font-weight: 500;
|
|
82
|
-
color: var(--muted-foreground);
|
|
83
|
-
padding: 8px 10px;
|
|
84
|
-
user-select: none;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
.select-item {
|
|
88
|
-
display: flex;
|
|
89
|
-
align-items: center;
|
|
90
|
-
justify-content: space-between;
|
|
91
|
-
width: 100%;
|
|
92
|
-
padding: 4px 10px;
|
|
93
|
-
color: var(--foreground);
|
|
94
|
-
border-radius: 4px;
|
|
95
|
-
cursor: pointer;
|
|
96
|
-
user-select: none;
|
|
97
|
-
box-sizing: border-box;
|
|
98
|
-
background: transparent;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.select-item:hover {
|
|
102
|
-
background-color: var(--secondary);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
.select-item-left {
|
|
106
|
-
font-size: 14px;
|
|
107
|
-
font-weight: 400;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.select-item-addon {
|
|
111
|
-
display: flex;
|
|
112
|
-
align-items: center;
|
|
113
|
-
gap: 8px;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
.select-item-addon > span {
|
|
117
|
-
font-size: 12px;
|
|
118
|
-
color: var(--muted-foreground);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
.select-item-addon > svg {
|
|
122
|
-
font-size: 14px;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.select-item-addon > b {
|
|
126
|
-
width: 16px;
|
|
127
|
-
height: 16px;
|
|
128
|
-
flex-shrink: 0;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
.select-item-right-container {
|
|
132
|
-
display: flex;
|
|
133
|
-
align-items: center;
|
|
134
|
-
gap: 8px;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.select-item-right {
|
|
138
|
-
font-size: 14px;
|
|
139
|
-
color: var(--muted-foreground);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
.select-item-check {
|
|
143
|
-
width: 16px;
|
|
144
|
-
height: 16px;
|
|
145
|
-
color: var(--foreground);
|
|
146
|
-
flex-shrink: 0;
|
|
147
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { Separator } from "./Separator";
|
|
3
|
-
|
|
4
|
-
const meta = {
|
|
5
|
-
title: "ImageEditor/UI/Separator",
|
|
6
|
-
component: Separator,
|
|
7
|
-
tags: ["autodocs"],
|
|
8
|
-
} satisfies Meta<typeof Separator>;
|
|
9
|
-
|
|
10
|
-
export default meta;
|
|
11
|
-
type Story = StoryObj<typeof meta>;
|
|
12
|
-
|
|
13
|
-
export const Horizontal: Story = {
|
|
14
|
-
args: {
|
|
15
|
-
orientation: "horizontal",
|
|
16
|
-
},
|
|
17
|
-
decorators: [
|
|
18
|
-
(Story) => (
|
|
19
|
-
<div style={{ width: 200 }}>
|
|
20
|
-
<Story />
|
|
21
|
-
</div>
|
|
22
|
-
),
|
|
23
|
-
],
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export const Vertical: Story = {
|
|
27
|
-
args: {
|
|
28
|
-
orientation: "vertical",
|
|
29
|
-
},
|
|
30
|
-
decorators: [
|
|
31
|
-
(Story) => (
|
|
32
|
-
<div style={{ height: 100, display: "flex" }}>
|
|
33
|
-
<Story />
|
|
34
|
-
</div>
|
|
35
|
-
),
|
|
36
|
-
],
|
|
37
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { forwardRef } from "react";
|
|
2
|
-
import "./separator.css";
|
|
3
|
-
|
|
4
|
-
interface SeparatorProps extends React.HTMLAttributes<HTMLHRElement> {
|
|
5
|
-
orientation?: "horizontal" | "vertical";
|
|
6
|
-
className?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const Separator = forwardRef<HTMLHRElement, SeparatorProps>(
|
|
10
|
-
({ className = "", orientation = "horizontal", ...props }, ref) => {
|
|
11
|
-
// Сглобяваме чистите класове
|
|
12
|
-
const combinedClasses = `separator ${className}`.trim();
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<hr
|
|
16
|
-
ref={ref}
|
|
17
|
-
role="separator"
|
|
18
|
-
aria-orientation={orientation}
|
|
19
|
-
data-orientation={orientation}
|
|
20
|
-
className={combinedClasses}
|
|
21
|
-
{...props}
|
|
22
|
-
/>
|
|
23
|
-
);
|
|
24
|
-
},
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
Separator.displayName = "Separator";
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
.separator {
|
|
2
|
-
display: block;
|
|
3
|
-
flex-shrink: 0;
|
|
4
|
-
background-color: var(--border);
|
|
5
|
-
border: none;
|
|
6
|
-
box-sizing: border-box;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
.separator[data-orientation="horizontal"] {
|
|
10
|
-
height: 1px;
|
|
11
|
-
width: 100%;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
.separator[data-orientation="vertical"] {
|
|
15
|
-
width: 1px;
|
|
16
|
-
height: 100%;
|
|
17
|
-
align-self: stretch;
|
|
18
|
-
margin: 0;
|
|
19
|
-
}
|