Compare commits
10 Commits
jazz-bette
...
feat/form-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
998927f752 | ||
|
|
1da5f9c150 | ||
|
|
ef242d9a7b | ||
|
|
ca4551b044 | ||
|
|
9cd8c407d2 | ||
|
|
88d393ac91 | ||
|
|
f04b11572d | ||
|
|
3cbd4eae3b | ||
|
|
63ab6abd4f | ||
|
|
52243161a3 |
@@ -6,6 +6,7 @@
|
||||
"fixed": [
|
||||
[
|
||||
"cojson",
|
||||
"cojson-storage",
|
||||
"cojson-storage-indexeddb",
|
||||
"cojson-storage-sqlite",
|
||||
"cojson-transport-ws",
|
||||
|
||||
5
.changeset/hungry-spiders-turn.md
Normal file
5
.changeset/hungry-spiders-turn.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"jazz-tools": patch
|
||||
---
|
||||
|
||||
Fix type error with `RNQuickCrypto` that prevented using it as a `CryptoProvider`
|
||||
5
.changeset/nasty-apples-bow.md
Normal file
5
.changeset/nasty-apples-bow.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"create-jazz-app": patch
|
||||
---
|
||||
|
||||
Add default project name
|
||||
7
.changeset/ten-jeans-live.md
Normal file
7
.changeset/ten-jeans-live.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"jazz-react-auth-betterauth": patch
|
||||
"jazz-auth-betterauth": patch
|
||||
"jazz-tools": patch
|
||||
---
|
||||
|
||||
Changes that verify the BetterAuth package for React also works with React Native and Expo apps
|
||||
35
.github/pull_request_template.md
vendored
35
.github/pull_request_template.md
vendored
@@ -1,23 +1,24 @@
|
||||
# Description
|
||||
<!-- Please include a summary of the change and which issue is fixed -->
|
||||
<!-- Please also include relevant motivation and context -->
|
||||
<!-- Include any links to documentation like RFC’s if necessary -->
|
||||
<!-- Add a link to to relevant preview environments or anything that would simplify visual review process -->
|
||||
<!-- Supplemental screenshots and video are encouraged, but the primary description should be in text -->
|
||||
### What this Does
|
||||
Brief summary of the change, ideally framed in user or product terms.
|
||||
|
||||
## Manual testing instructions
|
||||
### Why Are We Doing This?
|
||||
Link to the shaped pitch or explain what problem it solves.
|
||||
|
||||
<!-- Add any actions required to manually test the changes -->
|
||||
### Scope / Boundaries
|
||||
Includes:
|
||||
- [x] Core feature functionality
|
||||
- [x] Tests or validation steps
|
||||
|
||||
## Tests
|
||||
Do NOT include:
|
||||
- [ ] Related stretch features or follow-ups
|
||||
|
||||
- [ ] Tests have been added and/or updated
|
||||
- [ ] Tests have not been updated, because: <!-- Insert reason for not updating tests here -->
|
||||
- [ ] I need help with writing tests
|
||||
### Testing Instructions
|
||||
How a reviewer or QA can verify behavior, offer step-by-step instructions if possible. Screenshots or recordings are welcome.
|
||||
|
||||
### Known Issues / Open Questions (if any)
|
||||
- [ ] Note anything you’d like review on or decided async
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I've updated the part of the docs that are affected the PR changes
|
||||
- [ ] I've generated a changeset, if a version bump is required
|
||||
- [ ] I've updated the jsDoc comments to the public APIs I've modified, or added them when missing
|
||||
### Related Links
|
||||
- GitHub issue
|
||||
- Linear pitch
|
||||
- Design links or references
|
||||
|
||||
6
.github/workflows/code-quality.yml
vendored
6
.github/workflows/code-quality.yml
vendored
@@ -1,11 +1,5 @@
|
||||
name: Code quality
|
||||
|
||||
concurrency:
|
||||
# For pushes, this lets concurrent runs happen, so each push gets a result.
|
||||
# But for other events (e.g. PRs), we can cancel the previous runs.
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
|
||||
6
.github/workflows/e2e-rn-test.yml
vendored
6
.github/workflows/e2e-rn-test.yml
vendored
@@ -1,11 +1,5 @@
|
||||
name: End-to-End Tests for React Native
|
||||
|
||||
concurrency:
|
||||
# For pushes, this lets concurrent runs happen, so each push gets a result.
|
||||
# But for other events (e.g. PRs), we can cancel the previous runs.
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
6
.github/workflows/jazz-run.yml
vendored
6
.github/workflows/jazz-run.yml
vendored
@@ -1,11 +1,5 @@
|
||||
name: Jazz Run Tests
|
||||
|
||||
concurrency:
|
||||
# For pushes, this lets concurrent runs happen, so each push gets a result.
|
||||
# But for other events (e.g. PRs), we can cancel the previous runs.
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
|
||||
46
.github/workflows/playwright-homepage.yml
vendored
Normal file
46
.github/workflows/playwright-homepage.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Playwright Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
timeout-minutes: 60
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Source Code
|
||||
uses: ./.github/actions/source-code/
|
||||
|
||||
- name: Install root dependencies
|
||||
run: pnpm install && pnpm exec turbo build --filter="./packages/*"
|
||||
|
||||
- name: Install project dependencies
|
||||
run: pnpm install
|
||||
working-directory: ./homepage/homepage
|
||||
|
||||
- name: Pnpm Build
|
||||
run: pnpm exec turbo build
|
||||
working-directory: ./homepage/homepage
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: pnpm exec playwright install
|
||||
working-directory: ./homepage/homepage
|
||||
|
||||
- name: Run Playwright tests
|
||||
run: pnpm exec playwright test
|
||||
working-directory: ./homepage/homepage
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: homepage-playwright-report
|
||||
path: ./homepage/homepage/playwright-report/
|
||||
retention-days: 30
|
||||
164
.github/workflows/playwright.yml
vendored
164
.github/workflows/playwright.yml
vendored
@@ -1,11 +1,5 @@
|
||||
name: Playwright Tests
|
||||
|
||||
concurrency:
|
||||
# For pushes, this lets concurrent runs happen, so each push gets a result.
|
||||
# But for other events (e.g. PRs), we can cancel the previous runs.
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
@@ -19,7 +13,21 @@ jobs:
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
shard: ["1/2", "2/2"]
|
||||
project: [
|
||||
"tests/e2e",
|
||||
"examples/chat",
|
||||
"examples/chat-svelte",
|
||||
"examples/clerk",
|
||||
"examples/betterauth",
|
||||
"examples/file-share-svelte",
|
||||
"examples/form",
|
||||
"examples/inspector",
|
||||
"examples/music-player",
|
||||
"examples/organization",
|
||||
"starters/react-passkey-auth",
|
||||
"starters/svelte-passkey-auth",
|
||||
"tests/jazz-svelte"
|
||||
]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -29,129 +37,25 @@ jobs:
|
||||
- name: Setup Source Code
|
||||
uses: ./.github/actions/source-code/
|
||||
|
||||
- name: Pnpm Build
|
||||
run: |
|
||||
if [ -f .env.test ]; then
|
||||
cp .env.test .env
|
||||
fi
|
||||
pnpm turbo build
|
||||
working-directory: ./${{ matrix.project }}
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: pnpm exec playwright install
|
||||
working-directory: ./${{ matrix.project }}
|
||||
|
||||
- name: Run Playwright tests for shard ${{ matrix.shard }}
|
||||
run: |
|
||||
# Parse shard information (e.g., "1/2" -> shard_num=1, total_shards=2)
|
||||
IFS='/' read -r shard_num total_shards <<< "${{ matrix.shard }}"
|
||||
shard_index=$((shard_num - 1)) # Convert to 0-based index
|
||||
|
||||
# Debug: Print parsed values
|
||||
echo "Parsed shard_num: $shard_num"
|
||||
echo "Parsed total_shards: $total_shards"
|
||||
echo "Calculated shard_index: $shard_index"
|
||||
|
||||
# Define all projects to test
|
||||
all_projects=(
|
||||
"tests/e2e"
|
||||
"examples/chat"
|
||||
"examples/chat-svelte"
|
||||
"examples/clerk"
|
||||
"examples/betterauth"
|
||||
"examples/file-share-svelte"
|
||||
"examples/form"
|
||||
"examples/inspector"
|
||||
"examples/music-player"
|
||||
"examples/organization"
|
||||
"starters/react-passkey-auth"
|
||||
"starters/svelte-passkey-auth"
|
||||
"tests/jazz-svelte"
|
||||
)
|
||||
|
||||
# Calculate which projects this shard should run
|
||||
shard_projects=()
|
||||
for i in "${!all_projects[@]}"; do
|
||||
if [ $((i % total_shards)) -eq $shard_index ]; then
|
||||
shard_projects+=("${all_projects[i]}")
|
||||
fi
|
||||
done
|
||||
|
||||
# Track project results
|
||||
overall_exit_code=0
|
||||
failed_projects=()
|
||||
passed_projects=()
|
||||
|
||||
echo "=== Running tests for shard ${{ matrix.shard }} ==="
|
||||
echo "Projects in this shard:"
|
||||
printf '%s\n' "${shard_projects[@]}"
|
||||
echo
|
||||
|
||||
# Run tests for each project
|
||||
for project in "${shard_projects[@]}"; do
|
||||
echo "=== Testing project: $project ==="
|
||||
|
||||
# Check if project directory exists
|
||||
if [ ! -d "$project" ]; then
|
||||
echo "❌ FAILED: Project directory $project does not exist"
|
||||
failed_projects+=("$project (directory not found)")
|
||||
overall_exit_code=1
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check if project has package.json
|
||||
if [ ! -f "$project/package.json" ]; then
|
||||
echo "❌ FAILED: No package.json found in $project"
|
||||
failed_projects+=("$project (no package.json)")
|
||||
overall_exit_code=1
|
||||
continue
|
||||
fi
|
||||
|
||||
# Build the project
|
||||
echo "🔨 Building $project..."
|
||||
cd "$project"
|
||||
|
||||
if [ -f .env.test ]; then
|
||||
cp .env.test .env
|
||||
fi
|
||||
|
||||
if ! pnpm turbo build; then
|
||||
echo "❌ BUILD FAILED: $project"
|
||||
failed_projects+=("$project (build failed)")
|
||||
overall_exit_code=1
|
||||
cd - > /dev/null
|
||||
continue
|
||||
fi
|
||||
|
||||
# Run Playwright tests
|
||||
echo "🧪 Running Playwright tests for $project..."
|
||||
if ! pnpm exec playwright test; then
|
||||
echo "❌ TESTS FAILED: $project"
|
||||
failed_projects+=("$project (tests failed)")
|
||||
overall_exit_code=1
|
||||
else
|
||||
echo "✅ TESTS PASSED: $project"
|
||||
passed_projects+=("$project")
|
||||
fi
|
||||
|
||||
cd - > /dev/null
|
||||
echo "=== Finished testing $project ==="
|
||||
echo
|
||||
done
|
||||
|
||||
# Print summary report
|
||||
echo "=========================================="
|
||||
echo "📊 TEST SUMMARY FOR SHARD ${{ matrix.shard }}"
|
||||
echo "=========================================="
|
||||
|
||||
if [ ${#passed_projects[@]} -gt 0 ]; then
|
||||
echo "✅ PASSED (${#passed_projects[@]}):"
|
||||
printf ' - %s\n' "${passed_projects[@]}"
|
||||
echo
|
||||
fi
|
||||
|
||||
if [ ${#failed_projects[@]} -gt 0 ]; then
|
||||
echo "❌ FAILED (${#failed_projects[@]}):"
|
||||
printf ' - %s\n' "${failed_projects[@]}"
|
||||
echo
|
||||
fi
|
||||
|
||||
|
||||
echo "Total projects in shard: ${#shard_projects[@]}"
|
||||
echo "Passed: ${#passed_projects[@]}"
|
||||
echo "Failed: ${#failed_projects[@]}"
|
||||
echo "=========================================="
|
||||
|
||||
# Exit with overall status
|
||||
exit $overall_exit_code
|
||||
- name: Run Playwright tests
|
||||
run: pnpm exec playwright test
|
||||
working-directory: ./${{ matrix.project }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: ${{ hashFiles(format('{0}/package.json', matrix.project)) }}-playwright-report
|
||||
path: ./${{ matrix.project }}/playwright-report/
|
||||
retention-days: 30
|
||||
|
||||
7
.github/workflows/pre-release.yml
vendored
7
.github/workflows/pre-release.yml
vendored
@@ -1,11 +1,4 @@
|
||||
name: Pre-Publish tagged Pull Requests
|
||||
|
||||
concurrency:
|
||||
# For pushes, this lets concurrent runs happen, so each push gets a result.
|
||||
# But for other events (e.g. PRs), we can cancel the previous runs.
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
6
.github/workflows/unit-test.yml
vendored
6
.github/workflows/unit-test.yml
vendored
@@ -1,11 +1,5 @@
|
||||
name: Unit Tests
|
||||
|
||||
concurrency:
|
||||
# For pushes, this lets concurrent runs happen, so each push gets a result.
|
||||
# But for other events (e.g. PRs), we can cancel the previous runs.
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"include": ["packages/cojson/src/storage/*/**", "cojson-transport-ws/**"],
|
||||
"include": ["packages/cojson-storage*/**", "cojson-transport-ws/**"],
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
"@bacons/text-decoder": "^0.0.0",
|
||||
"@bam.tech/react-native-image-resizer": "^3.0.11",
|
||||
"@react-native-community/netinfo": "11.4.1",
|
||||
"expo": "54.0.0-canary-20250701-6a945c5",
|
||||
"expo": "~53.0.9",
|
||||
"expo-clipboard": "^7.1.4",
|
||||
"expo-secure-store": "~14.2.3",
|
||||
"expo-sqlite": "~15.2.10",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-native": "0.80.0",
|
||||
"react": "19.0.0",
|
||||
"react-native": "0.79.2",
|
||||
"react-native-get-random-values": "^1.11.0",
|
||||
"readable-stream": "^4.7.0"
|
||||
},
|
||||
@@ -29,4 +29,4 @@
|
||||
"typescript": "~5.8.3"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ react {
|
||||
// The root of your project, i.e. where "package.json" lives. Default is '../..'
|
||||
// root = file("../../")
|
||||
// The folder where the react-native NPM package is. Default is ../../node_modules/react-native
|
||||
reactNativeDir = file("../../../../node_modules/react-native")
|
||||
// reactNativeDir = file("../../node_modules/react-native")
|
||||
// The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
|
||||
codegenDir = file("../../../../node_modules/@react-native/codegen")
|
||||
// codegenDir = file("../../node_modules/@react-native/codegen")
|
||||
// The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js
|
||||
cliFile = file("../../../../node_modules/react-native/cli.js")
|
||||
// cliFile = file("../../node_modules/react-native/cli.js")
|
||||
|
||||
/* Variants */
|
||||
// The list of variants to that are debuggable. For those we're going to
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pluginManagement { includeBuild("../../../node_modules/@react-native/gradle-plugin") }
|
||||
pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
|
||||
plugins { id("com.facebook.react.settings") }
|
||||
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
|
||||
rootProject.name = 'ChatRN'
|
||||
include ':app'
|
||||
includeBuild('../../../node_modules/@react-native/gradle-plugin')
|
||||
includeBuild('../node_modules/@react-native/gradle-plugin')
|
||||
|
||||
@@ -380,7 +380,7 @@
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
||||
USE_HERMES = true;
|
||||
@@ -452,7 +452,7 @@
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
USE_HERMES = true;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
|
||||
@@ -2370,87 +2370,87 @@ PODS:
|
||||
- Yoga (0.0.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
- boost (from `../../../node_modules/react-native/third-party-podspecs/boost.podspec`)
|
||||
- DoubleConversion (from `../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||
- fast_float (from `../../../node_modules/react-native/third-party-podspecs/fast_float.podspec`)
|
||||
- FBLazyVector (from `../../../node_modules/react-native/Libraries/FBLazyVector`)
|
||||
- fmt (from `../../../node_modules/react-native/third-party-podspecs/fmt.podspec`)
|
||||
- glog (from `../../../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||
- hermes-engine (from `../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
|
||||
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
|
||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||
- fast_float (from `../node_modules/react-native/third-party-podspecs/fast_float.podspec`)
|
||||
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
|
||||
- fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`)
|
||||
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||
- hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
|
||||
- "op-sqlite (from `../../../node_modules/@op-engineering/op-sqlite`)"
|
||||
- RCT-Folly (from `../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
|
||||
- RCTDeprecation (from `../../../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`)
|
||||
- RCTRequired (from `../../../node_modules/react-native/Libraries/Required`)
|
||||
- RCTTypeSafety (from `../../../node_modules/react-native/Libraries/TypeSafety`)
|
||||
- React (from `../../../node_modules/react-native/`)
|
||||
- React-callinvoker (from `../../../node_modules/react-native/ReactCommon/callinvoker`)
|
||||
- React-Core (from `../../../node_modules/react-native/`)
|
||||
- React-Core/RCTWebSocket (from `../../../node_modules/react-native/`)
|
||||
- React-CoreModules (from `../../../node_modules/react-native/React/CoreModules`)
|
||||
- React-cxxreact (from `../../../node_modules/react-native/ReactCommon/cxxreact`)
|
||||
- React-debug (from `../../../node_modules/react-native/ReactCommon/react/debug`)
|
||||
- React-defaultsnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/defaults`)
|
||||
- React-domnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/dom`)
|
||||
- React-Fabric (from `../../../node_modules/react-native/ReactCommon`)
|
||||
- React-FabricComponents (from `../../../node_modules/react-native/ReactCommon`)
|
||||
- React-FabricImage (from `../../../node_modules/react-native/ReactCommon`)
|
||||
- React-featureflags (from `../../../node_modules/react-native/ReactCommon/react/featureflags`)
|
||||
- React-featureflagsnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/featureflags`)
|
||||
- React-graphics (from `../../../node_modules/react-native/ReactCommon/react/renderer/graphics`)
|
||||
- React-hermes (from `../../../node_modules/react-native/ReactCommon/hermes`)
|
||||
- React-idlecallbacksnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks`)
|
||||
- React-ImageManager (from `../../../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`)
|
||||
- React-jserrorhandler (from `../../../node_modules/react-native/ReactCommon/jserrorhandler`)
|
||||
- React-jsi (from `../../../node_modules/react-native/ReactCommon/jsi`)
|
||||
- React-jsiexecutor (from `../../../node_modules/react-native/ReactCommon/jsiexecutor`)
|
||||
- React-jsinspector (from `../../../node_modules/react-native/ReactCommon/jsinspector-modern`)
|
||||
- React-jsinspectorcdp (from `../../../node_modules/react-native/ReactCommon/jsinspector-modern/cdp`)
|
||||
- React-jsinspectornetwork (from `../../../node_modules/react-native/ReactCommon/jsinspector-modern/network`)
|
||||
- React-jsinspectortracing (from `../../../node_modules/react-native/ReactCommon/jsinspector-modern/tracing`)
|
||||
- React-jsitooling (from `../../../node_modules/react-native/ReactCommon/jsitooling`)
|
||||
- React-jsitracing (from `../../../node_modules/react-native/ReactCommon/hermes/executor/`)
|
||||
- React-logger (from `../../../node_modules/react-native/ReactCommon/logger`)
|
||||
- React-Mapbuffer (from `../../../node_modules/react-native/ReactCommon`)
|
||||
- React-microtasksnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
|
||||
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
|
||||
- RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`)
|
||||
- RCTRequired (from `../node_modules/react-native/Libraries/Required`)
|
||||
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
|
||||
- React (from `../node_modules/react-native/`)
|
||||
- React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
|
||||
- React-Core (from `../node_modules/react-native/`)
|
||||
- React-Core/RCTWebSocket (from `../node_modules/react-native/`)
|
||||
- React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
|
||||
- React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
|
||||
- React-debug (from `../node_modules/react-native/ReactCommon/react/debug`)
|
||||
- React-defaultsnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/defaults`)
|
||||
- React-domnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/dom`)
|
||||
- React-Fabric (from `../node_modules/react-native/ReactCommon`)
|
||||
- React-FabricComponents (from `../node_modules/react-native/ReactCommon`)
|
||||
- React-FabricImage (from `../node_modules/react-native/ReactCommon`)
|
||||
- React-featureflags (from `../node_modules/react-native/ReactCommon/react/featureflags`)
|
||||
- React-featureflagsnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/featureflags`)
|
||||
- React-graphics (from `../node_modules/react-native/ReactCommon/react/renderer/graphics`)
|
||||
- React-hermes (from `../node_modules/react-native/ReactCommon/hermes`)
|
||||
- React-idlecallbacksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks`)
|
||||
- React-ImageManager (from `../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`)
|
||||
- React-jserrorhandler (from `../node_modules/react-native/ReactCommon/jserrorhandler`)
|
||||
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
|
||||
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
|
||||
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`)
|
||||
- React-jsinspectorcdp (from `../node_modules/react-native/ReactCommon/jsinspector-modern/cdp`)
|
||||
- React-jsinspectornetwork (from `../node_modules/react-native/ReactCommon/jsinspector-modern/network`)
|
||||
- React-jsinspectortracing (from `../node_modules/react-native/ReactCommon/jsinspector-modern/tracing`)
|
||||
- React-jsitooling (from `../node_modules/react-native/ReactCommon/jsitooling`)
|
||||
- React-jsitracing (from `../node_modules/react-native/ReactCommon/hermes/executor/`)
|
||||
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
|
||||
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
|
||||
- React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
|
||||
- react-native-get-random-values (from `../../../node_modules/react-native-get-random-values`)
|
||||
- react-native-mmkv (from `../../../node_modules/react-native-mmkv`)
|
||||
- "react-native-netinfo (from `../../../node_modules/@react-native-community/netinfo`)"
|
||||
- react-native-safe-area-context (from `../../../node_modules/react-native-safe-area-context`)
|
||||
- React-NativeModulesApple (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
|
||||
- React-oscompat (from `../../../node_modules/react-native/ReactCommon/oscompat`)
|
||||
- React-perflogger (from `../../../node_modules/react-native/ReactCommon/reactperflogger`)
|
||||
- React-performancetimeline (from `../../../node_modules/react-native/ReactCommon/react/performance/timeline`)
|
||||
- React-RCTActionSheet (from `../../../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||
- React-RCTAnimation (from `../../../node_modules/react-native/Libraries/NativeAnimation`)
|
||||
- React-RCTAppDelegate (from `../../../node_modules/react-native/Libraries/AppDelegate`)
|
||||
- React-RCTBlob (from `../../../node_modules/react-native/Libraries/Blob`)
|
||||
- React-RCTFabric (from `../../../node_modules/react-native/React`)
|
||||
- React-RCTFBReactNativeSpec (from `../../../node_modules/react-native/React`)
|
||||
- React-RCTImage (from `../../../node_modules/react-native/Libraries/Image`)
|
||||
- React-RCTLinking (from `../../../node_modules/react-native/Libraries/LinkingIOS`)
|
||||
- React-RCTNetwork (from `../../../node_modules/react-native/Libraries/Network`)
|
||||
- React-RCTRuntime (from `../../../node_modules/react-native/React/Runtime`)
|
||||
- React-RCTSettings (from `../../../node_modules/react-native/Libraries/Settings`)
|
||||
- React-RCTText (from `../../../node_modules/react-native/Libraries/Text`)
|
||||
- React-RCTVibration (from `../../../node_modules/react-native/Libraries/Vibration`)
|
||||
- React-rendererconsistency (from `../../../node_modules/react-native/ReactCommon/react/renderer/consistency`)
|
||||
- React-renderercss (from `../../../node_modules/react-native/ReactCommon/react/renderer/css`)
|
||||
- React-rendererdebug (from `../../../node_modules/react-native/ReactCommon/react/renderer/debug`)
|
||||
- React-rncore (from `../../../node_modules/react-native/ReactCommon`)
|
||||
- React-RuntimeApple (from `../../../node_modules/react-native/ReactCommon/react/runtime/platform/ios`)
|
||||
- React-RuntimeCore (from `../../../node_modules/react-native/ReactCommon/react/runtime`)
|
||||
- React-runtimeexecutor (from `../../../node_modules/react-native/ReactCommon/runtimeexecutor`)
|
||||
- React-RuntimeHermes (from `../../../node_modules/react-native/ReactCommon/react/runtime`)
|
||||
- React-runtimescheduler (from `../../../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`)
|
||||
- React-timing (from `../../../node_modules/react-native/ReactCommon/react/timing`)
|
||||
- React-utils (from `../../../node_modules/react-native/ReactCommon/react/utils`)
|
||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
|
||||
- React-oscompat (from `../node_modules/react-native/ReactCommon/oscompat`)
|
||||
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
|
||||
- React-performancetimeline (from `../node_modules/react-native/ReactCommon/react/performance/timeline`)
|
||||
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
|
||||
- React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`)
|
||||
- React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
|
||||
- React-RCTFabric (from `../node_modules/react-native/React`)
|
||||
- React-RCTFBReactNativeSpec (from `../node_modules/react-native/React`)
|
||||
- React-RCTImage (from `../node_modules/react-native/Libraries/Image`)
|
||||
- React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`)
|
||||
- React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`)
|
||||
- React-RCTRuntime (from `../node_modules/react-native/React/Runtime`)
|
||||
- React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
|
||||
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
|
||||
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
|
||||
- React-rendererconsistency (from `../node_modules/react-native/ReactCommon/react/renderer/consistency`)
|
||||
- React-renderercss (from `../node_modules/react-native/ReactCommon/react/renderer/css`)
|
||||
- React-rendererdebug (from `../node_modules/react-native/ReactCommon/react/renderer/debug`)
|
||||
- React-rncore (from `../node_modules/react-native/ReactCommon`)
|
||||
- React-RuntimeApple (from `../node_modules/react-native/ReactCommon/react/runtime/platform/ios`)
|
||||
- React-RuntimeCore (from `../node_modules/react-native/ReactCommon/react/runtime`)
|
||||
- React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`)
|
||||
- React-RuntimeHermes (from `../node_modules/react-native/ReactCommon/react/runtime`)
|
||||
- React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`)
|
||||
- React-timing (from `../node_modules/react-native/ReactCommon/react/timing`)
|
||||
- React-utils (from `../node_modules/react-native/ReactCommon/react/utils`)
|
||||
- ReactAppDependencyProvider (from `build/generated/ios`)
|
||||
- ReactCodegen (from `build/generated/ios`)
|
||||
- ReactCommon/turbomodule/core (from `../../../node_modules/react-native/ReactCommon`)
|
||||
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
|
||||
- "RNCClipboard (from `../../../node_modules/@react-native-clipboard/clipboard`)"
|
||||
- RNScreens (from `../../../node_modules/react-native-screens`)
|
||||
- RNScreens (from `../node_modules/react-native-screens`)
|
||||
- SocketRocket (~> 0.7.1)
|
||||
- Yoga (from `../../../node_modules/react-native/ReactCommon/yoga`)
|
||||
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
@@ -2458,88 +2458,88 @@ SPEC REPOS:
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
boost:
|
||||
:podspec: "../../../node_modules/react-native/third-party-podspecs/boost.podspec"
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
|
||||
DoubleConversion:
|
||||
:podspec: "../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
||||
fast_float:
|
||||
:podspec: "../../../node_modules/react-native/third-party-podspecs/fast_float.podspec"
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/fast_float.podspec"
|
||||
FBLazyVector:
|
||||
:path: "../../../node_modules/react-native/Libraries/FBLazyVector"
|
||||
:path: "../node_modules/react-native/Libraries/FBLazyVector"
|
||||
fmt:
|
||||
:podspec: "../../../node_modules/react-native/third-party-podspecs/fmt.podspec"
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/fmt.podspec"
|
||||
glog:
|
||||
:podspec: "../../../node_modules/react-native/third-party-podspecs/glog.podspec"
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
|
||||
hermes-engine:
|
||||
:podspec: "../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
|
||||
:podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
|
||||
:tag: hermes-2025-05-06-RNv0.80.0-4eb6132a5bf0450bf4c6c91987675381d7ac8bca
|
||||
op-sqlite:
|
||||
:path: "../../../node_modules/@op-engineering/op-sqlite"
|
||||
RCT-Folly:
|
||||
:podspec: "../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
|
||||
RCTDeprecation:
|
||||
:path: "../../../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation"
|
||||
:path: "../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation"
|
||||
RCTRequired:
|
||||
:path: "../../../node_modules/react-native/Libraries/Required"
|
||||
:path: "../node_modules/react-native/Libraries/Required"
|
||||
RCTTypeSafety:
|
||||
:path: "../../../node_modules/react-native/Libraries/TypeSafety"
|
||||
:path: "../node_modules/react-native/Libraries/TypeSafety"
|
||||
React:
|
||||
:path: "../../../node_modules/react-native/"
|
||||
:path: "../node_modules/react-native/"
|
||||
React-callinvoker:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/callinvoker"
|
||||
:path: "../node_modules/react-native/ReactCommon/callinvoker"
|
||||
React-Core:
|
||||
:path: "../../../node_modules/react-native/"
|
||||
:path: "../node_modules/react-native/"
|
||||
React-CoreModules:
|
||||
:path: "../../../node_modules/react-native/React/CoreModules"
|
||||
:path: "../node_modules/react-native/React/CoreModules"
|
||||
React-cxxreact:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/cxxreact"
|
||||
:path: "../node_modules/react-native/ReactCommon/cxxreact"
|
||||
React-debug:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/debug"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/debug"
|
||||
React-defaultsnativemodule:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/defaults"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/defaults"
|
||||
React-domnativemodule:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/dom"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/dom"
|
||||
React-Fabric:
|
||||
:path: "../../../node_modules/react-native/ReactCommon"
|
||||
:path: "../node_modules/react-native/ReactCommon"
|
||||
React-FabricComponents:
|
||||
:path: "../../../node_modules/react-native/ReactCommon"
|
||||
:path: "../node_modules/react-native/ReactCommon"
|
||||
React-FabricImage:
|
||||
:path: "../../../node_modules/react-native/ReactCommon"
|
||||
:path: "../node_modules/react-native/ReactCommon"
|
||||
React-featureflags:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/featureflags"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/featureflags"
|
||||
React-featureflagsnativemodule:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/featureflags"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/featureflags"
|
||||
React-graphics:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/renderer/graphics"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/renderer/graphics"
|
||||
React-hermes:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/hermes"
|
||||
:path: "../node_modules/react-native/ReactCommon/hermes"
|
||||
React-idlecallbacksnativemodule:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks"
|
||||
React-ImageManager:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios"
|
||||
React-jserrorhandler:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/jserrorhandler"
|
||||
:path: "../node_modules/react-native/ReactCommon/jserrorhandler"
|
||||
React-jsi:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/jsi"
|
||||
:path: "../node_modules/react-native/ReactCommon/jsi"
|
||||
React-jsiexecutor:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/jsiexecutor"
|
||||
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
|
||||
React-jsinspector:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/jsinspector-modern"
|
||||
:path: "../node_modules/react-native/ReactCommon/jsinspector-modern"
|
||||
React-jsinspectorcdp:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/jsinspector-modern/cdp"
|
||||
:path: "../node_modules/react-native/ReactCommon/jsinspector-modern/cdp"
|
||||
React-jsinspectornetwork:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/jsinspector-modern/network"
|
||||
:path: "../node_modules/react-native/ReactCommon/jsinspector-modern/network"
|
||||
React-jsinspectortracing:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/jsinspector-modern/tracing"
|
||||
:path: "../node_modules/react-native/ReactCommon/jsinspector-modern/tracing"
|
||||
React-jsitooling:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/jsitooling"
|
||||
:path: "../node_modules/react-native/ReactCommon/jsitooling"
|
||||
React-jsitracing:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/hermes/executor/"
|
||||
:path: "../node_modules/react-native/ReactCommon/hermes/executor/"
|
||||
React-logger:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/logger"
|
||||
:path: "../node_modules/react-native/ReactCommon/logger"
|
||||
React-Mapbuffer:
|
||||
:path: "../../../node_modules/react-native/ReactCommon"
|
||||
:path: "../node_modules/react-native/ReactCommon"
|
||||
React-microtasksnativemodule:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
|
||||
react-native-get-random-values:
|
||||
:path: "../../../node_modules/react-native-get-random-values"
|
||||
react-native-mmkv:
|
||||
@@ -2547,75 +2547,75 @@ EXTERNAL SOURCES:
|
||||
react-native-netinfo:
|
||||
:path: "../../../node_modules/@react-native-community/netinfo"
|
||||
react-native-safe-area-context:
|
||||
:path: "../../../node_modules/react-native-safe-area-context"
|
||||
:path: "../node_modules/react-native-safe-area-context"
|
||||
React-NativeModulesApple:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios"
|
||||
React-oscompat:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/oscompat"
|
||||
:path: "../node_modules/react-native/ReactCommon/oscompat"
|
||||
React-perflogger:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/reactperflogger"
|
||||
:path: "../node_modules/react-native/ReactCommon/reactperflogger"
|
||||
React-performancetimeline:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/performance/timeline"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/performance/timeline"
|
||||
React-RCTActionSheet:
|
||||
:path: "../../../node_modules/react-native/Libraries/ActionSheetIOS"
|
||||
:path: "../node_modules/react-native/Libraries/ActionSheetIOS"
|
||||
React-RCTAnimation:
|
||||
:path: "../../../node_modules/react-native/Libraries/NativeAnimation"
|
||||
:path: "../node_modules/react-native/Libraries/NativeAnimation"
|
||||
React-RCTAppDelegate:
|
||||
:path: "../../../node_modules/react-native/Libraries/AppDelegate"
|
||||
:path: "../node_modules/react-native/Libraries/AppDelegate"
|
||||
React-RCTBlob:
|
||||
:path: "../../../node_modules/react-native/Libraries/Blob"
|
||||
:path: "../node_modules/react-native/Libraries/Blob"
|
||||
React-RCTFabric:
|
||||
:path: "../../../node_modules/react-native/React"
|
||||
:path: "../node_modules/react-native/React"
|
||||
React-RCTFBReactNativeSpec:
|
||||
:path: "../../../node_modules/react-native/React"
|
||||
:path: "../node_modules/react-native/React"
|
||||
React-RCTImage:
|
||||
:path: "../../../node_modules/react-native/Libraries/Image"
|
||||
:path: "../node_modules/react-native/Libraries/Image"
|
||||
React-RCTLinking:
|
||||
:path: "../../../node_modules/react-native/Libraries/LinkingIOS"
|
||||
:path: "../node_modules/react-native/Libraries/LinkingIOS"
|
||||
React-RCTNetwork:
|
||||
:path: "../../../node_modules/react-native/Libraries/Network"
|
||||
:path: "../node_modules/react-native/Libraries/Network"
|
||||
React-RCTRuntime:
|
||||
:path: "../../../node_modules/react-native/React/Runtime"
|
||||
:path: "../node_modules/react-native/React/Runtime"
|
||||
React-RCTSettings:
|
||||
:path: "../../../node_modules/react-native/Libraries/Settings"
|
||||
:path: "../node_modules/react-native/Libraries/Settings"
|
||||
React-RCTText:
|
||||
:path: "../../../node_modules/react-native/Libraries/Text"
|
||||
:path: "../node_modules/react-native/Libraries/Text"
|
||||
React-RCTVibration:
|
||||
:path: "../../../node_modules/react-native/Libraries/Vibration"
|
||||
:path: "../node_modules/react-native/Libraries/Vibration"
|
||||
React-rendererconsistency:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/renderer/consistency"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/renderer/consistency"
|
||||
React-renderercss:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/renderer/css"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/renderer/css"
|
||||
React-rendererdebug:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/renderer/debug"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/renderer/debug"
|
||||
React-rncore:
|
||||
:path: "../../../node_modules/react-native/ReactCommon"
|
||||
:path: "../node_modules/react-native/ReactCommon"
|
||||
React-RuntimeApple:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/runtime/platform/ios"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/runtime/platform/ios"
|
||||
React-RuntimeCore:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/runtime"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/runtime"
|
||||
React-runtimeexecutor:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/runtimeexecutor"
|
||||
:path: "../node_modules/react-native/ReactCommon/runtimeexecutor"
|
||||
React-RuntimeHermes:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/runtime"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/runtime"
|
||||
React-runtimescheduler:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler"
|
||||
React-timing:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/timing"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/timing"
|
||||
React-utils:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/react/utils"
|
||||
:path: "../node_modules/react-native/ReactCommon/react/utils"
|
||||
ReactAppDependencyProvider:
|
||||
:path: build/generated/ios
|
||||
ReactCodegen:
|
||||
:path: build/generated/ios
|
||||
ReactCommon:
|
||||
:path: "../../../node_modules/react-native/ReactCommon"
|
||||
:path: "../node_modules/react-native/ReactCommon"
|
||||
RNCClipboard:
|
||||
:path: "../../../node_modules/@react-native-clipboard/clipboard"
|
||||
RNScreens:
|
||||
:path: "../../../node_modules/react-native-screens"
|
||||
:path: "../node_modules/react-native-screens"
|
||||
Yoga:
|
||||
:path: "../../../node_modules/react-native/ReactCommon/yoga"
|
||||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90
|
||||
@@ -2692,7 +2692,7 @@ SPEC CHECKSUMS:
|
||||
React-timing: a275a1c2e6112dba17f8f7dd496d439213bbea0d
|
||||
React-utils: 449a6e1fd53886510e284e80bdbb1b1c6db29452
|
||||
ReactAppDependencyProvider: 3267432b637c9b38e86961b287f784ee1b08dde0
|
||||
ReactCodegen: d82f538f70f00484d418803f74b5a0ea09cc8689
|
||||
ReactCodegen: 5d41e1df061200130dd326e55cdfdf94b0289c6e
|
||||
ReactCommon: b028d09a66e60ebd83ca59d8cc9a1216360db147
|
||||
RNCClipboard: 54ff19965d7c816febbafe5f520c2c3e7b677a49
|
||||
RNScreens: ee2abe7e0c548eed14e92742e81ed991165c56aa
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"@azure/core-asynciterator-polyfill": "^1.0.2",
|
||||
"@bacons/text-decoder": "0.0.0",
|
||||
"@op-engineering/op-sqlite": "14.1.0",
|
||||
"@react-native-clipboard/clipboard": "1.16.3",
|
||||
"@react-native-clipboard/clipboard": "1.16.2",
|
||||
"@react-native-community/netinfo": "11.4.1",
|
||||
"@react-navigation/native": "7.1.14",
|
||||
"@react-navigation/native-stack": "7.3.19",
|
||||
@@ -40,7 +40,7 @@
|
||||
"@react-native/typescript-config": "0.80.0",
|
||||
"@rnx-kit/metro-config": "^2.0.1",
|
||||
"@rnx-kit/metro-resolver-symlinks": "^0.2.5",
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react": "19.1.0",
|
||||
"eslint": "^8.19.0",
|
||||
"pod-install": "^0.3.5",
|
||||
"prettier": "2.8.8",
|
||||
|
||||
@@ -1,72 +1,5 @@
|
||||
# passkey-svelte
|
||||
|
||||
## 0.0.102
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6c76ff8]
|
||||
- jazz-tools@0.15.13
|
||||
|
||||
## 0.0.101
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d1c1b0c]
|
||||
- Updated dependencies [cf4ad72]
|
||||
- jazz-tools@0.15.12
|
||||
|
||||
## 0.0.100
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [bdc9aee]
|
||||
- jazz-tools@0.15.11
|
||||
|
||||
## 0.0.99
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9815ec6]
|
||||
- Updated dependencies [b4fdab4]
|
||||
- jazz-tools@0.15.10
|
||||
|
||||
## 0.0.98
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [27b4837]
|
||||
- jazz-tools@0.15.9
|
||||
|
||||
## 0.0.97
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3844666]
|
||||
- jazz-tools@0.15.8
|
||||
|
||||
## 0.0.96
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c09b636]
|
||||
- jazz-tools@0.15.7
|
||||
|
||||
## 0.0.95
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [a5ceaff]
|
||||
- jazz-tools@0.15.6
|
||||
|
||||
## 0.0.94
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [23bfea5]
|
||||
- Updated dependencies [e4ba23c]
|
||||
- Updated dependencies [4b89838]
|
||||
- jazz-tools@0.15.5
|
||||
|
||||
## 0.0.93
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-svelte",
|
||||
"version": "0.0.102",
|
||||
"version": "0.0.93",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -16,15 +16,15 @@
|
||||
"hash-slash": "workspace:*",
|
||||
"jazz-tools": "workspace:*",
|
||||
"lucide-react": "^0.274.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"zod": "3.25.28"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react-swc": "^3.10.1",
|
||||
"is-ci": "^3.0.1",
|
||||
"postcss": "^8.4.40",
|
||||
@@ -32,4 +32,4 @@
|
||||
"typescript": "5.6.2",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { co } from "jazz-tools";
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
export const Message = co.map({
|
||||
text: co.plainText(),
|
||||
image: co.optional(co.image()),
|
||||
image: z.optional(co.image()),
|
||||
});
|
||||
export type Message = co.loaded<typeof Message>;
|
||||
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
"@bam.tech/react-native-image-resizer": "^3.0.11",
|
||||
"@clerk/clerk-expo": "^2.13.1",
|
||||
"@react-native-community/netinfo": "11.4.1",
|
||||
"expo": "54.0.0-canary-20250701-6a945c5",
|
||||
"expo": "~53.0.9",
|
||||
"expo-crypto": "~14.1.5",
|
||||
"expo-linking": "~7.1.5",
|
||||
"expo-secure-store": "~14.2.3",
|
||||
"expo-sqlite": "~15.2.10",
|
||||
"expo-web-browser": "~14.2.0",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-native": "0.80.0",
|
||||
"react": "19.0.0",
|
||||
"react-native": "0.79.2",
|
||||
"react-native-get-random-values": "^1.11.0",
|
||||
"readable-stream": "^4.7.0"
|
||||
},
|
||||
@@ -32,4 +32,4 @@
|
||||
"typescript": "~5.8.3"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,17 +14,17 @@
|
||||
"dependencies": {
|
||||
"@clerk/clerk-react": "^5.4.1",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"typescript": "5.6.2",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"is-ci": "^3.0.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { co } from "jazz-tools";
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
export const JazzProfile = co.profile({
|
||||
file: co.optional(co.fileStream()),
|
||||
file: z.optional(co.fileStream()),
|
||||
});
|
||||
|
||||
export const JazzAccount = co.account({
|
||||
|
||||
@@ -10,18 +10,19 @@
|
||||
"format-and-lint:fix": "biome check . --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-form": "^0.33.0",
|
||||
"hash-slash": "workspace:*",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"is-ci": "^3.0.1",
|
||||
@@ -30,4 +31,4 @@
|
||||
"typescript": "5.6.2",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
import { useCoState } from "jazz-tools/react";
|
||||
import { LinkToHome } from "./LinkToHome.tsx";
|
||||
import { OrderForm } from "./OrderForm.tsx";
|
||||
import { OrderFormWithSaveButton } from "./OrderFormWithSaveButton.tsx";
|
||||
import { OrderThumbnail } from "./OrderThumbnail.tsx";
|
||||
import { BubbleTeaOrder } from "./schema.ts";
|
||||
|
||||
export function EditOrder(props: { id: string }) {
|
||||
const order = useCoState(BubbleTeaOrder, props.id);
|
||||
const order = useCoState(BubbleTeaOrder, props.id, {
|
||||
resolve: { addOns: true, instructions: true },
|
||||
});
|
||||
|
||||
if (!order) return;
|
||||
|
||||
@@ -13,13 +15,17 @@ export function EditOrder(props: { id: string }) {
|
||||
<>
|
||||
<LinkToHome />
|
||||
|
||||
<OrderThumbnail order={order} />
|
||||
<div>
|
||||
<p>Saved order:</p>
|
||||
|
||||
<OrderThumbnail order={order} />
|
||||
</div>
|
||||
|
||||
<h1 className="text-lg">
|
||||
<strong>Edit your bubble tea order 🧋</strong>
|
||||
</h1>
|
||||
|
||||
<OrderForm order={order} />
|
||||
<OrderFormWithSaveButton order={order} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
214
examples/form/src/OrderFormWithSaveButton.tsx
Normal file
214
examples/form/src/OrderFormWithSaveButton.tsx
Normal file
@@ -0,0 +1,214 @@
|
||||
import { CoPlainText, Loaded } from "jazz-tools";
|
||||
import { useForm } from "@tanstack/react-form";
|
||||
import {
|
||||
BubbleTeaAddOnTypes,
|
||||
BubbleTeaBaseTeaTypes,
|
||||
BubbleTeaOrder,
|
||||
} from "./schema.ts";
|
||||
import { OrderThumbnail } from "./OrderThumbnail.tsx";
|
||||
|
||||
type LoadedBubbleTeaOrder = Loaded<
|
||||
typeof BubbleTeaOrder,
|
||||
{ addOns: { $each: true }; instructions: true }
|
||||
>;
|
||||
|
||||
// Would be great to derive this type from the CoValue schema
|
||||
export type OrderFormData = {
|
||||
id: string;
|
||||
baseTea: (typeof BubbleTeaBaseTeaTypes)[number];
|
||||
addOns: (typeof BubbleTeaAddOnTypes)[number][];
|
||||
deliveryDate: Date;
|
||||
withMilk: boolean;
|
||||
instructions?: string;
|
||||
};
|
||||
|
||||
export function OrderFormWithSaveButton({
|
||||
order: originalOrder,
|
||||
}: {
|
||||
order: LoadedBubbleTeaOrder;
|
||||
}) {
|
||||
const defaultValues = originalOrder.toJSON();
|
||||
// Convert timestamp to Date
|
||||
defaultValues.deliveryDate = new Date(defaultValues.deliveryDate);
|
||||
|
||||
const form = useForm<OrderFormData>({
|
||||
defaultValues,
|
||||
onSubmit: async ({ value }: { value: OrderFormData }) => {
|
||||
console.log("submit form", value);
|
||||
|
||||
// Apply changes to the original Jazz order
|
||||
originalOrder.baseTea = value.baseTea;
|
||||
originalOrder.addOns.applyDiff(value.addOns);
|
||||
originalOrder.deliveryDate = new Date(value.deliveryDate);
|
||||
originalOrder.withMilk = value.withMilk;
|
||||
|
||||
// `applyDiff` requires nested objects to be CoValues as well
|
||||
const instructions = originalOrder.instructions ?? CoPlainText.create("");
|
||||
if (value.instructions) {
|
||||
instructions.applyDiff(value.instructions);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
className="grid gap-5"
|
||||
>
|
||||
{/* TODO refactor OrderThumbnail to support receiving a plain JSON object */}
|
||||
<div>
|
||||
<p>Unsaved order preview:</p>
|
||||
<form.Subscribe
|
||||
selector={(state) => [state.values]}
|
||||
children={([values]) => <OrderThumbnail order={values} />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="baseTea">Base tea</label>
|
||||
<form.Field
|
||||
name="baseTea"
|
||||
validators={{
|
||||
onChange: ({ value }) =>
|
||||
!value ? "Please select your preferred base tea" : undefined,
|
||||
}}
|
||||
children={(field) => (
|
||||
<>
|
||||
<select
|
||||
id="baseTea"
|
||||
className="dark:bg-transparent"
|
||||
value={field.state.value}
|
||||
onChange={(e) =>
|
||||
field.handleChange(e.target.value as typeof field.state.value)
|
||||
}
|
||||
onBlur={field.handleBlur}
|
||||
>
|
||||
<option value="" disabled>
|
||||
Please select your preferred base tea
|
||||
</option>
|
||||
{BubbleTeaBaseTeaTypes.map((teaType) => (
|
||||
<option key={teaType} value={teaType}>
|
||||
{teaType}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{field.state.meta.errors.length > 0 && (
|
||||
<span className="text-red-500 text-sm">
|
||||
{field.state.meta.errors[0]}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
<legend className="mb-2">Add-ons</legend>
|
||||
<form.Field
|
||||
name="addOns"
|
||||
mode="array"
|
||||
children={(field) => (
|
||||
<>
|
||||
{BubbleTeaAddOnTypes.map((addOn) => (
|
||||
<div key={addOn} className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={addOn}
|
||||
checked={field.state.value.includes(addOn)}
|
||||
onChange={(e) => {
|
||||
const currentValue = field.state.value;
|
||||
if (e.target.checked) {
|
||||
field.handleChange([...currentValue, addOn]);
|
||||
} else {
|
||||
field.handleChange(
|
||||
currentValue.filter((item) => item !== addOn),
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<label htmlFor={addOn}>{addOn}</label>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</fieldset>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="deliveryDate">Delivery date</label>
|
||||
<form.Field
|
||||
name="deliveryDate"
|
||||
validators={{
|
||||
onChange: ({ value }) =>
|
||||
!value ? "Delivery date is required" : undefined,
|
||||
}}
|
||||
children={(field) => (
|
||||
<>
|
||||
<input
|
||||
type="date"
|
||||
id="deliveryDate"
|
||||
className="dark:bg-transparent"
|
||||
value={field.state.value.toISOString().split("T")[0]}
|
||||
onChange={(e) => field.handleChange(new Date(e.target.value))}
|
||||
onBlur={field.handleBlur}
|
||||
/>
|
||||
{field.state.meta.errors.length > 0 && (
|
||||
<span className="text-red-500 text-sm">
|
||||
{field.state.meta.errors[0]}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<form.Field
|
||||
name="withMilk"
|
||||
children={(field) => (
|
||||
<input
|
||||
type="checkbox"
|
||||
id="withMilk"
|
||||
checked={field.state.value}
|
||||
onChange={(e) => field.handleChange(e.target.checked)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<label htmlFor="withMilk">With milk?</label>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="instructions">Special instructions</label>
|
||||
<form.Field
|
||||
name="instructions"
|
||||
children={(field) => (
|
||||
<textarea
|
||||
id="instructions"
|
||||
className="dark:bg-transparent"
|
||||
value={field.state.value || ""}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
onBlur={field.handleBlur}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<form.Subscribe
|
||||
selector={(state) => [state.canSubmit, state.isSubmitting]}
|
||||
children={([canSubmit, isSubmitting]) => (
|
||||
<button
|
||||
type="submit"
|
||||
disabled={!canSubmit}
|
||||
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded disabled:bg-gray-400"
|
||||
>
|
||||
{isSubmitting ? "Submitting..." : "Submit"}
|
||||
</button>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Loaded } from "jazz-tools";
|
||||
import { BubbleTeaOrder } from "./schema.ts";
|
||||
import { OrderFormData } from "./OrderFormWithSaveButton.tsx";
|
||||
|
||||
export function OrderThumbnail({
|
||||
order,
|
||||
}: {
|
||||
order: Loaded<typeof BubbleTeaOrder>;
|
||||
order: Loaded<typeof BubbleTeaOrder> | OrderFormData;
|
||||
}) {
|
||||
const { id, baseTea, addOns, instructions, deliveryDate, withMilk } = order;
|
||||
const date = deliveryDate.toLocaleDateString();
|
||||
|
||||
@@ -28,16 +28,16 @@ export const BubbleTeaOrder = co.map({
|
||||
addOns: ListOfBubbleTeaAddOns,
|
||||
deliveryDate: z.date(),
|
||||
withMilk: z.boolean(),
|
||||
instructions: co.optional(co.plainText()),
|
||||
instructions: z.optional(co.plainText()),
|
||||
});
|
||||
|
||||
export const DraftBubbleTeaOrder = co
|
||||
.map({
|
||||
baseTea: z.optional(z.literal([...BubbleTeaBaseTeaTypes])),
|
||||
addOns: co.optional(ListOfBubbleTeaAddOns),
|
||||
addOns: z.optional(ListOfBubbleTeaAddOns),
|
||||
deliveryDate: z.optional(z.date()),
|
||||
withMilk: z.optional(z.boolean()),
|
||||
instructions: co.optional(co.plainText()),
|
||||
instructions: z.optional(co.plainText()),
|
||||
})
|
||||
.withHelpers((Self) => ({
|
||||
hasChanges(order: Loaded<typeof Self> | undefined) {
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"typescript": "5.6.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { co } from "jazz-tools";
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
export const JazzProfile = co.profile({
|
||||
image: co.optional(co.image()),
|
||||
image: z.optional(co.image()),
|
||||
});
|
||||
|
||||
export const JazzAccount = co.account({
|
||||
|
||||
@@ -17,15 +17,15 @@
|
||||
"cojson-transport-ws": "workspace:*",
|
||||
"hash-slash": "workspace:*",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-use": "^17.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react-swc": "^3.10.1",
|
||||
"postcss": "^8.4.40",
|
||||
"tailwindcss": "^4.1.10",
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
"dependencies": {
|
||||
"jazz-tools": "workspace:*",
|
||||
"next": "15.3.2",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
@@ -21,4 +21,4 @@
|
||||
"tailwindcss": "^4.1.10",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,15 +24,15 @@
|
||||
"clsx": "^2.1.1",
|
||||
"jazz-tools": "workspace:*",
|
||||
"lucide-react": "^0.485.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"tailwindcss": "^4.0.17",
|
||||
"tw-animate-css": "^1.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"jazz-run": "workspace:*",
|
||||
"npm-run-all": "^4.1.5",
|
||||
|
||||
@@ -8,7 +8,7 @@ export type Player = co.loaded<typeof Player>;
|
||||
|
||||
export const Game = co.map({
|
||||
player1: Player,
|
||||
player2: co.optional(Player),
|
||||
player2: z.optional(Player),
|
||||
outcome: z.optional(z.literal(["player1", "player2", "draw"])),
|
||||
player1Score: z.number(),
|
||||
player2Score: z.number(),
|
||||
@@ -17,8 +17,8 @@ export type Game = co.loaded<typeof Game>;
|
||||
|
||||
export const WaitingRoom = co.map({
|
||||
account1: co.account(),
|
||||
account2: co.optional(co.account()),
|
||||
game: co.optional(Game),
|
||||
account2: z.optional(co.account()),
|
||||
game: z.optional(Game),
|
||||
});
|
||||
export type WaitingRoom = co.loaded<typeof WaitingRoom>;
|
||||
|
||||
@@ -47,7 +47,7 @@ export const JoinGameRequest = co.map({
|
||||
});
|
||||
export type JoinGameRequest = co.loaded<typeof JoinGameRequest>;
|
||||
|
||||
export const InboxMessage = co.discriminatedUnion("type", [
|
||||
export const InboxMessage = z.discriminatedUnion("type", [
|
||||
PlayIntent,
|
||||
NewGameIntent,
|
||||
CreateGameRequest,
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
"dependencies": {
|
||||
"@react-spring/web": "^9.7.5",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"zod": "3.25.28"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"is-ci": "^3.0.1",
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
"dependencies": {
|
||||
"@clerk/clerk-react": "^5.4.1",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"tailwindcss": "^4.1.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"typescript": "5.6.2",
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
"clsx": "^2.1.1",
|
||||
"jazz-tools": "workspace:*",
|
||||
"lucide-react": "^0.274.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-router": "^6.16.0",
|
||||
"react-router-dom": "^6.16.0",
|
||||
"tailwind-merge": "^1.14.0"
|
||||
@@ -32,8 +32,8 @@
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react-swc": "^3.10.1",
|
||||
"postcss": "^8.4.27",
|
||||
"tailwindcss": "^4.1.10",
|
||||
|
||||
@@ -66,7 +66,7 @@ export const MusicaAccountRoot = co.map({
|
||||
// track and playlist
|
||||
// You can also add the position in time if you want make it possible
|
||||
// to resume the song
|
||||
activeTrack: co.optional(MusicTrack),
|
||||
activeTrack: z.optional(MusicTrack),
|
||||
activePlaylist: Playlist,
|
||||
|
||||
exampleDataLoaded: z.optional(z.boolean()),
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
"dependencies": {
|
||||
"jazz-tools": "workspace:*",
|
||||
"lucide-react": "^0.274.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-router": "^6.16.0",
|
||||
"react-router-dom": "^6.16.0"
|
||||
},
|
||||
@@ -24,8 +24,8 @@
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"postcss": "^8.4.40",
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"tailwindcss": "^4.1.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"typescript": "5.6.2",
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
"dependencies": {
|
||||
"hash-slash": "workspace:*",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"tailwindcss": "^4.1.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"typescript": "5.6.2",
|
||||
|
||||
@@ -19,15 +19,15 @@
|
||||
"prosemirror-schema-list": "^1.5.1",
|
||||
"prosemirror-state": "^1.4.3",
|
||||
"prosemirror-view": "^1.39.1",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"is-ci": "^3.0.1",
|
||||
|
||||
@@ -22,15 +22,15 @@
|
||||
"clsx": "^2.1.1",
|
||||
"jazz-tools": "workspace:*",
|
||||
"lucide-react": "^0.509.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"is-ci": "^3.0.1",
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"ignoreCommand": "echo true"
|
||||
}
|
||||
8
examples/tanstack-form/.prettierrc
Normal file
8
examples/tanstack-form/.prettierrc
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
||||
35
examples/tanstack-form/CHANGELOG.md
Normal file
35
examples/tanstack-form/CHANGELOG.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Changelog
|
||||
|
||||
## v0.1.0 - Initial Svelte Migration
|
||||
|
||||
### Added
|
||||
- Complete Svelte implementation of the form example
|
||||
- Native Svelte reactive state management (replacing TanStack Form)
|
||||
- SvelteKit routing with hash router
|
||||
- Tailwind CSS styling
|
||||
- Jazz tools integration for Svelte
|
||||
|
||||
### Changes from React Version
|
||||
- **Form Management**: Replaced TanStack Form with native Svelte `$state` and `$derived` reactives
|
||||
- **Components**: Converted all React components to Svelte components
|
||||
- **Event Handling**: Used Svelte's native event handling instead of React event handlers
|
||||
- **State Management**: Leveraged Svelte's built-in reactivity instead of external form libraries
|
||||
- **Validation**: Implemented custom validation using Svelte reactive patterns
|
||||
|
||||
### Features
|
||||
- ✅ Create new bubble tea orders
|
||||
- ✅ Edit existing orders
|
||||
- ✅ Real-time form preview
|
||||
- ✅ Form validation with error messages
|
||||
- ✅ Date input handling with proper format conversion
|
||||
- ✅ Multiple selection for add-ons
|
||||
- ✅ Jazz CoValue synchronization
|
||||
- ✅ Responsive design with Tailwind CSS
|
||||
|
||||
### Technical Implementation
|
||||
- **Framework**: SvelteKit 2.x with Svelte 5.x
|
||||
- **State**: Native Svelte `$state` runes for reactive state
|
||||
- **Derived State**: `$derived` for computed values like form validity
|
||||
- **Effects**: `$effect` for side effects and lifecycle management
|
||||
- **TypeScript**: Full type safety maintained
|
||||
- **Styling**: Tailwind CSS for consistent design
|
||||
126
examples/tanstack-form/README.md
Normal file
126
examples/tanstack-form/README.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Jazz Form Example - Svelte
|
||||
|
||||
A comprehensive form example built with Svelte and Jazz, demonstrating reactive form handling, validation, and real-time data synchronization.
|
||||
|
||||
## Overview
|
||||
|
||||
This example showcases a bubble tea ordering system with the following features:
|
||||
|
||||
- **Create Orders**: Build new bubble tea orders with base tea, add-ons, delivery date, and special instructions
|
||||
- **Edit Orders**: Modify existing orders with real-time preview
|
||||
- **Form Validation**: Client-side validation with error messages
|
||||
- **Real-time Sync**: Jazz CoValues for real-time data synchronization
|
||||
- **Responsive Design**: Modern UI with Tailwind CSS
|
||||
|
||||
## Key Technologies
|
||||
|
||||
- **Svelte 5.x**: Modern reactive UI framework with runes
|
||||
- **SvelteKit**: Full-stack Svelte framework
|
||||
- **Jazz Tools**: Real-time collaborative data layer
|
||||
- **TypeScript**: Full type safety
|
||||
- **Tailwind CSS**: Utility-first CSS framework
|
||||
|
||||
## Architecture
|
||||
|
||||
### State Management
|
||||
Instead of external form libraries, this example uses Svelte's native reactivity:
|
||||
|
||||
- `$state` - For mutable reactive state
|
||||
- `$derived` - For computed values (like form validity)
|
||||
- `$effect` - For side effects and lifecycle management
|
||||
|
||||
### Form Pattern
|
||||
```svelte
|
||||
<script>
|
||||
// Reactive form state
|
||||
let formData = $state({
|
||||
baseTea: '',
|
||||
addOns: [],
|
||||
deliveryDate: new Date(),
|
||||
withMilk: false,
|
||||
instructions: ''
|
||||
});
|
||||
|
||||
// Derived validation
|
||||
const isValid = $derived(
|
||||
formData.baseTea && formData.deliveryDate
|
||||
);
|
||||
|
||||
// Event handlers
|
||||
function handleSubmit(event) {
|
||||
// Handle form submission
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### Jazz Integration
|
||||
The example demonstrates Jazz CoValues for:
|
||||
- **Account Management**: User authentication and profile
|
||||
- **Data Persistence**: Bubble tea orders stored as CoMaps
|
||||
- **Real-time Sync**: Changes sync across sessions
|
||||
- **Collaborative Features**: Shared data structures
|
||||
|
||||
## Getting Started
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
pnpm install
|
||||
|
||||
# Start development server
|
||||
pnpm dev
|
||||
|
||||
# Build for production
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── lib/
|
||||
│ ├── components/
|
||||
│ │ ├── CreateOrder.svelte # New order creation
|
||||
│ │ ├── EditOrder.svelte # Edit existing orders
|
||||
│ │ ├── Orders.svelte # Order list view
|
||||
│ │ ├── OrderFormWithSaveButton.svelte # Main form component
|
||||
│ │ ├── OrderThumbnail.svelte # Order preview
|
||||
│ │ └── LinkToHome.svelte # Navigation
|
||||
│ ├── schema.ts # Jazz CoValue definitions
|
||||
│ └── apiKey.ts # Jazz API configuration
|
||||
├── routes/
|
||||
│ ├── +layout.svelte # App layout with Jazz provider
|
||||
│ └── +page.svelte # Main app with routing
|
||||
└── app.html # HTML template
|
||||
```
|
||||
|
||||
## Migration from React
|
||||
|
||||
This Svelte version was migrated from a React + TanStack Form implementation. Key differences:
|
||||
|
||||
| React | Svelte |
|
||||
|-------|--------|
|
||||
| `useForm` hook | `$state` runes |
|
||||
| `form.Field` components | Native form elements |
|
||||
| `form.Subscribe` | `$derived` reactives |
|
||||
| Event handlers via props | Native Svelte event handling |
|
||||
| TanStack Form validation | Custom validation functions |
|
||||
|
||||
The Svelte implementation is more concise while maintaining the same functionality and type safety.
|
||||
|
||||
## Features Demonstrated
|
||||
|
||||
- ✅ **Form State Management**: Reactive state with validation
|
||||
- ✅ **Real-time Preview**: Live updates as user types
|
||||
- ✅ **Date Handling**: Proper Date ↔ string conversion for inputs
|
||||
- ✅ **Array Fields**: Dynamic add-on selection
|
||||
- ✅ **Error Handling**: Field-level validation messages
|
||||
- ✅ **Submit State**: Loading states and form submission
|
||||
- ✅ **Jazz Integration**: CoValue creation and updates
|
||||
- ✅ **TypeScript**: Full type safety throughout
|
||||
|
||||
## Learn More
|
||||
|
||||
- [Svelte Documentation](https://svelte.dev/docs)
|
||||
- [SvelteKit Documentation](https://kit.svelte.dev/docs)
|
||||
- [Jazz Documentation](https://jazz.tools/docs)
|
||||
- [Tailwind CSS](https://tailwindcss.com/docs)
|
||||
32
examples/tanstack-form/eslint.config.js
Normal file
32
examples/tanstack-form/eslint.config.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import eslint from "@eslint/js";
|
||||
import prettier from "eslint-config-prettier";
|
||||
import svelte from "eslint-plugin-svelte";
|
||||
import globals from "globals";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default tseslint.config(
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
...svelte.configs["flat/recommended"],
|
||||
prettier,
|
||||
...svelte.configs["flat/prettier"],
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.svelte"],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
parser: tseslint.parser,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ["build/", ".svelte-kit/", "dist/"],
|
||||
},
|
||||
);
|
||||
43
examples/tanstack-form/package.json
Normal file
43
examples/tanstack-form/package.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "tanstack-form",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "pnpm run check && vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"format": "prettier --write .",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format-and-lint": "pnpm run format && pnpm run lint",
|
||||
"format-and-lint:fix": "pnpm run format --write && pnpm run lint --fix",
|
||||
"test:e2e": "playwright test",
|
||||
"test:e2e:ui": "playwright test --ui"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^3.3.1",
|
||||
"@sveltejs/kit": "^2.21.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-svelte": "^2.46.1",
|
||||
"globals": "^15.15.0",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-svelte": "^3.4.0",
|
||||
"svelte": "^5.31.1",
|
||||
"svelte-check": "^4.2.1",
|
||||
"tailwindcss": "^4.1.7",
|
||||
"typescript": "5.6.2",
|
||||
"typescript-eslint": "^8.32.1",
|
||||
"vite": "6.0.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.1.7",
|
||||
"@tanstack/svelte-form": "^1.12.4",
|
||||
"jazz-tools": "workspace:*",
|
||||
"tailwindcss": "^4.1.7"
|
||||
}
|
||||
}
|
||||
1
examples/tanstack-form/src/app.css
Normal file
1
examples/tanstack-form/src/app.css
Normal file
@@ -0,0 +1 @@
|
||||
@import 'tailwindcss';
|
||||
13
examples/tanstack-form/src/app.d.ts
vendored
Normal file
13
examples/tanstack-form/src/app.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
12
examples/tanstack-form/src/app.html
Normal file
12
examples/tanstack-form/src/app.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
1
examples/tanstack-form/src/lib/apiKey.ts
Normal file
1
examples/tanstack-form/src/lib/apiKey.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const apiKey = 'tanstack-form-example_bYRMYEZmBNwE1qKQ';
|
||||
50
examples/tanstack-form/src/lib/components/CreateOrder.svelte
Normal file
50
examples/tanstack-form/src/lib/components/CreateOrder.svelte
Normal file
@@ -0,0 +1,50 @@
|
||||
<script lang="ts">
|
||||
import { AccountCoState } from 'jazz-tools/svelte';
|
||||
import { JazzAccount, BubbleTeaOrder, BubbleTeaBaseTeaTypes, ListOfBubbleTeaAddOns } from '$lib/schema';
|
||||
import OrderFormWithSaveButton from './OrderFormWithSaveButton.svelte';
|
||||
import LinkToHome from './LinkToHome.svelte';
|
||||
import { CoPlainText } from 'jazz-tools';
|
||||
|
||||
const me = new AccountCoState(JazzAccount, {
|
||||
resolve: {
|
||||
profile: true,
|
||||
root: {
|
||||
orders: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const orders = $derived(me.current?.root.orders);
|
||||
|
||||
// Create a new order when component loads
|
||||
let newOrder = $state<any>(null);
|
||||
|
||||
$effect(() => {
|
||||
if (orders && !newOrder) {
|
||||
const order = BubbleTeaOrder.create({
|
||||
baseTea: BubbleTeaBaseTeaTypes[0],
|
||||
addOns: ListOfBubbleTeaAddOns.create([], orders._owner),
|
||||
deliveryDate: new Date(),
|
||||
withMilk: false,
|
||||
instructions: CoPlainText.create(''),
|
||||
}, orders._owner);
|
||||
|
||||
orders.push(order);
|
||||
newOrder = order;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<LinkToHome />
|
||||
|
||||
<h1 class="text-lg mb-4">
|
||||
<strong>Create your bubble tea order 🧋</strong>
|
||||
</h1>
|
||||
|
||||
{#if newOrder}
|
||||
<OrderFormWithSaveButton order={newOrder} />
|
||||
{:else}
|
||||
<p>Creating order...</p>
|
||||
{/if}
|
||||
</div>
|
||||
39
examples/tanstack-form/src/lib/components/EditOrder.svelte
Normal file
39
examples/tanstack-form/src/lib/components/EditOrder.svelte
Normal file
@@ -0,0 +1,39 @@
|
||||
<script lang="ts">
|
||||
import { CoState } from 'jazz-tools/svelte';
|
||||
import LinkToHome from './LinkToHome.svelte';
|
||||
import OrderFormWithSaveButton from './OrderFormWithSaveButton.svelte';
|
||||
import OrderThumbnail from './OrderThumbnail.svelte';
|
||||
import { BubbleTeaOrder } from '$lib/schema';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
let { id }: Props = $props();
|
||||
|
||||
const order = new CoState(BubbleTeaOrder, id, {
|
||||
resolve: { addOns: true, instructions: true },
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if order.current}
|
||||
<div>
|
||||
<LinkToHome />
|
||||
|
||||
<div class="mb-6">
|
||||
<p>Saved order:</p>
|
||||
<OrderThumbnail order={order.current} />
|
||||
</div>
|
||||
|
||||
<h1 class="text-lg mb-4">
|
||||
<strong>Edit your bubble tea order 🧋</strong>
|
||||
</h1>
|
||||
|
||||
<OrderFormWithSaveButton order={order.current} />
|
||||
</div>
|
||||
{:else}
|
||||
<div>
|
||||
<LinkToHome />
|
||||
<p>Loading order...</p>
|
||||
</div>
|
||||
{/if}
|
||||
14
examples/tanstack-form/src/lib/components/LinkToHome.svelte
Normal file
14
examples/tanstack-form/src/lib/components/LinkToHome.svelte
Normal file
@@ -0,0 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
function navigateHome() {
|
||||
goto('#/');
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
onclick={navigateHome}
|
||||
class="mb-4 text-blue-500 hover:text-blue-700 underline"
|
||||
>
|
||||
← Back to orders
|
||||
</button>
|
||||
@@ -0,0 +1,194 @@
|
||||
<script lang="ts">
|
||||
import { CoPlainText, type Loaded } from 'jazz-tools';
|
||||
import {
|
||||
BubbleTeaAddOnTypes,
|
||||
BubbleTeaBaseTeaTypes,
|
||||
type BubbleTeaOrder
|
||||
} from '$lib/schema';
|
||||
import { createForm } from '@tanstack/svelte-form';
|
||||
import OrderThumbnail from './OrderThumbnail.svelte';
|
||||
|
||||
type LoadedBubbleTeaOrder = Loaded<
|
||||
typeof BubbleTeaOrder,
|
||||
{ addOns: { $each: true }; instructions: true }
|
||||
>;
|
||||
|
||||
type OrderFormData = {
|
||||
id: string;
|
||||
baseTea: (typeof BubbleTeaBaseTeaTypes)[number];
|
||||
addOns: (typeof BubbleTeaAddOnTypes)[number][];
|
||||
deliveryDate: Date;
|
||||
withMilk: boolean;
|
||||
instructions?: string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
order: LoadedBubbleTeaOrder;
|
||||
};
|
||||
|
||||
let { order: originalOrder }: Props = $props();
|
||||
|
||||
const defaultValues: OrderFormData = originalOrder.toJSON();
|
||||
defaultValues.deliveryDate = new Date(defaultValues.deliveryDate);
|
||||
|
||||
const form = createForm(() => ({
|
||||
defaultValues,
|
||||
onSubmit: async ({ value }) => {
|
||||
console.log('submit form', value);
|
||||
|
||||
// Apply changes to the original Jazz order
|
||||
originalOrder.baseTea = value.baseTea;
|
||||
originalOrder.addOns.applyDiff(value.addOns);
|
||||
originalOrder.deliveryDate = value.deliveryDate;
|
||||
originalOrder.withMilk = value.withMilk;
|
||||
|
||||
// Handle instructions (CoPlainText)
|
||||
const instructions = originalOrder.instructions ?? CoPlainText.create('');
|
||||
if (value.instructions) {
|
||||
instructions.applyDiff(value.instructions);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
function handleSubmit(event: Event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
form.handleSubmit();
|
||||
}
|
||||
</script>
|
||||
|
||||
<form onsubmit={handleSubmit} class="grid gap-5">
|
||||
<form.Subscribe selector={(state) => state.values}>
|
||||
{#snippet children(values)}
|
||||
<p>Unsaved order preview:</p>
|
||||
<OrderThumbnail order={values} />
|
||||
{/snippet}
|
||||
</form.Subscribe>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<label for="baseTea">Base tea</label>
|
||||
<form.Field
|
||||
name="baseTea"
|
||||
validators={{
|
||||
onChange: ({ value }) => {
|
||||
if (!value) {
|
||||
return 'Please select your preferred base tea';
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
}}
|
||||
>
|
||||
{#snippet children(field)}
|
||||
<select
|
||||
id="baseTea"
|
||||
class="dark:bg-transparent"
|
||||
value={field.state.value}
|
||||
onchange={(e) => field.handleChange(e.currentTarget.value as typeof BubbleTeaBaseTeaTypes[number])}
|
||||
onblur={field.handleBlur}
|
||||
>
|
||||
<option value="" disabled>
|
||||
Please select your preferred base tea
|
||||
</option>
|
||||
{#each BubbleTeaBaseTeaTypes as teaType}
|
||||
<option value={teaType}>
|
||||
{teaType}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
{#if field.state.meta.errors.length > 0}
|
||||
<span class="text-red-500 text-sm">{field.state.meta.errors[0]}</span>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</form.Field>
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
<legend class="mb-2">Add-ons</legend>
|
||||
<form.Field name="addOns">
|
||||
{#snippet children(field)}
|
||||
{#each BubbleTeaAddOnTypes as addOn}
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={addOn}
|
||||
checked={field.state.value.includes(addOn)}
|
||||
onchange={(e) => {
|
||||
const updatedAddons = (e.currentTarget.checked) ? [...field.state.value, addOn] : field.state.value.filter((item: string) => item !== addOn)
|
||||
field.handleChange(updatedAddons)}}
|
||||
/>
|
||||
<label for={addOn}>{addOn}</label>
|
||||
</div>
|
||||
{/each}
|
||||
{/snippet}
|
||||
</form.Field>
|
||||
</fieldset>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<label for="deliveryDate">Delivery date</label>
|
||||
<form.Field
|
||||
name="deliveryDate"
|
||||
validators={{
|
||||
onChange: ({ value }) => {
|
||||
if (!value) {
|
||||
return 'Delivery date is required';
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
}}
|
||||
>
|
||||
{#snippet children(field)}
|
||||
<input
|
||||
type="date"
|
||||
id="deliveryDate"
|
||||
class="dark:bg-transparent"
|
||||
value={field.state.value.toISOString().split('T')[0]}
|
||||
onchange={(e) => field.handleChange(new Date(e.currentTarget.value))}
|
||||
onblur={field.handleBlur}
|
||||
/>
|
||||
{#if field.state.meta.errors.length > 0}
|
||||
<span class="text-red-500 text-sm">{field.state.meta.errors[0]}</span>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</form.Field>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<form.Field name="withMilk">
|
||||
{#snippet children(field)}
|
||||
<input
|
||||
type="checkbox"
|
||||
id="withMilk"
|
||||
checked={field.state.value}
|
||||
onchange={(e) => field.handleChange(e.currentTarget.checked)}
|
||||
/>
|
||||
<label for="withMilk">With milk?</label>
|
||||
{/snippet}
|
||||
</form.Field>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<label for="instructions">Special instructions</label>
|
||||
<form.Field name="instructions">
|
||||
{#snippet children(field)}
|
||||
<textarea
|
||||
id="instructions"
|
||||
class="dark:bg-transparent"
|
||||
value={field.state.value || ''}
|
||||
onchange={(e) => field.handleChange(e.currentTarget.value)}
|
||||
></textarea>
|
||||
{/snippet}
|
||||
</form.Field>
|
||||
</div>
|
||||
|
||||
<form.Subscribe selector={(state) => [state.canSubmit, state.isSubmitting]}>
|
||||
{#snippet children([canSubmit, isSubmitting])}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={!canSubmit}
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded disabled:bg-gray-400"
|
||||
>
|
||||
{isSubmitting ? 'Submitting...' : 'Submit'}
|
||||
</button>
|
||||
{/snippet}
|
||||
</form.Subscribe>
|
||||
</form>
|
||||
@@ -0,0 +1,39 @@
|
||||
<script lang="ts">
|
||||
import { type Loaded } from 'jazz-tools';
|
||||
import { type BubbleTeaOrder } from '$lib/schema';
|
||||
|
||||
type Props = {
|
||||
order: Loaded<typeof BubbleTeaOrder> | {
|
||||
baseTea: string;
|
||||
addOns: string[];
|
||||
deliveryDate: Date;
|
||||
withMilk: boolean;
|
||||
instructions?: string;
|
||||
};
|
||||
};
|
||||
|
||||
let { order }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="border p-3 bg-gray-50 dark:bg-gray-800 rounded">
|
||||
<strong>
|
||||
{order.baseTea || "(No tea selected)"}
|
||||
{order.withMilk ? " milk " : " "}
|
||||
tea
|
||||
</strong>
|
||||
{#if order.addOns && order.addOns.length > 0}
|
||||
<p class="text-sm text-stone-600">
|
||||
with {order.addOns.join(", ").toLowerCase()}
|
||||
</p>
|
||||
{/if}
|
||||
{#if order.instructions}
|
||||
<p class="text-sm text-stone-600 italic">
|
||||
{order.instructions}
|
||||
</p>
|
||||
{/if}
|
||||
{#if order.deliveryDate}
|
||||
<p class="text-sm text-stone-600">
|
||||
Delivery: {order.deliveryDate.toLocaleDateString()}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
59
examples/tanstack-form/src/lib/components/Orders.svelte
Normal file
59
examples/tanstack-form/src/lib/components/Orders.svelte
Normal file
@@ -0,0 +1,59 @@
|
||||
<script lang="ts">
|
||||
import { AccountCoState } from 'jazz-tools/svelte';
|
||||
import { JazzAccount, type BubbleTeaOrder } from '$lib/schema';
|
||||
import { type Loaded } from 'jazz-tools';
|
||||
import OrderThumbnail from './OrderThumbnail.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
const me = new AccountCoState(JazzAccount, {
|
||||
resolve: {
|
||||
profile: true,
|
||||
root: {
|
||||
orders: {
|
||||
$each: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const orders = $derived(me.current?.root.orders);
|
||||
|
||||
function navigateToCreateOrder() {
|
||||
goto('#/order');
|
||||
}
|
||||
|
||||
function navigateToEditOrder(id: string) {
|
||||
goto(`#/order/${id}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h1 class="text-lg mb-4">
|
||||
<strong>Your bubble tea orders 🧋</strong>
|
||||
</h1>
|
||||
|
||||
{#if orders && orders.length > 0}
|
||||
<div class="space-y-4">
|
||||
{#each orders as order}
|
||||
{#if order}
|
||||
<button
|
||||
type="button"
|
||||
class="border rounded-lg p-4 cursor-pointer hover:bg-gray-50 text-left w-full"
|
||||
onclick={() => navigateToEditOrder(order.id)}
|
||||
>
|
||||
<OrderThumbnail {order} />
|
||||
</button>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<p class="text-gray-500">No orders yet. Create your first order!</p>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
onclick={navigateToCreateOrder}
|
||||
class="mt-6 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
|
||||
>
|
||||
Create New Order
|
||||
</button>
|
||||
</div>
|
||||
86
examples/tanstack-form/src/lib/schema.ts
Normal file
86
examples/tanstack-form/src/lib/schema.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { type Loaded, co, z } from 'jazz-tools';
|
||||
|
||||
export const BubbleTeaAddOnTypes = [
|
||||
'Pearl',
|
||||
'Lychee jelly',
|
||||
'Red bean',
|
||||
'Brown sugar',
|
||||
'Taro'
|
||||
] as const;
|
||||
|
||||
export const BubbleTeaBaseTeaTypes = ['Black', 'Oolong', 'Jasmine', 'Thai'] as const;
|
||||
|
||||
export const ListOfBubbleTeaAddOns = co
|
||||
.list(z.literal([...BubbleTeaAddOnTypes]))
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
.withHelpers((Self) => ({
|
||||
hasChanges(list?: Loaded<typeof Self> | null) {
|
||||
return list && Object.entries(list._raw.insertions).length > 0;
|
||||
}
|
||||
}));
|
||||
|
||||
export const BubbleTeaOrder = co.map({
|
||||
baseTea: z.literal([...BubbleTeaBaseTeaTypes]),
|
||||
addOns: ListOfBubbleTeaAddOns,
|
||||
deliveryDate: z.date(),
|
||||
withMilk: z.boolean(),
|
||||
instructions: z.optional(co.plainText())
|
||||
});
|
||||
|
||||
export const DraftBubbleTeaOrder = co
|
||||
.map({
|
||||
baseTea: z.optional(z.literal([...BubbleTeaBaseTeaTypes])),
|
||||
addOns: z.optional(ListOfBubbleTeaAddOns),
|
||||
deliveryDate: z.optional(z.date()),
|
||||
withMilk: z.optional(z.boolean()),
|
||||
instructions: z.optional(co.plainText())
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
.withHelpers((Self) => ({
|
||||
hasChanges(order: Loaded<typeof Self> | undefined) {
|
||||
return (
|
||||
!!order &&
|
||||
(Object.keys(order._edits).length > 1 || ListOfBubbleTeaAddOns.hasChanges(order.addOns))
|
||||
);
|
||||
},
|
||||
|
||||
validate(order: Loaded<typeof Self>) {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (!order.baseTea) {
|
||||
errors.push('Please select your preferred base tea.');
|
||||
}
|
||||
if (!order.deliveryDate) {
|
||||
errors.push('Plese select a delivery date.');
|
||||
}
|
||||
|
||||
return { errors };
|
||||
}
|
||||
}));
|
||||
|
||||
/** The root is an app-specific per-user private `CoMap`
|
||||
* where you can store top-level objects for that user */
|
||||
export const AccountRoot = co.map({
|
||||
draft: DraftBubbleTeaOrder,
|
||||
orders: co.list(BubbleTeaOrder)
|
||||
});
|
||||
|
||||
export const JazzAccount = co
|
||||
.account({
|
||||
root: AccountRoot,
|
||||
profile: co.profile()
|
||||
})
|
||||
.withMigration((account) => {
|
||||
if (!account.root) {
|
||||
const orders = co.list(BubbleTeaOrder).create([], account);
|
||||
const draft = DraftBubbleTeaOrder.create(
|
||||
{
|
||||
addOns: ListOfBubbleTeaAddOns.create([], account),
|
||||
instructions: co.plainText().create('', account)
|
||||
},
|
||||
account
|
||||
);
|
||||
|
||||
account.root = AccountRoot.create({ draft, orders }, account);
|
||||
}
|
||||
});
|
||||
28
examples/tanstack-form/src/routes/+layout.svelte
Normal file
28
examples/tanstack-form/src/routes/+layout.svelte
Normal file
@@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { JazzSvelteProvider } from 'jazz-tools/svelte';
|
||||
import 'jazz-tools/inspector/register-custom-element';
|
||||
import { PasskeyAuthBasicUI } from 'jazz-tools/svelte';
|
||||
import { apiKey } from '$lib/apiKey';
|
||||
import { JazzAccount } from '$lib/schema';
|
||||
import '../app.css';
|
||||
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Form Example</title>
|
||||
</svelte:head>
|
||||
|
||||
<JazzSvelteProvider
|
||||
AccountSchema={JazzAccount}
|
||||
sync={{
|
||||
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
|
||||
}}
|
||||
>
|
||||
<jazz-inspector></jazz-inspector>
|
||||
<PasskeyAuthBasicUI appName="Form Example">
|
||||
<div class="min-h-screen bg-gray-100">
|
||||
{@render children()}
|
||||
</div>
|
||||
</PasskeyAuthBasicUI>
|
||||
</JazzSvelteProvider>
|
||||
22
examples/tanstack-form/src/routes/+page.svelte
Normal file
22
examples/tanstack-form/src/routes/+page.svelte
Normal file
@@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import CreateOrder from '$lib/components/CreateOrder.svelte';
|
||||
import EditOrder from '$lib/components/EditOrder.svelte';
|
||||
import Orders from '$lib/components/Orders.svelte';
|
||||
|
||||
// Get current route from URL hash or default to '/'
|
||||
const currentRoute = $derived($page.url.hash || '#/');
|
||||
const route = $derived(currentRoute.replace('#', '') || '/');
|
||||
</script>
|
||||
|
||||
<main class="max-w-xl mx-auto px-3 py-8 space-y-8">
|
||||
{#if route === '/'}
|
||||
<Orders />
|
||||
{:else if route === '/order'}
|
||||
<CreateOrder />
|
||||
{:else if route.startsWith('/order/')}
|
||||
{#if route.split('/').length === 3}
|
||||
<EditOrder id={route.split('/')[2]} />
|
||||
{/if}
|
||||
{/if}
|
||||
</main>
|
||||
13
examples/tanstack-form/svelte.config.js
Normal file
13
examples/tanstack-form/svelte.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import adapter from "@sveltejs/adapter-auto";
|
||||
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
preprocess: vitePreprocess(),
|
||||
|
||||
kit: {
|
||||
adapter: adapter(),
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
14
examples/tanstack-form/tsconfig.json
Normal file
14
examples/tanstack-form/tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
}
|
||||
10
examples/tanstack-form/vite.config.ts
Normal file
10
examples/tanstack-form/vite.config.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit(), tailwindcss()],
|
||||
optimizeDeps: {
|
||||
exclude: ['jazz-tools']
|
||||
}
|
||||
});
|
||||
@@ -10,6 +10,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^9.7.0",
|
||||
"@radix-ui/react-checkbox": "^1.3.2",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-toast": "^1.2.14",
|
||||
@@ -18,8 +19,8 @@
|
||||
"jazz-tools": "workspace:*",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-router": "^6.16.0",
|
||||
"react-router-dom": "^6.16.0",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
@@ -29,8 +30,8 @@
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/qrcode": "^1.5.1",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react-swc": "^3.10.1",
|
||||
"postcss": "^8.4.27",
|
||||
"tailwindcss": "^4.1.10",
|
||||
|
||||
@@ -17,12 +17,12 @@ import React from "react";
|
||||
import { TodoAccount, TodoProject } from "./1_schema.ts";
|
||||
import { NewProjectForm } from "./3_NewProjectForm.tsx";
|
||||
import { ProjectTodoTable } from "./4_ProjectTodoTable.tsx";
|
||||
import { apiKey } from "./apiKey.ts";
|
||||
import {
|
||||
Button,
|
||||
ThemeProvider,
|
||||
TitleAndLogo,
|
||||
} from "./basicComponents/index.ts";
|
||||
import { TaskGenerator } from "./components/TaskGenerator.tsx";
|
||||
import { wordlist } from "./wordlist.ts";
|
||||
|
||||
/**
|
||||
@@ -41,7 +41,7 @@ function JazzAndAuth({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<JazzReactProvider
|
||||
sync={{
|
||||
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
|
||||
peer: `ws://localhost:4200`,
|
||||
}}
|
||||
AccountSchema={TodoAccount}
|
||||
>
|
||||
@@ -92,6 +92,10 @@ export default function App() {
|
||||
path: "/invite/*",
|
||||
element: <p>Accepting invite...</p>,
|
||||
},
|
||||
{
|
||||
path: "/generate",
|
||||
element: <TaskGenerator />,
|
||||
},
|
||||
]);
|
||||
|
||||
// `useAcceptInvite()` is a hook that accepts an invite link from the URL hash,
|
||||
|
||||
63
examples/todo/src/components/TaskGenerator.tsx
Normal file
63
examples/todo/src/components/TaskGenerator.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { TodoAccount } from "@/1_schema";
|
||||
import { FormEvent, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { generateRandomProject } from "../generate";
|
||||
|
||||
export function TaskGenerator() {
|
||||
const [isGenerating, setIsGenerating] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const numTasks = Math.max(
|
||||
1,
|
||||
parseInt(formData.get("numTasks") as string) || 1,
|
||||
);
|
||||
|
||||
setIsGenerating(true);
|
||||
const project = generateRandomProject(numTasks);
|
||||
|
||||
const { root } = await TodoAccount.getMe().ensureLoaded({
|
||||
resolve: {
|
||||
root: {
|
||||
projects: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
root.projects.push(project.value);
|
||||
|
||||
await project.done;
|
||||
|
||||
navigate(`/project/${project.value.id}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 border rounded-lg shadow-xs bg-white">
|
||||
<h2 className="text-lg font-semibold mb-4">Generate Random Tasks</h2>
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<label htmlFor="numTasks" className="text-sm font-medium">
|
||||
Number of tasks:
|
||||
</label>
|
||||
<input
|
||||
id="numTasks"
|
||||
name="numTasks"
|
||||
type="number"
|
||||
min="1"
|
||||
defaultValue={5}
|
||||
className="w-20 px-2 py-1 border rounded"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isGenerating}
|
||||
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:bg-blue-300"
|
||||
>
|
||||
{isGenerating ? "Generating..." : "Generate Tasks"}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,9 @@ import { CoPlainText } from "jazz-tools";
|
||||
import { Task, TodoProject } from "./1_schema";
|
||||
|
||||
export function generateRandomProject(numTasks: number) {
|
||||
// Generate a random project title
|
||||
const projectTitle = faker.company.catchPhrase();
|
||||
|
||||
// Create a list of tasks
|
||||
const tasks = TodoProject.def.shape.tasks.create([]);
|
||||
|
||||
@@ -15,6 +18,7 @@ export function generateRandomProject(numTasks: number) {
|
||||
faker.lorem.sentence({ min: 3, max: 8 }),
|
||||
tasks._owner,
|
||||
),
|
||||
version: 1,
|
||||
});
|
||||
tasks.push(task);
|
||||
}
|
||||
@@ -23,7 +27,7 @@ export function generateRandomProject(numTasks: number) {
|
||||
// Create and return the project
|
||||
return {
|
||||
value: TodoProject.create({
|
||||
title: `${numTasks} tasks`,
|
||||
title: projectTitle,
|
||||
tasks: tasks,
|
||||
}),
|
||||
done: new Promise((resolve) => {
|
||||
@@ -12,14 +12,14 @@
|
||||
"dependencies": {
|
||||
"@tailwindcss/forms": "^0.5.9",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"tailwindcss": "^4.1.10",
|
||||
|
||||
@@ -27,26 +27,28 @@ export default function ButtonsPage() {
|
||||
return (
|
||||
<>
|
||||
<h3 className="text-lg mt-5 mb-2 font-bold">Variants</h3>
|
||||
|
||||
<p className="my-3">Buttons are styled with the variant prop.</p>
|
||||
<p className="mb-3">
|
||||
For compatibility the shadcn/ui variants are mapped to the design
|
||||
system.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<Button variant="default">default</Button>
|
||||
<Button variant="link">link</Button>
|
||||
<Button variant="ghost">ghost</Button>
|
||||
<Button variant="outline">outline</Button>
|
||||
<Button variant="secondary">secondary</Button>
|
||||
<Button variant="destructive">destructive</Button>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg mt-5 font-bold">Intents</h3>
|
||||
<p className="my-3">
|
||||
We have extended the variants to include more styles via the intent
|
||||
prop.
|
||||
<h3 className="text-lg mt-5 mb-2 font-bold">Intents</h3>
|
||||
<p>
|
||||
We have extended the shadcn/ui variants to include more styles via the
|
||||
intent prop.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<Button intent="default">default</Button>
|
||||
<Button intent="muted">muted</Button>
|
||||
<Button intent="strong">strong</Button>
|
||||
{/* <Button intent="default">default</Button> */}
|
||||
<Button intent="primary">primary</Button>
|
||||
<Button intent="tip">tip</Button>
|
||||
<Button intent="info">info</Button>
|
||||
@@ -54,6 +56,8 @@ export default function ButtonsPage() {
|
||||
<Button intent="warning">warning</Button>
|
||||
<Button intent="alert">alert</Button>
|
||||
<Button intent="danger">danger</Button>
|
||||
<Button intent="muted">muted</Button>
|
||||
<Button intent="strong">strong</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center w-48 mt-10">
|
||||
@@ -85,7 +89,7 @@ export default function ButtonsPage() {
|
||||
|
||||
<p className="text-sm mt-2 mb-5">
|
||||
<strong>NB:</strong> Variants and styles are interchangeable. See the
|
||||
intent on each variant with the dropdown.
|
||||
intent on each variant with the dropdown
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
@@ -103,19 +107,9 @@ export default function ButtonsPage() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p className="my-3">
|
||||
For compatibility the shadcn/ui variants are mapped to the design
|
||||
system.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<Button variant="secondary">secondary</Button>
|
||||
<Button variant="destructive">destructive</Button>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-bold mt-5">Icons</h3>
|
||||
|
||||
<p className="my-3">Buttons can also contain an icon and text.</p>
|
||||
<p>Buttons can also contain an icon and text.</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<Button
|
||||
@@ -136,7 +130,7 @@ export default function ButtonsPage() {
|
||||
>
|
||||
outline info with icon
|
||||
</Button>
|
||||
<p className="col-span-2 my-2">
|
||||
<p className="col-span-2">
|
||||
Or just use the icon prop with any of the button variants, style
|
||||
variants and colors.
|
||||
</p>
|
||||
@@ -157,7 +151,6 @@ const buttonPropsTableData = {
|
||||
{
|
||||
prop: "intent?",
|
||||
types: [
|
||||
"default",
|
||||
"primary",
|
||||
"tip",
|
||||
"info",
|
||||
@@ -181,7 +174,7 @@ const buttonPropsTableData = {
|
||||
"secondary",
|
||||
"destructive",
|
||||
],
|
||||
default: "default",
|
||||
default: "undefined",
|
||||
},
|
||||
{
|
||||
prop: "icon?",
|
||||
|
||||
@@ -159,8 +159,8 @@ const styleClasses = (intent: Style, variant: Variant | undefined) => {
|
||||
inverted: `${styleToTextMap[intent]} ${colorToBgHoverMap30[styleToColorMap[intent] as VariantColor]} ${colorToBgMap[styleToColorMap[intent] as VariantColor]} ${colorToBgActiveMap50[styleToColorMap[intent] as VariantColor]} ${shadowClassesBase}`,
|
||||
ghost: `bg-transparent ${styleToTextMap[intent]} ${colorToBgHoverMap10[styleToColorMap[intent] as VariantColor]} ${colorToBgActiveMap25[styleToColorMap[intent] as VariantColor]}`,
|
||||
link: `bg-transparent ${styleToTextMap[intent]} underline underline-offset-2 p-0 hover:bg-transparent ${styleToTextHoverMap[intent]} ${styleToTextActiveMap[intent]} active:underline-stone-500`,
|
||||
secondary: variantClass("muted"),
|
||||
destructive: variantClass("danger"),
|
||||
secondary: `bg-stone-300 ${styleToTextMap[intent]} hover:bg-stone-400/80 active:bg-stone-500/80`,
|
||||
destructive: `bg-danger text-white hover:bg-red/80 active:bg-red/70`,
|
||||
default: `${styleToBgGradientColorMap["default"]} ${styleToBgGradientHoverMap["default"]} ${textColorVariant("default")} ${styleToButtonStateMap["default"]} ${shadowClassesBase} shadow-stone-400/20`,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
ChevronLeftIcon,
|
||||
ChevronRight,
|
||||
ChevronRightIcon,
|
||||
ClipboardCheckIcon,
|
||||
ClipboardIcon,
|
||||
CodeIcon,
|
||||
Eye,
|
||||
@@ -67,7 +66,6 @@ export const icons = {
|
||||
close: XIcon,
|
||||
code: CodeIcon,
|
||||
copy: ClipboardIcon,
|
||||
copySuccess: ClipboardCheckIcon,
|
||||
cursor: MousePointer2Icon,
|
||||
darkTheme: MoonIcon,
|
||||
delete: TrashIcon,
|
||||
|
||||
@@ -11,7 +11,7 @@ export function CopyButton({
|
||||
onCopy,
|
||||
}: {
|
||||
code: string;
|
||||
size: "sm" | "md" | "lg";
|
||||
size: "md" | "lg";
|
||||
className?: string;
|
||||
onCopy?: () => void;
|
||||
}) {
|
||||
@@ -32,13 +32,13 @@ export function CopyButton({
|
||||
type="button"
|
||||
className={clsx(
|
||||
className,
|
||||
"group/button absolute overflow-hidden rounded text-2xs font-medium md:opacity-0 backdrop-blur transition md:focus:opacity-100 group-hover:opacity-100 items-center align-middle p-0",
|
||||
"group/button absolute overflow-hidden rounded text-2xs font-medium md:opacity-0 backdrop-blur transition md:focus:opacity-100 group-hover:opacity-100",
|
||||
copied
|
||||
? "bg-blue-400/10 ring-1 ring-inset ring-blue-400/20"
|
||||
? "bg-emerald-400/10 ring-1 ring-inset ring-emerald-400/20"
|
||||
: "bg-white/5 hover:bg-white/7.5 dark:bg-white/2.5 dark:hover:bg-white/5",
|
||||
size === "md"
|
||||
size == "md"
|
||||
? "right-[8.5px] top-[8.5px] py-[2px] pl-1 pr-2"
|
||||
: "right-2 top-2 py-1 pl-2 pr-2",
|
||||
: "right-2 top-2 py-1 pl-2 pr-3",
|
||||
)}
|
||||
onClick={() => {
|
||||
window.navigator.clipboard.writeText(code).then(() => {
|
||||
@@ -60,22 +60,18 @@ export function CopyButton({
|
||||
className={clsx(
|
||||
size === "md" ? "size-3" : "size-4",
|
||||
"stroke-stone-500 transition-colors group-hover/button:stroke-stone-600 dark:group-hover/button:stroke-stone-400",
|
||||
copied && "stroke-primary",
|
||||
)}
|
||||
/>
|
||||
{size !== "sm" && "Copy"}
|
||||
Copy
|
||||
</span>
|
||||
<span
|
||||
aria-hidden={!copied}
|
||||
className={clsx(
|
||||
"pointer-events-none absolute inset-0 flex items-center justify-center text-primary transition duration-300",
|
||||
"pointer-events-none absolute inset-0 flex items-center justify-center text-emerald-600 transition duration-300 dark:text-emerald-400",
|
||||
!copied && "translate-y-1.5 opacity-0",
|
||||
)}
|
||||
>
|
||||
{size === "sm" && (
|
||||
<Icon name="copySuccess" size="xs" className="stroke-primary" />
|
||||
)}
|
||||
{size !== "sm" && "Copied!"}
|
||||
Copied!
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -44,7 +44,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
: icon && iconPosition === "right";
|
||||
|
||||
const inputClassName = clsx(
|
||||
"w-full rounded-md border px-2.5 py-1 shadow-sm h-[36px]",
|
||||
"w-full rounded-md border px-3.5 py-2 shadow-sm",
|
||||
"font-medium text-stone-900",
|
||||
"dark:text-white dark:bg-stone-925",
|
||||
);
|
||||
|
||||
@@ -60,7 +60,7 @@ export function DropdownItem({
|
||||
let classes = clsx(
|
||||
className,
|
||||
// Base styles
|
||||
"group rounded-md space-x-2 focus:outline-none px-2.5 py-1.5",
|
||||
"group rounded-md space-x-2 focus:outline-none px-2.5 py-1.5",
|
||||
// Text styles
|
||||
"text-left text-sm/6 dark:text-white forced-colors:text-[CanvasText]",
|
||||
// Focus
|
||||
|
||||
@@ -21,8 +21,8 @@ export type Style =
|
||||
|
||||
export const sizeClasses = {
|
||||
sm: "text-sm py-1 px-2",
|
||||
md: "py-1.5 px-3 h-[36px]",
|
||||
lg: "py-2 px-5 md:px-6 md:py-2.5",
|
||||
md: "py-1.5 px-3",
|
||||
lg: "md:text-lg py-2 px-3 md:px-8 md:py-3",
|
||||
};
|
||||
|
||||
export const styleToBorderMap = {
|
||||
|
||||
@@ -42,6 +42,15 @@ export const team: Array<TeamMember> = [
|
||||
linkedin: "giordanoricci",
|
||||
image: "gio.jpg",
|
||||
},
|
||||
{
|
||||
name: "Trisha Lim",
|
||||
slug: "trisha",
|
||||
titles: ["Frontend Dev", "Marketing"],
|
||||
image: "trisha.png",
|
||||
location: "Lisbon, Portugal ",
|
||||
github: "trishalim",
|
||||
website: "https://trishalim.com",
|
||||
},
|
||||
{
|
||||
name: "Meg Culotta",
|
||||
slug: "meg",
|
||||
@@ -64,7 +73,7 @@ export const team: Array<TeamMember> = [
|
||||
name: "Sammii Kellow",
|
||||
slug: "sammii",
|
||||
location: "London, UK",
|
||||
titles: ["Frontend & Design Engineer", "Marketing"],
|
||||
titles: ["Design Engineer", "Marketing"],
|
||||
x: "SammiiHaylock",
|
||||
github: "sammii-hk",
|
||||
website: "https://sammii.dev",
|
||||
@@ -82,25 +91,4 @@ export const team: Array<TeamMember> = [
|
||||
linkedin: "boorad",
|
||||
image: "brad.png",
|
||||
},
|
||||
{
|
||||
name: "Divya S",
|
||||
slug: "div",
|
||||
location: "New York, US",
|
||||
titles: ["Platform Engineer"],
|
||||
x: "shortdiv",
|
||||
github: "shortdiv",
|
||||
website: "https://shortdiv.com",
|
||||
bluesky: "shortdiv.bsky.social",
|
||||
linkedin: "shortdiv",
|
||||
image: "div.jpg",
|
||||
},
|
||||
{
|
||||
name: "Nico Rainhart",
|
||||
slug: "nico",
|
||||
location: "Buenos Aires, Argentina",
|
||||
titles: ["Full-Stack Dev", "Framework Engineer"],
|
||||
image: "nico.jpeg",
|
||||
github: "nrainhart",
|
||||
linkedin: "nicolás-rainhart",
|
||||
},
|
||||
];
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 279 KiB |
BIN
homepage/gcmp/public/team/trisha.png
Normal file
BIN
homepage/gcmp/public/team/trisha.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 186 KiB |
@@ -4,7 +4,6 @@ import { ComingSoonSection } from "@/components/home/ComingSoonSection";
|
||||
import { EarlyAdopterSection } from "@/components/home/EarlyAdopterSection";
|
||||
import { EncryptionSection } from "@/components/home/EncryptionSection";
|
||||
import { FeaturesSection } from "@/components/home/FeaturesSection";
|
||||
import { GetStartedSnippetSelect } from "@/components/home/GetStartedSnippetSelect";
|
||||
import { HeroSection } from "@/components/home/HeroSection";
|
||||
import { HowJazzWorksSection } from "@/components/home/HowJazzWorksSection";
|
||||
import { LocalFirstFeaturesSection } from "@/components/home/LocalFirstFeaturesSection";
|
||||
@@ -17,8 +16,7 @@ export default function Home() {
|
||||
<>
|
||||
<HeroSection />
|
||||
|
||||
<div className="container flex flex-col gap-12 lg:gap-20">
|
||||
<GetStartedSnippetSelect />
|
||||
<div className="container flex flex-col gap-12 mt-12 lg:gap-20 lg:mt-20">
|
||||
<SupportedEnvironmentsSection />
|
||||
<HowJazzWorksSection />
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export function SideNavItem({
|
||||
}) {
|
||||
const classes = clsx(
|
||||
className,
|
||||
"py-1 px-2 group rounded-md flex items-center transition-colors",
|
||||
"py-1 px-2 -mx-2 group rounded-md flex items-center transition-colors",
|
||||
);
|
||||
const path = usePathname();
|
||||
|
||||
@@ -28,7 +28,7 @@ export function SideNavItem({
|
||||
className={clsx(
|
||||
classes,
|
||||
path === href
|
||||
? "text-stone-900 font-medium bg-stone-200/50 dark:text-white dark:bg-stone-800/50"
|
||||
? "text-stone-900 font-medium bg-stone-100 dark:text-white dark:bg-stone-900"
|
||||
: "hover:text-stone-900 dark:hover:text-stone-200",
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -10,21 +10,10 @@ import {
|
||||
DropdownItem,
|
||||
DropdownMenu,
|
||||
} from "@garden-co/design-system/src/components/organisms/Dropdown";
|
||||
import clsx from "clsx";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
|
||||
export function FrameworkSelect({
|
||||
onSelect,
|
||||
size = "md",
|
||||
routerPush = true,
|
||||
className,
|
||||
}: {
|
||||
onSelect?: (framework: Framework) => void;
|
||||
size?: "sm" | "md";
|
||||
routerPush?: boolean;
|
||||
className?: string;
|
||||
}) {
|
||||
export function FrameworkSelect() {
|
||||
const router = useRouter();
|
||||
const defaultFramework = useFramework();
|
||||
const [selectedFramework, setSelectedFramework] =
|
||||
@@ -34,26 +23,26 @@ export function FrameworkSelect({
|
||||
|
||||
const selectFramework = (newFramework: Framework) => {
|
||||
setSelectedFramework(newFramework);
|
||||
onSelect && onSelect(newFramework);
|
||||
routerPush && router.push(path.replace(defaultFramework, newFramework));
|
||||
router.push(path.replace(defaultFramework, newFramework));
|
||||
};
|
||||
|
||||
return (
|
||||
<Dropdown>
|
||||
<DropdownButton
|
||||
className={clsx("w-full justify-between overflow-hidden text-nowrap", size === "sm" && "text-sm", className)}
|
||||
className="w-full justify-between"
|
||||
as={Button}
|
||||
variant="outline"
|
||||
intent="default"
|
||||
>
|
||||
<span className="text-nowrap max-w-full overflow-hidden text-ellipsis">{frameworkNames[selectedFramework].label}</span>
|
||||
{frameworkNames[selectedFramework].label}
|
||||
<Icon name="chevronDown" size="sm" />
|
||||
</DropdownButton>
|
||||
<DropdownMenu className="w-[--button-width] z-50" anchor="bottom start">
|
||||
{Object.entries(frameworkNames)
|
||||
.filter(([_, framework]) => !framework.hidden)
|
||||
.map(([key, framework]) => (
|
||||
<DropdownItem
|
||||
className={clsx("items-baseline", size === "sm" && "text-xs text-nowrap", selectedFramework === key && "text-primary dark:text-primary")}
|
||||
className="items-baseline"
|
||||
key={key}
|
||||
onClick={() => selectFramework(key as Framework)}
|
||||
>
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { Framework } from "@/content/framework";
|
||||
import { useFramework } from "@/lib/use-framework";
|
||||
import NpxCreateJazzApp from "@/components/home/NpxCreateJazzApp.mdx";
|
||||
import { CopyButton } from "@garden-co/design-system/src/components/molecules/CodeGroup";
|
||||
import { useState } from "react";
|
||||
import { Button } from "@garden-co/design-system/src/components/atoms/Button";
|
||||
import Link from "next/link";
|
||||
import { FrameworkSelect } from "../docs/FrameworkSelect";
|
||||
import clsx from "clsx";
|
||||
import { track } from "@vercel/analytics";
|
||||
import { GappedGrid } from "@garden-co/design-system/src/components/molecules/GappedGrid";
|
||||
|
||||
export function GetStartedSnippetSelect() {
|
||||
const defaultFramework = useFramework();
|
||||
const [selectedFramework, setSelectedFramework] =
|
||||
useState<Framework>(defaultFramework);
|
||||
|
||||
return (
|
||||
<GappedGrid>
|
||||
<div className="relative w-full col-span-2 lg:col-span-3 border-2 border-primary rounded-lg overflow-hidden">
|
||||
<CopyButton
|
||||
code="npx create-jazz-app@latest"
|
||||
size="sm"
|
||||
className={clsx("mt-0.5 mr-0.5 z-100 md:opacity-100 hidden md:block")}
|
||||
onCopy={() => track("create-jazz-app command copied from hero")}
|
||||
/>
|
||||
<NpxCreateJazzApp />
|
||||
</div>
|
||||
<div className="col-span-2 lg:col-span-3 flex flex-row gap-2">
|
||||
<div className="h-full items-center w-[175px]">
|
||||
<FrameworkSelect onSelect={setSelectedFramework} size="md" routerPush={false} className="h-full md:px-4" />
|
||||
</div>
|
||||
<div className="flex h-full items-center">
|
||||
<Button intent="primary" size="lg" className="w-full">
|
||||
<Link className="my-[0.11rem]" href={`/docs/${selectedFramework}`}>Get started</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</GappedGrid>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import CreateJazzApp from "@/components/home/CreateJazzApp.mdx";
|
||||
import { marketingCopy } from "@/content/marketingCopy";
|
||||
import { H1 } from "@garden-co/design-system/src/components/atoms/Headings";
|
||||
import {
|
||||
@@ -7,10 +8,11 @@ import {
|
||||
type IconName,
|
||||
} from "@garden-co/design-system/src/components/atoms/Icon";
|
||||
import { Kicker } from "@garden-co/design-system/src/components/atoms/Kicker";
|
||||
import { CopyButton } from "@garden-co/design-system/src/components/molecules/CodeGroup";
|
||||
import { Prose } from "@garden-co/design-system/src/components/molecules/Prose";
|
||||
import { SectionHeader } from "@garden-co/design-system/src/components/molecules/SectionHeader";
|
||||
import { track } from "@vercel/analytics";
|
||||
import Link from "next/link";
|
||||
import { GetStartedSnippetSelect } from "./GetStartedSnippetSelect";
|
||||
|
||||
|
||||
const features: Array<{
|
||||
title: string;
|
||||
@@ -52,8 +54,8 @@ const features: Array<{
|
||||
|
||||
export function HeroSection() {
|
||||
return (
|
||||
<div className="container grid items-center gap-x-8 gap-y-12 my-12 md:my-16 lg:my-24 lg:gap-x-10 lg:grid-cols-12">
|
||||
<div className="flex flex-col justify-center gap-5 lg:col-span-11 lg:gap-8">
|
||||
<div className="container grid items-center gap-x-8 gap-y-12 my-12 md:my-16 lg:my-24 lg:gap-x-10 lg:grid-cols-3">
|
||||
<div className="flex flex-col justify-center gap-5 lg:col-span-2 lg:gap-8">
|
||||
<Kicker>Toolkit for backendless apps</Kicker>
|
||||
<H1>
|
||||
<span className="inline-block text-highlight">
|
||||
@@ -92,6 +94,31 @@ export function HeroSection() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-full group grid md:grid-cols-2 items-center lg:grid-cols-1 lg:pt-36">
|
||||
<SectionHeader
|
||||
className="md:col-span-2 lg:sr-only"
|
||||
title="Get a Jazz app running in minutes."
|
||||
/>
|
||||
<div className="overflow-hidden sm:rounded-xl sm:border h-full sm:px-8 sm:pt-6 bg-stone-50 dark:bg-stone-950">
|
||||
<div className="rounded-lg bg-white dark:bg-stone-925 sm:ring-4 ring-stone-400/20 sm:shadow-xl sm:shadow-blue/20 border relative sm:top-2 h-full w-full">
|
||||
<div className="py-4 flex items-center gap-2.5 px-6 border-b">
|
||||
<span className="rounded-full size-3 bg-stone-200 dark:bg-stone-900" />
|
||||
<span className="rounded-full size-3 bg-stone-200 dark:bg-stone-900" />
|
||||
<span className="rounded-full size-3 bg-stone-200 dark:bg-stone-900" />
|
||||
<CopyButton
|
||||
code="npx create-jazz-app@latest"
|
||||
size="md"
|
||||
className="mt-0.5 mr-0.5"
|
||||
onCopy={() => track("create-jazz-app command copied from hero")}
|
||||
/>
|
||||
</div>
|
||||
<div className="p-3">
|
||||
<CreateJazzApp />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
```sh
|
||||
npx create-jazz-app@latest
|
||||
```
|
||||
@@ -118,7 +118,7 @@ To use it, install the following Packages:
|
||||
|
||||
<CodeGroup>
|
||||
```bash
|
||||
pnpm add react-native-quick-crypto@1.0.0-beta.18 react-native-nitro-modules react-native-fast-encoder
|
||||
pnpm add react-native-quick-crypto@1.0.0-beta.18 react-native-nitro-modules
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ To use it, install the following Packages:
|
||||
|
||||
<CodeGroup>
|
||||
```bash
|
||||
pnpm add react-native-quick-crypto@1.0.0-beta.18 react-native-nitro-modules react-native-fast-encoder
|
||||
pnpm add react-native-quick-crypto@1.0.0-beta.18 react-native-nitro-modules
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ npx expo prebuild
|
||||
<CodeGroup>
|
||||
```bash
|
||||
# Expo dependencies
|
||||
npx expo install expo-linking expo-secure-store expo-sqlite expo-file-system @react-native-community/netinfo @bam.tech/react-native-image-resizer
|
||||
npx expo install expo-linking expo-secure-store expo-file-system @react-native-community/netinfo @bam.tech/react-native-image-resizer
|
||||
|
||||
# React Native polyfills
|
||||
npm i -S @azure/core-asynciterator-polyfill react-native-url-polyfill readable-stream react-native-get-random-values
|
||||
|
||||
@@ -202,7 +202,7 @@ See the corresponding sections for [creating](/docs/using-covalues/filestreams#c
|
||||
|
||||
### Unions of CoMaps (declaration)
|
||||
|
||||
You can declare unions of CoMaps that have discriminating fields, using `co.discriminatedUnion()`.
|
||||
You can declare unions of CoMaps that have discriminating fields, using `z.discriminatedUnion()`.
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -220,7 +220,7 @@ const SliderWidget = co.map({
|
||||
max: z.number(),
|
||||
});
|
||||
|
||||
const WidgetUnion = co.discriminatedUnion("type", [ButtonWidget, SliderWidget]);
|
||||
const WidgetUnion = z.discriminatedUnion("type", [ButtonWidget, SliderWidget]);
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -321,7 +321,7 @@ const Company = co.map({
|
||||
|
||||
#### Optional References
|
||||
|
||||
You can make references optional with `co.optional()`:
|
||||
You can make references optional with `z.optional()`:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -331,7 +331,7 @@ const Pet = co.map({
|
||||
});
|
||||
// ---cut---
|
||||
const Person = co.map({
|
||||
pet: co.optional(Pet),
|
||||
pet: z.optional(Pet),
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -372,7 +372,7 @@ const ListOfPeople = co.list(Person);
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
Note: similarly, if you use modifiers like `co.optional()` you'll need to help TypeScript along:
|
||||
Note: similarly, if you use modifiers like `z.optional()` you'll need to help TypeScript along:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -381,7 +381,7 @@ import { co, z } from "jazz-tools";
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
get bestFriend(): z.ZodOptional<typeof Person> {
|
||||
return co.optional(Person);
|
||||
return z.optional(Person);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
@@ -24,7 +24,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: co.optional(Member),
|
||||
coordinator: z.optional(Member),
|
||||
});
|
||||
export type Project = co.loaded<typeof Project>;
|
||||
```
|
||||
@@ -54,7 +54,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: co.optional(Member),
|
||||
coordinator: z.optional(Member),
|
||||
});
|
||||
const Inventory = co.record(z.string(), z.number());
|
||||
// ---cut---
|
||||
@@ -90,7 +90,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: co.optional(Member),
|
||||
coordinator: z.optional(Member),
|
||||
});
|
||||
|
||||
// ---cut---
|
||||
@@ -134,7 +134,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: co.optional(Member),
|
||||
coordinator: z.optional(Member),
|
||||
});
|
||||
const project = Project.create(
|
||||
{
|
||||
@@ -165,7 +165,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: co.optional(Member),
|
||||
coordinator: z.optional(Member),
|
||||
});
|
||||
const project = Project.create(
|
||||
{
|
||||
@@ -197,7 +197,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: co.optional(Member),
|
||||
coordinator: z.optional(Member),
|
||||
get subProject() {
|
||||
return Project.optional();
|
||||
}
|
||||
@@ -219,9 +219,9 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: co.optional(Member),
|
||||
coordinator: z.optional(Member),
|
||||
get subProjects(): z.ZodOptional<CoListSchema<typeof Project>> {
|
||||
return co.optional(co.list(Project));
|
||||
return z.optional(co.list(Project));
|
||||
}
|
||||
});
|
||||
export type Project = co.loaded<typeof Project>;
|
||||
@@ -265,7 +265,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: co.optional(Member),
|
||||
coordinator: z.optional(Member),
|
||||
});
|
||||
const Inventory = co.record(z.string(), z.number());
|
||||
const project = Project.create(
|
||||
@@ -297,7 +297,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: co.optional(Member),
|
||||
coordinator: z.optional(Member),
|
||||
});
|
||||
const Inventory = co.record(z.string(), z.number());
|
||||
const project = Project.create(
|
||||
@@ -347,7 +347,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: co.optional(Member),
|
||||
coordinator: z.optional(Member),
|
||||
});
|
||||
const Inventory = co.record(z.string(), z.number());
|
||||
const project = Project.create(
|
||||
@@ -445,7 +445,7 @@ const TaskV2 = co.map({
|
||||
|
||||
// Export the discriminated union; because some users might
|
||||
// not be able to run the migration
|
||||
export const Task = co.discriminatedUnion("version", [
|
||||
export const Task = z.discriminatedUnion("version", [
|
||||
TaskV1,
|
||||
TaskV2,
|
||||
]);
|
||||
|
||||
@@ -6,7 +6,7 @@ export const metadata = {
|
||||
|
||||
# Connecting CoValues with direct linking
|
||||
CoValues can form relationships with each other by **linking directly to other CoValues**. This creates a powerful connection where one CoValue can point to the unique identity of another.
|
||||
Instead of embedding all the details of one CoValue directly within another, you use its Jazz-Tools schema as the field type. This allows multiple CoValues to point to the same piece of data effortlessly.
|
||||
Instead of embedding all of the details of one coValue directly within another, you use its Jazz-Tools schema as the field type. This allows multiple CoValues to point to the same piece of data effortlessly.
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -50,51 +50,3 @@ export type User = co.loaded<typeof User>;
|
||||
This direct linking approach offers a single source of truth. When you update a referenced CoValue, all other CoValues that point to it are automatically updated, ensuring data consistency across your application.
|
||||
|
||||
By connecting CoValues through these direct references, you can build robust and collaborative applications where data is consistent, efficient to manage, and relationships are clearly defined. The ability to link different CoValue types to the same underlying data is fundamental to building complex applications with Jazz.
|
||||
|
||||
|
||||
## Recursive references with DiscriminatedUnion
|
||||
In advanced schemas, you may want a CoValue that recursively references itself. For example, a `ReferenceItem` that contains a list of other items like `NoteItem` or `AttachmentItem`. This is common in tree-like structures such as threaded comments or nested project outlines.
|
||||
|
||||
You can model this with a Zod `z.discriminatedUnion`, but TypeScript’s type inference doesn't handle recursive unions well without a workaround.
|
||||
|
||||
Here’s how to structure your schema to avoid circular reference errors.
|
||||
|
||||
### Use this pattern for recursive discriminated unions
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { CoListSchema, co, z } from "jazz-tools";
|
||||
|
||||
// Recursive item modeling pattern using discriminated unions
|
||||
// First, define the non-recursive types
|
||||
export const NoteItem = co.map({
|
||||
type: z.literal("note"),
|
||||
internal: z.boolean(),
|
||||
content: co.plainText(),
|
||||
});
|
||||
|
||||
export const AttachmentItem = co.map({
|
||||
type: z.literal("attachment"),
|
||||
internal: z.boolean(),
|
||||
content: co.fileStream(),
|
||||
});
|
||||
|
||||
export const ReferenceItem = co.map({
|
||||
type: z.literal("reference"),
|
||||
internal: z.boolean(),
|
||||
content: z.string(),
|
||||
|
||||
// Workaround: declare the field type using CoListSchema and ZodDiscriminatedUnion so TS can safely recurse
|
||||
get children(): CoListSchema<z.ZodDiscriminatedUnion<[typeof NoteItem, typeof AttachmentItem, typeof ReferenceItem]>> {
|
||||
return ProjectContextItemList;
|
||||
},
|
||||
});
|
||||
|
||||
// Create the recursive union
|
||||
export const ProjectContextItem = z.discriminatedUnion("type", [NoteItem, AttachmentItem, ReferenceItem]);
|
||||
|
||||
// Final list of recursive types
|
||||
export const ProjectContextItemList = co.list(ProjectContextItem);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Even though this seems like a shortcut, TypeScript and Zod can't resolve the circular reference this way. Always define the discriminated union before introducing recursive links.
|
||||
|
||||
1543
homepage/pnpm-lock.yaml
generated
1543
homepage/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@ packages:
|
||||
- "gcmp"
|
||||
|
||||
catalog:
|
||||
"react": "19.1.0"
|
||||
"react-dom": "19.1.0"
|
||||
"@types/react": "19.1.0"
|
||||
"@types/react-dom": "19.1.0"
|
||||
"react": "19.0.0"
|
||||
"react-dom": "19.0.0"
|
||||
"@types/react": "19.0.0"
|
||||
"@types/react-dom": "19.0.0"
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
"changeset-version": "changeset version && pnpm i --no-frozen-lockfile",
|
||||
"release": "turbo run build --filter='./packages/*' && pnpm changeset publish && git push --follow-tags",
|
||||
"clean": "rm -rf ./packages/*/dist && rm -rf ./packages/*/node_modules && rm -rf ./examples/*/node_modules && rm -rf ./examples/*/dist",
|
||||
"postinstall": "lefthook install",
|
||||
"check-catalog-deps": "node scripts/check-catalog-deps.js"
|
||||
},
|
||||
"version": "0.0.0",
|
||||
@@ -51,10 +50,10 @@
|
||||
"ignoreMissing": ["@babel/*", "expo-modules-*", "typescript"]
|
||||
},
|
||||
"overrides": {
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"vite": "6.3.5",
|
||||
"esbuild": "0.24.0"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user