@noya-app/noya-designsystem 0.1.49 → 0.1.50
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +11 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +137 -12
- package/dist/index.d.ts +137 -12
- package/dist/index.js +1627 -1129
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1582 -1102
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/src/components/Avatar.tsx +2 -2
- package/src/components/Banner.tsx +29 -0
- package/src/components/Button.tsx +2 -2
- package/src/components/Chip.tsx +8 -8
- package/src/components/Collection.tsx +6 -1
- package/src/components/Dialog.tsx +2 -2
- package/src/components/FillInputField.tsx +1 -1
- package/src/components/Grid.tsx +1 -3
- package/src/components/Label.tsx +11 -6
- package/src/components/LabeledField.tsx +6 -1
- package/src/components/List.tsx +4 -4
- package/src/components/ListView.tsx +10 -5
- package/src/components/MediaThumbnail.tsx +11 -3
- package/src/components/Popover.tsx +9 -6
- package/src/components/Section.tsx +172 -0
- package/src/components/SelectMenu.tsx +1 -1
- package/src/components/Slider.tsx +1 -1
- package/src/components/Sortable.tsx +124 -22
- package/src/components/UserPointer.tsx +1 -1
- package/src/components/ai-assistant/AIAssistantLayout.tsx +102 -0
- package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +71 -0
- package/src/components/file-explorer/FileExplorerLayout.tsx +71 -0
- package/src/components/internal/Menu.tsx +12 -7
- package/src/components/pipeline/PipelineResultLayout.tsx +32 -0
- package/src/index.css +118 -118
- package/src/index.tsx +7 -0
- package/src/utils/classNames.ts +27 -2
- package/src/utils/moveTreeItem.ts +99 -0
- package/tailwind.config.ts +79 -79
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from "@dnd-kit/sortable";
|
|
20
20
|
import { memoGeneric } from "@noya-app/react-utils";
|
|
21
21
|
import * as React from "react";
|
|
22
|
+
import { memo } from "react";
|
|
22
23
|
import { createPortal } from "react-dom";
|
|
23
24
|
|
|
24
25
|
export type RelativeDropPosition = "above" | "below" | "inside";
|
|
@@ -96,42 +97,67 @@ export function validateDropIndicator({
|
|
|
96
97
|
|
|
97
98
|
const acceptsDropInside = acceptsDrop(activeIndex, overIndex, "inside");
|
|
98
99
|
|
|
99
|
-
//
|
|
100
|
-
// and dropping inside is allowed, show "inside" indicator
|
|
100
|
+
// Drop into the middle third if possible
|
|
101
101
|
if (
|
|
102
|
-
|
|
103
|
-
offsetStart <= elementStart + (elementSize * 2) / 3 &&
|
|
102
|
+
isContainedInMiddleThird(elementStart, elementSize, offsetStart) &&
|
|
104
103
|
acceptsDropInside
|
|
105
|
-
)
|
|
104
|
+
) {
|
|
106
105
|
return "inside";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const containingHalf = getContainingHalf(
|
|
109
|
+
elementStart,
|
|
110
|
+
elementSize,
|
|
111
|
+
offsetStart
|
|
112
|
+
);
|
|
107
113
|
|
|
108
|
-
//
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
// Drop above or below if possible
|
|
115
|
+
const acceptedHalf = acceptsDrop(activeIndex, overIndex, containingHalf);
|
|
116
|
+
|
|
117
|
+
return acceptedHalf
|
|
118
|
+
? containingHalf
|
|
119
|
+
: // Lastly, check if inside but not middle third
|
|
120
|
+
acceptsDropInside
|
|
115
121
|
? "inside"
|
|
116
122
|
: undefined;
|
|
117
123
|
}
|
|
118
124
|
|
|
125
|
+
function isContainedInMiddleThird(
|
|
126
|
+
elementStart: number,
|
|
127
|
+
elementSize: number,
|
|
128
|
+
offsetStart: number
|
|
129
|
+
): boolean {
|
|
130
|
+
return (
|
|
131
|
+
offsetStart >= elementStart + elementSize / 3 &&
|
|
132
|
+
offsetStart <= elementStart + (elementSize * 2) / 3
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getContainingHalf(
|
|
137
|
+
elementStart: number,
|
|
138
|
+
elementSize: number,
|
|
139
|
+
offsetStart: number
|
|
140
|
+
): "above" | "below" {
|
|
141
|
+
if (offsetStart < elementStart + elementSize / 2) return "above";
|
|
142
|
+
return "below";
|
|
143
|
+
}
|
|
144
|
+
|
|
119
145
|
/* ----------------------------------------------------------------------------
|
|
120
146
|
* Item
|
|
121
147
|
* ------------------------------------------------------------------------- */
|
|
122
148
|
|
|
123
149
|
type UseSortableReturnType = ReturnType<typeof useSortable>;
|
|
124
150
|
|
|
151
|
+
type ItemChildrenProps<T> = {
|
|
152
|
+
ref: React.Ref<T>;
|
|
153
|
+
relativeDropPosition?: RelativeDropPosition;
|
|
154
|
+
style?: React.CSSProperties;
|
|
155
|
+
} & UseSortableReturnType["attributes"];
|
|
156
|
+
|
|
125
157
|
interface ItemProps<T> {
|
|
126
158
|
id: UniqueIdentifier;
|
|
127
159
|
disabled?: boolean;
|
|
128
|
-
children: (
|
|
129
|
-
props: {
|
|
130
|
-
ref: React.Ref<T>;
|
|
131
|
-
relativeDropPosition?: RelativeDropPosition;
|
|
132
|
-
style?: React.CSSProperties;
|
|
133
|
-
} & UseSortableReturnType["attributes"]
|
|
134
|
-
) => JSX.Element;
|
|
160
|
+
children: (props: ItemChildrenProps<T>) => JSX.Element;
|
|
135
161
|
}
|
|
136
162
|
|
|
137
163
|
function SortableItem<T extends HTMLElement>({
|
|
@@ -360,7 +386,83 @@ function SortableRoot({
|
|
|
360
386
|
);
|
|
361
387
|
}
|
|
362
388
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
389
|
+
// Create a map of all subcomponents
|
|
390
|
+
const SortableSubcomponents = {
|
|
391
|
+
Root: memo(SortableRoot),
|
|
392
|
+
Item: memoGeneric(SortableItem),
|
|
393
|
+
} as const;
|
|
394
|
+
|
|
395
|
+
interface SortableProps<T, TElement extends HTMLElement>
|
|
396
|
+
extends Omit<RootProps, "children" | "keys" | "renderOverlay"> {
|
|
397
|
+
items: T[];
|
|
398
|
+
keyExtractor: (item: T) => UniqueIdentifier;
|
|
399
|
+
shouldRenderOverlay?: boolean;
|
|
400
|
+
renderItem: (
|
|
401
|
+
item: T,
|
|
402
|
+
props: ItemChildrenProps<TElement>,
|
|
403
|
+
{ isOverlay }: { isOverlay: boolean }
|
|
404
|
+
) => JSX.Element;
|
|
366
405
|
}
|
|
406
|
+
|
|
407
|
+
const SortableComponent = memoGeneric(function Sortable<
|
|
408
|
+
TItem,
|
|
409
|
+
TElement extends HTMLElement,
|
|
410
|
+
>(props: SortableProps<TItem, TElement>) {
|
|
411
|
+
const {
|
|
412
|
+
renderItem,
|
|
413
|
+
items,
|
|
414
|
+
keyExtractor,
|
|
415
|
+
shouldRenderOverlay = true,
|
|
416
|
+
...rest
|
|
417
|
+
} = props;
|
|
418
|
+
|
|
419
|
+
const keys = React.useMemo(
|
|
420
|
+
() => items.map(keyExtractor),
|
|
421
|
+
[items, keyExtractor]
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
const renderOverlay = React.useCallback(
|
|
425
|
+
(index: number) => {
|
|
426
|
+
return renderItem(
|
|
427
|
+
items[index],
|
|
428
|
+
{
|
|
429
|
+
ref: () => {},
|
|
430
|
+
style: {},
|
|
431
|
+
tabIndex: -1,
|
|
432
|
+
"aria-disabled": true,
|
|
433
|
+
"aria-pressed": false,
|
|
434
|
+
"aria-roledescription": "Item being dragged",
|
|
435
|
+
"aria-describedby": "Item being dragged",
|
|
436
|
+
role: "button",
|
|
437
|
+
},
|
|
438
|
+
{ isOverlay: true }
|
|
439
|
+
);
|
|
440
|
+
},
|
|
441
|
+
[renderItem, items]
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
return (
|
|
445
|
+
<SortableSubcomponents.Root
|
|
446
|
+
{...rest}
|
|
447
|
+
keys={keys}
|
|
448
|
+
renderOverlay={shouldRenderOverlay ? renderOverlay : undefined}
|
|
449
|
+
>
|
|
450
|
+
{keys.map((key, index) => (
|
|
451
|
+
<SortableSubcomponents.Item key={key} id={key}>
|
|
452
|
+
{(props) => {
|
|
453
|
+
return renderItem(
|
|
454
|
+
items[index],
|
|
455
|
+
props as ItemChildrenProps<TElement>,
|
|
456
|
+
{ isOverlay: false }
|
|
457
|
+
);
|
|
458
|
+
}}
|
|
459
|
+
</SortableSubcomponents.Item>
|
|
460
|
+
))}
|
|
461
|
+
</SortableSubcomponents.Root>
|
|
462
|
+
);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// Export both the namespace and the component
|
|
466
|
+
const Sortable = Object.assign(SortableComponent, SortableSubcomponents);
|
|
467
|
+
|
|
468
|
+
export { Sortable };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { IconButton } from "../IconButton";
|
|
2
|
+
|
|
3
|
+
import { cssVars } from "../../theme";
|
|
4
|
+
|
|
5
|
+
import { Avatar } from "../Avatar";
|
|
6
|
+
import { Logo } from "../NoyaLogo";
|
|
7
|
+
|
|
8
|
+
import React, { forwardRef } from "react";
|
|
9
|
+
import { INPUT_HEIGHT } from "../../theme";
|
|
10
|
+
import { cx } from "../../utils/classNames";
|
|
11
|
+
import { TextAreaRow } from "../TextArea";
|
|
12
|
+
|
|
13
|
+
export const AIAssistantLoadingIndicator = () => {
|
|
14
|
+
return (
|
|
15
|
+
<div className="flex gap-2">
|
|
16
|
+
<Avatar
|
|
17
|
+
name="Assistant"
|
|
18
|
+
size={INPUT_HEIGHT}
|
|
19
|
+
backgroundColor={cssVars.colors.primaryPastel}
|
|
20
|
+
>
|
|
21
|
+
<Logo style={{ width: 15 }} fill={cssVars.colors.primary} />
|
|
22
|
+
</Avatar>
|
|
23
|
+
<div className="animate-pulse">▋</div>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type AIAssistantInputProps = {
|
|
29
|
+
value: string;
|
|
30
|
+
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
|
|
31
|
+
onSubmitClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
32
|
+
onKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
|
|
33
|
+
disabled: boolean;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const AIAssistantInput = forwardRef<
|
|
37
|
+
HTMLDivElement,
|
|
38
|
+
AIAssistantInputProps
|
|
39
|
+
>(({ value, onChange, onSubmitClick, onKeyDown, disabled }, ref) => (
|
|
40
|
+
<TextAreaRow
|
|
41
|
+
autoResize
|
|
42
|
+
autoFocus
|
|
43
|
+
value={value}
|
|
44
|
+
onChange={onChange}
|
|
45
|
+
ref={ref}
|
|
46
|
+
textAreaClassName="py-2 pl-3 !pr-9 w-full mt-3"
|
|
47
|
+
placeholder="Tell me what you'd like to do..."
|
|
48
|
+
onKeyDown={onKeyDown}
|
|
49
|
+
disabled={disabled}
|
|
50
|
+
end={
|
|
51
|
+
<IconButton
|
|
52
|
+
iconName="ArrowUpIcon"
|
|
53
|
+
onClick={onSubmitClick}
|
|
54
|
+
disabled={!value.trim() || disabled}
|
|
55
|
+
className="h-8 w-8 z-20 rounded-full"
|
|
56
|
+
size={20}
|
|
57
|
+
color={cssVars.colors.text}
|
|
58
|
+
style={{
|
|
59
|
+
backgroundColor: cssVars.colors.chipDefaultBg,
|
|
60
|
+
}}
|
|
61
|
+
/>
|
|
62
|
+
}
|
|
63
|
+
/>
|
|
64
|
+
));
|
|
65
|
+
|
|
66
|
+
type AIAssistantLayoutProps = {
|
|
67
|
+
renderMessages: () => React.ReactNode;
|
|
68
|
+
renderInput: () => React.ReactNode;
|
|
69
|
+
isLoading: boolean;
|
|
70
|
+
renderLoadingIndicator?: () => React.ReactNode;
|
|
71
|
+
className?: string;
|
|
72
|
+
style?: React.CSSProperties;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const AIAssistantLayout = forwardRef<
|
|
76
|
+
HTMLDivElement,
|
|
77
|
+
AIAssistantLayoutProps
|
|
78
|
+
>(
|
|
79
|
+
(
|
|
80
|
+
{
|
|
81
|
+
renderMessages,
|
|
82
|
+
renderInput,
|
|
83
|
+
isLoading,
|
|
84
|
+
renderLoadingIndicator,
|
|
85
|
+
className,
|
|
86
|
+
style,
|
|
87
|
+
}: AIAssistantLayoutProps,
|
|
88
|
+
ref
|
|
89
|
+
) => {
|
|
90
|
+
return (
|
|
91
|
+
<div className={cx("flex flex-col flex-1", className)} style={style}>
|
|
92
|
+
<div className="flex-1 min-h-0 overflow-auto" ref={ref}>
|
|
93
|
+
<div className="flex flex-col gap-2 pb-4 pt-1">
|
|
94
|
+
{renderMessages()}
|
|
95
|
+
{isLoading ? renderLoadingIndicator?.() : null}
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
<div className="border-t border-divider pt-1 px-1">{renderInput()}</div>
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { cssVars } from "../../theme";
|
|
2
|
+
|
|
3
|
+
import { ChatBubbleWithDotsIcon } from "@noya-app/noya-icons";
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { INPUT_HEIGHT } from "../../theme";
|
|
6
|
+
import { colorFromString } from "../../utils/colorFromString";
|
|
7
|
+
import { Avatar, AvatarProps, AvatarStack } from "../Avatar";
|
|
8
|
+
import Button from "../Button";
|
|
9
|
+
import { Spacer } from "../Spacer";
|
|
10
|
+
import { Small } from "../Text";
|
|
11
|
+
import { Tooltip } from "../Tooltip";
|
|
12
|
+
|
|
13
|
+
export const UserAvatar = ({ userId, name, image }: AvatarProps) => (
|
|
14
|
+
<Tooltip
|
|
15
|
+
key={userId}
|
|
16
|
+
content={
|
|
17
|
+
<div className="flex flex-col">
|
|
18
|
+
<Small>{name}</Small>
|
|
19
|
+
</div>
|
|
20
|
+
}
|
|
21
|
+
>
|
|
22
|
+
<Avatar size={INPUT_HEIGHT} userId={userId} name={name} image={image} />
|
|
23
|
+
</Tooltip>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
type ConnectedUsersMenuLayoutProps = {
|
|
27
|
+
renderUsers: () => React.ReactNode;
|
|
28
|
+
currentUserId?: string;
|
|
29
|
+
launchAIAssistant?: () => void;
|
|
30
|
+
isAssistantOpen?: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const ConnectedUsersMenuLayout = ({
|
|
34
|
+
renderUsers,
|
|
35
|
+
currentUserId,
|
|
36
|
+
launchAIAssistant,
|
|
37
|
+
isAssistantOpen,
|
|
38
|
+
}: ConnectedUsersMenuLayoutProps) => {
|
|
39
|
+
return (
|
|
40
|
+
<div className="flex gap-1.5">
|
|
41
|
+
<AvatarStack size={INPUT_HEIGHT}>{renderUsers()}</AvatarStack>
|
|
42
|
+
|
|
43
|
+
{/* AI Assistant Avatar */}
|
|
44
|
+
{launchAIAssistant && (
|
|
45
|
+
<Tooltip
|
|
46
|
+
content={
|
|
47
|
+
<div className="flex flex-col">
|
|
48
|
+
<Small>AI Assistant</Small>
|
|
49
|
+
</div>
|
|
50
|
+
}
|
|
51
|
+
>
|
|
52
|
+
<Button
|
|
53
|
+
variant={isAssistantOpen ? "secondary" : undefined}
|
|
54
|
+
onClick={launchAIAssistant}
|
|
55
|
+
style={{
|
|
56
|
+
backgroundColor: isAssistantOpen
|
|
57
|
+
? colorFromString(currentUserId ?? "")
|
|
58
|
+
: undefined,
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
AI
|
|
62
|
+
<Spacer.Horizontal size={8} />
|
|
63
|
+
<ChatBubbleWithDotsIcon
|
|
64
|
+
color={isAssistantOpen ? undefined : cssVars.colors.icon}
|
|
65
|
+
/>
|
|
66
|
+
</Button>
|
|
67
|
+
</Tooltip>
|
|
68
|
+
)}
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Banner,
|
|
3
|
+
BannerProps,
|
|
4
|
+
IconButton,
|
|
5
|
+
Section,
|
|
6
|
+
SectionProps,
|
|
7
|
+
} from "@noya-app/noya-designsystem";
|
|
8
|
+
import { forwardRefGeneric } from "@noya-app/react-utils";
|
|
9
|
+
import React from "react";
|
|
10
|
+
import { cx } from "../../utils/classNames";
|
|
11
|
+
import { Collection, CollectionProps, CollectionRef } from "../Collection";
|
|
12
|
+
|
|
13
|
+
export const FileExplorerLayout = ({
|
|
14
|
+
children,
|
|
15
|
+
className,
|
|
16
|
+
...props
|
|
17
|
+
}: SectionProps) => (
|
|
18
|
+
<Section className={cx(className, "px-3 flex-1")} {...props}>
|
|
19
|
+
{children}
|
|
20
|
+
</Section>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export const FileExplorerUploadButton = ({
|
|
24
|
+
showUploadButton,
|
|
25
|
+
onUpload,
|
|
26
|
+
children,
|
|
27
|
+
}: {
|
|
28
|
+
showUploadButton?: boolean;
|
|
29
|
+
onUpload?: () => void;
|
|
30
|
+
children?: React.ReactNode;
|
|
31
|
+
}) => (
|
|
32
|
+
<div className="flex items-center gap-2">
|
|
33
|
+
{showUploadButton && (
|
|
34
|
+
<IconButton
|
|
35
|
+
iconName="UploadIcon"
|
|
36
|
+
onClick={onUpload}
|
|
37
|
+
aria-label="Upload media"
|
|
38
|
+
tooltip="Upload media"
|
|
39
|
+
/>
|
|
40
|
+
)}
|
|
41
|
+
{children}
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
export const FileExplorerCollection = forwardRefGeneric(
|
|
46
|
+
function FileExplorerCollection<T, M extends string = string>(
|
|
47
|
+
{ ...props }: CollectionProps<T, M>,
|
|
48
|
+
ref: React.ForwardedRef<CollectionRef>
|
|
49
|
+
) {
|
|
50
|
+
return <Collection<T, M> ref={ref} className="-mx-3 flex-1" {...props} />;
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
export const FileExplorerDetail = ({
|
|
55
|
+
selected,
|
|
56
|
+
children,
|
|
57
|
+
}: {
|
|
58
|
+
selected: boolean;
|
|
59
|
+
children: React.ReactNode;
|
|
60
|
+
}) => (
|
|
61
|
+
<span
|
|
62
|
+
className={cx("text-sm", selected ? "text-primary" : "text-text-muted")}
|
|
63
|
+
>
|
|
64
|
+
{children}
|
|
65
|
+
</span>
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
export const FileExplorerEmptyState = ({
|
|
69
|
+
label = "No files",
|
|
70
|
+
...props
|
|
71
|
+
}: BannerProps) => <Banner label={label} className="mx-3" {...props} />;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getShortcutDisplayParts } from "@noya-app/noya-keymap";
|
|
2
2
|
import React, { memo, ReactNode } from "react";
|
|
3
3
|
import { useDesignSystemConfiguration } from "../../contexts/DesignSystemConfiguration";
|
|
4
|
-
import { cx } from "../../utils/classNames";
|
|
4
|
+
import { cx, mergeConflictingClassNames } from "../../utils/classNames";
|
|
5
5
|
import { IScoredItem } from "../../utils/fuzzyScorer";
|
|
6
6
|
import withSeparatorElements from "../../utils/withSeparatorElements";
|
|
7
7
|
import { IconName } from "../Icons";
|
|
@@ -270,12 +270,17 @@ export const SectionHeader = memo(function SectionHeader({
|
|
|
270
270
|
return (
|
|
271
271
|
<span
|
|
272
272
|
id={id}
|
|
273
|
-
className={
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
273
|
+
className={mergeConflictingClassNames(
|
|
274
|
+
[
|
|
275
|
+
variant === "label"
|
|
276
|
+
? `text-label ${textStyles.label} font-bold text-text-disabled`
|
|
277
|
+
: "font-sans text-heading5 font-normal",
|
|
278
|
+
"bg-listview-raised-background flex items-center py-1.5 px-3",
|
|
279
|
+
isFirst && "-mt-1",
|
|
280
|
+
],
|
|
281
|
+
{
|
|
282
|
+
categories: ["font"],
|
|
283
|
+
}
|
|
279
284
|
)}
|
|
280
285
|
>
|
|
281
286
|
{indented && (
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Link1Icon } from "@noya-app/noya-icons";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { DividerVertical } from "../Divider";
|
|
4
|
+
import { Small } from "../Text";
|
|
5
|
+
|
|
6
|
+
export const PipelineResultLink = ({ url }: { url: string }) => {
|
|
7
|
+
return (
|
|
8
|
+
<div className="flex flex-col gap-0.5">
|
|
9
|
+
{/* <Label position="start">{name}</Label> */}
|
|
10
|
+
<a
|
|
11
|
+
href={url}
|
|
12
|
+
target="_blank"
|
|
13
|
+
rel="noopener noreferrer"
|
|
14
|
+
className="flex text-primary hover:opacity-80 border border-divider rounded-md"
|
|
15
|
+
>
|
|
16
|
+
<div className="flex gap-1 items-center p-2">
|
|
17
|
+
<Link1Icon className="w-[15px] h-[15px] flex-shrink-0" />
|
|
18
|
+
</div>
|
|
19
|
+
<DividerVertical />
|
|
20
|
+
<Small className="truncate p-2 flex-1">{url}</Small>
|
|
21
|
+
</a>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const PipelineResultLayout = ({
|
|
27
|
+
children,
|
|
28
|
+
}: {
|
|
29
|
+
children: React.ReactNode;
|
|
30
|
+
}) => {
|
|
31
|
+
return <div className="flex flex-col gap-2">{children}</div>;
|
|
32
|
+
};
|