daply-ui 0.0.0 → 1.0.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 CHANGED
@@ -1,73 +1,409 @@
1
- # React + TypeScript + Vite
1
+ # @daply-ui
2
2
 
3
- This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
3
+ A React component library for building beautiful, modern forms that integrate seamlessly with the Daply backend. Features professional styling inspired by Daply forms with automatic light/dark theme support.
4
4
 
5
- Currently, two official plugins are available:
5
+ ## Features
6
6
 
7
- - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
8
- - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
7
+ - 🎨 **Modern Design** - Clean, professional styling matching Daply form aesthetics
8
+ - 🌓 **Theme Support** - Automatic light/dark theme adaptation
9
+ - ✅ **Built-in Validation** - Required fields, email, URL validation
10
+ - 🔧 **Custom Validation** - Define your own validation rules
11
+ - 🚀 **API Integration** - Automatic submission to Daply backend
12
+ - 📱 **Responsive** - Mobile-first design that works everywhere
13
+ - 💪 **TypeScript** - Full type safety and IntelliSense support
14
+ - 🎯 **Customizable** - Override any styling with custom classes
9
15
 
10
- ## React Compiler
16
+ ## Installation
11
17
 
12
- The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
18
+ ```bash
19
+ npm install daply-ui
20
+ ```
21
+
22
+ **No additional dependencies required!** The library uses inline styles, so it works out of the box without needing Tailwind CSS or any other CSS framework.
23
+
24
+ ## Components
25
+
26
+ ### Form Component
27
+
28
+ A fully-featured form component with built-in validation and Daply backend integration.
29
+
30
+ ## Basic Usage
31
+
32
+ ```tsx
33
+ import { Form } from 'daply-ui';
34
+
35
+ function App() {
36
+ return (
37
+ <Form
38
+ formId="contact-form"
39
+ siteId="my-website"
40
+ baseUrl="https://api.daply.com"
41
+ apiToken="your-api-token-here"
42
+ encryptedDbName="ff95d16da2340fe621ce03a61b9e6ee5b2c5dacbb12bd1914cb3dae6e5ad1bea73029edd7bf8bae1eefaa23cde52ea28"
43
+ theme="dark" // 'light', 'dark', or 'auto' (default)
44
+ fields={[
45
+ {
46
+ name: "name",
47
+ type: "text",
48
+ label: "Name",
49
+ required: true,
50
+ placeholder: "Enter your name"
51
+ },
52
+ {
53
+ name: "email",
54
+ type: "email",
55
+ label: "Email",
56
+ required: true,
57
+ placeholder: "your.email@example.com"
58
+ },
59
+ {
60
+ name: "subject",
61
+ type: "text",
62
+ label: "Subject",
63
+ required: true,
64
+ placeholder: "What's this about?"
65
+ },
66
+ {
67
+ name: "message",
68
+ type: "textarea",
69
+ label: "Message",
70
+ required: true,
71
+ rows: 6,
72
+ placeholder: "Your message here..."
73
+ }
74
+ ]}
75
+ onSuccess={(response) => {
76
+ console.log("Form submitted successfully!", response);
77
+ alert("Thank you for your submission!");
78
+ }}
79
+ onError={(error) => {
80
+ console.error("Form submission failed:", error);
81
+ alert("Something went wrong. Please try again.");
82
+ }}
83
+ />
84
+ );
85
+ }
86
+ ```
13
87
 
14
- ## Expanding the ESLint configuration
88
+ ## Advanced Usage
15
89
 
16
- If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
90
+ ### Custom Styling
17
91
 
