Skip to content

Commit c598308

Browse files
committed
Enhance AJAX demo with global script tag support
1 parent f4f32c1 commit c598308

File tree

15 files changed

+683
-9
lines changed

15 files changed

+683
-9
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,6 @@ yarn-error.log*
3333

3434
.vscode
3535

36-
.local-dev
36+
.local-dev
37+
38+
.claude

CLAUDE.md

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,47 @@
1-
# Formspree JS Development Guidelines
1+
# CLAUDE.md
22

3-
## @formspree/ajax Package
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44

5-
This package is a recreation of [statickit-html](https://github.com/formspree/statickit-html) using `@formspree/core` as the base.
5+
## Build and Development Commands
6+
7+
```sh
8+
yarn # Install dependencies
9+
yarn build # Build all packages (required before dev)
10+
yarn dev # Run dev server with cra-demo example on localhost:3000
11+
yarn test # Run tests across all packages
12+
yarn typecheck # Type check all packages
13+
yarn lint # Lint all packages
14+
yarn format # Format code with Prettier
15+
yarn changeset # Create a changeset for PRs
16+
```
17+
18+
### Package-specific commands
19+
20+
Run from package directory (e.g., `packages/formspree-core`):
21+
22+
```sh
23+
yarn test # Run tests for this package
24+
yarn build # Build this package only
25+
yarn lint # Lint this package
26+
```
27+
28+
For the ajax package specifically:
29+
30+
```sh
31+
yarn demo # Build and run ajax-demo example (from packages/formspree-ajax)
32+
```
33+
34+
## Architecture
35+
36+
This is a Yarn workspaces monorepo managed by Turborepo with three packages:
37+
38+
- **@formspree/core** - Core submission client and types. Provides `createClient`, `getDefaultClient`, `SubmissionError`, and submission types.
39+
- **@formspree/react** - React hooks (`useForm`, `useSubmit`) built on top of core. Depends on core.
40+
- **@formspree/ajax** - Vanilla JS library for declarative form handling. Recreation of [statickit-html](https://github.com/formspree/statickit-html) using core.
41+
42+
All packages use `tsup` for building and output to `dist/`.
43+
44+
## @formspree/ajax Package Guidelines
645

746
Reference API from statickit-html:
847

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// This file simulates loading dist/global.js in the Vite dev environment.
2+
// In production, you'd use: <script src="https://unpkg.com/@formspree/ajax@1/dist/global.js" defer></script>
3+
import '@formspree/ajax/global';
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Formspree AJAX - Script Tag Demo</title>
7+
<style>
8+
* {
9+
margin: 0;
10+
padding: 0;
11+
box-sizing: border-box;
12+
}
13+
14+
body {
15+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
16+
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
17+
min-height: 100vh;
18+
display: flex;
19+
align-items: center;
20+
justify-content: center;
21+
padding: 20px;
22+
}
23+
24+
.container {
25+
background: white;
26+
border-radius: 12px;
27+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
28+
max-width: 500px;
29+
width: 100%;
30+
padding: 40px;
31+
}
32+
33+
h1 {
34+
color: #333;
35+
margin-bottom: 10px;
36+
font-size: 28px;
37+
}
38+
39+
.subtitle {
40+
color: #666;
41+
margin-bottom: 30px;
42+
font-size: 14px;
43+
}
44+
45+
.badge {
46+
display: inline-block;
47+
background: #f5576c;
48+
color: white;
49+
font-size: 11px;
50+
font-weight: 600;
51+
padding: 3px 8px;
52+
border-radius: 4px;
53+
margin-left: 8px;
54+
vertical-align: middle;
55+
}
56+
57+
.form-group {
58+
margin-bottom: 20px;
59+
}
60+
61+
label {
62+
display: block;
63+
color: #333;
64+
font-weight: 500;
65+
margin-bottom: 8px;
66+
font-size: 14px;
67+
}
68+
69+
input[type="text"],
70+
input[type="email"],
71+
textarea {
72+
width: 100%;
73+
padding: 12px;
74+
border: 2px solid #e0e0e0;
75+
border-radius: 8px;
76+
font-size: 14px;
77+
font-family: inherit;
78+
transition: border-color 0.3s;
79+
}
80+
81+
input[type="text"]:focus,
82+
input[type="email"]:focus,
83+
textarea:focus {
84+
outline: none;
85+
border-color: #f5576c;
86+
}
87+
88+
textarea {
89+
resize: vertical;
90+
min-height: 120px;
91+
}
92+
93+
button {
94+
width: 100%;
95+
padding: 14px;
96+
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
97+
color: white;
98+
border: none;
99+
border-radius: 8px;
100+
font-size: 16px;
101+
font-weight: 600;
102+
cursor: pointer;
103+
transition: transform 0.2s, box-shadow 0.2s;
104+
}
105+
106+
button:hover {
107+
transform: translateY(-2px);
108+
box-shadow: 0 10px 20px rgba(245, 87, 108, 0.4);
109+
}
110+
111+
button:active {
112+
transform: translateY(0);
113+
}
114+
115+
button:disabled {
116+
opacity: 0.6;
117+
cursor: not-allowed;
118+
transform: none;
119+
}
120+
121+
.note {
122+
margin-top: 20px;
123+
padding: 12px;
124+
background: #f8f9fa;
125+
border-radius: 8px;
126+
font-size: 12px;
127+
color: #666;
128+
}
129+
130+
.note strong {
131+
color: #333;
132+
}
133+
134+
.note code {
135+
background: #e9ecef;
136+
padding: 2px 4px;
137+
border-radius: 3px;
138+
font-size: 11px;
139+
}
140+
141+
.back-link {
142+
display: inline-block;
143+
margin-bottom: 20px;
144+
color: #f5576c;
145+
text-decoration: none;
146+
font-size: 14px;
147+
}
148+
149+
.back-link:hover {
150+
text-decoration: underline;
151+
}
152+
</style>
153+
</head>
154+
<body>
155+
<div class="container">
156+
<a href="/" class="back-link">&larr; ESM Demo</a>
157+
158+
<h1>Newsletter Signup <span class="badge">Script Tag</span></h1>
159+
<p class="subtitle">Using <code>window.formspree</code> global API &mdash; no bundler required</p>
160+
161+
<div data-fs-message></div>
162+
163+
<form id="newsletter-form">
164+
<div class="form-group">
165+
<label for="name">Name</label>
166+
<input
167+
type="text"
168+
id="name"
169+
name="name"
170+
placeholder="Your name"
171+
data-fs-field="name"
172+
>
173+
<span data-fs-error="name"></span>
174+
</div>
175+
176+
<div class="form-group">
177+
<label for="email">Email</label>
178+
<input
179+
type="email"
180+
id="email"
181+
name="email"
182+
placeholder="your.email@example.com"
183+
data-fs-field="email"
184+
>
185+
<span data-fs-error="email"></span>
186+
</div>
187+
188+
<div class="form-group">
189+
<label for="message">Message (optional)</label>
190+
<textarea
191+
id="message"
192+
name="message"
193+
placeholder="Anything you'd like to share..."
194+
data-fs-field="message"
195+
></textarea>
196+
<span data-fs-error="message"></span>
197+
</div>
198+
199+
<button type="submit" data-fs-submit-btn>
200+
Subscribe
201+
</button>
202+
</form>
203+
204+
<div class="note">
205+
<strong>How it works:</strong> Load <code>global.js</code> via a script tag.
206+
It sets up <code>window.formspree()</code>, then call it to initialize your form.
207+
No bundler needed!
208+
</div>
209+
</div>
210+
211+
<!--
212+
Production usage:
213+
214+
<script src="https://unpkg.com/@formspree/ajax@1/dist/global.js"></script>
215+
<script>
216+
formspree('form', '#newsletter-form', { formId: 'YOUR_FORM_ID' });
217+
</script>
218+
219+
In this Vite demo, global.js is loaded via a module entry that aliases to the source.
220+
-->
221+
222+
<!-- Step 1: Load the library -->
223+
<script type="module" src="/global-entry.ts"></script>
224+
225+
<!-- Step 2: Initialize the form -->
226+
<script type="module">
227+
formspree('form', '#newsletter-form', {
228+
formId: 'meekngvn',
229+
debug: true,
230+
fields: {
231+
name: { prettyName: 'Name' },
232+
email: {
233+
prettyName: 'Email',
234+
errorMessages: {
235+
typeEmail: 'Please enter a valid email',
236+
requiredFieldEmpty: 'Email is required',
237+
},
238+
},
239+
},
240+
});
241+
</script>
242+
</body>
243+
</html>

examples/ajax-demo/public/index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ <h1>Contact Form</h1>
186186

187187
<div class="note">
188188
<strong>Note:</strong> Set <code>VITE_FORMSPREE_FORM_ID</code> in <code>.env.local</code> or replace <code>YOUR_FORM_ID</code> in <code>main.ts</code> with your actual Formspree form ID.
189+
<br><br>
190+
<a href="/global.html">See the Script Tag demo &rarr;</a>
189191
</div>
190192
</div>
191193

examples/ajax-demo/vite.config.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { defineConfig } from 'vite';
22
import { resolve } from 'path';
33

4+
const ajaxPkg = resolve(__dirname, '../../packages/formspree-ajax');
5+
46
export default defineConfig({
57
root: './public',
68
envDir: '..',
@@ -9,13 +11,15 @@ export default defineConfig({
911
emptyOutDir: true,
1012
rollupOptions: {
1113
input: {
12-
main: resolve(__dirname, 'public/index.html')
14+
main: resolve(__dirname, 'public/index.html'),
15+
global: resolve(__dirname, 'public/global.html')
1316
}
1417
}
1518
},
1619
resolve: {
1720
alias: {
18-
'@formspree/ajax': resolve(__dirname, '../../packages/formspree-ajax/src/index.ts'),
21+
'@formspree/ajax/global': resolve(ajaxPkg, 'src/global.ts'),
22+
'@formspree/ajax': resolve(ajaxPkg, 'src/index.ts'),
1923
'@formspree/core': resolve(__dirname, '../../packages/formspree-core/src/index.ts')
2024
}
2125
}

packages/formspree-ajax/.eslintrc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ extends:
44
ignorePatterns:
55
- jest.config.js
66
- jest.setup.js
7+
- tsup.config.ts
78
- dist/
89
parserOptions:
910
project: './tsconfig.json'

packages/formspree-ajax/package.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,23 @@
1515
"contributors": [
1616
"Cole Krumbholz"
1717
],
18-
"sideEffects": false,
18+
"sideEffects": [
19+
"./dist/global.js",
20+
"./src/global.ts"
21+
],
1922
"main": "./dist/index.js",
23+
"unpkg": "./dist/global.js",
2024
"module": "./dist/index.mjs",
25+
"browser": "./dist/global.js",
2126
"types": "./dist/index.d.ts",
2227
"files": [
2328
"dist/**"
2429
],
2530
"scripts": {
26-
"build": "tsup src/index.ts --format esm,cjs --dts --minify",
31+
"build": "tsup",
2732
"clean": "rm -rf dist && rm -rf node_modules",
28-
"dev": "tsup src/index.ts --format esm,cjs --dts --sourcemap --watch",
33+
"demo": "npm run build && npm run --prefix ../../examples/ajax-demo dev",
34+
"dev": "tsup --watch",
2935
"lint": "eslint ./src ./test",
3036
"test": "jest",
3137
"typecheck": "tsc --noEmit"

0 commit comments

Comments
 (0)