@opensite/ui 0.0.1
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/LICENSE +28 -0
- package/README.md +315 -0
- package/dist/animated-dialog.cjs +168 -0
- package/dist/animated-dialog.cjs.map +1 -0
- package/dist/animated-dialog.d.cts +24 -0
- package/dist/animated-dialog.d.ts +24 -0
- package/dist/animated-dialog.js +166 -0
- package/dist/animated-dialog.js.map +1 -0
- package/dist/badge.cjs +49 -0
- package/dist/badge.cjs.map +1 -0
- package/dist/badge.d.cts +13 -0
- package/dist/badge.d.ts +13 -0
- package/dist/badge.js +46 -0
- package/dist/badge.js.map +1 -0
- package/dist/button.cjs +63 -0
- package/dist/button.cjs.map +1 -0
- package/dist/button.d.cts +14 -0
- package/dist/button.d.ts +14 -0
- package/dist/button.js +60 -0
- package/dist/button.js.map +1 -0
- package/dist/card.cjs +99 -0
- package/dist/card.cjs.map +1 -0
- package/dist/card.d.cts +12 -0
- package/dist/card.d.ts +12 -0
- package/dist/card.js +91 -0
- package/dist/card.js.map +1 -0
- package/dist/components.cjs +533 -0
- package/dist/components.cjs.map +1 -0
- package/dist/components.d.cts +14 -0
- package/dist/components.d.ts +14 -0
- package/dist/components.js +494 -0
- package/dist/components.js.map +1 -0
- package/dist/container.cjs +47 -0
- package/dist/container.cjs.map +1 -0
- package/dist/container.d.cts +16 -0
- package/dist/container.d.ts +16 -0
- package/dist/container.js +41 -0
- package/dist/container.js.map +1 -0
- package/dist/index.cjs +534 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +494 -0
- package/dist/index.js.map +1 -0
- package/dist/page-hero-banner.cjs +119 -0
- package/dist/page-hero-banner.cjs.map +1 -0
- package/dist/page-hero-banner.d.cts +22 -0
- package/dist/page-hero-banner.d.ts +22 -0
- package/dist/page-hero-banner.js +113 -0
- package/dist/page-hero-banner.js.map +1 -0
- package/dist/popover.cjs +73 -0
- package/dist/popover.cjs.map +1 -0
- package/dist/popover.d.cts +10 -0
- package/dist/popover.d.ts +10 -0
- package/dist/popover.js +48 -0
- package/dist/popover.js.map +1 -0
- package/dist/section.cjs +96 -0
- package/dist/section.cjs.map +1 -0
- package/dist/section.d.cts +21 -0
- package/dist/section.d.ts +21 -0
- package/dist/section.js +90 -0
- package/dist/section.js.map +1 -0
- package/dist/types.cjs +4 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +180 -0
- package/dist/types.d.ts +180 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.cjs +13 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.d.cts +5 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +11 -0
- package/dist/utils.js.map +1 -0
- package/package.json +152 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, OpenSite AI. All rights reserved.
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# @opensite/ui
|
|
2
|
+
|
|
3
|
+
Foundational UI component library for the OpenSite Semantic Site Builder ecosystem. Provides tree-shakable, performance-optimized components with abstract styling support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎨 **Abstract Styling**: Components use CSS variables for full theme customization
|
|
8
|
+
- 📦 **Tree-Shakable**: Granular imports for optimal bundle sizes
|
|
9
|
+
- ⚡ **Performance First**: Optimized for Core Web Vitals (LCP ≤2.5s, INP ≤200ms, CLS ≤0.1)
|
|
10
|
+
- 🎯 **TypeScript**: Full type safety with strict mode
|
|
11
|
+
- 🧩 **shadcn/ui Compatible**: Built on shadcn/ui foundations with Tailwind CSS v4
|
|
12
|
+
- 🔧 **Flexible**: Support for both default Tailwind styles and custom semantic builder styles
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @opensite/ui
|
|
18
|
+
# or
|
|
19
|
+
npm install @opensite/ui
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Peer Dependencies
|
|
23
|
+
|
|
24
|
+
This library requires React 16.8.0 or higher:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pnpm add react react-dom
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
### Tree-Shakable Imports (Recommended)
|
|
33
|
+
|
|
34
|
+
For optimal bundle sizes, import components individually:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
// Import specific components
|
|
38
|
+
import { Container } from "@opensite/ui/components/container";
|
|
39
|
+
import { Section } from "@opensite/ui/components/section";
|
|
40
|
+
|
|
41
|
+
// Or import multiple from grouped export
|
|
42
|
+
import { Container, Section, Button } from "@opensite/ui/components";
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Full Import (Not Recommended)
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
// Import all (larger bundle)
|
|
49
|
+
import * as UI from "@opensite/ui";
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Components
|
|
53
|
+
|
|
54
|
+
### Container
|
|
55
|
+
|
|
56
|
+
Layout container for consistent content width and centering.
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { Container } from "@opensite/ui/components/container";
|
|
60
|
+
|
|
61
|
+
<Container maxWidth="xl">
|
|
62
|
+
<h1>Page Content</h1>
|
|
63
|
+
</Container>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Props:**
|
|
67
|
+
- `maxWidth?: "sm" | "md" | "lg" | "xl" | "2xl" | "4xl" | "full"` - Maximum width (default: "xl")
|
|
68
|
+
- `as?: keyof JSX.IntrinsicElements` - HTML element to render (default: "div")
|
|
69
|
+
- `className?: string` - Additional CSS classes
|
|
70
|
+
- All standard HTML attributes
|
|
71
|
+
|
|
72
|
+
### Section
|
|
73
|
+
|
|
74
|
+
Section wrapper with optional title, subtitle, and background variants.
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
import { Section } from "@opensite/ui/components/section";
|
|
78
|
+
|
|
79
|
+
<Section
|
|
80
|
+
id="features"
|
|
81
|
+
title="Our Features"
|
|
82
|
+
subtitle="What we offer"
|
|
83
|
+
background="gradient"
|
|
84
|
+
spacing="lg"
|
|
85
|
+
>
|
|
86
|
+
<p>Section content here</p>
|
|
87
|
+
</Section>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Props:**
|
|
91
|
+
- `id?: string` - Section ID for anchor links
|
|
92
|
+
- `title?: string` - Section title (renders as h2)
|
|
93
|
+
- `subtitle?: string` - Section subtitle/eyebrow
|
|
94
|
+
- `background?: "white" | "gray" | "dark" | "gradient" | "primary" | "secondary" | "muted"` (default: "white")
|
|
95
|
+
- `spacing?: "sm" | "md" | "lg" | "xl"` (default: "lg")
|
|
96
|
+
- `className?: string` - Additional CSS classes
|
|
97
|
+
- All standard HTML attributes
|
|
98
|
+
|
|
99
|
+
### AnimatedDialog
|
|
100
|
+
|
|
101
|
+
Animated modal dialog component using framer-motion.
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { AnimatedDialog } from "@opensite/ui/components/animated-dialog";
|
|
105
|
+
import { useState } from "react";
|
|
106
|
+
|
|
107
|
+
function MyComponent() {
|
|
108
|
+
const [open, setOpen] = useState(false);
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<AnimatedDialog
|
|
112
|
+
open={open}
|
|
113
|
+
onOpenChange={setOpen}
|
|
114
|
+
title="Welcome"
|
|
115
|
+
eyebrow="Hello"
|
|
116
|
+
description="This is a modal dialog"
|
|
117
|
+
size="lg"
|
|
118
|
+
footer={
|
|
119
|
+
<button onClick={() => setOpen(false)}>Close</button>
|
|
120
|
+
}
|
|
121
|
+
>
|
|
122
|
+
<p>Dialog content here</p>
|
|
123
|
+
</AnimatedDialog>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Props:**
|
|
129
|
+
- `open: boolean` - Whether the dialog is open (required)
|
|
130
|
+
- `onOpenChange: (open: boolean) => void` - Callback when dialog state changes (required)
|
|
131
|
+
- `title?: string` - Dialog title
|
|
132
|
+
- `eyebrow?: string` - Eyebrow text above title
|
|
133
|
+
- `description?: string` - Dialog description
|
|
134
|
+
- `header?: ReactNode` - Custom header (overrides title/eyebrow/description)
|
|
135
|
+
- `footer?: ReactNode` - Footer content
|
|
136
|
+
- `size?: "sm" | "md" | "lg" | "xl" | "full"` (default: "lg")
|
|
137
|
+
- `className?: string` - Additional CSS classes for container
|
|
138
|
+
- `contentClassName?: string` - Additional CSS classes for content area
|
|
139
|
+
|
|
140
|
+
### PageHeroBanner
|
|
141
|
+
|
|
142
|
+
Hero banner component with image or video background support.
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
import { PageHeroBanner } from "@opensite/ui/components/page-hero-banner";
|
|
146
|
+
|
|
147
|
+
<PageHeroBanner
|
|
148
|
+
imageUrl="https://example.com/hero.jpg"
|
|
149
|
+
alt="Hero banner"
|
|
150
|
+
minHeight="600px"
|
|
151
|
+
showOverlay={true}
|
|
152
|
+
overlayOpacity={0.6}
|
|
153
|
+
contentMaxWidth="4xl"
|
|
154
|
+
>
|
|
155
|
+
<h1>Welcome to Our Site</h1>
|
|
156
|
+
<p>Discover amazing content</p>
|
|
157
|
+
</PageHeroBanner>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Props:**
|
|
161
|
+
- `imageUrl?: string` - Image URL or Media ID (either imageUrl or videoUrl required)
|
|
162
|
+
- `videoUrl?: string` - Video URL or Media ID (either imageUrl or videoUrl required)
|
|
163
|
+
- `alt?: string` - Alt text for image (default: "Hero banner")
|
|
164
|
+
- `loading?: "eager" | "lazy"` (default: "eager")
|
|
165
|
+
- `minHeight?: string` (default: "500px")
|
|
166
|
+
- `showOverlay?: boolean` (default: true)
|
|
167
|
+
- `overlayOpacity?: number` (default: 0.6)
|
|
168
|
+
- `contentMaxWidth?: ContainerMaxWidth` (default: "4xl")
|
|
169
|
+
- `className?: string` - Additional CSS classes
|
|
170
|
+
- All standard div attributes
|
|
171
|
+
|
|
172
|
+
### shadcn/ui Components
|
|
173
|
+
|
|
174
|
+
Additional components from shadcn/ui are available:
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import { Button } from "@opensite/ui/components/button";
|
|
178
|
+
import { Card, CardHeader, CardContent, CardFooter } from "@opensite/ui/components/card";
|
|
179
|
+
import { Badge } from "@opensite/ui/components/badge";
|
|
180
|
+
import { Popover, PopoverTrigger, PopoverContent } from "@opensite/ui/components/popover";
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Styling
|
|
184
|
+
|
|
185
|
+
### CSS Variables
|
|
186
|
+
|
|
187
|
+
Components use CSS variables for theming. Define these in your global CSS:
|
|
188
|
+
|
|
189
|
+
```css
|
|
190
|
+
:root {
|
|
191
|
+
--background: 0 0% 100%;
|
|
192
|
+
--foreground: 222.2 84% 4.9%;
|
|
193
|
+
--primary: 222.2 47.4% 11.2%;
|
|
194
|
+
--primary-foreground: 210 40% 98%;
|
|
195
|
+
--secondary: 210 40% 96.1%;
|
|
196
|
+
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
197
|
+
--muted: 210 40% 96.1%;
|
|
198
|
+
--muted-foreground: 215.4 16.3% 46.9%;
|
|
199
|
+
/* ... more variables */
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Tailwind CSS Configuration
|
|
204
|
+
|
|
205
|
+
Ensure your `tailwind.config.js` includes the library components:
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
module.exports = {
|
|
209
|
+
content: [
|
|
210
|
+
"./src/**/*.{js,ts,jsx,tsx}",
|
|
211
|
+
"./node_modules/@opensite/ui/dist/**/*.{js,mjs}",
|
|
212
|
+
],
|
|
213
|
+
theme: {
|
|
214
|
+
extend: {
|
|
215
|
+
// Your custom theme
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Custom Styling
|
|
222
|
+
|
|
223
|
+
Override component styles using the `className` prop:
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
<Container className="bg-blue-500 text-white px-8">
|
|
227
|
+
Custom styled container
|
|
228
|
+
</Container>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## TypeScript
|
|
232
|
+
|
|
233
|
+
Full TypeScript support with exported types:
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
import type {
|
|
237
|
+
ContainerProps,
|
|
238
|
+
ContainerMaxWidth,
|
|
239
|
+
SectionProps,
|
|
240
|
+
SectionBackground,
|
|
241
|
+
SectionSpacing,
|
|
242
|
+
AnimatedDialogProps,
|
|
243
|
+
PageHeroBannerProps,
|
|
244
|
+
} from "@opensite/ui/types";
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Performance
|
|
248
|
+
|
|
249
|
+
### Bundle Sizes
|
|
250
|
+
|
|
251
|
+
- **Core Components**: ≤50KB gzipped
|
|
252
|
+
- **Individual Components**: Container (~1KB), Section (~2.5KB), AnimatedDialog (~5KB), PageHeroBanner (~3KB)
|
|
253
|
+
|
|
254
|
+
### Core Web Vitals
|
|
255
|
+
|
|
256
|
+
All components are optimized for:
|
|
257
|
+
- **LCP** (Largest Contentful Paint): ≤2.5s
|
|
258
|
+
- **INP** (Interaction to Next Paint): ≤200ms
|
|
259
|
+
- **CLS** (Cumulative Layout Shift): ≤0.1
|
|
260
|
+
|
|
261
|
+
### Tree-Shaking
|
|
262
|
+
|
|
263
|
+
The library is fully tree-shakable. Import only what you need:
|
|
264
|
+
|
|
265
|
+
```tsx
|
|
266
|
+
// ✅ Good - Only imports Container (~1KB)
|
|
267
|
+
import { Container } from "@opensite/ui/components/container";
|
|
268
|
+
|
|
269
|
+
// ❌ Avoid - Imports everything (~50KB)
|
|
270
|
+
import * as UI from "@opensite/ui";
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Development
|
|
274
|
+
|
|
275
|
+
### Building
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
pnpm build
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Testing
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
# Run tests
|
|
285
|
+
pnpm test
|
|
286
|
+
|
|
287
|
+
# Run tests in watch mode
|
|
288
|
+
pnpm test:watch
|
|
289
|
+
|
|
290
|
+
# Run tests with coverage
|
|
291
|
+
pnpm test -- --coverage
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Type Checking
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
pnpm type-check
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Requirements
|
|
301
|
+
|
|
302
|
+
- **Node.js**: >=18.0.0
|
|
303
|
+
- **pnpm**: >=9.0.0
|
|
304
|
+
- **React**: >=16.8.0
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
MIT
|
|
309
|
+
|
|
310
|
+
## Related Packages
|
|
311
|
+
|
|
312
|
+
- [@opensite/blocks](https://github.com/opensite-ai/opensite-blocks) - Ultra-lightweight React rendering runtime
|
|
313
|
+
- [@opensite/img](https://github.com/opensite-ai/opensite-img) - Performance-optimized image component
|
|
314
|
+
- [@opensite/video](https://github.com/opensite-ai/opensite-video) - Performance-optimized video component
|
|
315
|
+
- [@opensite/hooks](https://github.com/opensite-ai/opensite-hooks) - Custom React hooks library
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var framerMotion = require('framer-motion');
|
|
5
|
+
var clsx = require('clsx');
|
|
6
|
+
var tailwindMerge = require('tailwind-merge');
|
|
7
|
+
var hooks = require('@opensite/hooks');
|
|
8
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
9
|
+
|
|
10
|
+
function cn(...inputs) {
|
|
11
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
12
|
+
}
|
|
13
|
+
var sizeStyles = {
|
|
14
|
+
sm: "max-w-md",
|
|
15
|
+
md: "max-w-2xl",
|
|
16
|
+
lg: "max-w-4xl",
|
|
17
|
+
xl: "max-w-5xl",
|
|
18
|
+
full: "max-w-7xl"
|
|
19
|
+
};
|
|
20
|
+
var dialogTransition = {
|
|
21
|
+
duration: 0.35,
|
|
22
|
+
ease: [0.16, 1, 0.3, 1]
|
|
23
|
+
};
|
|
24
|
+
function AnimatedDialog({
|
|
25
|
+
open,
|
|
26
|
+
onOpenChange,
|
|
27
|
+
title,
|
|
28
|
+
eyebrow,
|
|
29
|
+
description,
|
|
30
|
+
children,
|
|
31
|
+
header,
|
|
32
|
+
footer,
|
|
33
|
+
size = "lg",
|
|
34
|
+
className,
|
|
35
|
+
contentClassName
|
|
36
|
+
}) {
|
|
37
|
+
const titleId = react.useId();
|
|
38
|
+
const descriptionId = react.useId();
|
|
39
|
+
const containerRef = react.useRef(null);
|
|
40
|
+
hooks.useOnClickOutside(containerRef, () => {
|
|
41
|
+
if (open) {
|
|
42
|
+
onOpenChange(false);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
react.useEffect(() => {
|
|
46
|
+
if (!open) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const onKeyDown = (event) => {
|
|
50
|
+
if (event.key === "Escape") {
|
|
51
|
+
onOpenChange(false);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const previousOverflow = document.body.style.overflow;
|
|
55
|
+
document.body.style.overflow = "hidden";
|
|
56
|
+
window.addEventListener("keydown", onKeyDown);
|
|
57
|
+
return () => {
|
|
58
|
+
document.body.style.overflow = previousOverflow;
|
|
59
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
60
|
+
};
|
|
61
|
+
}, [open, onOpenChange]);
|
|
62
|
+
return /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: open ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50 h-screen overflow-y-auto", children: [
|
|
63
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
64
|
+
framerMotion.motion.div,
|
|
65
|
+
{
|
|
66
|
+
initial: { opacity: 0 },
|
|
67
|
+
animate: { opacity: 1, transition: dialogTransition },
|
|
68
|
+
exit: { opacity: 0, transition: dialogTransition },
|
|
69
|
+
className: "fixed inset-0 h-full w-full bg-foreground/80 backdrop-blur-lg"
|
|
70
|
+
}
|
|
71
|
+
),
|
|
72
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
73
|
+
framerMotion.motion.div,
|
|
74
|
+
{
|
|
75
|
+
initial: { opacity: 0, y: 24, scale: 0.98 },
|
|
76
|
+
animate: {
|
|
77
|
+
opacity: 1,
|
|
78
|
+
y: 0,
|
|
79
|
+
scale: 1,
|
|
80
|
+
transition: dialogTransition
|
|
81
|
+
},
|
|
82
|
+
exit: {
|
|
83
|
+
opacity: 0,
|
|
84
|
+
y: 12,
|
|
85
|
+
scale: 0.98,
|
|
86
|
+
transition: dialogTransition
|
|
87
|
+
},
|
|
88
|
+
ref: containerRef,
|
|
89
|
+
role: "dialog",
|
|
90
|
+
"aria-modal": "true",
|
|
91
|
+
"aria-labelledby": title ? titleId : void 0,
|
|
92
|
+
"aria-describedby": description ? descriptionId : void 0,
|
|
93
|
+
className: cn(
|
|
94
|
+
"relative z-[60] mx-auto my-10 flex w-[92vw] max-h-[85vh] flex-col overflow-hidden rounded-3xl bg-card p-4 shadow-2xl ring-1 ring-border/10 md:my-16 md:p-10",
|
|
95
|
+
sizeStyles[size],
|
|
96
|
+
className
|
|
97
|
+
),
|
|
98
|
+
children: [
|
|
99
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-6", children: [
|
|
100
|
+
header ? header : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
101
|
+
eyebrow ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-semibold uppercase tracking-[0.3em] text-primary", children: eyebrow }) : null,
|
|
102
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
103
|
+
"h2",
|
|
104
|
+
{
|
|
105
|
+
id: titleId,
|
|
106
|
+
className: "text-2xl font-semibold text-card-foreground md:text-4xl",
|
|
107
|
+
children: title
|
|
108
|
+
}
|
|
109
|
+
) : null,
|
|
110
|
+
description ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
111
|
+
"p",
|
|
112
|
+
{
|
|
113
|
+
id: descriptionId,
|
|
114
|
+
className: "text-sm text-muted-foreground md:text-base",
|
|
115
|
+
children: description
|
|
116
|
+
}
|
|
117
|
+
) : null
|
|
118
|
+
] }),
|
|
119
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
120
|
+
"button",
|
|
121
|
+
{
|
|
122
|
+
type: "button",
|
|
123
|
+
"aria-label": "Close dialog",
|
|
124
|
+
className: "flex h-9 w-9 items-center justify-center rounded-full bg-foreground text-background transition hover:bg-foreground/80",
|
|
125
|
+
onClick: () => onOpenChange(false),
|
|
126
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
127
|
+
"svg",
|
|
128
|
+
{
|
|
129
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
130
|
+
width: "24",
|
|
131
|
+
height: "24",
|
|
132
|
+
viewBox: "0 0 24 24",
|
|
133
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
134
|
+
"path",
|
|
135
|
+
{
|
|
136
|
+
fill: "none",
|
|
137
|
+
stroke: "currentColor",
|
|
138
|
+
strokeLinecap: "round",
|
|
139
|
+
strokeLinejoin: "round",
|
|
140
|
+
strokeWidth: "2",
|
|
141
|
+
d: "M18 6L6 18M6 6l12 12"
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
] }),
|
|
149
|
+
children ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
150
|
+
"div",
|
|
151
|
+
{
|
|
152
|
+
className: cn(
|
|
153
|
+
"mt-6 flex-1 min-h-0 overflow-y-auto pr-2",
|
|
154
|
+
contentClassName
|
|
155
|
+
),
|
|
156
|
+
children
|
|
157
|
+
}
|
|
158
|
+
) : null,
|
|
159
|
+
footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6", children: footer }) : null
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
)
|
|
163
|
+
] }) : null });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
exports.AnimatedDialog = AnimatedDialog;
|
|
167
|
+
//# sourceMappingURL=animated-dialog.cjs.map
|
|
168
|
+
//# sourceMappingURL=animated-dialog.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../lib/utils.ts","../components/ui/animated-dialog.tsx"],"names":["twMerge","clsx","useId","useRef","useOnClickOutside","useEffect","AnimatePresence","jsxs","jsx","motion"],"mappings":"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","file":"animated-dialog.cjs","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport React, { useEffect, useId, useRef } from \"react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { cn } from \"../../lib/utils\";\nimport { useOnClickOutside } from \"@opensite/hooks\";\nimport type { AnimatedDialogProps } from \"../../src/types\";\n\n/**\n * Size variants for the dialog\n */\nconst sizeStyles = {\n sm: \"max-w-md\",\n md: \"max-w-2xl\",\n lg: \"max-w-4xl\",\n xl: \"max-w-5xl\",\n full: \"max-w-7xl\",\n};\n\n/**\n * Animation transition configuration\n */\nconst dialogTransition = {\n duration: 0.35,\n ease: [0.16, 1, 0.3, 1] as const,\n};\n\n/**\n * Animated dialog component with framer-motion animations\n *\n * @example\n * ```tsx\n * const [open, setOpen] = useState(false);\n *\n * <AnimatedDialog\n * open={open}\n * onOpenChange={setOpen}\n * title=\"Dialog Title\"\n * description=\"Dialog description\"\n * >\n * <div>Dialog content</div>\n * </AnimatedDialog>\n * ```\n */\nexport function AnimatedDialog({\n open,\n onOpenChange,\n title,\n eyebrow,\n description,\n children,\n header,\n footer,\n size = \"lg\",\n className,\n contentClassName,\n}: AnimatedDialogProps) {\n const titleId = useId();\n const descriptionId = useId();\n const containerRef = useRef<HTMLDivElement>(null!);\n\n useOnClickOutside(containerRef, () => {\n if (open) {\n onOpenChange(false);\n }\n });\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n onOpenChange(false);\n }\n };\n\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n window.addEventListener(\"keydown\", onKeyDown);\n\n return () => {\n document.body.style.overflow = previousOverflow;\n window.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [open, onOpenChange]);\n\n return (\n <AnimatePresence>\n {open ? (\n <div className=\"fixed inset-0 z-50 h-screen overflow-y-auto\">\n {/* Backdrop */}\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1, transition: dialogTransition }}\n exit={{ opacity: 0, transition: dialogTransition }}\n className=\"fixed inset-0 h-full w-full bg-foreground/80 backdrop-blur-lg\"\n />\n\n {/* Dialog container */}\n <motion.div\n initial={{ opacity: 0, y: 24, scale: 0.98 }}\n animate={{\n opacity: 1,\n y: 0,\n scale: 1,\n transition: dialogTransition,\n }}\n exit={{\n opacity: 0,\n y: 12,\n scale: 0.98,\n transition: dialogTransition,\n }}\n ref={containerRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-describedby={description ? descriptionId : undefined}\n className={cn(\n \"relative z-[60] mx-auto my-10 flex w-[92vw] max-h-[85vh] flex-col overflow-hidden rounded-3xl bg-card p-4 shadow-2xl ring-1 ring-border/10 md:my-16 md:p-10\",\n sizeStyles[size],\n className\n )}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between gap-6\">\n {header ? (\n header\n ) : (\n <div className=\"space-y-3\">\n {eyebrow ? (\n <p className=\"text-xs font-semibold uppercase tracking-[0.3em] text-primary\">\n {eyebrow}\n </p>\n ) : null}\n {title ? (\n <h2\n id={titleId}\n className=\"text-2xl font-semibold text-card-foreground md:text-4xl\"\n >\n {title}\n </h2>\n ) : null}\n {description ? (\n <p\n id={descriptionId}\n className=\"text-sm text-muted-foreground md:text-base\"\n >\n {description}\n </p>\n ) : null}\n </div>\n )}\n\n {/* Close button */}\n <button\n type=\"button\"\n aria-label=\"Close dialog\"\n className=\"flex h-9 w-9 items-center justify-center rounded-full bg-foreground text-background transition hover:bg-foreground/80\"\n onClick={() => onOpenChange(false)}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n >\n <path\n fill=\"none\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth=\"2\"\n d=\"M18 6L6 18M6 6l12 12\"\n />\n </svg>\n </button>\n </div>\n\n {/* Content */}\n {children ? (\n <div\n className={cn(\n \"mt-6 flex-1 min-h-0 overflow-y-auto pr-2\",\n contentClassName\n )}\n >\n {children}\n </div>\n ) : null}\n\n {/* Footer */}\n {footer ? <div className=\"mt-6\">{footer}</div> : null}\n </motion.div>\n </div>\n ) : null}\n </AnimatePresence>\n );\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { AnimatedDialogProps } from './types.cjs';
|
|
3
|
+
import 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Animated dialog component with framer-motion animations
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const [open, setOpen] = useState(false);
|
|
11
|
+
*
|
|
12
|
+
* <AnimatedDialog
|
|
13
|
+
* open={open}
|
|
14
|
+
* onOpenChange={setOpen}
|
|
15
|
+
* title="Dialog Title"
|
|
16
|
+
* description="Dialog description"
|
|
17
|
+
* >
|
|
18
|
+
* <div>Dialog content</div>
|
|
19
|
+
* </AnimatedDialog>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare function AnimatedDialog({ open, onOpenChange, title, eyebrow, description, children, header, footer, size, className, contentClassName, }: AnimatedDialogProps): react_jsx_runtime.JSX.Element;
|
|
23
|
+
|
|
24
|
+
export { AnimatedDialog };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { AnimatedDialogProps } from './types.js';
|
|
3
|
+
import 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Animated dialog component with framer-motion animations
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const [open, setOpen] = useState(false);
|
|
11
|
+
*
|
|
12
|
+
* <AnimatedDialog
|
|
13
|
+
* open={open}
|
|
14
|
+
* onOpenChange={setOpen}
|
|
15
|
+
* title="Dialog Title"
|
|
16
|
+
* description="Dialog description"
|
|
17
|
+
* >
|
|
18
|
+
* <div>Dialog content</div>
|
|
19
|
+
* </AnimatedDialog>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare function AnimatedDialog({ open, onOpenChange, title, eyebrow, description, children, header, footer, size, className, contentClassName, }: AnimatedDialogProps): react_jsx_runtime.JSX.Element;
|
|
23
|
+
|
|
24
|
+
export { AnimatedDialog };
|