18
- ```js
19
- export default defineConfig([
20
- globalIgnores(['dist']),
21
- {
22
- files: ['**/*.{ts,tsx}'],
23
- extends: [
24
- // Other configs...
92
+ ```tsx
93
+ <Form
94
+ formId="styled-form"
95
+ siteId="my-website"
96
+ baseUrl="https://api.daply.com"
97
+ apiToken="your-api-token-here"
98
+ fields={[
99
+ {
100
+ name: "username",
101
+ type: "text",
102
+ label: "Username",
103
+ required: true,
104
+ className: "custom-input-class"
105
+ }
106
+ ]}
107
+ formClassName="my-custom-form"
108
+ submitButtonClassName="my-custom-button"
109
+ submitButtonText="Send Message"
110
+ loadingText="Sending..."
111
+ />
112
+ ```
113
+
114
+ ### With Metadata and Tracking
25
115
 
26
- // Remove tseslint.configs.recommended and replace with this
27
- tseslint.configs.recommendedTypeChecked,
28
- // Alternatively, use this for stricter rules
29
- tseslint.configs.strictTypeChecked,
30
- // Optionally, add this for stylistic rules
31
- tseslint.configs.stylisticTypeChecked,
116
+ ```tsx
117
+ <Form
118
+ formId="contact-form"
119
+ siteId="my-website"
120
+ baseUrl="https://api.daply.com"
121
+ apiToken="your-api-token-here"
122
+ pageUrl={window.location.href}
123
+ pageId="contact-page"
124
+ sessionId="user-session-id"
125
+ metadata={{
126
+ source: "contact page",
127
+ campaign: "organic",
128
+ userAgent: navigator.userAgent
129
+ }}
130
+ fields={[
131
+ // ... your fields
132
+ ]}
133
+ />
134
+ ```
32
135
 
33
- // Other configs...
34
- ],
35
- languageOptions: {
36
- parserOptions: {
37
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
38
- tsconfigRootDir: import.meta.dirname,
39
- },
40
- // other options...
136
+ ### Custom Validation
137
+
138
+ ```tsx
139
+ <Form
140
+ formId="signup-form"
141
+ siteId="my-website"
142
+ baseUrl="https://api.daply.com"
143
+ apiToken="your-api-token-here"
144
+ fields={[
145
+ {
146
+ name: "password",
147
+ type: "text",
148
+ label: "Password",
149
+ required: true,
150
+ validation: (value) => {
151
+ if (value.length < 8) {
152
+ return "Password must be at least 8 characters";
153
+ }
154
+ if (!/[A-Z]/.test(value)) {
155
+ return "Password must contain at least one uppercase letter";
156
+ }
157
+ if (!/[0-9]/.test(value)) {
158
+ return "Password must contain at least one number";
159
+ }
160
+ return undefined; // No error
161
+ }
162
+ }
163
+ ]}
164
+ />
165
+ ```
166
+
167
+ ### All Available Field Types
168
+
169
+ ```tsx
170
+ <Form
171
+ formId="all-fields-form"
172
+ siteId="my-website"
173
+ baseUrl="https://api.daply.com"
174
+ apiToken="your-api-token-here"
175
+ fields={[
176
+ {
177
+ name: "text_field",
178
+ type: "text",
179
+ label: "Text Input",
180
+ placeholder: "Enter text"
41
181
  },
42
- },
43
- ])
44
- ```
45
-
46
- You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
47
-
48
- ```js
49
- // eslint.config.js
50
- import reactX from 'eslint-plugin-react-x'
51
- import reactDom from 'eslint-plugin-react-dom'
52
-
53
- export default defineConfig([
54
- globalIgnores(['dist']),
55
- {
56
- files: ['**/*.{ts,tsx}'],
57
- extends: [
58
- // Other configs...
59
- // Enable lint rules for React
60
- reactX.configs['recommended-typescript'],
61
- // Enable lint rules for React DOM
62
- reactDom.configs.recommended,
63
- ],
64
- languageOptions: {
65
- parserOptions: {
66
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
67
- tsconfigRootDir: import.meta.dirname,
68
- },
69
- // other options...
182
+ {
183
+ name: "email_field",
184
+ type: "email",
185
+ label: "Email Input",
186
+ placeholder: "email@example.com"
187
+ },
188
+ {
189
+ name: "number_field",
190
+ type: "number",
191
+ label: "Number Input",
192
+ placeholder: "123"
70
193
  },
194
+ {
195
+ name: "tel_field",
196
+ type: "tel",
197
+ label: "Phone Number",
198
+ placeholder: "+1 (555) 123-4567"
199
+ },
200
+ {
201
+ name: "url_field",
202
+ type: "url",
203
+ label: "Website URL",
204
+ placeholder: "https://example.com"
205
+ },
206
+ {
207
+ name: "textarea_field",
208
+ type: "textarea",
209
+ label: "Textarea",
210
+ rows: 5,
211
+ placeholder: "Enter multiple lines..."
212
+ }
213
+ ]}
214
+ />
215
+ ```
216
+
217
+ ## API Reference
218
+
219
+ ### FormConfig Props
220
+
221
+ | Prop | Type | Required | Description |
222
+ |------|------|----------|-------------|
223
+ | `formId` | `string` | Yes | Unique identifier for the form |
224
+ | `siteId` | `string` | Yes | Identifier for your site |
225
+ | `baseUrl` | `string` | Yes | Daply API base URL |
226
+ | `apiToken` | `string` | Yes | Bearer token for authentication |
227
+ | `encryptedDbName` | `string` | Yes | Encrypted database name for x-encrypted-db-name header |
228
+ | `fields` | `FormField[]` | Yes | Array of form fields (see below) |
229
+ | `theme` | `'light' \| 'dark' \| 'auto'` | No | Theme mode (default: 'auto') |
230
+ | `pageUrl` | `string` | No | Current page URL for tracking |
231
+ | `pageId` | `string` | No | Page identifier for tracking |
232
+ | `sessionId` | `string` | No | User session ID for tracking |
233
+ | `metadata` | `object` | No | Additional metadata to send with form |
234
+ | `onSuccess` | `(response: any) => void` | No | Callback when form submits successfully |
235
+ | `onError` | `(error: Error) => void` | No | Callback when form submission fails |
236
+ | `submitButtonText` | `string` | No | Submit button text (default: "Submit") |
237
+ | `submitButtonClassName` | `string` | No | Custom CSS class for submit button |
238
+ | `formClassName` | `string` | No | Custom CSS class for form wrapper |
239
+ | `loadingText` | `string` | No | Loading text (default: "Submitting...") |
240
+
241
+ ### FormField Props
242
+
243
+ | Prop | Type | Required | Description |
244
+ |------|------|----------|-------------|
245
+ | `name` | `string` | Yes | Field name (must be unique) |
246
+ | `type` | `FieldType` | Yes | Field type: `text`, `email`, `textarea`, `number`, `tel`, `url` |
247
+ | `label` | `string` | No | Label text displayed above field |
248
+ | `required` | `boolean` | No | Whether field is required (default: false) |
249
+ | `placeholder` | `string` | No | Placeholder text |
250
+ | `defaultValue` | `string` | No | Default value for the field |
251
+ | `rows` | `number` | No | Number of rows (for textarea only) |
252
+ | `className` | `string` | No | Custom CSS class for input element |
253
+ | `validation` | `(value: string) => string \| undefined` | No | Custom validation function |
254
+
255
+ ## Styling
256
+
257
+ The components use **inline styles** for maximum compatibility and zero dependencies. They work out of the box without requiring Tailwind CSS or any other CSS framework.
258
+
259
+ ### Theme Support
260
+
261
+ The components automatically adapt to light and dark modes:
262
+
263
+ ```tsx
264
+ // Light theme
265
+ <Form theme="light" {...props} />
266
+
267
+ // Dark theme (like Oasis Energy form)
268
+ <Form theme="dark" {...props} />
269
+
270
+ // Auto-detect user's system preference (default)
271
+ <Form theme="auto" {...props} />
272
+ ```
273
+
274
+ ### Default Styles
275
+ - **Form Container**: Clean white (light) / dark `#1a1a1a` (dark) with subtle shadows
276
+ - **Input Fields**: Large padding (16px), rounded corners, smooth transitions
277
+ - **Focus States**: Blue ring with smooth animations
278
+ - **Error States**: Red border and background tint
279
+ - **Submit Button**: Gradient blue with hover effects and uppercase text
280
+
281
+ ### Customization Options
282
+
283
+ You can add custom classes for additional styling:
284
+
285
+ ```tsx
286
+ <Form
287
+ formClassName="my-custom-form"
288
+ submitButtonClassName="my-custom-button"
289
+ fields={[
290
+ {
291
+ name: "email",
292
+ type: "email",
293
+ className: "my-custom-input"
294
+ // Add your own CSS classes
295
+ }
296
+ ]}
297
+ />
298
+ ```
299
+
300
+ **Note:** Inline styles take precedence, but you can use `!important` in your custom CSS to override them if needed.
301
+
302
+ ## TypeScript Support
303
+
304
+ Full TypeScript support with exported types:
305
+
306
+ ```tsx
307
+ import type {
308
+ FormConfig,
309
+ FormField,
310
+ FormSchema,
311
+ FormData,
312
+ FieldType,
313
+ FieldSchema,
314
+ DaplyFormSubmission
315
+ } from 'daply-ui';
316
+ ```
317
+
318
+ ## API Endpoint
319
+
320
+ The form submits to:
321
+ ```
322
+ POST {{baseUrl}}/api/v1/form-submissions
323
+ ```
324
+
325
+ With the following payload structure:
326
+
327
+ ```json
328
+ {
329
+ "formId": "contact-form",
330
+ "siteId": "my-website",
331
+ "formData": {
332
+ "name": "John Doe",
333
+ "email": "john@example.com",
334
+ "subject": "Inquiry",
335
+ "message": "This is a test message."
71
336
  },
72
- ])
337
+ "formSchema": {
338
+ "name": { "type": "text", "required": true },
339
+ "email": { "type": "email", "required": true },
340
+ "subject": { "type": "text", "required": true },
341
+ "message": { "type": "textarea", "required": true }
342
+ },
343
+ "pageUrl": "https://example.com/contact",
344
+ "pageId": "contact-page",
345
+ "sessionId": "sess_123456789",
346
+ "metadata": {
347
+ "source": "contact page",
348
+ "campaign": "organic"
349
+ }
350
+ }
351
+ ```
352
+
353
+ Headers:
354
+ ```
355
+ Content-Type: application/json
356
+ Authorization: Bearer {your-api-token}
357
+ x-encrypted-db-name: {your-encrypted-db-name}
358
+ ```
359
+
360
+ **Important:** Store your `apiToken` and `encryptedDbName` securely in environment variables:
361
+
362
+ ```tsx
363
+ <Form
364
+ apiToken={process.env.REACT_APP_DAPLY_TOKEN}
365
+ encryptedDbName={process.env.REACT_APP_ENCRYPTED_DB_NAME}
366
+ // ... other props
367
+ />
73
368
  ```
369
+
370
+ ## Features
371
+
372
+ - ✅ Built-in validation (required fields, email, URL)
373
+ - ✅ Custom validation functions
374
+ - ✅ Automatic form submission to Daply backend
375
+ - ✅ Loading states with smooth animations
376
+ - ✅ Error handling with user-friendly messages
377
+ - ✅ Success/error callbacks
378
+ - ✅ Full TypeScript support
379
+ - ✅ Modern, clean design inspired by Daply forms
380
+ - ✅ Light & Dark theme support (automatic)
381
+ - ✅ Customizable styling with Tailwind CSS
382
+ - ✅ Multiple field types (text, email, textarea, number, tel, url)
383
+ - ✅ Metadata and tracking support
384
+ - ✅ Smooth transitions and hover effects
385
+ - ✅ Mobile-responsive design
386
+
387
+ ## Development
388
+
389
+ ```bash
390
+ # Install dependencies
391
+ npm install
392
+
393
+ # Run development server
394
+ npm run dev
395
+
396
+ # Build library
397
+ npm run build
398
+
399
+ # Publish to npm
400
+ npm run publish
401
+ ```
402
+
403
+ ## License
404
+
405
+ MIT
406
+
407
+ ## Support
408
+
409
+ For issues and questions, please visit our [GitHub repository](https://github.com/yourusername/daply-ui).
package/dist/daply-ui.js CHANGED
@@ -1,5 +1,268 @@
1
- import { jsx as o } from "react/jsx-runtime";
2
- const n = ({ children: t }) => /* @__PURE__ */ o("button", { children: t });
1
+ import { jsx as s, jsxs as y } from "react/jsx-runtime";
2
+ import { useState as I } from "react";
3
+ const J = ({ children: i }) => /* @__PURE__ */ s("button", { children: i }), W = ({ name: i, value: x, onChange: S, error: a, schema: w, theme: v = "auto" }) => {
4
+ const {
5
+ type: k = "text",
6
+ required: d = !1,
7
+ placeholder: m = "",
8
+ label: u,
9
+ className: b = ""
10
+ } = w, o = v === "dark" || v === "auto" && typeof window < "u" && window.matchMedia("(prefers-color-scheme: dark)").matches, l = {
11
+ width: "100%",
12
+ boxSizing: "border-box",
13
+ // 👈 CRITICAL
14
+ padding: "16px",
15
+ borderRadius: "8px",
16
+ outline: "none",
17
+ transition: "all 0.2s ease",
18
+ fontSize: "16px",
19
+ fontFamily: "inherit",
20
+ border: a ? `2px solid ${o ? "#f87171" : "#ef4444"}` : `1px solid ${o ? "#374151" : "#d1d5db"}`,
21
+ backgroundColor: a ? o ? "rgba(153, 27, 27, 0.1)" : "rgba(254, 226, 226, 1)" : o ? "#2a2a2a" : "#ffffff",
22
+ color: o ? "#ffffff" : "#111827"
23
+ }, F = {
24
+ ...l,
25
+ borderColor: a ? o ? "#f87171" : "#ef4444" : "#3b82f6",
26
+ boxShadow: a ? `0 0 0 3px ${o ? "rgba(248, 113, 113, 0.2)" : "rgba(239, 68, 68, 0.2)"}` : "0 0 0 3px rgba(59, 130, 246, 0.2)"
27
+ }, $ = {
28
+ display: "block",
29
+ fontWeight: "500",
30
+ marginBottom: "8px",
31
+ color: o ? "#ffffff" : "#111827",
32
+ fontSize: "14px"
33
+ }, j = {
34
+ marginTop: "4px",
35
+ fontSize: "14px",
36
+ color: o ? "#f87171" : "#dc2626"
37
+ }, D = {
38
+ color: o ? "#f87171" : "#ef4444",
39
+ marginLeft: "4px"
40
+ }, p = {
41
+ marginBottom: "24px",
42
+ display: "flex",
43
+ flexDirection: "column",
44
+ alignItems: "flex-start",
45
+ width: "100%"
46
+ }, g = o ? "#6b7280" : "#9ca3af";
47
+ return k === "textarea" ? /* @__PURE__ */ y("div", { style: p, children: [
48
+ u ? /* @__PURE__ */ y("label", { htmlFor: i, style: $, children: [
49
+ u,
50
+ d ? /* @__PURE__ */ s("span", { style: D, children: "*" }) : null
51
+ ] }) : null,
52
+ /* @__PURE__ */ s(
53
+ "textarea",
54
+ {
55
+ id: i,
56
+ name: i,
57
+ value: x,
58
+ onChange: S,
59
+ required: d,
60
+ placeholder: m,
61
+ rows: w.rows || 4,
62
+ style: l,
63
+ onFocus: (n) => {
64
+ Object.assign(n.target.style, F);
65
+ },
66
+ onBlur: (n) => {
67
+ Object.assign(n.target.style, l);
68
+ },
69
+ className: b
70
+ }
71
+ ),
72
+ a ? /* @__PURE__ */ s("p", { style: j, children: a }) : null,
73
+ /* @__PURE__ */ s("style", { children: `
74
+ textarea::placeholder { color: ${g}; }
75
+ ` })
76
+ ] }) : /* @__PURE__ */ y("div", { style: p, children: [
77
+ u ? /* @__PURE__ */ y("label", { htmlFor: i, style: $, children: [
78
+ u,
79
+ d ? /* @__PURE__ */ s("span", { style: D, children: "*" }) : null
80
+ ] }) : null,
81
+ /* @__PURE__ */ s(
82
+ "input",
83
+ {
84
+ id: i,
85
+ name: i,
86
+ type: k,
87
+ value: x,
88
+ onChange: S,
89
+ required: d,
90
+ placeholder: m,
91
+ style: l,
92
+ onFocus: (n) => {
93
+ Object.assign(n.target.style, F);
94
+ },
95
+ onBlur: (n) => {
96
+ Object.assign(n.target.style, l);
97
+ },
98
+ className: b
99
+ }
100
+ ),
101
+ a ? /* @__PURE__ */ s("p", { style: j, children: a }) : null,
102
+ /* @__PURE__ */ s("style", { children: `
103
+ input::placeholder { color: ${g}; }
104
+ ` })
105
+ ] });
106
+ }, G = (i) => {
107
+ const {
108
+ formId: x,
109
+ siteId: S,
110
+ fields: a,
111
+ baseUrl: w,
112
+ apiToken: v,
113
+ encryptedDbName: k,
114
+ pageUrl: d,
115
+ pageId: m,
116
+ sessionId: u,
117
+ metadata: b,
118
+ onSuccess: o,
119
+ onError: l,
120
+ submitButtonText: F = "Submit",
121
+ submitButtonClassName: $ = "",
122
+ formClassName: j = "",
123
+ loadingText: D = "Submitting...",
124
+ theme: p = "auto"
125
+ } = i, g = a.reduce((t, e) => (t[e.name] = e.defaultValue || "", t), {}), [n, N] = I(g), [O, T] = I({}), [f, R] = I(!1), [z, q] = I(!1), C = p === "dark" || p === "auto" && typeof window < "u" && window.matchMedia("(prefers-color-scheme: dark)").matches, L = (t) => {
126
+ const { name: e, value: h } = t.target;
127
+ N((r) => ({
128
+ ...r,
129
+ [e]: h
130
+ })), O[e] && T((r) => {
131
+ const c = { ...r };
132
+ return delete c[e], c;
133
+ });
134
+ }, M = () => {
135
+ const t = {};
136
+ return a.forEach((e) => {
137
+ const h = n[e.name], r = String(h || "");
138
+ if (e.required && !r.trim()) {
139
+ t[e.name] = `${e.label || e.name} is required`;
140
+ return;
141
+ }
142
+ if (e.type === "email" && r && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(r)) {
143
+ t[e.name] = "Please enter a valid email address";
144
+ return;
145
+ }
146
+ if (e.type === "url" && r)
147
+ try {
148
+ new URL(r);
149
+ } catch {
150
+ t[e.name] = "Please enter a valid URL";
151
+ return;
152
+ }
153
+ if (e.validation && r) {
154
+ const c = e.validation(r);
155
+ if (c) {
156
+ t[e.name] = c;
157
+ return;
158
+ }
159
+ }
160
+ }), T(t), Object.keys(t).length === 0;
161
+ }, P = async (t) => {
162
+ if (t.preventDefault(), !!M()) {
163
+ R(!0);
164
+ try {
165
+ const e = a.reduce((B, E) => (B[E.name] = {
166
+ type: E.type,
167
+ required: E.required
168
+ }, B), {}), h = {
169
+ formId: x,
170
+ siteId: S,
171
+ formData: n,
172
+ formSchema: e,
173
+ ...d && { pageUrl: d },
174
+ ...m && { pageId: m },
175
+ ...u && { sessionId: u },
176
+ ...b && { metadata: b }
177
+ }, r = await fetch(`${w}/api/v1/form-submissions`, {
178
+ method: "POST",
179
+ headers: {
180
+ "Content-Type": "application/json",
181
+ Authorization: `Bearer ${v}`,
182
+ "x-encrypted-db-name": k
183
+ },
184
+ body: JSON.stringify(h)
185
+ });
186
+ if (!r.ok) {
187
+ const B = await r.json().catch(() => ({}));
188
+ throw new Error(B.message || `HTTP error! status: ${r.status}`);
189
+ }
190
+ const c = await r.json();
191
+ N(g), T({}), o && o(c);
192
+ } catch (e) {
193
+ console.error("Form submission error:", e), l && l(e);
194
+ } finally {
195
+ R(!1);
196
+ }
197
+ }
198
+ }, U = {
199
+ maxWidth: "612px",
200
+ margin: "0 auto",
201
+ padding: "32px",
202
+ backgroundColor: C ? "#1a1a1a" : "#ffffff",
203
+ borderRadius: "12px",
204
+ boxShadow: C ? "0 25px 50px -12px rgba(0, 0, 0, 0.5)" : "0 10px 15px -3px rgba(0, 0, 0, 0.1)",
205
+ border: `1px solid ${C ? "#1f2937" : "#e5e7eb"}`
206
+ }, H = {
207
+ width: "100%",
208
+ padding: "16px 24px",
209
+ background: f ? "linear-gradient(to right, #4b5563, #4b5563)" : z ? "linear-gradient(to right, #1d4ed8, #1e40af)" : "linear-gradient(to right, #2563eb, #1d4ed8)",
210
+ color: "#ffffff",
211
+ fontWeight: "bold",
212
+ fontSize: "18px",
213
+ textTransform: "uppercase",
214
+ letterSpacing: "0.05em",
215
+ borderRadius: "8px",
216
+ border: "none",
217
+ cursor: f ? "not-allowed" : "pointer",
218
+ opacity: f ? 0.5 : 1,
219
+ transition: "all 0.2s ease",
220
+ outline: "none",
221
+ fontFamily: "inherit"
222
+ };
223
+ return /* @__PURE__ */ y(
224
+ "form",
225
+ {
226
+ onSubmit: P,
227
+ style: U,
228
+ className: j,
229
+ children: [
230
+ a.map((t) => /* @__PURE__ */ s(
231
+ W,
232
+ {
233
+ name: t.name,
234
+ value: String(n[t.name] || ""),
235
+ onChange: L,
236
+ error: O[t.name],
237
+ schema: t,
238
+ theme: p
239
+ },
240
+ t.name
241
+ )),
242
+ /* @__PURE__ */ s(
243
+ "button",
244
+ {
245
+ type: "submit",
246
+ disabled: f,
247
+ style: H,
248
+ onMouseEnter: () => !f && q(!0),
249
+ onMouseLeave: () => q(!1),
250
+ onFocus: (t) => {
251
+ f || (t.target.style.boxShadow = "0 0 0 3px rgba(59, 130, 246, 0.3)");
252
+ },
253
+ onBlur: (t) => {
254
+ t.target.style.boxShadow = "none";
255
+ },
256
+ className: $,
257
+ children: f ? D : F
258
+ }
259
+ )
260
+ ]
261
+ }
262
+ );
263
+ };
3
264
  export {
4
- n as Button
265
+ J as Button,
266
+ G as Form,
267
+ W as InputField
5
268
  };
@@ -1 +1,5 @@
1
- (function(e,t){typeof exports=="object"&&typeof module<"u"?t(exports,require("react/jsx-runtime")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime"],t):(e=typeof globalThis<"u"?globalThis:e||self,t(e["daply-ui"]={},e["react/jsx-runtime"]))})(this,(function(e,t){"use strict";const n=({children:i})=>t.jsx("button",{children:i});e.Button=n,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(i,r){typeof exports=="object"&&typeof module<"u"?r(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],r):(i=typeof globalThis<"u"?globalThis:i||self,r(i["daply-ui"]={},i["react/jsx-runtime"],i.React))})(this,(function(i,r,x){"use strict";const P=({children:l})=>r.jsx("button",{children:l}),E=({name:l,value:S,onChange:w,error:n,schema:v,theme:F="auto"})=>{const{type:j="text",required:u=!1,placeholder:m="",label:f,className:y=""}=v,a=F==="dark"||F==="auto"&&typeof window<"u"&&window.matchMedia("(prefers-color-scheme: dark)").matches,d={width:"100%",boxSizing:"border-box",padding:"16px",borderRadius:"8px",outline:"none",transition:"all 0.2s ease",fontSize:"16px",fontFamily:"inherit",border:n?`2px solid ${a?"#f87171":"#ef4444"}`:`1px solid ${a?"#374151":"#d1d5db"}`,backgroundColor:n?a?"rgba(153, 27, 27, 0.1)":"rgba(254, 226, 226, 1)":a?"#2a2a2a":"#ffffff",color:a?"#ffffff":"#111827"},k={...d,borderColor:n?a?"#f87171":"#ef4444":"#3b82f6",boxShadow:n?`0 0 0 3px ${a?"rgba(248, 113, 113, 0.2)":"rgba(239, 68, 68, 0.2)"}`:"0 0 0 3px rgba(59, 130, 246, 0.2)"},T={display:"block",fontWeight:"500",marginBottom:"8px",color:a?"#ffffff":"#111827",fontSize:"14px"},$={marginTop:"4px",fontSize:"14px",color:a?"#f87171":"#dc2626"},B={color:a?"#f87171":"#ef4444",marginLeft:"4px"},b={marginBottom:"24px",display:"flex",flexDirection:"column",alignItems:"flex-start",width:"100%"},g=a?"#6b7280":"#9ca3af";return j==="textarea"?r.jsxs("div",{style:b,children:[f?r.jsxs("label",{htmlFor:l,style:T,children:[f,u?r.jsx("span",{style:B,children:"*"}):null]}):null,r.jsx("textarea",{id:l,name:l,value:S,onChange:w,required:u,placeholder:m,rows:v.rows||4,style:d,onFocus:s=>{Object.assign(s.target.style,k)},onBlur:s=>{Object.assign(s.target.style,d)},className:y}),n?r.jsx("p",{style:$,children:n}):null,r.jsx("style",{children:`
2
+ textarea::placeholder { color: ${g}; }
3
+ `})]}):r.jsxs("div",{style:b,children:[f?r.jsxs("label",{htmlFor:l,style:T,children:[f,u?r.jsx("span",{style:B,children:"*"}):null]}):null,r.jsx("input",{id:l,name:l,type:j,value:S,onChange:w,required:u,placeholder:m,style:d,onFocus:s=>{Object.assign(s.target.style,k)},onBlur:s=>{Object.assign(s.target.style,d)},className:y}),n?r.jsx("p",{style:$,children:n}):null,r.jsx("style",{children:`
4
+ input::placeholder { color: ${g}; }
5
+ `})]})},L=l=>{const{formId:S,siteId:w,fields:n,baseUrl:v,apiToken:F,encryptedDbName:j,pageUrl:u,pageId:m,sessionId:f,metadata:y,onSuccess:a,onError:d,submitButtonText:k="Submit",submitButtonClassName:T="",formClassName:$="",loadingText:B="Submitting...",theme:b="auto"}=l,g=n.reduce((t,e)=>(t[e.name]=e.defaultValue||"",t),{}),[s,O]=x.useState(g),[N,I]=x.useState({}),[p,z]=x.useState(!1),[U,M]=x.useState(!1),q=b==="dark"||b==="auto"&&typeof window<"u"&&window.matchMedia("(prefers-color-scheme: dark)").matches,H=t=>{const{name:e,value:h}=t.target;O(o=>({...o,[e]:h})),N[e]&&I(o=>{const c={...o};return delete c[e],c})},W=()=>{const t={};return n.forEach(e=>{const h=s[e.name],o=String(h||"");if(e.required&&!o.trim()){t[e.name]=`${e.label||e.name} is required`;return}if(e.type==="email"&&o&&!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(o)){t[e.name]="Please enter a valid email address";return}if(e.type==="url"&&o)try{new URL(o)}catch{t[e.name]="Please enter a valid URL";return}if(e.validation&&o){const c=e.validation(o);if(c){t[e.name]=c;return}}}),I(t),Object.keys(t).length===0},V=async t=>{if(t.preventDefault(),!!W()){z(!0);try{const e=n.reduce((D,C)=>(D[C.name]={type:C.type,required:C.required},D),{}),h={formId:S,siteId:w,formData:s,formSchema:e,...u&&{pageUrl:u},...m&&{pageId:m},...f&&{sessionId:f},...y&&{metadata:y}},o=await fetch(`${v}/api/v1/form-submissions`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${F}`,"x-encrypted-db-name":j},body:JSON.stringify(h)});if(!o.ok){const D=await o.json().catch(()=>({}));throw new Error(D.message||`HTTP error! status: ${o.status}`)}const c=await o.json();O(g),I({}),a&&a(c)}catch(e){console.error("Form submission error:",e),d&&d(e)}finally{z(!1)}}},A={maxWidth:"612px",margin:"0 auto",padding:"32px",backgroundColor:q?"#1a1a1a":"#ffffff",borderRadius:"12px",boxShadow:q?"0 25px 50px -12px rgba(0, 0, 0, 0.5)":"0 10px 15px -3px rgba(0, 0, 0, 0.1)",border:`1px solid ${q?"#1f2937":"#e5e7eb"}`},J={width:"100%",padding:"16px 24px",background:p?"linear-gradient(to right, #4b5563, #4b5563)":U?"linear-gradient(to right, #1d4ed8, #1e40af)":"linear-gradient(to right, #2563eb, #1d4ed8)",color:"#ffffff",fontWeight:"bold",fontSize:"18px",textTransform:"uppercase",letterSpacing:"0.05em",borderRadius:"8px",border:"none",cursor:p?"not-allowed":"pointer",opacity:p?.5:1,transition:"all 0.2s ease",outline:"none",fontFamily:"inherit"};return r.jsxs("form",{onSubmit:V,style:A,className:$,children:[n.map(t=>r.jsx(E,{name:t.name,value:String(s[t.name]||""),onChange:H,error:N[t.name],schema:t,theme:b},t.name)),r.jsx("button",{type:"submit",disabled:p,style:J,onMouseEnter:()=>!p&&M(!0),onMouseLeave:()=>M(!1),onFocus:t=>{p||(t.target.style.boxShadow="0 0 0 3px rgba(59, 130, 246, 0.3)")},onBlur:t=>{t.target.style.boxShadow="none"},className:T,children:p?B:k})]})};i.Button=P,i.Form=L,i.InputField=E,Object.defineProperty(i,Symbol.toStringTag,{value:"Module"})}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "daply-ui",
3
- "version": "0.0.0",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "scripts": {
17
17
  "dev": "vite",
18
- "prebuild": "rm -rf dist",
18
+ "prebuild": "rimraf dist",
19
19
  "build": "tsc -b && vite build",
20
20
  "lint": "eslint .",
21
21
  "preview": "vite preview",
@@ -36,6 +36,7 @@
36
36
  "eslint-plugin-react-hooks": "^7.0.1",
37
37
  "eslint-plugin-react-refresh": "^0.4.24",
38
38
  "globals": "^16.5.0",
39
+ "rimraf": "^6.1.2",
39
40
  "typescript": "~5.9.3",
40
41
  "typescript-eslint": "^8.46.4",
41
42
  "vite": "^7.2.4"