@syscore/ui-library 1.1.9 → 1.1.11
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/client/App.tsx +47 -0
- package/client/components/icons/ConceptIcons.tsx +667 -0
- package/client/components/icons/NavAccount.tsx +31 -0
- package/client/components/icons/NavBullet.tsx +19 -0
- package/client/components/icons/NavLogo.tsx +36 -0
- package/client/components/icons/ProviderBadges.tsx +295 -0
- package/client/components/icons/ProviderSeals.tsx +319 -0
- package/client/components/icons/SealHealthSafetyRating.tsx +65 -0
- package/client/components/icons/SealIwbiMember.tsx +86 -0
- package/client/components/icons/SealWell.tsx +84 -0
- package/client/components/icons/SealWellCertification.tsx +138 -0
- package/client/components/icons/SealWellCommunity.tsx +122 -0
- package/client/components/icons/SealWellResidence.tsx +122 -0
- package/client/components/icons/SealWorksWithWell.tsx +140 -0
- package/client/components/icons/UtilityAccordion.tsx +21 -0
- package/client/components/icons/UtilityChevronDown.tsx +36 -0
- package/client/components/icons/UtilityClassification.tsx +45 -0
- package/client/components/icons/UtilityClose.tsx +41 -0
- package/client/components/icons/UtilityDrag.tsx +69 -0
- package/client/components/icons/UtilityEdit.tsx +42 -0
- package/client/components/icons/UtilityOptions.tsx +45 -0
- package/client/components/icons/UtilityPortfolio.tsx +87 -0
- package/client/components/icons/UtilityReset.tsx +41 -0
- package/client/components/icons/UtilityScoring.tsx +43 -0
- package/client/components/icons/UtilitySearch.tsx +38 -0
- package/client/components/icons/UtilitySort.tsx +52 -0
- package/client/components/icons/UtilityText.tsx +34 -0
- package/client/components/icons/WaterMarkWWWProducts.tsx +26 -0
- package/client/components/icons/WaterMarkWellProjects.tsx +30 -0
- package/client/components/icons/WatermarkMemberOrg.tsx +59 -0
- package/client/components/icons/WellSeal.tsx +79 -0
- package/client/components/icons/X.tsx +35 -0
- package/client/components/ui/Navigation.tsx +958 -0
- package/client/components/ui/SearchField.tsx +157 -0
- package/client/components/ui/StrategyTable.tsx +303 -0
- package/client/components/ui/Tag.tsx +127 -0
- package/client/components/ui/alert-dialog.tsx +1 -1
- package/client/components/ui/button.tsx +67 -127
- package/client/components/ui/calendar.tsx +2 -2
- package/client/components/ui/card.tsx +10 -13
- package/client/components/ui/carousel.tsx +56 -46
- package/client/components/ui/command.tsx +27 -16
- package/client/components/ui/dialog.tsx +113 -92
- package/client/components/ui/label.tsx +5 -3
- package/client/components/ui/menubar.tsx +1 -1
- package/client/components/ui/pagination.tsx +3 -3
- package/client/components/ui/sidebar.tsx +1 -1
- package/client/components/ui/tabs.tsx +350 -5
- package/client/components/ui/toggle.tsx +71 -19
- package/client/components/ui/tooltip.tsx +69 -18
- package/client/global.css +635 -58
- package/client/hooks/UseTabs.tsx +35 -0
- package/client/hooks/use-mobile.tsx +21 -0
- package/client/hooks/use-segmented-control.ts +42 -0
- package/client/hooks/use-toast.ts +188 -0
- package/client/pages/Index.tsx +88 -0
- package/client/pages/NotFound.tsx +29 -0
- package/client/ui/Accordion/Accordion.stories.tsx +74 -0
- package/client/ui/Alert/Alert.stories.tsx +82 -0
- package/client/ui/AlertDialog/AlertDialog.stories.tsx +106 -0
- package/client/ui/AspectRatio.stories.tsx +78 -0
- package/client/ui/Avatar/Avatar.stories.tsx +94 -0
- package/client/ui/Badge/Badge.stories.tsx +60 -0
- package/client/ui/Breadcrumb/Breadcrumb.stories.tsx +97 -0
- package/client/ui/Button.stories.tsx +429 -0
- package/client/ui/Calendar/Calendar.stories.tsx +99 -0
- package/client/ui/Card.stories.tsx +84 -0
- package/client/ui/Carousel/Carousel.stories.tsx +85 -0
- package/client/ui/Chart/Chart.stories.tsx +58 -0
- package/client/ui/Checkbox/Checkbox.stories.tsx +112 -0
- package/client/ui/Collapsible/Collapsible.stories.tsx +101 -0
- package/client/ui/Colors.stories.tsx +1041 -0
- package/client/ui/Command/Command.stories.tsx +97 -0
- package/client/ui/ContextMenu/ContextMenu.stories.tsx +74 -0
- package/client/ui/Dialog.stories.tsx +69 -0
- package/client/ui/Drawer/Drawer.stories.tsx +87 -0
- package/client/ui/DropdownMenu/DropdownMenu.stories.tsx +139 -0
- package/client/ui/Form/Form.stories.tsx +74 -0
- package/client/ui/HoverCard/HoverCard.stories.tsx +94 -0
- package/client/ui/Icons.stories.tsx +328 -0
- package/client/ui/Input/Input.stories.tsx +69 -0
- package/client/ui/InputOTP/InputOTP.stories.tsx +85 -0
- package/client/ui/Label.stories.tsx +66 -0
- package/client/ui/Menubar/Menubar.stories.tsx +88 -0
- package/client/ui/Navigation.stories.tsx +57 -0
- package/client/ui/NavigationMenu/NavigationMenu.stories.tsx +106 -0
- package/client/ui/Pagination/Pagination.stories.tsx +115 -0
- package/client/ui/Popover/Popover.stories.tsx +99 -0
- package/client/ui/Progress/Progress.stories.tsx +63 -0
- package/client/ui/RadioGroup/RadioGroup.stories.tsx +110 -0
- package/client/ui/Resizable/Resizable.stories.tsx +88 -0
- package/client/ui/ScrollArea/ScrollArea.stories.tsx +64 -0
- package/client/ui/SearchField.stories.tsx +63 -0
- package/client/ui/Select/Select.stories.tsx +111 -0
- package/client/ui/Separator/Separator.stories.tsx +67 -0
- package/client/ui/Sheet/Sheet.stories.tsx +138 -0
- package/client/ui/Sidebar/Sidebar.stories.tsx +92 -0
- package/client/ui/Skeleton/Skeleton.stories.tsx +65 -0
- package/client/ui/Slider/Slider.stories.tsx +101 -0
- package/client/ui/Sonner/Sonner.stories.tsx +48 -0
- package/client/ui/StrategyTable.stories.tsx +138 -0
- package/client/ui/Switch/Switch.stories.tsx +96 -0
- package/client/ui/Table/Table.stories.tsx +135 -0
- package/client/ui/Tabs.stories.tsx +33 -0
- package/client/ui/Tag.stories.tsx +190 -0
- package/client/ui/Textarea/Textarea.stories.tsx +56 -0
- package/client/ui/Toast/Toast.stories.tsx +76 -0
- package/client/ui/Toaster/Toaster.stories.tsx +52 -0
- package/client/ui/Toggle.stories.tsx +248 -0
- package/client/ui/ToggleGroup/ToggleGroup.stories.tsx +88 -0
- package/client/ui/Tooltip.stories.tsx +72 -0
- package/client/ui/Typography.stories.tsx +421 -0
- package/client/ui/WELLDashboard/WELLDashboard.stories.tsx +115 -0
- package/client/ui/WELLDashboard/index.tsx +221 -0
- package/client/vite-env.d.ts +1 -0
- package/dist/ui/fonts/FT-Made/FTMade-Regular.otf +0 -0
- package/dist/ui/fonts/FT-Made/FTMade-Regular.ttf +0 -0
- package/dist/ui/fonts/FT-Made/FTMade-Regular.woff +0 -0
- package/dist/ui/fonts/FT-Made/FTMade-Regular.woff2 +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-black.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-blackitalic.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-bold.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-bolditalic.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-extrabold.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-extrabolditalic.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-extralight.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-extralightitalic.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-italic.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-light.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-lightitalic.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-medium.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-mediumitalic.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-regular.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-semibold.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-semibolditalic.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-thin.otf +0 -0
- package/dist/ui/fonts/Mazzard-M/mazzardsoftm-thinitalic.otf +0 -0
- package/dist/ui/index.cjs.js +1 -1
- package/dist/ui/index.d.ts +2 -1
- package/dist/ui/index.es.js +563 -329
- package/package.json +4 -2
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useState, useCallback } from "react";
|
|
2
|
+
|
|
3
|
+
export type Tab = {
|
|
4
|
+
label: string;
|
|
5
|
+
value: string;
|
|
6
|
+
subRoutes?: unknown;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
interface UseTabsProps {
|
|
10
|
+
tabs: Tab[];
|
|
11
|
+
initialTabId: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function useTabs({ tabs, initialTabId }: UseTabsProps) {
|
|
15
|
+
const initialIndex = tabs.findIndex((tab) => tab.value === initialTabId);
|
|
16
|
+
const [selectedTabIndex, setSelectedTabIndex] = useState(
|
|
17
|
+
initialIndex >= 0 ? initialIndex : 0
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const setSelectedTab = useCallback(([index, direction]: [number, number]) => {
|
|
21
|
+
setSelectedTabIndex(index);
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
const selectedTab = tabs[selectedTabIndex] || tabs[0];
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
tabProps: {
|
|
28
|
+
tabs,
|
|
29
|
+
selectedTabIndex,
|
|
30
|
+
setSelectedTab,
|
|
31
|
+
},
|
|
32
|
+
selectedTab,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768;
|
|
4
|
+
|
|
5
|
+
export function useIsMobile() {
|
|
6
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
|
7
|
+
undefined,
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
React.useEffect(() => {
|
|
11
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
12
|
+
const onChange = () => {
|
|
13
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
14
|
+
};
|
|
15
|
+
mql.addEventListener("change", onChange);
|
|
16
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
17
|
+
return () => mql.removeEventListener("change", onChange);
|
|
18
|
+
}, []);
|
|
19
|
+
|
|
20
|
+
return !!isMobile;
|
|
21
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from "react";
|
|
2
|
+
|
|
3
|
+
export interface UseSegmentedControlOptions<T extends string> {
|
|
4
|
+
value?: T;
|
|
5
|
+
defaultValue?: T;
|
|
6
|
+
onValueChange?: (value: T) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function useSegmentedControl<T extends string>({
|
|
10
|
+
value,
|
|
11
|
+
defaultValue,
|
|
12
|
+
onValueChange,
|
|
13
|
+
}: UseSegmentedControlOptions<T>) {
|
|
14
|
+
const [internalValue, setInternalValue] = useState<T | undefined>(
|
|
15
|
+
value ?? defaultValue,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
// Sync with controlled value
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (value !== undefined) {
|
|
21
|
+
setInternalValue(value);
|
|
22
|
+
}
|
|
23
|
+
}, [value]);
|
|
24
|
+
|
|
25
|
+
const handleValueChange = useCallback(
|
|
26
|
+
(newValue: T) => {
|
|
27
|
+
if (value === undefined) {
|
|
28
|
+
setInternalValue(newValue);
|
|
29
|
+
}
|
|
30
|
+
onValueChange?.(newValue);
|
|
31
|
+
},
|
|
32
|
+
[value, onValueChange],
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const currentValue = value ?? internalValue;
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
value: currentValue,
|
|
39
|
+
onValueChange: handleValueChange,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import type { ToastActionElement, ToastProps } from "@/components/ui/toast";
|
|
4
|
+
|
|
5
|
+
const TOAST_LIMIT = 1;
|
|
6
|
+
const TOAST_REMOVE_DELAY = 1000000;
|
|
7
|
+
|
|
8
|
+
type ToasterToast = ToastProps & {
|
|
9
|
+
id: string;
|
|
10
|
+
title?: React.ReactNode;
|
|
11
|
+
description?: React.ReactNode;
|
|
12
|
+
action?: ToastActionElement;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const actionTypes = {
|
|
16
|
+
ADD_TOAST: "ADD_TOAST",
|
|
17
|
+
UPDATE_TOAST: "UPDATE_TOAST",
|
|
18
|
+
DISMISS_TOAST: "DISMISS_TOAST",
|
|
19
|
+
REMOVE_TOAST: "REMOVE_TOAST",
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
let count = 0;
|
|
23
|
+
|
|
24
|
+
function genId() {
|
|
25
|
+
count = (count + 1) % Number.MAX_SAFE_INTEGER;
|
|
26
|
+
return count.toString();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type ActionType = typeof actionTypes;
|
|
30
|
+
|
|
31
|
+
type Action =
|
|
32
|
+
| {
|
|
33
|
+
type: ActionType["ADD_TOAST"];
|
|
34
|
+
toast: ToasterToast;
|
|
35
|
+
}
|
|
36
|
+
| {
|
|
37
|
+
type: ActionType["UPDATE_TOAST"];
|
|
38
|
+
toast: Partial<ToasterToast>;
|
|
39
|
+
}
|
|
40
|
+
| {
|
|
41
|
+
type: ActionType["DISMISS_TOAST"];
|
|
42
|
+
toastId?: ToasterToast["id"];
|
|
43
|
+
}
|
|
44
|
+
| {
|
|
45
|
+
type: ActionType["REMOVE_TOAST"];
|
|
46
|
+
toastId?: ToasterToast["id"];
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
interface State {
|
|
50
|
+
toasts: ToasterToast[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
|
|
54
|
+
|
|
55
|
+
const addToRemoveQueue = (toastId: string) => {
|
|
56
|
+
if (toastTimeouts.has(toastId)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const timeout = setTimeout(() => {
|
|
61
|
+
toastTimeouts.delete(toastId);
|
|
62
|
+
dispatch({
|
|
63
|
+
type: "REMOVE_TOAST",
|
|
64
|
+
toastId: toastId,
|
|
65
|
+
});
|
|
66
|
+
}, TOAST_REMOVE_DELAY);
|
|
67
|
+
|
|
68
|
+
toastTimeouts.set(toastId, timeout);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const reducer = (state: State, action: Action): State => {
|
|
72
|
+
switch (action.type) {
|
|
73
|
+
case "ADD_TOAST":
|
|
74
|
+
return {
|
|
75
|
+
...state,
|
|
76
|
+
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
case "UPDATE_TOAST":
|
|
80
|
+
return {
|
|
81
|
+
...state,
|
|
82
|
+
toasts: state.toasts.map((t) =>
|
|
83
|
+
t.id === action.toast.id ? { ...t, ...action.toast } : t,
|
|
84
|
+
),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
case "DISMISS_TOAST": {
|
|
88
|
+
const { toastId } = action;
|
|
89
|
+
|
|
90
|
+
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
|
91
|
+
// but I'll keep it here for simplicity
|
|
92
|
+
if (toastId) {
|
|
93
|
+
addToRemoveQueue(toastId);
|
|
94
|
+
} else {
|
|
95
|
+
state.toasts.forEach((toast) => {
|
|
96
|
+
addToRemoveQueue(toast.id);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
...state,
|
|
102
|
+
toasts: state.toasts.map((t) =>
|
|
103
|
+
t.id === toastId || toastId === undefined
|
|
104
|
+
? {
|
|
105
|
+
...t,
|
|
106
|
+
open: false,
|
|
107
|
+
}
|
|
108
|
+
: t,
|
|
109
|
+
),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
case "REMOVE_TOAST":
|
|
113
|
+
if (action.toastId === undefined) {
|
|
114
|
+
return {
|
|
115
|
+
...state,
|
|
116
|
+
toasts: [],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
...state,
|
|
121
|
+
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const listeners: Array<(state: State) => void> = [];
|
|
127
|
+
|
|
128
|
+
let memoryState: State = { toasts: [] };
|
|
129
|
+
|
|
130
|
+
function dispatch(action: Action) {
|
|
131
|
+
memoryState = reducer(memoryState, action);
|
|
132
|
+
listeners.forEach((listener) => {
|
|
133
|
+
listener(memoryState);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
type Toast = Omit<ToasterToast, "id">;
|
|
138
|
+
|
|
139
|
+
function toast({ ...props }: Toast) {
|
|
140
|
+
const id = genId();
|
|
141
|
+
|
|
142
|
+
const update = (props: ToasterToast) =>
|
|
143
|
+
dispatch({
|
|
144
|
+
type: "UPDATE_TOAST",
|
|
145
|
+
toast: { ...props, id },
|
|
146
|
+
});
|
|
147
|
+
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
|
|
148
|
+
|
|
149
|
+
dispatch({
|
|
150
|
+
type: "ADD_TOAST",
|
|
151
|
+
toast: {
|
|
152
|
+
...props,
|
|
153
|
+
id,
|
|
154
|
+
open: true,
|
|
155
|
+
onOpenChange: (open) => {
|
|
156
|
+
if (!open) dismiss();
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
id: id,
|
|
163
|
+
dismiss,
|
|
164
|
+
update,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function useToast() {
|
|
169
|
+
const [state, setState] = React.useState<State>(memoryState);
|
|
170
|
+
|
|
171
|
+
React.useEffect(() => {
|
|
172
|
+
listeners.push(setState);
|
|
173
|
+
return () => {
|
|
174
|
+
const index = listeners.indexOf(setState);
|
|
175
|
+
if (index > -1) {
|
|
176
|
+
listeners.splice(index, 1);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}, [state]);
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
...state,
|
|
183
|
+
toast,
|
|
184
|
+
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export { useToast, toast };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { Button } from "../ui/Button";
|
|
3
|
+
|
|
4
|
+
export default function Index() {
|
|
5
|
+
const commands = useMemo(
|
|
6
|
+
() => [
|
|
7
|
+
{ label: "Start Storybook", cmd: "pnpm storybook" },
|
|
8
|
+
{ label: "Build UI library", cmd: "pnpm build:ui" },
|
|
9
|
+
],
|
|
10
|
+
[],
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="min-h-screen bg-[radial-gradient(60%_80%_at_50%_-20%,hsl(252_95%_90%/.6),transparent_70%),linear-gradient(to_bottom_right,hsl(258_90%_98%),hsl(240_20%_98%))]">
|
|
15
|
+
<section className="px-6 py-20 md:py-28">
|
|
16
|
+
<div className="mx-auto max-w-6xl text-center">
|
|
17
|
+
<span className="inline-flex items-center gap-2 rounded-full bg-violet-100 px-3 py-1 text-xs font-semibold text-violet-700 ring-1 ring-inset ring-violet-200">
|
|
18
|
+
React UI Library
|
|
19
|
+
</span>
|
|
20
|
+
<h1 className="mt-6 text-4xl font-extrabold tracking-tight text-foreground sm:text-5xl">
|
|
21
|
+
Elegant, composable components for modern apps
|
|
22
|
+
</h1>
|
|
23
|
+
<p className="mx-auto mt-4 max-w-2xl text-muted-foreground">
|
|
24
|
+
View and develop components in Storybook. Ship a reusable package
|
|
25
|
+
you can import like any other React library.
|
|
26
|
+
</p>
|
|
27
|
+
<div className="mt-8 flex flex-wrap items-center justify-center gap-3">
|
|
28
|
+
<Button size="lg" className="shadow-lg">
|
|
29
|
+
Open Storybook
|
|
30
|
+
</Button>
|
|
31
|
+
<Button variant="secondary" size="lg">
|
|
32
|
+
Install Library
|
|
33
|
+
</Button>
|
|
34
|
+
</div>
|
|
35
|
+
<div className="mx-auto mt-10 grid max-w-3xl grid-cols-1 gap-3 text-left sm:grid-cols-2">
|
|
36
|
+
{commands.map((c) => (
|
|
37
|
+
<div
|
|
38
|
+
key={c.cmd}
|
|
39
|
+
className="rounded-lg border bg-card/70 p-4 shadow-xs backdrop-blur-sm"
|
|
40
|
+
>
|
|
41
|
+
<p className="text-xs font-semibold text-muted-foreground">
|
|
42
|
+
{c.label}
|
|
43
|
+
</p>
|
|
44
|
+
<code className="mt-1 block select-all rounded-md bg-foreground/95 px-3 py-2 font-mono text-[12px] text-background">
|
|
45
|
+
{c.cmd}
|
|
46
|
+
</code>
|
|
47
|
+
</div>
|
|
48
|
+
))}
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</section>
|
|
52
|
+
|
|
53
|
+
<section className="px-6 pb-24">
|
|
54
|
+
<div className="mx-auto max-w-6xl">
|
|
55
|
+
<div className="mb-6 flex items-end justify-between">
|
|
56
|
+
<h2 className="text-left text-2xl font-bold text-foreground">
|
|
57
|
+
Button component
|
|
58
|
+
</h2>
|
|
59
|
+
<p className="text-sm text-muted-foreground">
|
|
60
|
+
Variants and sizes ready to use
|
|
61
|
+
</p>
|
|
62
|
+
</div>
|
|
63
|
+
<div className="grid grid-cols-1 gap-6 rounded-xl border bg-card/60 p-6 shadow-xs backdrop-blur-sm md:grid-cols-2">
|
|
64
|
+
<div>
|
|
65
|
+
<h3 className="text-sm font-semibold text-foreground">
|
|
66
|
+
Variants
|
|
67
|
+
</h3>
|
|
68
|
+
<div className="mt-3 flex flex-wrap items-center gap-3">
|
|
69
|
+
<Button>Primary</Button>
|
|
70
|
+
<Button variant="secondary">Secondary</Button>
|
|
71
|
+
<Button variant="ghost">Ghost</Button>
|
|
72
|
+
<Button variant="destructive">Destructive</Button>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
<div>
|
|
76
|
+
<h3 className="text-sm font-semibold text-foreground">Sizes</h3>
|
|
77
|
+
<div className="mt-3 flex flex-wrap items-center gap-3">
|
|
78
|
+
<Button size="sm">Small</Button>
|
|
79
|
+
<Button size="md">Medium</Button>
|
|
80
|
+
<Button size="lg">Large</Button>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</section>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useLocation } from "react-router-dom";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
|
|
4
|
+
const NotFound = () => {
|
|
5
|
+
const location = useLocation();
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
console.error(
|
|
9
|
+
"404 Error: User attempted to access non-existent route:",
|
|
10
|
+
location.pathname,
|
|
11
|
+
);
|
|
12
|
+
}, [location.pathname]);
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div className="min-h-screen flex items-center justify-center bg-muted">
|
|
16
|
+
<div className="text-center">
|
|
17
|
+
<h1 className="text-4xl font-bold mb-4 text-foreground">404</h1>
|
|
18
|
+
<p className="text-xl text-muted-foreground mb-4">
|
|
19
|
+
Oops! Page not found
|
|
20
|
+
</p>
|
|
21
|
+
<a href="/" className="text-primary hover:text-primary/80 underline">
|
|
22
|
+
Return to Home
|
|
23
|
+
</a>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default NotFound;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import {
|
|
3
|
+
Accordion,
|
|
4
|
+
AccordionItem,
|
|
5
|
+
AccordionTrigger,
|
|
6
|
+
AccordionContent,
|
|
7
|
+
} from "../../components/ui/accordion";
|
|
8
|
+
|
|
9
|
+
const meta = {
|
|
10
|
+
title: "UI/Accordion",
|
|
11
|
+
component: Accordion,
|
|
12
|
+
tags: ["autodocs"],
|
|
13
|
+
parameters: {
|
|
14
|
+
layout: "padded",
|
|
15
|
+
},
|
|
16
|
+
} satisfies Meta<typeof Accordion>;
|
|
17
|
+
|
|
18
|
+
export default meta;
|
|
19
|
+
|
|
20
|
+
type Story = StoryObj<typeof meta>;
|
|
21
|
+
|
|
22
|
+
export const Default: Story = {
|
|
23
|
+
render: () => (
|
|
24
|
+
<Accordion type="single" collapsible className="w-full max-w-md">
|
|
25
|
+
<AccordionItem value="item-1">
|
|
26
|
+
<AccordionTrigger>Is it accessible?</AccordionTrigger>
|
|
27
|
+
<AccordionContent>
|
|
28
|
+
Yes. It adheres to the WAI-ARIA design pattern.
|
|
29
|
+
</AccordionContent>
|
|
30
|
+
</AccordionItem>
|
|
31
|
+
<AccordionItem value="item-2">
|
|
32
|
+
<AccordionTrigger>Is it styled?</AccordionTrigger>
|
|
33
|
+
<AccordionContent>
|
|
34
|
+
Yes. It comes with default styles you can customize using CSS.
|
|
35
|
+
</AccordionContent>
|
|
36
|
+
</AccordionItem>
|
|
37
|
+
<AccordionItem value="item-3">
|
|
38
|
+
<AccordionTrigger>Is it animated?</AccordionTrigger>
|
|
39
|
+
<AccordionContent>
|
|
40
|
+
Yes. It's animated by default, but you can disable it if you
|
|
41
|
+
prefer.
|
|
42
|
+
</AccordionContent>
|
|
43
|
+
</AccordionItem>
|
|
44
|
+
</Accordion>
|
|
45
|
+
),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const Multiple: Story = {
|
|
49
|
+
render: () => (
|
|
50
|
+
<Accordion type="multiple" className="w-full max-w-md">
|
|
51
|
+
<AccordionItem value="item-1">
|
|
52
|
+
<AccordionTrigger>Feature One</AccordionTrigger>
|
|
53
|
+
<AccordionContent>
|
|
54
|
+
This is the content for feature one. You can open multiple items at
|
|
55
|
+
once.
|
|
56
|
+
</AccordionContent>
|
|
57
|
+
</AccordionItem>
|
|
58
|
+
<AccordionItem value="item-2">
|
|
59
|
+
<AccordionTrigger>Feature Two</AccordionTrigger>
|
|
60
|
+
<AccordionContent>
|
|
61
|
+
This is the content for feature two. All sections can be expanded
|
|
62
|
+
simultaneously.
|
|
63
|
+
</AccordionContent>
|
|
64
|
+
</AccordionItem>
|
|
65
|
+
<AccordionItem value="item-3">
|
|
66
|
+
<AccordionTrigger>Feature Three</AccordionTrigger>
|
|
67
|
+
<AccordionContent>
|
|
68
|
+
This is the content for feature three. Great for detailed information
|
|
69
|
+
display.
|
|
70
|
+
</AccordionContent>
|
|
71
|
+
</AccordionItem>
|
|
72
|
+
</Accordion>
|
|
73
|
+
),
|
|
74
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Alert, AlertTitle, AlertDescription } from "../../components/ui/alert";
|
|
3
|
+
import { AlertCircle, Terminal, CheckCircle2 } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: "UI/Alert",
|
|
7
|
+
component: Alert,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: "padded",
|
|
11
|
+
},
|
|
12
|
+
} satisfies Meta<typeof Alert>;
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
|
|
16
|
+
type Story = StoryObj<typeof meta>;
|
|
17
|
+
|
|
18
|
+
export const Default: Story = {
|
|
19
|
+
render: () => (
|
|
20
|
+
<Alert>
|
|
21
|
+
<Terminal className="h-4 w-4" />
|
|
22
|
+
<AlertTitle>Heads up!</AlertTitle>
|
|
23
|
+
<AlertDescription>
|
|
24
|
+
You can add components to your app using the code below.
|
|
25
|
+
</AlertDescription>
|
|
26
|
+
</Alert>
|
|
27
|
+
),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const Destructive: Story = {
|
|
31
|
+
render: () => (
|
|
32
|
+
<Alert variant="destructive">
|
|
33
|
+
<AlertCircle className="h-4 w-4" />
|
|
34
|
+
<AlertTitle>Error</AlertTitle>
|
|
35
|
+
<AlertDescription>
|
|
36
|
+
Your session has expired. Please log in again.
|
|
37
|
+
</AlertDescription>
|
|
38
|
+
</Alert>
|
|
39
|
+
),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const Success: Story = {
|
|
43
|
+
render: () => (
|
|
44
|
+
<Alert>
|
|
45
|
+
<CheckCircle2 className="h-4 w-4" />
|
|
46
|
+
<AlertTitle>Success</AlertTitle>
|
|
47
|
+
<AlertDescription>
|
|
48
|
+
Your changes have been saved successfully.
|
|
49
|
+
</AlertDescription>
|
|
50
|
+
</Alert>
|
|
51
|
+
),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const AllVariants: Story = {
|
|
55
|
+
render: () => (
|
|
56
|
+
<div className="space-y-4 max-w-md">
|
|
57
|
+
<Alert>
|
|
58
|
+
<Terminal className="h-4 w-4" />
|
|
59
|
+
<AlertTitle>Heads up!</AlertTitle>
|
|
60
|
+
<AlertDescription>
|
|
61
|
+
You can add components to your app using the code below.
|
|
62
|
+
</AlertDescription>
|
|
63
|
+
</Alert>
|
|
64
|
+
|
|
65
|
+
<Alert variant="destructive">
|
|
66
|
+
<AlertCircle className="h-4 w-4" />
|
|
67
|
+
<AlertTitle>Error</AlertTitle>
|
|
68
|
+
<AlertDescription>
|
|
69
|
+
Your session has expired. Please log in again.
|
|
70
|
+
</AlertDescription>
|
|
71
|
+
</Alert>
|
|
72
|
+
|
|
73
|
+
<Alert>
|
|
74
|
+
<CheckCircle2 className="h-4 w-4" />
|
|
75
|
+
<AlertTitle>Success</AlertTitle>
|
|
76
|
+
<AlertDescription>
|
|
77
|
+
Your changes have been saved successfully.
|
|
78
|
+
</AlertDescription>
|
|
79
|
+
</Alert>
|
|
80
|
+
</div>
|
|
81
|
+
),
|
|
82
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import {
|
|
3
|
+
AlertDialog,
|
|
4
|
+
AlertDialogTrigger,
|
|
5
|
+
AlertDialogContent,
|
|
6
|
+
AlertDialogHeader,
|
|
7
|
+
AlertDialogTitle,
|
|
8
|
+
AlertDialogDescription,
|
|
9
|
+
AlertDialogFooter,
|
|
10
|
+
AlertDialogAction,
|
|
11
|
+
AlertDialogCancel,
|
|
12
|
+
} from "../../components/ui/alert-dialog";
|
|
13
|
+
import { Button } from "../../components/ui/button";
|
|
14
|
+
import { useState } from "react";
|
|
15
|
+
|
|
16
|
+
const meta = {
|
|
17
|
+
title: "UI/AlertDialog",
|
|
18
|
+
component: AlertDialog,
|
|
19
|
+
tags: ["autodocs"],
|
|
20
|
+
parameters: {
|
|
21
|
+
layout: "centered",
|
|
22
|
+
},
|
|
23
|
+
} satisfies Meta<typeof AlertDialog>;
|
|
24
|
+
|
|
25
|
+
export default meta;
|
|
26
|
+
|
|
27
|
+
type Story = StoryObj<typeof meta>;
|
|
28
|
+
|
|
29
|
+
export const Default: Story = {
|
|
30
|
+
render: () => {
|
|
31
|
+
const [open, setOpen] = useState(false);
|
|
32
|
+
return (
|
|
33
|
+
<AlertDialog open={open} onOpenChange={setOpen}>
|
|
34
|
+
<AlertDialogTrigger asChild>
|
|
35
|
+
<Button>Open Alert</Button>
|
|
36
|
+
</AlertDialogTrigger>
|
|
37
|
+
<AlertDialogContent>
|
|
38
|
+
<AlertDialogHeader>
|
|
39
|
+
<AlertDialogTitle>Confirm Action</AlertDialogTitle>
|
|
40
|
+
<AlertDialogDescription>
|
|
41
|
+
Are you sure you want to proceed with this action?
|
|
42
|
+
</AlertDialogDescription>
|
|
43
|
+
</AlertDialogHeader>
|
|
44
|
+
<AlertDialogFooter>
|
|
45
|
+
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
46
|
+
<AlertDialogAction>Continue</AlertDialogAction>
|
|
47
|
+
</AlertDialogFooter>
|
|
48
|
+
</AlertDialogContent>
|
|
49
|
+
</AlertDialog>
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const Delete: Story = {
|
|
55
|
+
render: () => {
|
|
56
|
+
const [open, setOpen] = useState(false);
|
|
57
|
+
return (
|
|
58
|
+
<AlertDialog open={open} onOpenChange={setOpen}>
|
|
59
|
+
<AlertDialogTrigger asChild>
|
|
60
|
+
<Button variant="destructive">Delete Item</Button>
|
|
61
|
+
</AlertDialogTrigger>
|
|
62
|
+
<AlertDialogContent>
|
|
63
|
+
<AlertDialogHeader>
|
|
64
|
+
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
|
65
|
+
<AlertDialogDescription>
|
|
66
|
+
This action cannot be undone. This will permanently delete the
|
|
67
|
+
item.
|
|
68
|
+
</AlertDialogDescription>
|
|
69
|
+
</AlertDialogHeader>
|
|
70
|
+
<AlertDialogFooter>
|
|
71
|
+
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
72
|
+
<AlertDialogAction className="bg-destructive text-destructive-foreground hover:bg-destructive/90">
|
|
73
|
+
Delete
|
|
74
|
+
</AlertDialogAction>
|
|
75
|
+
</AlertDialogFooter>
|
|
76
|
+
</AlertDialogContent>
|
|
77
|
+
</AlertDialog>
|
|
78
|
+
);
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const Warning: Story = {
|
|
83
|
+
render: () => {
|
|
84
|
+
const [open, setOpen] = useState(false);
|
|
85
|
+
return (
|
|
86
|
+
<AlertDialog open={open} onOpenChange={setOpen}>
|
|
87
|
+
<AlertDialogTrigger asChild>
|
|
88
|
+
<Button variant="outline">Show Warning</Button>
|
|
89
|
+
</AlertDialogTrigger>
|
|
90
|
+
<AlertDialogContent>
|
|
91
|
+
<AlertDialogHeader>
|
|
92
|
+
<AlertDialogTitle>Warning</AlertDialogTitle>
|
|
93
|
+
<AlertDialogDescription>
|
|
94
|
+
This action will have significant consequences. Please confirm
|
|
95
|
+
that you understand the risks.
|
|
96
|
+
</AlertDialogDescription>
|
|
97
|
+
</AlertDialogHeader>
|
|
98
|
+
<AlertDialogFooter>
|
|
99
|
+
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
100
|
+
<AlertDialogAction>I Understand</AlertDialogAction>
|
|
101
|
+
</AlertDialogFooter>
|
|
102
|
+
</AlertDialogContent>
|
|
103
|
+
</AlertDialog>
|
|
104
|
+
);
|
|
105
|
+
},
|
|
106
|
+
};
|