@ponti-studios/ui 1.6.0 → 1.8.0
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 +7 -11
- package/package.json +6 -31
- package/src/components/feedback/alert.tsx +8 -4
- package/src/components/feedback/index.ts +0 -4
- package/src/components/feedback/progress.stories.tsx +15 -0
- package/src/components/feedback/progress.tsx +14 -3
- package/src/components/feedback/update-guard.stories.tsx +4 -8
- package/src/components/feedback/update-guard.tsx +41 -272
- package/src/components/filters/filter-select.tsx +3 -5
- package/src/components/forms/date-picker.stories.tsx +158 -18
- package/src/components/forms/date-picker.tsx +286 -64
- package/src/components/forms/date-utils.ts +7 -2
- package/src/components/forms/drop-zone.tsx +11 -12
- package/src/components/forms/index.ts +2 -6
- package/src/components/forms/passkey-management.stories.tsx +8 -4
- package/src/components/forms/passkey-management.tsx +78 -42
- package/src/components/forms/radio-group.stories.tsx +3 -5
- package/src/components/forms/radio-group.tsx +2 -2
- package/src/components/forms/select.stories.tsx +16 -13
- package/src/components/forms/textarea.stories.tsx +1 -1
- package/src/components/layout/index.ts +2 -0
- package/src/components/layout/particle-background.stories.tsx +38 -0
- package/src/components/layout/particle-background.tsx +378 -0
- package/src/components/layout/scroll-area.stories.tsx +12 -4
- package/src/components/layout/scroll-area.tsx +41 -4
- package/src/components/navigation/tabs.stories.tsx +0 -21
- package/src/components/overlays/dialog.stories.tsx +38 -1
- package/src/components/overlays/dialog.tsx +53 -12
- package/src/components/overlays/index.ts +0 -11
- package/src/components/primitives/avatar-placeholder.svg +13 -0
- package/src/components/primitives/avatar.stories.tsx +2 -1
- package/src/components/primitives/badge.stories.tsx +13 -3
- package/src/components/primitives/badge.tsx +1 -1
- package/src/components/primitives/button.tsx +1 -1
- package/src/components/primitives/card.tsx +6 -2
- package/src/components/primitives/index.ts +0 -3
- package/src/index.ts +1 -3
- package/src/lib/index.ts +2 -3
- package/src/styles/animations.css +62 -0
- package/src/styles/index.css +31 -4
- package/src/styles/tokens/color-systems.stories.tsx +173 -25
- package/src/styles/tokens/generated/dark.colors.generated.ts +20 -12
- package/src/styles/tokens/generated/dark.css +20 -12
- package/src/styles/tokens/generated/light.colors.generated.ts +19 -11
- package/src/styles/tokens/generated/light.css +19 -11
- package/src/styles/tokens/index.ts +0 -3
- package/src/styles/tokens/scripts/check-source.mjs +7 -0
- package/src/styles/tokens/source/dark.tokens.json +44 -12
- package/src/styles/tokens/source/light.tokens.json +43 -11
- package/src/styles/tokens/typography.stories.tsx +157 -0
- package/src/components/feedback/file-upload-status.stories.tsx +0 -57
- package/src/components/feedback/file-upload-status.tsx +0 -160
- package/src/components/feedback/progress-bar.stories.tsx +0 -39
- package/src/components/feedback/progress-bar.tsx +0 -22
- package/src/components/forms/calendar.stories.tsx +0 -30
- package/src/components/forms/calendar.tsx +0 -75
- package/src/components/forms/date-field.stories.tsx +0 -202
- package/src/components/forms/date-field.tsx +0 -179
- package/src/components/overlays/alert-dialog.stories.tsx +0 -45
- package/src/components/overlays/alert-dialog.tsx +0 -116
- package/src/components/primitives/label.stories.tsx +0 -29
- package/src/components/primitives/label.tsx +0 -20
- package/src/components/primitives/status-badge.stories.tsx +0 -52
- package/src/components/primitives/status-badge.tsx +0 -33
- package/src/lib/create-memory-storage.ts +0 -24
- package/src/native/card.tsx +0 -36
- package/src/native/icon-button.tsx +0 -41
- package/src/native/index.ts +0 -38
- package/src/native/list-row.tsx +0 -67
- package/src/native/make-styles.ts +0 -32
- package/src/native/shadows.ts +0 -38
- package/src/native/text-field.tsx +0 -34
- package/src/native/uniwind.d.ts +0 -1
- package/src/native/use-theme-colors.ts +0 -19
- package/src/styles/tokens/foundations.stories.tsx +0 -109
- package/src/styles/tokens/generated/native.generated.ts +0 -266
- package/src/styles/tokens/normalize-native.ts +0 -88
- package/src/styles/tokens/source/native.tokens.json +0 -204
- package/src/styles/tokens/style-dictionary.native.mjs +0 -26
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { format } from "date-fns";
|
|
2
|
-
import { CalendarIcon } from "lucide-react";
|
|
3
|
-
import { useId, useState } from "react";
|
|
4
|
-
|
|
5
|
-
import { Popover, PopoverContent, PopoverTrigger } from "../overlays/popover";
|
|
6
|
-
import { Button } from "../primitives/button";
|
|
7
|
-
import { Calendar } from "./calendar";
|
|
8
|
-
import { formatDateInput, parseDateInput } from "./date-utils";
|
|
9
|
-
import { DatePicker, type DatePickerProps } from "./date-picker";
|
|
10
|
-
|
|
11
|
-
export interface DateRangeValue {
|
|
12
|
-
from?: Date | string | null;
|
|
13
|
-
to?: Date | string | null;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
interface DateFieldCommonProps {
|
|
17
|
-
id?: string;
|
|
18
|
-
label?: string;
|
|
19
|
-
showLabel?: boolean;
|
|
20
|
-
placeholder?: string;
|
|
21
|
-
dateFormat?: string;
|
|
22
|
-
disabled?: boolean;
|
|
23
|
-
containerClassName?: string;
|
|
24
|
-
popoverAlign?: DatePickerProps["popoverAlign"];
|
|
25
|
-
variant?: DatePickerProps["variant"];
|
|
26
|
-
captionLayout?: DatePickerProps["captionLayout"];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface SingleDateFieldProps extends DateFieldCommonProps {
|
|
30
|
-
mode?: "single";
|
|
31
|
-
name: string;
|
|
32
|
-
defaultValue?: Date | string | null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface RangeDateFieldProps extends DateFieldCommonProps {
|
|
36
|
-
mode: "range";
|
|
37
|
-
startName: string;
|
|
38
|
-
endName: string;
|
|
39
|
-
defaultValue?: DateRangeValue;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export type DateFieldProps = SingleDateFieldProps | RangeDateFieldProps;
|
|
43
|
-
|
|
44
|
-
const TRIGGER_CLASSES =
|
|
45
|
-
"border-border bg-background text-foreground placeholder:text-muted-foreground min-h-10 max-h-10 w-full justify-start rounded-l-none rounded-r-xl border-l-0 text-left font-normal";
|
|
46
|
-
|
|
47
|
-
function SingleDateField({
|
|
48
|
-
name,
|
|
49
|
-
defaultValue,
|
|
50
|
-
id,
|
|
51
|
-
label,
|
|
52
|
-
showLabel,
|
|
53
|
-
placeholder,
|
|
54
|
-
dateFormat,
|
|
55
|
-
disabled,
|
|
56
|
-
containerClassName,
|
|
57
|
-
popoverAlign,
|
|
58
|
-
variant,
|
|
59
|
-
captionLayout,
|
|
60
|
-
}: SingleDateFieldProps) {
|
|
61
|
-
const generatedId = useId();
|
|
62
|
-
const inputId = id ?? generatedId;
|
|
63
|
-
const [value, setValue] = useState<Date | undefined>(() => parseDateInput(defaultValue));
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<div>
|
|
67
|
-
<DatePicker
|
|
68
|
-
id={inputId}
|
|
69
|
-
label={label}
|
|
70
|
-
showLabel={showLabel}
|
|
71
|
-
placeholder={placeholder}
|
|
72
|
-
dateFormat={dateFormat}
|
|
73
|
-
disabled={disabled}
|
|
74
|
-
containerClassName={containerClassName}
|
|
75
|
-
popoverAlign={popoverAlign}
|
|
76
|
-
variant={variant}
|
|
77
|
-
captionLayout={captionLayout}
|
|
78
|
-
value={value}
|
|
79
|
-
onSelect={setValue}
|
|
80
|
-
/>
|
|
81
|
-
<input type="hidden" name={name} value={value ? formatDateInput(value) : ""} />
|
|
82
|
-
</div>
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function RangeDateField({
|
|
87
|
-
startName,
|
|
88
|
-
endName,
|
|
89
|
-
defaultValue,
|
|
90
|
-
id,
|
|
91
|
-
label,
|
|
92
|
-
showLabel = true,
|
|
93
|
-
placeholder = "Pick a date range",
|
|
94
|
-
dateFormat = "MMM d, yyyy",
|
|
95
|
-
disabled = false,
|
|
96
|
-
containerClassName = "flex-1 min-w-0",
|
|
97
|
-
popoverAlign = "start",
|
|
98
|
-
variant = "outline",
|
|
99
|
-
captionLayout = "label",
|
|
100
|
-
}: RangeDateFieldProps) {
|
|
101
|
-
const generatedId = useId();
|
|
102
|
-
const inputId = id ?? generatedId;
|
|
103
|
-
const [range, setRange] = useState<{ from?: Date; to?: Date }>(() => ({
|
|
104
|
-
from: parseDateInput(defaultValue?.from),
|
|
105
|
-
to: parseDateInput(defaultValue?.to),
|
|
106
|
-
}));
|
|
107
|
-
|
|
108
|
-
const fromLabel = range.from ? format(range.from, dateFormat) : null;
|
|
109
|
-
const toLabel = range.to ? format(range.to, dateFormat) : null;
|
|
110
|
-
const triggerLabel = fromLabel
|
|
111
|
-
? toLabel
|
|
112
|
-
? `${fromLabel} – ${toLabel}`
|
|
113
|
-
: `From ${fromLabel}`
|
|
114
|
-
: toLabel
|
|
115
|
-
? `Until ${toLabel}`
|
|
116
|
-
: placeholder;
|
|
117
|
-
|
|
118
|
-
return (
|
|
119
|
-
<div className={containerClassName}>
|
|
120
|
-
{showLabel && label ? (
|
|
121
|
-
<label htmlFor={inputId} className="text-foreground mb-2 block text-sm font-medium">
|
|
122
|
-
{label}
|
|
123
|
-
</label>
|
|
124
|
-
) : null}
|
|
125
|
-
<Popover>
|
|
126
|
-
<div className="flex w-full items-center">
|
|
127
|
-
<div className="border-border bg-background text-foreground flex size-10 shrink-0 items-center justify-center rounded-l-xl border">
|
|
128
|
-
<CalendarIcon className="size-4" aria-hidden />
|
|
129
|
-
</div>
|
|
130
|
-
<PopoverTrigger asChild>
|
|
131
|
-
<Button id={inputId} variant={variant} disabled={disabled} className={TRIGGER_CLASSES}>
|
|
132
|
-
{triggerLabel}
|
|
133
|
-
</Button>
|
|
134
|
-
</PopoverTrigger>
|
|
135
|
-
</div>
|
|
136
|
-
<PopoverContent
|
|
137
|
-
className="bg-popover w-auto p-0"
|
|
138
|
-
align={popoverAlign}
|
|
139
|
-
aria-label={label ?? "Choose a date range"}
|
|
140
|
-
>
|
|
141
|
-
<Calendar
|
|
142
|
-
mode="range"
|
|
143
|
-
selected={range.from ? { from: range.from, to: range.to } : undefined}
|
|
144
|
-
defaultMonth={range.from ?? range.to}
|
|
145
|
-
resetOnSelect
|
|
146
|
-
onSelect={(next) => setRange({ from: next?.from, to: next?.to })}
|
|
147
|
-
captionLayout={captionLayout}
|
|
148
|
-
/>
|
|
149
|
-
</PopoverContent>
|
|
150
|
-
</Popover>
|
|
151
|
-
<input type="hidden" name={startName} value={range.from ? formatDateInput(range.from) : ""} />
|
|
152
|
-
<input type="hidden" name={endName} value={range.to ? formatDateInput(range.to) : ""} />
|
|
153
|
-
</div>
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* DateField — calendar picker backed by hidden inputs that submit "YYYY-MM-DD"
|
|
159
|
-
* local date strings, so forms never receive UTC-shifted dates. Supports both
|
|
160
|
-
* a single date (`name`) and a range (`startName` + `endName`).
|
|
161
|
-
*
|
|
162
|
-
* @example
|
|
163
|
-
* <DateField name="startDate" defaultValue="2020-01-15" label="Start date" />
|
|
164
|
-
* <DateField
|
|
165
|
-
* mode="range"
|
|
166
|
-
* startName="startDate"
|
|
167
|
-
* endName="endDate"
|
|
168
|
-
* defaultValue={{ from: "2020-01-15", to: "2020-06-30" }}
|
|
169
|
-
* label="Active period"
|
|
170
|
-
* />
|
|
171
|
-
*/
|
|
172
|
-
function DateField(props: DateFieldProps) {
|
|
173
|
-
if (props.mode === "range") {
|
|
174
|
-
return <RangeDateField {...props} />;
|
|
175
|
-
}
|
|
176
|
-
return <SingleDateField {...props} />;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export { DateField };
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
AlertDialog,
|
|
5
|
-
AlertDialogAction,
|
|
6
|
-
AlertDialogCancel,
|
|
7
|
-
AlertDialogContent,
|
|
8
|
-
AlertDialogDescription,
|
|
9
|
-
AlertDialogFooter,
|
|
10
|
-
AlertDialogHeader,
|
|
11
|
-
AlertDialogTitle,
|
|
12
|
-
AlertDialogTrigger,
|
|
13
|
-
} from "./alert-dialog";
|
|
14
|
-
|
|
15
|
-
const meta = {
|
|
16
|
-
title: "Overlays/AlertDialog",
|
|
17
|
-
component: AlertDialog,
|
|
18
|
-
tags: ["autodocs"],
|
|
19
|
-
} satisfies Meta<typeof AlertDialog>;
|
|
20
|
-
|
|
21
|
-
export default meta;
|
|
22
|
-
type Story = StoryObj<typeof meta>;
|
|
23
|
-
|
|
24
|
-
export const Default: Story = {
|
|
25
|
-
render: () => (
|
|
26
|
-
<AlertDialog>
|
|
27
|
-
<AlertDialogTrigger className="rounded border px-4 py-2 text-sm">
|
|
28
|
-
Open Dialog
|
|
29
|
-
</AlertDialogTrigger>
|
|
30
|
-
<AlertDialogContent>
|
|
31
|
-
<AlertDialogHeader>
|
|
32
|
-
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
|
33
|
-
<AlertDialogDescription>
|
|
34
|
-
This action cannot be undone. This will permanently delete your account and remove your
|
|
35
|
-
data from our servers.
|
|
36
|
-
</AlertDialogDescription>
|
|
37
|
-
</AlertDialogHeader>
|
|
38
|
-
<AlertDialogFooter>
|
|
39
|
-
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
40
|
-
<AlertDialogAction>Continue</AlertDialogAction>
|
|
41
|
-
</AlertDialogFooter>
|
|
42
|
-
</AlertDialogContent>
|
|
43
|
-
</AlertDialog>
|
|
44
|
-
),
|
|
45
|
-
};
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog";
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
|
|
4
|
-
import { cn } from "../../lib/utils";
|
|
5
|
-
import { buttonVariants } from "../primitives/button";
|
|
6
|
-
|
|
7
|
-
const AlertDialog = AlertDialogPrimitive.Root;
|
|
8
|
-
const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
|
|
9
|
-
|
|
10
|
-
const AlertDialogPortal = ({
|
|
11
|
-
children,
|
|
12
|
-
...props
|
|
13
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) => (
|
|
14
|
-
<AlertDialogPrimitive.Portal {...props}>
|
|
15
|
-
<div className="fixed inset-0 z-50 flex items-end justify-center sm:items-center">
|
|
16
|
-
{children}
|
|
17
|
-
</div>
|
|
18
|
-
</AlertDialogPrimitive.Portal>
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
const AlertDialogOverlay = React.forwardRef<
|
|
22
|
-
React.ComponentRef<typeof AlertDialogPrimitive.Backdrop>,
|
|
23
|
-
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Backdrop>
|
|
24
|
-
>(({ className, ...props }, ref) => (
|
|
25
|
-
<AlertDialogPrimitive.Backdrop
|
|
26
|
-
ref={ref}
|
|
27
|
-
className={cn(
|
|
28
|
-
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 bg-black/80 transition-opacity",
|
|
29
|
-
className,
|
|
30
|
-
)}
|
|
31
|
-
{...props}
|
|
32
|
-
/>
|
|
33
|
-
));
|
|
34
|
-
|
|
35
|
-
const AlertDialogContent = React.forwardRef<
|
|
36
|
-
React.ComponentRef<typeof AlertDialogPrimitive.Popup>,
|
|
37
|
-
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Popup>
|
|
38
|
-
>(({ className, ...props }, ref) => (
|
|
39
|
-
<AlertDialogPortal>
|
|
40
|
-
<AlertDialogOverlay />
|
|
41
|
-
<AlertDialogPrimitive.Popup
|
|
42
|
-
ref={ref}
|
|
43
|
-
className={cn(
|
|
44
|
-
"bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 sm:data-open:slide-in-from-bottom-0 fixed bottom-0 z-50 grid w-full max-w-lg gap-6 rounded-t-xl border p-4 outline-none sm:top-[50%] sm:right-auto sm:bottom-auto sm:left-[50%] sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-xl sm:p-6",
|
|
45
|
-
className,
|
|
46
|
-
)}
|
|
47
|
-
{...props}
|
|
48
|
-
/>
|
|
49
|
-
</AlertDialogPortal>
|
|
50
|
-
));
|
|
51
|
-
|
|
52
|
-
function AlertDialogHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
53
|
-
return <div className={cn("flex flex-col gap-2 text-left", className)} {...props} />;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function AlertDialogFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
57
|
-
return (
|
|
58
|
-
<div
|
|
59
|
-
className={cn("flex flex-col-reverse gap-3 sm:flex-row sm:justify-end", className)}
|
|
60
|
-
{...props}
|
|
61
|
-
/>
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const AlertDialogTitle = React.forwardRef<
|
|
66
|
-
React.ComponentRef<typeof AlertDialogPrimitive.Title>,
|
|
67
|
-
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
|
|
68
|
-
>(({ className, ...props }, ref) => (
|
|
69
|
-
<AlertDialogPrimitive.Title
|
|
70
|
-
ref={ref}
|
|
71
|
-
className={cn("text-lg font-semibold tracking-tight", className)}
|
|
72
|
-
{...props}
|
|
73
|
-
/>
|
|
74
|
-
));
|
|
75
|
-
|
|
76
|
-
const AlertDialogDescription = React.forwardRef<
|
|
77
|
-
React.ComponentRef<typeof AlertDialogPrimitive.Description>,
|
|
78
|
-
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
|
|
79
|
-
>(({ className, ...props }, ref) => (
|
|
80
|
-
<AlertDialogPrimitive.Description
|
|
81
|
-
ref={ref}
|
|
82
|
-
className={cn("text-muted-foreground max-w-prose text-sm", className)}
|
|
83
|
-
{...props}
|
|
84
|
-
/>
|
|
85
|
-
));
|
|
86
|
-
|
|
87
|
-
const AlertDialogAction = React.forwardRef<
|
|
88
|
-
React.ComponentRef<typeof AlertDialogPrimitive.Close>,
|
|
89
|
-
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Close>
|
|
90
|
-
>(({ className, ...props }, ref) => (
|
|
91
|
-
<AlertDialogPrimitive.Close ref={ref} className={cn(buttonVariants(), className)} {...props} />
|
|
92
|
-
));
|
|
93
|
-
|
|
94
|
-
const AlertDialogCancel = React.forwardRef<
|
|
95
|
-
React.ComponentRef<typeof AlertDialogPrimitive.Close>,
|
|
96
|
-
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Close>
|
|
97
|
-
>(({ className, ...props }, ref) => (
|
|
98
|
-
<AlertDialogPrimitive.Close
|
|
99
|
-
ref={ref}
|
|
100
|
-
className={cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className)}
|
|
101
|
-
{...props}
|
|
102
|
-
/>
|
|
103
|
-
));
|
|
104
|
-
|
|
105
|
-
export {
|
|
106
|
-
AlertDialog,
|
|
107
|
-
AlertDialogAction,
|
|
108
|
-
AlertDialogCancel,
|
|
109
|
-
AlertDialogContent,
|
|
110
|
-
AlertDialogDescription,
|
|
111
|
-
AlertDialogFooter,
|
|
112
|
-
AlertDialogHeader,
|
|
113
|
-
AlertDialogOverlay,
|
|
114
|
-
AlertDialogTitle,
|
|
115
|
-
AlertDialogTrigger,
|
|
116
|
-
};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
|
|
4
|
-
import { Input } from "../forms/input";
|
|
5
|
-
import { Label } from "../primitives/label";
|
|
6
|
-
|
|
7
|
-
const meta: Meta<typeof Label> = {
|
|
8
|
-
title: "Primitives/Label",
|
|
9
|
-
component: Label,
|
|
10
|
-
tags: ["autodocs"],
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export default meta;
|
|
14
|
-
type Story = StoryObj<typeof meta>;
|
|
15
|
-
|
|
16
|
-
export const Default: Story = {
|
|
17
|
-
args: {
|
|
18
|
-
children: "Email address",
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export const WithInput: Story = {
|
|
23
|
-
render: () => (
|
|
24
|
-
<div className="flex w-full max-w-sm flex-col gap-2">
|
|
25
|
-
<Label htmlFor="email">Email address</Label>
|
|
26
|
-
<Input id="email" type="email" placeholder="you@example.com" />
|
|
27
|
-
</div>
|
|
28
|
-
),
|
|
29
|
-
};
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import { cn } from "../../lib/utils";
|
|
4
|
-
|
|
5
|
-
const Label = React.forwardRef<HTMLLabelElement, React.LabelHTMLAttributes<HTMLLabelElement>>(
|
|
6
|
-
({ className, ...props }, ref) => (
|
|
7
|
-
<label
|
|
8
|
-
htmlFor={props.htmlFor}
|
|
9
|
-
ref={ref}
|
|
10
|
-
className={cn(
|
|
11
|
-
"text-sm font-medium leading-none text-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
12
|
-
className,
|
|
13
|
-
)}
|
|
14
|
-
{...props}
|
|
15
|
-
/>
|
|
16
|
-
),
|
|
17
|
-
);
|
|
18
|
-
Label.displayName = "Label";
|
|
19
|
-
|
|
20
|
-
export { Label };
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import { CheckCircle, Clock, XCircle } from "lucide-react";
|
|
3
|
-
|
|
4
|
-
import { StatusBadge, type StatusBadgeConfig } from "./status-badge";
|
|
5
|
-
|
|
6
|
-
type ConnectionStatus = "active" | "error" | "pending" | "revoked";
|
|
7
|
-
|
|
8
|
-
const connectionStatusConfig: Record<ConnectionStatus, StatusBadgeConfig> = {
|
|
9
|
-
active: { label: "Active", variant: "default", icon: <CheckCircle className="mr-1 size-3" /> },
|
|
10
|
-
error: { label: "Error", variant: "destructive", icon: <XCircle className="mr-1 size-3" /> },
|
|
11
|
-
pending: { label: "Pending", variant: "secondary", icon: <Clock className="mr-1 size-3" /> },
|
|
12
|
-
revoked: { label: "Revoked", variant: "outline" },
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const meta = {
|
|
16
|
-
title: "Primitives/StatusBadge",
|
|
17
|
-
component: StatusBadge<ConnectionStatus>,
|
|
18
|
-
tags: ["autodocs"],
|
|
19
|
-
} satisfies Meta<typeof StatusBadge<ConnectionStatus>>;
|
|
20
|
-
|
|
21
|
-
export default meta;
|
|
22
|
-
type Story = StoryObj<typeof meta>;
|
|
23
|
-
|
|
24
|
-
export const Default: Story = {
|
|
25
|
-
args: {
|
|
26
|
-
status: "active",
|
|
27
|
-
config: connectionStatusConfig,
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export const AllStatuses: Story = {
|
|
32
|
-
args: {
|
|
33
|
-
status: "active",
|
|
34
|
-
config: connectionStatusConfig,
|
|
35
|
-
},
|
|
36
|
-
render: () => (
|
|
37
|
-
<div className="flex flex-wrap gap-2">
|
|
38
|
-
<StatusBadge status="active" config={connectionStatusConfig} />
|
|
39
|
-
<StatusBadge status="error" config={connectionStatusConfig} />
|
|
40
|
-
<StatusBadge status="pending" config={connectionStatusConfig} />
|
|
41
|
-
<StatusBadge status="revoked" config={connectionStatusConfig} />
|
|
42
|
-
</div>
|
|
43
|
-
),
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export const WithFallback: Story = {
|
|
47
|
-
args: {
|
|
48
|
-
status: null,
|
|
49
|
-
config: connectionStatusConfig,
|
|
50
|
-
fallback: { label: "Unknown", variant: "outline" },
|
|
51
|
-
},
|
|
52
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
|
|
3
|
-
import { Badge, type badgeVariants } from "./badge";
|
|
4
|
-
import type { VariantProps } from "class-variance-authority";
|
|
5
|
-
|
|
6
|
-
export interface StatusBadgeConfig {
|
|
7
|
-
label: string;
|
|
8
|
-
variant?: VariantProps<typeof badgeVariants>["variant"];
|
|
9
|
-
className?: string;
|
|
10
|
-
icon?: ReactNode;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface StatusBadgeProps<T extends string> {
|
|
14
|
-
status: T | null | undefined;
|
|
15
|
-
config: Record<T, StatusBadgeConfig>;
|
|
16
|
-
fallback?: StatusBadgeConfig;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** Renders a `Badge` from a status → { label, variant, icon } config map. */
|
|
20
|
-
export function StatusBadge<T extends string>({ status, config, fallback }: StatusBadgeProps<T>) {
|
|
21
|
-
const entry = (status != null ? config[status] : undefined) ?? fallback;
|
|
22
|
-
|
|
23
|
-
if (!entry) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<Badge variant={entry.variant ?? "outline"} className={entry.className}>
|
|
29
|
-
{entry.icon}
|
|
30
|
-
{entry.label}
|
|
31
|
-
</Badge>
|
|
32
|
-
);
|
|
33
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export function createMemoryStorage(): Storage {
|
|
2
|
-
const store = new Map<string, string>();
|
|
3
|
-
|
|
4
|
-
return {
|
|
5
|
-
get length() {
|
|
6
|
-
return store.size;
|
|
7
|
-
},
|
|
8
|
-
clear() {
|
|
9
|
-
store.clear();
|
|
10
|
-
},
|
|
11
|
-
getItem(key) {
|
|
12
|
-
return store.get(key) ?? null;
|
|
13
|
-
},
|
|
14
|
-
key(index) {
|
|
15
|
-
return Array.from(store.keys())[index] ?? null;
|
|
16
|
-
},
|
|
17
|
-
removeItem(key) {
|
|
18
|
-
store.delete(key);
|
|
19
|
-
},
|
|
20
|
-
setItem(key, value) {
|
|
21
|
-
store.set(key, value);
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
}
|
package/src/native/card.tsx
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { Text, View, type TextProps, type ViewProps } from "react-native";
|
|
3
|
-
|
|
4
|
-
import { cn } from "../lib/utils";
|
|
5
|
-
|
|
6
|
-
function Card({ className, ...props }: ViewProps) {
|
|
7
|
-
return (
|
|
8
|
-
<View className={cn("bg-card gap-4 rounded border border-border p-4", className)} {...props} />
|
|
9
|
-
);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function CardHeader({ className, ...props }: ViewProps) {
|
|
13
|
-
return <View className={cn("gap-1", className)} {...props} />;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function CardTitle({ className, ...props }: TextProps) {
|
|
17
|
-
return <Text className={cn("text-card-foreground text-xl", className)} {...props} />;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function CardDescription({ className, ...props }: TextProps) {
|
|
21
|
-
return <Text className={cn("text-muted-foreground text-xs", className)} {...props} />;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function CardAction({ className, ...props }: ViewProps) {
|
|
25
|
-
return <View className={cn("self-start", className)} {...props} />;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function CardContent({ className, ...props }: ViewProps) {
|
|
29
|
-
return <View className={className} {...props} />;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function CardFooter({ className, ...props }: ViewProps) {
|
|
33
|
-
return <View className={cn("flex-row items-center", className)} {...props} />;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle };
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
import { Pressable } from "react-native";
|
|
3
|
-
import { useCSSVariable } from "uniwind";
|
|
4
|
-
|
|
5
|
-
interface IconButtonProps {
|
|
6
|
-
accessibilityLabel?: string;
|
|
7
|
-
children: ReactNode;
|
|
8
|
-
disabled?: boolean;
|
|
9
|
-
onPress?: () => void;
|
|
10
|
-
testID?: string;
|
|
11
|
-
variant?: "bordered" | "plain";
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function IconButton({
|
|
15
|
-
accessibilityLabel,
|
|
16
|
-
children,
|
|
17
|
-
disabled = false,
|
|
18
|
-
onPress,
|
|
19
|
-
testID,
|
|
20
|
-
variant = "bordered",
|
|
21
|
-
}: IconButtonProps) {
|
|
22
|
-
const [border] = useCSSVariable(["--color-border"]) as string[];
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<Pressable
|
|
26
|
-
accessibilityLabel={accessibilityLabel}
|
|
27
|
-
accessibilityRole="button"
|
|
28
|
-
disabled={disabled}
|
|
29
|
-
onPress={onPress}
|
|
30
|
-
testID={testID}
|
|
31
|
-
className={`w-8 h-8 rounded-full border items-center justify-center ${variant === "plain" ? "border-0" : ""}`}
|
|
32
|
-
style={({ pressed }) => [
|
|
33
|
-
{ borderColor: variant === "plain" ? "transparent" : border },
|
|
34
|
-
pressed && { opacity: 0.7 },
|
|
35
|
-
disabled && { opacity: 0.4 },
|
|
36
|
-
]}
|
|
37
|
-
>
|
|
38
|
-
{children}
|
|
39
|
-
</Pressable>
|
|
40
|
-
);
|
|
41
|
-
}
|
package/src/native/index.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* React Native entry point.
|
|
3
|
-
*
|
|
4
|
-
* Kept as its own subpath (`@ponti-studios/ui/native`) because it is the only
|
|
5
|
-
* part of the package that imports `react-native`. Nothing here may import web
|
|
6
|
-
* code, so web consumers never pull React Native into their graph and native
|
|
7
|
-
* consumers never pull in react-dom.
|
|
8
|
-
*/
|
|
9
|
-
export {
|
|
10
|
-
Card,
|
|
11
|
-
CardAction,
|
|
12
|
-
CardContent,
|
|
13
|
-
CardDescription,
|
|
14
|
-
CardFooter,
|
|
15
|
-
CardHeader,
|
|
16
|
-
CardTitle,
|
|
17
|
-
} from "./card";
|
|
18
|
-
export { IconButton } from "./icon-button";
|
|
19
|
-
export { ListRow } from "./list-row";
|
|
20
|
-
export { createMakeStyles, type NamedStyles } from "./make-styles";
|
|
21
|
-
export { nativeShadows, type NativeShadowLayer } from "./shadows";
|
|
22
|
-
export { TextField } from "./text-field";
|
|
23
|
-
export { useColorMode, useThemeColors } from "./use-theme-colors";
|
|
24
|
-
|
|
25
|
-
export {
|
|
26
|
-
borderRadius as nativeBorderRadius,
|
|
27
|
-
componentSize as nativeComponentSize,
|
|
28
|
-
fontFamily as nativeFontFamily,
|
|
29
|
-
screenMargin as nativeScreenMargin,
|
|
30
|
-
spacing as nativeSpacing,
|
|
31
|
-
streamItem as nativeStreamItem,
|
|
32
|
-
typography as nativeTypography,
|
|
33
|
-
type BorderRadiusToken,
|
|
34
|
-
type ComponentSizeToken,
|
|
35
|
-
type SpacingToken,
|
|
36
|
-
type TypographyVariant,
|
|
37
|
-
} from "../styles/tokens/normalize-native";
|
|
38
|
-
export { motion as nativeMotion } from "../styles/tokens";
|
package/src/native/list-row.tsx
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
import { Pressable, type StyleProp, Text, type TextStyle, View } from "react-native";
|
|
3
|
-
import { useCSSVariable } from "uniwind";
|
|
4
|
-
|
|
5
|
-
interface ListRowProps {
|
|
6
|
-
accessibilityLabel: string;
|
|
7
|
-
actionTestID?: string;
|
|
8
|
-
eyebrow?: string;
|
|
9
|
-
leading?: ReactNode;
|
|
10
|
-
onLongPress?: () => void;
|
|
11
|
-
onPress: () => void;
|
|
12
|
-
subtitle?: string | null;
|
|
13
|
-
testID?: string;
|
|
14
|
-
title: string;
|
|
15
|
-
titleStyle?: StyleProp<TextStyle>;
|
|
16
|
-
trailing?: ReactNode;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function ListRow({
|
|
20
|
-
accessibilityLabel,
|
|
21
|
-
actionTestID,
|
|
22
|
-
eyebrow,
|
|
23
|
-
leading,
|
|
24
|
-
onLongPress,
|
|
25
|
-
onPress,
|
|
26
|
-
subtitle,
|
|
27
|
-
testID,
|
|
28
|
-
title,
|
|
29
|
-
titleStyle,
|
|
30
|
-
trailing,
|
|
31
|
-
}: ListRowProps) {
|
|
32
|
-
const [muted] = useCSSVariable(["--color-muted"]) as string[];
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<Pressable
|
|
36
|
-
accessibilityLabel={accessibilityLabel}
|
|
37
|
-
accessibilityRole="button"
|
|
38
|
-
onLongPress={onLongPress}
|
|
39
|
-
onPress={onPress}
|
|
40
|
-
className="flex-row items-center gap-3 min-h-14 border-b border-border px-4 py-2"
|
|
41
|
-
style={({ pressed }) => pressed && { backgroundColor: muted }}
|
|
42
|
-
testID={testID ?? actionTestID}
|
|
43
|
-
>
|
|
44
|
-
{leading ? <View className="items-center justify-center w-6">{leading}</View> : null}
|
|
45
|
-
<View className="flex-1 gap-0.5 min-w-0">
|
|
46
|
-
{eyebrow ? (
|
|
47
|
-
<Text className="text-caption2 text-muted-foreground tracking-[0.2]">{eyebrow}</Text>
|
|
48
|
-
) : null}
|
|
49
|
-
<Text numberOfLines={2} className="text-body text-foreground" style={titleStyle}>
|
|
50
|
-
{title}
|
|
51
|
-
</Text>
|
|
52
|
-
{subtitle ? (
|
|
53
|
-
<Text
|
|
54
|
-
ellipsizeMode="tail"
|
|
55
|
-
numberOfLines={1}
|
|
56
|
-
className="text-caption1 text-muted-foreground"
|
|
57
|
-
>
|
|
58
|
-
{subtitle}
|
|
59
|
-
</Text>
|
|
60
|
-
) : null}
|
|
61
|
-
</View>
|
|
62
|
-
{trailing ? <View className="items-center justify-center">{trailing}</View> : null}
|
|
63
|
-
</Pressable>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export { ListRow };
|