@krrli/cm-designsystem 1.1.0 → 1.19.7
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 +391 -2
- package/dist/components/avatar/Avatar.d.ts +71 -0
- package/dist/components/avatar/Avatar.js +63 -0
- package/dist/components/branding/BrandingGallery.d.ts +1 -0
- package/dist/components/branding/BrandingGallery.js +139 -0
- package/dist/components/button/Button.d.ts +54 -0
- package/dist/components/button/Button.js +56 -0
- package/dist/components/button/Button.test.d.ts +1 -0
- package/dist/components/button/Button.test.js +30 -0
- package/dist/components/color/ColorDoc.d.ts +4 -0
- package/dist/components/color/ColorDoc.js +10 -0
- package/dist/components/file-upload/FileUpload.d.ts +83 -0
- package/dist/components/file-upload/FileUpload.js +70 -0
- package/dist/components/form/Form.d.ts +54 -0
- package/dist/components/form/Form.js +38 -0
- package/dist/components/icon-button/IconButton.d.ts +50 -0
- package/dist/components/icon-button/IconButton.js +22 -0
- package/dist/components/icon-button/IconButton.test.d.ts +1 -0
- package/dist/components/icon-button/IconButton.test.js +22 -0
- package/dist/components/icons/IconBase.d.ts +5 -0
- package/dist/components/icons/IconBase.js +9 -0
- package/dist/components/icons/generated/ArrowDown.d.ts +3 -0
- package/dist/components/icons/generated/ArrowDown.js +4 -0
- package/dist/components/icons/generated/ArrowLeft.d.ts +3 -0
- package/dist/components/icons/generated/ArrowLeft.js +4 -0
- package/dist/components/icons/generated/ArrowRight.d.ts +3 -0
- package/dist/components/icons/generated/ArrowRight.js +4 -0
- package/dist/components/icons/generated/ArrowUp.d.ts +3 -0
- package/dist/components/icons/generated/ArrowUp.js +4 -0
- package/dist/components/icons/generated/Calendar.d.ts +3 -0
- package/dist/components/icons/generated/Calendar.js +4 -0
- package/dist/components/icons/generated/Cancel.d.ts +3 -0
- package/dist/components/icons/generated/Cancel.js +4 -0
- package/dist/components/icons/generated/Checkmark.d.ts +3 -0
- package/dist/components/icons/generated/Checkmark.js +4 -0
- package/dist/components/icons/generated/Edit.d.ts +3 -0
- package/dist/components/icons/generated/Edit.js +4 -0
- package/dist/components/icons/generated/Eye.d.ts +3 -0
- package/dist/components/icons/generated/Eye.js +4 -0
- package/dist/components/icons/generated/Fullscreen.d.ts +3 -0
- package/dist/components/icons/generated/Fullscreen.js +4 -0
- package/dist/components/icons/generated/HeartFilled.d.ts +3 -0
- package/dist/components/icons/generated/HeartFilled.js +4 -0
- package/dist/components/icons/generated/HeartOutline.d.ts +3 -0
- package/dist/components/icons/generated/HeartOutline.js +4 -0
- package/dist/components/icons/generated/Location.d.ts +3 -0
- package/dist/components/icons/generated/Location.js +4 -0
- package/dist/components/icons/generated/LogOut.d.ts +3 -0
- package/dist/components/icons/generated/LogOut.js +4 -0
- package/dist/components/icons/generated/Mumble.d.ts +3 -0
- package/dist/components/icons/generated/Mumble.js +4 -0
- package/dist/components/icons/generated/Profile.d.ts +3 -0
- package/dist/components/icons/generated/Profile.js +4 -0
- package/dist/components/icons/generated/ReplyFilled.d.ts +3 -0
- package/dist/components/icons/generated/ReplyFilled.js +4 -0
- package/dist/components/icons/generated/ReplyOutline.d.ts +3 -0
- package/dist/components/icons/generated/ReplyOutline.js +4 -0
- package/dist/components/icons/generated/Repost.d.ts +3 -0
- package/dist/components/icons/generated/Repost.js +4 -0
- package/dist/components/icons/generated/Send.d.ts +3 -0
- package/dist/components/icons/generated/Send.js +4 -0
- package/dist/components/icons/generated/Settings.d.ts +3 -0
- package/dist/components/icons/generated/Settings.js +4 -0
- package/dist/components/icons/generated/Share.d.ts +3 -0
- package/dist/components/icons/generated/Share.js +4 -0
- package/dist/components/icons/generated/Time.d.ts +3 -0
- package/dist/components/icons/generated/Time.js +4 -0
- package/dist/components/icons/generated/Upload.d.ts +3 -0
- package/dist/components/icons/generated/Upload.js +4 -0
- package/dist/components/icons/generated/index.d.ts +24 -0
- package/dist/components/icons/generated/index.js +24 -0
- package/dist/components/index.d.ts +25 -0
- package/dist/components/index.js +25 -0
- package/dist/components/input/Input.d.ts +61 -0
- package/dist/components/input/Input.js +47 -0
- package/dist/components/like-toggle/LikeToggle.d.ts +97 -0
- package/dist/components/like-toggle/LikeToggle.js +185 -0
- package/dist/components/like-toggle/LikeToggle.test.d.ts +1 -0
- package/dist/components/like-toggle/LikeToggle.test.js +35 -0
- package/dist/components/modal/Modal.d.ts +75 -0
- package/dist/components/modal/Modal.js +63 -0
- package/dist/components/modal/Modal.test.d.ts +1 -0
- package/dist/components/modal/Modal.test.js +24 -0
- package/dist/components/navi-button/NaviButton.d.ts +26 -0
- package/dist/components/navi-button/NaviButton.js +29 -0
- package/dist/components/navi-button/NaviButton.test.d.ts +1 -0
- package/dist/components/navi-button/NaviButton.test.js +22 -0
- package/dist/components/navi-user-button/NaviUserButton.d.ts +26 -0
- package/dist/components/navi-user-button/NaviUserButton.js +29 -0
- package/dist/components/round-button/RoundButton.d.ts +25 -0
- package/dist/components/round-button/RoundButton.js +28 -0
- package/dist/components/round-button/RoundButton.test.d.ts +1 -0
- package/dist/components/round-button/RoundButton.test.js +21 -0
- package/dist/components/tabs/TabItem.d.ts +11 -0
- package/dist/components/tabs/TabItem.js +13 -0
- package/dist/components/tabs/Tabs.d.ts +67 -0
- package/dist/components/tabs/Tabs.js +67 -0
- package/dist/components/tabs/Tabs.test.d.ts +1 -0
- package/dist/components/tabs/Tabs.test.js +61 -0
- package/dist/components/text-link/TextLink.d.ts +9 -0
- package/dist/components/text-link/TextLink.js +15 -0
- package/dist/components/text-link/TextLink.test.d.ts +1 -0
- package/dist/components/text-link/TextLink.test.js +14 -0
- package/dist/components/textarea/Textarea.d.ts +48 -0
- package/dist/components/textarea/Textarea.js +46 -0
- package/dist/components/timed-button/TimedButton.d.ts +56 -0
- package/dist/components/timed-button/TimedButton.js +106 -0
- package/dist/components/timed-button/TimedButton.test.d.ts +1 -0
- package/dist/components/timed-button/TimedButton.test.js +35 -0
- package/dist/components/toggle/Toggle.d.ts +62 -0
- package/dist/components/toggle/Toggle.js +67 -0
- package/dist/components/toggle/Toggle.test.d.ts +1 -0
- package/dist/components/toggle/Toggle.test.js +93 -0
- package/dist/components/typography/Heading.d.ts +17 -0
- package/dist/components/typography/Heading.js +11 -0
- package/dist/components/typography/Label.d.ts +15 -0
- package/dist/components/typography/Label.js +7 -0
- package/dist/components/typography/Paragraph.d.ts +15 -0
- package/dist/components/typography/Paragraph.js +7 -0
- package/dist/components/typography/Placeholder.d.ts +13 -0
- package/dist/components/typography/Placeholder.js +7 -0
- package/dist/components/typography/ValidationMessage.d.ts +15 -0
- package/dist/components/typography/ValidationMessage.js +9 -0
- package/dist/components/typography/styles.d.ts +74 -0
- package/dist/components/typography/styles.js +52 -0
- package/dist/favicon.svg +18 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +7550 -0
- package/dist/index.js +2 -0
- package/dist/logo-inline-gradient.svg +43 -0
- package/dist/setupTests.d.ts +1 -0
- package/dist/setupTests.js +7 -0
- package/package.json +78 -33
- package/.github/CODEOWNERS +0 -7
- package/.github/semantic.yml +0 -24
- package/.github/workflows/publish-npm.yml +0 -29
- package/.github/workflows/storybook.yml +0 -44
- package/.releaserc.json +0 -9
- package/.storybook/main.ts +0 -19
- package/.storybook/preview.ts +0 -21
- package/.storybook/vitest.setup.ts +0 -7
- package/src/index.ts +0 -4
- package/stories/Button.stories.ts +0 -54
- package/stories/Button.tsx +0 -37
- package/stories/Button2.stories.ts +0 -54
- package/stories/Button2.tsx +0 -41
- package/stories/Configure.mdx +0 -364
- package/stories/Header.stories.ts +0 -34
- package/stories/Header.tsx +0 -56
- package/stories/Page.stories.ts +0 -33
- package/stories/Page.tsx +0 -73
- package/stories/assets/accessibility.png +0 -0
- package/stories/assets/accessibility.svg +0 -1
- package/stories/assets/addon-library.png +0 -0
- package/stories/assets/assets.png +0 -0
- package/stories/assets/avif-test-image.avif +0 -0
- package/stories/assets/context.png +0 -0
- package/stories/assets/discord.svg +0 -1
- package/stories/assets/docs.png +0 -0
- package/stories/assets/figma-plugin.png +0 -0
- package/stories/assets/github.svg +0 -1
- package/stories/assets/share.png +0 -0
- package/stories/assets/styling.png +0 -0
- package/stories/assets/testing.png +0 -0
- package/stories/assets/theming.png +0 -0
- package/stories/assets/tutorials.svg +0 -1
- package/stories/assets/youtube.svg +0 -1
- package/stories/button.css +0 -30
- package/stories/button2.css +0 -30
- package/stories/header.css +0 -32
- package/stories/page.css +0 -68
- package/tsconfig.json +0 -13
- package/vitest.config.ts +0 -35
- package/vitest.shims.d.ts +0 -1
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as RadixToggle from "@radix-ui/react-toggle";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { tv } from "tailwind-variants";
|
|
5
|
+
import { HeartFilled, HeartOutline } from "../icons/generated";
|
|
6
|
+
import { Label } from "../typography/Label";
|
|
7
|
+
const toggleStyles = tv({
|
|
8
|
+
slots: {
|
|
9
|
+
base: [
|
|
10
|
+
"inline-flex",
|
|
11
|
+
"items-center",
|
|
12
|
+
"justify-center",
|
|
13
|
+
"gap-2",
|
|
14
|
+
"h-8",
|
|
15
|
+
"px-3",
|
|
16
|
+
"py-2",
|
|
17
|
+
"rounded-full",
|
|
18
|
+
"transition-all",
|
|
19
|
+
"duration-150",
|
|
20
|
+
"ease-in-out",
|
|
21
|
+
"focus-visible:outline-none",
|
|
22
|
+
"focus-visible:ring-2",
|
|
23
|
+
"focus-visible:ring-offset-2",
|
|
24
|
+
"disabled:opacity-50",
|
|
25
|
+
"disabled:pointer-events-none",
|
|
26
|
+
],
|
|
27
|
+
icon: ["inline-flex"],
|
|
28
|
+
label: [],
|
|
29
|
+
},
|
|
30
|
+
variants: {
|
|
31
|
+
pressed: {
|
|
32
|
+
false: {
|
|
33
|
+
base: ["bg-transparent", "hover:bg-pink-50", "hover:text-pink-600"],
|
|
34
|
+
icon: [
|
|
35
|
+
"text-inherit",
|
|
36
|
+
"hover:transition-all",
|
|
37
|
+
"hover:duration-350",
|
|
38
|
+
"hover:ease-in-out",
|
|
39
|
+
],
|
|
40
|
+
label: [
|
|
41
|
+
"text-inherit",
|
|
42
|
+
"hover:transition-all",
|
|
43
|
+
"hover:duration-350",
|
|
44
|
+
"hover:ease-in-out",
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
true: {
|
|
48
|
+
base: [],
|
|
49
|
+
icon: ["text-pink-500", "transition-all", "duration-300", "ease-out"],
|
|
50
|
+
label: [
|
|
51
|
+
"text-pink-900",
|
|
52
|
+
"transition-transform",
|
|
53
|
+
"duration-300",
|
|
54
|
+
"ease-out",
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
hasLikes: {
|
|
59
|
+
true: {
|
|
60
|
+
base: ["hover:bg-pink-50", "hover:text-pink-600"],
|
|
61
|
+
icon: ["text-pink-500"],
|
|
62
|
+
label: ["text-pink-900"],
|
|
63
|
+
},
|
|
64
|
+
false: "",
|
|
65
|
+
},
|
|
66
|
+
animating: {
|
|
67
|
+
true: {},
|
|
68
|
+
false: {},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
compoundVariants: [
|
|
72
|
+
// pressed + animating --> Label ausblenden / nach oben verschieben
|
|
73
|
+
{
|
|
74
|
+
pressed: true,
|
|
75
|
+
animating: true,
|
|
76
|
+
class: {
|
|
77
|
+
label: [
|
|
78
|
+
"opacity-0",
|
|
79
|
+
"-translate-y-1",
|
|
80
|
+
"transition-all",
|
|
81
|
+
"duration-300",
|
|
82
|
+
"ease-out",
|
|
83
|
+
],
|
|
84
|
+
icon: ["text-pink-500"],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
// pressed + nicht animating --> Label sichtbar
|
|
88
|
+
{
|
|
89
|
+
pressed: true,
|
|
90
|
+
animating: false,
|
|
91
|
+
class: {
|
|
92
|
+
label: [
|
|
93
|
+
"opacity-100",
|
|
94
|
+
"translate-y-0",
|
|
95
|
+
"text-pink-900",
|
|
96
|
+
"transition-all",
|
|
97
|
+
"duration-300",
|
|
98
|
+
"ease-out",
|
|
99
|
+
],
|
|
100
|
+
icon: ["text-pink-500"],
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
// pressed + hasLikes --> überschreibt Hover-Farbe
|
|
104
|
+
{
|
|
105
|
+
pressed: true,
|
|
106
|
+
hasLikes: true,
|
|
107
|
+
class: {
|
|
108
|
+
label: ["text-pink-900"],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
// not pressed + animating
|
|
112
|
+
{
|
|
113
|
+
pressed: false,
|
|
114
|
+
animating: true,
|
|
115
|
+
class: {
|
|
116
|
+
label: [
|
|
117
|
+
"opacity-0",
|
|
118
|
+
"-translate-y-1",
|
|
119
|
+
"transition-all",
|
|
120
|
+
"duration-300",
|
|
121
|
+
"ease-out",
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
// not pressed + nicht animating
|
|
126
|
+
{
|
|
127
|
+
pressed: false,
|
|
128
|
+
animating: false,
|
|
129
|
+
class: {
|
|
130
|
+
label: [
|
|
131
|
+
"opacity-100",
|
|
132
|
+
"translate-y-0",
|
|
133
|
+
"transition-all",
|
|
134
|
+
"duration-300",
|
|
135
|
+
"ease-out",
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
defaultVariants: {
|
|
141
|
+
pressed: false,
|
|
142
|
+
animating: false,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
export const LikeToggle = ({ pressed = false, likes = 0, onLikeChange, }) => {
|
|
146
|
+
const [animating, setAnimating] = useState(false);
|
|
147
|
+
const [selected, setSelected] = useState(pressed);
|
|
148
|
+
const [currentLikes, setCurrentLikes] = useState(likes);
|
|
149
|
+
const [label, setLabel] = useState(likes ? (likes === 1 ? `${likes} Like` : `${likes} Likes`) : "Like");
|
|
150
|
+
const { base, icon, label: labelSlot, } = toggleStyles({
|
|
151
|
+
pressed: selected,
|
|
152
|
+
hasLikes: currentLikes > 0,
|
|
153
|
+
animating,
|
|
154
|
+
});
|
|
155
|
+
const handlePressedChange = (nextSelected) => {
|
|
156
|
+
// Ignore redundant events
|
|
157
|
+
if (nextSelected === selected)
|
|
158
|
+
return;
|
|
159
|
+
setSelected(nextSelected);
|
|
160
|
+
// Immediate label: show 'Liked' when liking, otherwise reflect current count or 'Like' if zero
|
|
161
|
+
if (nextSelected) {
|
|
162
|
+
setLabel("Liked");
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
setLabel(currentLikes === 0
|
|
166
|
+
? "Like"
|
|
167
|
+
: currentLikes === 1
|
|
168
|
+
? "1 Like"
|
|
169
|
+
: `${currentLikes} Likes`);
|
|
170
|
+
}
|
|
171
|
+
onLikeChange(nextSelected);
|
|
172
|
+
// After 2s apply count change + dissolve animation
|
|
173
|
+
setTimeout(() => {
|
|
174
|
+
setCurrentLikes((prev) => {
|
|
175
|
+
const updated = Math.max(0, prev + (nextSelected ? 1 : -1));
|
|
176
|
+
// Final label after animation
|
|
177
|
+
setLabel(updated === 0 ? "Like" : updated === 1 ? "1 Like" : `${updated} Likes`);
|
|
178
|
+
setAnimating(true);
|
|
179
|
+
setTimeout(() => setAnimating(false), 300);
|
|
180
|
+
return updated;
|
|
181
|
+
});
|
|
182
|
+
}, 2000);
|
|
183
|
+
};
|
|
184
|
+
return (_jsxs(RadixToggle.Root, { className: base(), pressed: selected, onPressedChange: handlePressedChange, children: [_jsx("span", { className: icon(), "aria-hidden": "true", "aria-label": selected ? "HeartFilled" : "HeartOutline", children: selected ? _jsx(HeartFilled, {}) : _jsx(HeartOutline, {}) }), _jsx("span", { className: labelSlot(), children: _jsx(Label, { as: "span", size: "md", children: label }) })] }));
|
|
185
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import { describe, expect, test, vi } from "vitest";
|
|
4
|
+
import { LikeToggle } from "./LikeToggle";
|
|
5
|
+
describe("LikeToggle", () => {
|
|
6
|
+
test("should render icon and label", async () => {
|
|
7
|
+
// Arrange
|
|
8
|
+
render(_jsx(LikeToggle, { likes: 3, onLikeChange: () => { } }));
|
|
9
|
+
const button = screen.getByRole("button");
|
|
10
|
+
expect(button).toBeVisible();
|
|
11
|
+
expect(button).toHaveTextContent("3 Likes");
|
|
12
|
+
// Icon prüfen
|
|
13
|
+
expect(screen.getByLabelText("HeartOutline")).toBeInTheDocument();
|
|
14
|
+
});
|
|
15
|
+
test("renders initial state and toggles correctly", async () => {
|
|
16
|
+
// Arrange
|
|
17
|
+
const onLikeChange = vi.fn();
|
|
18
|
+
render(_jsx(LikeToggle, { likes: 3, onLikeChange: onLikeChange }));
|
|
19
|
+
const button = screen.getByRole("button");
|
|
20
|
+
expect(button).toBeVisible();
|
|
21
|
+
// Initial state with Likes
|
|
22
|
+
expect(button).toHaveTextContent("3 Likes");
|
|
23
|
+
expect(screen.getByLabelText("HeartOutline")).toBeInTheDocument();
|
|
24
|
+
// Simulate click
|
|
25
|
+
fireEvent.click(button);
|
|
26
|
+
// Immediate state after click: "Liked" label + HeartFilled
|
|
27
|
+
expect(button).toHaveTextContent("Liked");
|
|
28
|
+
expect(screen.getByLabelText("HeartFilled")).toBeInTheDocument();
|
|
29
|
+
expect(onLikeChange).toHaveBeenCalledWith(true);
|
|
30
|
+
// Wait for animation + likes count update (2s delay + 300ms animation)
|
|
31
|
+
await waitFor(() => {
|
|
32
|
+
expect(button).toHaveTextContent("4 Likes");
|
|
33
|
+
}, { timeout: 2500 });
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type VariantProps } from "tailwind-variants";
|
|
3
|
+
declare const modalStyles: import("tailwind-variants").TVReturnType<{
|
|
4
|
+
[key: string]: {
|
|
5
|
+
[key: string]: import("tailwind-merge").ClassNameValue | {
|
|
6
|
+
title?: import("tailwind-merge").ClassNameValue;
|
|
7
|
+
content?: import("tailwind-merge").ClassNameValue;
|
|
8
|
+
overlay?: import("tailwind-merge").ClassNameValue;
|
|
9
|
+
container?: import("tailwind-merge").ClassNameValue;
|
|
10
|
+
actions?: import("tailwind-merge").ClassNameValue;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
} | {
|
|
14
|
+
[x: string]: {
|
|
15
|
+
[x: string]: import("tailwind-merge").ClassNameValue | {
|
|
16
|
+
title?: import("tailwind-merge").ClassNameValue;
|
|
17
|
+
content?: import("tailwind-merge").ClassNameValue;
|
|
18
|
+
overlay?: import("tailwind-merge").ClassNameValue;
|
|
19
|
+
container?: import("tailwind-merge").ClassNameValue;
|
|
20
|
+
actions?: import("tailwind-merge").ClassNameValue;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
} | {}, {
|
|
24
|
+
overlay: string[];
|
|
25
|
+
content: string[];
|
|
26
|
+
title: string[];
|
|
27
|
+
container: string[];
|
|
28
|
+
actions: string[];
|
|
29
|
+
}, undefined, {
|
|
30
|
+
[key: string]: {
|
|
31
|
+
[key: string]: import("tailwind-merge").ClassNameValue | {
|
|
32
|
+
title?: import("tailwind-merge").ClassNameValue;
|
|
33
|
+
content?: import("tailwind-merge").ClassNameValue;
|
|
34
|
+
overlay?: import("tailwind-merge").ClassNameValue;
|
|
35
|
+
container?: import("tailwind-merge").ClassNameValue;
|
|
36
|
+
actions?: import("tailwind-merge").ClassNameValue;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
} | {}, {
|
|
40
|
+
overlay: string[];
|
|
41
|
+
content: string[];
|
|
42
|
+
title: string[];
|
|
43
|
+
container: string[];
|
|
44
|
+
actions: string[];
|
|
45
|
+
}, import("tailwind-variants").TVReturnType<unknown, {
|
|
46
|
+
overlay: string[];
|
|
47
|
+
content: string[];
|
|
48
|
+
title: string[];
|
|
49
|
+
container: string[];
|
|
50
|
+
actions: string[];
|
|
51
|
+
}, undefined, unknown, unknown, undefined>>;
|
|
52
|
+
type ModalVariants = VariantProps<typeof modalStyles>;
|
|
53
|
+
interface ModalProps extends ModalVariants {
|
|
54
|
+
title: string;
|
|
55
|
+
open: boolean;
|
|
56
|
+
onOpenChange: (isOpen: boolean) => void;
|
|
57
|
+
children: React.ReactNode;
|
|
58
|
+
}
|
|
59
|
+
export declare const Modal: React.FC<ModalProps> & {
|
|
60
|
+
Body: typeof ModalBody;
|
|
61
|
+
Actions: typeof ModalActions;
|
|
62
|
+
};
|
|
63
|
+
export declare function ModalBody({ children }: {
|
|
64
|
+
children: React.ReactNode;
|
|
65
|
+
}): React.ReactNode;
|
|
66
|
+
export declare namespace ModalBody {
|
|
67
|
+
var displayName: string;
|
|
68
|
+
}
|
|
69
|
+
export declare function ModalActions({ children }: {
|
|
70
|
+
children: React.ReactNode;
|
|
71
|
+
}): React.ReactNode;
|
|
72
|
+
export declare namespace ModalActions {
|
|
73
|
+
var displayName: string;
|
|
74
|
+
}
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as RadixDialog from "@radix-ui/react-dialog";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { tv } from "tailwind-variants";
|
|
5
|
+
import { Cancel } from "../icons/generated";
|
|
6
|
+
import { Heading } from "../typography/Heading";
|
|
7
|
+
const modalStyles = tv({
|
|
8
|
+
slots: {
|
|
9
|
+
overlay: ["fixed", "inset-0", "bg-black/20"],
|
|
10
|
+
content: [
|
|
11
|
+
"fixed",
|
|
12
|
+
"top-1/2",
|
|
13
|
+
"left-1/2",
|
|
14
|
+
"w-full",
|
|
15
|
+
"max-w-md",
|
|
16
|
+
"-translate-x-1/2",
|
|
17
|
+
"-translate-y-1/2",
|
|
18
|
+
"rounded-2xl",
|
|
19
|
+
"bg-white",
|
|
20
|
+
],
|
|
21
|
+
title: [
|
|
22
|
+
"flex",
|
|
23
|
+
"justify-between",
|
|
24
|
+
"rounded-t-2xl",
|
|
25
|
+
"bg-violet-600",
|
|
26
|
+
"pt-6",
|
|
27
|
+
"pr-8",
|
|
28
|
+
"pb-6",
|
|
29
|
+
"pl-8",
|
|
30
|
+
"text-white",
|
|
31
|
+
],
|
|
32
|
+
container: ["flex", "flex-col", "items-center", "gap-12", "p-8"],
|
|
33
|
+
actions: ["flex", "items-center", "gap-4"],
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
export const Modal = (props) => {
|
|
37
|
+
let modalBody = null;
|
|
38
|
+
let modalActions = null;
|
|
39
|
+
React.Children.forEach(props.children, (child) => {
|
|
40
|
+
if (!React.isValidElement(child))
|
|
41
|
+
return;
|
|
42
|
+
switch (child.type) {
|
|
43
|
+
case ModalBody:
|
|
44
|
+
modalBody = child;
|
|
45
|
+
break;
|
|
46
|
+
case ModalActions:
|
|
47
|
+
modalActions = child;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
const { overlay, content, title, container, actions } = modalStyles(props);
|
|
52
|
+
return (_jsx(RadixDialog.Root, { open: props.open, onOpenChange: props.onOpenChange, children: _jsxs(RadixDialog.Portal, { children: [_jsx(RadixDialog.Overlay, { className: overlay() }), _jsxs(RadixDialog.Content, { className: content(), children: [_jsxs(RadixDialog.Title, { className: title(), children: [_jsx(Heading, { as: "span", size: "3", children: props.title }), _jsx(RadixDialog.Close, { children: _jsx(Cancel, {}) })] }), _jsxs("div", { className: container(), children: [_jsx("div", { children: modalBody }), _jsx("div", { className: actions(), children: modalActions })] })] })] }) }));
|
|
53
|
+
};
|
|
54
|
+
ModalBody.displayName = "ModalBody";
|
|
55
|
+
export function ModalBody({ children }) {
|
|
56
|
+
return children;
|
|
57
|
+
}
|
|
58
|
+
ModalActions.displayName = "ModalActions";
|
|
59
|
+
export function ModalActions({ children }) {
|
|
60
|
+
return children;
|
|
61
|
+
}
|
|
62
|
+
Modal.Body = ModalBody;
|
|
63
|
+
Modal.Actions = ModalActions;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { describe, expect, test } from "vitest";
|
|
4
|
+
import { Button } from "../button/Button";
|
|
5
|
+
import { Cancel, Checkmark } from "../icons/generated";
|
|
6
|
+
import { Modal } from "./Modal";
|
|
7
|
+
describe("Modal", () => {
|
|
8
|
+
test("should open modal", async () => {
|
|
9
|
+
// Arrange
|
|
10
|
+
render(_jsx("div", { children: _jsxs(Modal, { open: true, onOpenChange: () => { }, title: "Modal", children: [_jsx(Modal.Body, { children: "Body" }), _jsxs(Modal.Actions, { children: [_jsx(Button, { intent: "primary", size: "md", label: "Exit", onClick: () => { }, children: _jsx(Cancel, {}) }), _jsx(Button, { intent: "secondary", size: "md", label: "Save", onClick: () => { }, children: _jsx(Checkmark, {}) })] })] }) }));
|
|
11
|
+
// Assert
|
|
12
|
+
expect(screen.getByRole("dialog")).toBeVisible();
|
|
13
|
+
expect(screen.getByText(/body/i)).toBeVisible();
|
|
14
|
+
expect(screen.getByText(/modal/i)).toBeVisible();
|
|
15
|
+
expect(screen.getByRole("button", { name: /exit/i })).toBeVisible();
|
|
16
|
+
expect(screen.getByRole("button", { name: /save/i })).toBeVisible();
|
|
17
|
+
});
|
|
18
|
+
test("should open modal", async () => {
|
|
19
|
+
// Arrange
|
|
20
|
+
render(_jsx("div", { children: _jsxs(Modal, { open: false, onOpenChange: () => { }, title: "Modal", children: [_jsx(Modal.Body, { children: "Body" }), _jsxs(Modal.Actions, { children: [_jsx(Button, { intent: "primary", size: "md", label: "Exit", onClick: () => { }, children: _jsx(Cancel, {}) }), _jsx(Button, { intent: "secondary", size: "md", label: "Save", onClick: () => { }, children: _jsx(Checkmark, {}) })] })] }) }));
|
|
21
|
+
// Assert
|
|
22
|
+
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type VariantProps } from "tailwind-variants";
|
|
3
|
+
import type { IconBaseProps } from "../icons/IconBase";
|
|
4
|
+
declare const naviButtonStyles: import("tailwind-variants").TVReturnType<{
|
|
5
|
+
intent: {
|
|
6
|
+
secondary: string[];
|
|
7
|
+
};
|
|
8
|
+
}, undefined, string[], {
|
|
9
|
+
intent: {
|
|
10
|
+
secondary: string[];
|
|
11
|
+
};
|
|
12
|
+
}, undefined, import("tailwind-variants").TVReturnType<{
|
|
13
|
+
intent: {
|
|
14
|
+
secondary: string[];
|
|
15
|
+
};
|
|
16
|
+
}, undefined, string[], unknown, unknown, undefined>>;
|
|
17
|
+
type NaviButtonVariants = VariantProps<typeof naviButtonStyles>;
|
|
18
|
+
type NaviButtonIntent = "secondary";
|
|
19
|
+
interface NaviButtonProps extends NaviButtonVariants {
|
|
20
|
+
label: string;
|
|
21
|
+
intent?: NaviButtonIntent;
|
|
22
|
+
onClick: () => void;
|
|
23
|
+
children: React.ReactElement<IconBaseProps>;
|
|
24
|
+
}
|
|
25
|
+
export declare const NaviButton: ({ intent, ...props }: NaviButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { tv } from "tailwind-variants";
|
|
4
|
+
import { Label } from "../typography/Label";
|
|
5
|
+
const naviButtonStyles = tv({
|
|
6
|
+
base: [
|
|
7
|
+
"flex",
|
|
8
|
+
"flex-col",
|
|
9
|
+
"gap-1",
|
|
10
|
+
"transition",
|
|
11
|
+
"duration-300",
|
|
12
|
+
"ease-in-out",
|
|
13
|
+
"text-white",
|
|
14
|
+
"rounded-lg",
|
|
15
|
+
"items-center",
|
|
16
|
+
"pt-3",
|
|
17
|
+
"pb-3",
|
|
18
|
+
"pl-2",
|
|
19
|
+
"pr-2",
|
|
20
|
+
],
|
|
21
|
+
variants: {
|
|
22
|
+
intent: {
|
|
23
|
+
secondary: ["bg-violet-600", "hover:bg-violet-700"],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
export const NaviButton = ({ intent = "secondary", ...props }) => {
|
|
28
|
+
return (_jsxs("button", { className: naviButtonStyles({ intent, ...props }), onClick: props.onClick, "aria-label": props.label, children: [props.children, _jsx(Label, { as: "span", size: "sm", children: props.label })] }));
|
|
29
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
3
|
+
import { describe, expect, test, vi } from "vitest";
|
|
4
|
+
import { Mumble } from "../icons/generated";
|
|
5
|
+
import { NaviButton } from "./NaviButton";
|
|
6
|
+
describe("NaviButton", () => {
|
|
7
|
+
test("should render button with icon", async () => {
|
|
8
|
+
// Arrange
|
|
9
|
+
render(_jsx(NaviButton, { label: "button", onClick: vi.fn(), children: _jsx(Mumble, {}) }));
|
|
10
|
+
// Assert
|
|
11
|
+
expect(screen.getByRole("button")).toBeVisible();
|
|
12
|
+
expect(screen.getByRole("button")).toHaveTextContent("button");
|
|
13
|
+
expect(screen.getByText("Mumble")).toBeVisible();
|
|
14
|
+
});
|
|
15
|
+
test("should call onClick when clicked", () => {
|
|
16
|
+
// Arrange
|
|
17
|
+
const onClick = vi.fn();
|
|
18
|
+
render(_jsx(NaviButton, { label: "button", onClick: onClick, children: _jsx(Mumble, {}) }));
|
|
19
|
+
fireEvent.click(screen.getByRole("button"));
|
|
20
|
+
expect(onClick).toHaveBeenCalled();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type VariantProps } from "tailwind-variants";
|
|
3
|
+
declare const naviUserButtonStyles: import("tailwind-variants").TVReturnType<{
|
|
4
|
+
intent: {
|
|
5
|
+
secondary: string[];
|
|
6
|
+
};
|
|
7
|
+
}, undefined, string[], {
|
|
8
|
+
intent: {
|
|
9
|
+
secondary: string[];
|
|
10
|
+
};
|
|
11
|
+
}, undefined, import("tailwind-variants").TVReturnType<{
|
|
12
|
+
intent: {
|
|
13
|
+
secondary: string[];
|
|
14
|
+
};
|
|
15
|
+
}, undefined, string[], unknown, unknown, undefined>>;
|
|
16
|
+
type NaviUserButtonVariants = VariantProps<typeof naviUserButtonStyles>;
|
|
17
|
+
type NaviUserButtonIntent = "secondary";
|
|
18
|
+
interface NaviUserButtonProps extends NaviUserButtonVariants {
|
|
19
|
+
ariaLabel: string;
|
|
20
|
+
intent?: NaviUserButtonIntent;
|
|
21
|
+
src: string;
|
|
22
|
+
onClick: () => void;
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
export declare const NaviUserButton: ({ intent, ...props }: NaviUserButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { tv } from "tailwind-variants";
|
|
4
|
+
import { Avatar } from "../avatar/Avatar";
|
|
5
|
+
const naviUserButtonStyles = tv({
|
|
6
|
+
base: [
|
|
7
|
+
"flex",
|
|
8
|
+
"flex-col",
|
|
9
|
+
"gap-1",
|
|
10
|
+
"transition",
|
|
11
|
+
"duration-300",
|
|
12
|
+
"ease-in-out",
|
|
13
|
+
"text-white",
|
|
14
|
+
"rounded-lg",
|
|
15
|
+
"items-center",
|
|
16
|
+
"pt-3",
|
|
17
|
+
"pb-3",
|
|
18
|
+
"pl-2",
|
|
19
|
+
"pr-2",
|
|
20
|
+
],
|
|
21
|
+
variants: {
|
|
22
|
+
intent: {
|
|
23
|
+
secondary: ["bg-violet-600", "hover:bg-violet-700"],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
export const NaviUserButton = ({ intent = "secondary", ...props }) => {
|
|
28
|
+
return (_jsx("button", { className: naviUserButtonStyles({ intent, ...props }), onClick: props.onClick, "aria-label": props.ariaLabel, children: _jsx(Avatar, { label: props.ariaLabel, size: "sm", src: props.src, children: props.children }) }));
|
|
29
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type VariantProps } from "tailwind-variants";
|
|
2
|
+
import type { IconBaseProps } from "../icons/IconBase";
|
|
3
|
+
declare const roundButtonStyles: import("tailwind-variants").TVReturnType<{
|
|
4
|
+
intent: {
|
|
5
|
+
primary: string[];
|
|
6
|
+
};
|
|
7
|
+
}, undefined, string[], {
|
|
8
|
+
intent: {
|
|
9
|
+
primary: string[];
|
|
10
|
+
};
|
|
11
|
+
}, undefined, import("tailwind-variants").TVReturnType<{
|
|
12
|
+
intent: {
|
|
13
|
+
primary: string[];
|
|
14
|
+
};
|
|
15
|
+
}, undefined, string[], unknown, unknown, undefined>>;
|
|
16
|
+
type RoundButtonVariants = VariantProps<typeof roundButtonStyles>;
|
|
17
|
+
type RoundButtonIntent = "primary";
|
|
18
|
+
interface RoundButtonProps extends RoundButtonVariants {
|
|
19
|
+
intent?: RoundButtonIntent;
|
|
20
|
+
ariaLabel: string;
|
|
21
|
+
onClick: () => void;
|
|
22
|
+
children: React.ReactElement<IconBaseProps>;
|
|
23
|
+
}
|
|
24
|
+
export declare const RoundButton: ({ intent, ...props }: RoundButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { tv } from "tailwind-variants";
|
|
3
|
+
const roundButtonStyles = tv({
|
|
4
|
+
base: [
|
|
5
|
+
"text-white",
|
|
6
|
+
"rounded-full",
|
|
7
|
+
"hover:ring-3",
|
|
8
|
+
"active:ring-4",
|
|
9
|
+
"transition",
|
|
10
|
+
"duration-350",
|
|
11
|
+
"active:duration-300",
|
|
12
|
+
"ease-in-out",
|
|
13
|
+
"p-4",
|
|
14
|
+
],
|
|
15
|
+
variants: {
|
|
16
|
+
intent: {
|
|
17
|
+
primary: [
|
|
18
|
+
"bg-slate-600",
|
|
19
|
+
"hover:bg-slate-700",
|
|
20
|
+
"hover:ring-slate-100",
|
|
21
|
+
"active:ring-violet-200",
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
export const RoundButton = ({ intent = "primary", ...props }) => {
|
|
27
|
+
return (_jsx("button", { className: roundButtonStyles({ intent }), onClick: props.onClick, "aria-label": props.ariaLabel, children: props.children }));
|
|
28
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
3
|
+
import { describe, expect, test, vi } from "vitest";
|
|
4
|
+
import { Mumble } from "../icons/generated";
|
|
5
|
+
import { RoundButton } from "./RoundButton";
|
|
6
|
+
describe("RoundButton", () => {
|
|
7
|
+
test("should render button with icon", async () => {
|
|
8
|
+
// Arrange
|
|
9
|
+
render(_jsx(RoundButton, { onClick: vi.fn(), ariaLabel: "Mumble", children: _jsx(Mumble, {}) }));
|
|
10
|
+
// Assert
|
|
11
|
+
expect(screen.getByRole("button")).toBeVisible();
|
|
12
|
+
expect(screen.getByText("Mumble")).toBeVisible();
|
|
13
|
+
});
|
|
14
|
+
test("should call onChange when second tab clicked", () => {
|
|
15
|
+
// Arrange
|
|
16
|
+
const onClick = vi.fn();
|
|
17
|
+
render(_jsx(RoundButton, { onClick: onClick, ariaLabel: "Mumble", children: _jsx(Mumble, {}) }));
|
|
18
|
+
fireEvent.click(screen.getByRole("button"));
|
|
19
|
+
expect(onClick).toHaveBeenCalled();
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type VariantProps } from "tailwind-variants";
|
|
3
|
+
declare const tabItemStyles: import("tailwind-variants").TVReturnType<{} | {} | {}, undefined, undefined, {} | {}, undefined, import("tailwind-variants").TVReturnType<unknown, undefined, undefined, unknown, unknown, undefined>>;
|
|
4
|
+
type TabItemVariants = VariantProps<typeof tabItemStyles>;
|
|
5
|
+
export interface TabItemProps extends TabItemVariants {
|
|
6
|
+
value: string;
|
|
7
|
+
label: string;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
export declare const TabItem: (props: TabItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as RadixTabs from "@radix-ui/react-tabs";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { tv } from "tailwind-variants";
|
|
5
|
+
const tabItemStyles = tv({});
|
|
6
|
+
export const TabItem = (props) => {
|
|
7
|
+
return (_jsx(RadixTabs.Content, { value: props.value, className: tabItemStyles(props), children: React.Children.map(props.children, (child, index) => {
|
|
8
|
+
if (React.isValidElement(child)) {
|
|
9
|
+
return React.cloneElement(child, { key: props.value || index });
|
|
10
|
+
}
|
|
11
|
+
return child;
|
|
12
|
+
}) }));
|
|
13
|
+
};
|