├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ └── config.yml
└── workflows
│ ├── docs.yml
│ ├── lint.yml
│ └── release-please.yml
├── .gitignore
├── .release-please-manifest.json
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── dev
├── eslint.config.mjs
├── index.html
├── package.json
├── postcss.config.cjs
├── src
│ ├── AraraLab.tsx
│ ├── index.css
│ └── index.tsx
├── tailwind.config.js
├── tsconfig.json
└── vite.config.ts
├── package.json
├── packages
└── core
│ ├── README.md
│ ├── eslint.config.mjs
│ ├── package.json
│ ├── src
│ ├── animation
│ │ ├── animation.ts
│ │ └── index.ts
│ ├── composers
│ │ ├── additive-pass.ts
│ │ ├── dimension-pass.ts
│ │ ├── index.ts
│ │ └── subctractive-pass.ts
│ ├── index.ts
│ ├── physics
│ │ ├── body-2d-animation.ts
│ │ ├── body-3d-animation.ts
│ │ ├── body-animation.ts
│ │ ├── index.ts
│ │ └── physics.ts
│ ├── primitives
│ │ ├── sine-wave
│ │ │ ├── index.ts
│ │ │ ├── sine-wave.ts
│ │ │ ├── sine-wave2d.ts
│ │ │ └── sine-wave3d.ts
│ │ └── spring
│ │ │ ├── index.ts
│ │ │ ├── spring.ts
│ │ │ ├── spring2d.ts
│ │ │ └── spring3d.ts
│ └── utils
│ │ └── interpolation.ts
│ ├── tsconfig.json
│ └── tsup.config.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── public
├── html5.svg
└── threejs.svg
├── release-please-config.json
├── turbo.json
└── web
├── .env.example
├── .prettierrc.mjs
├── astro.config.ts
├── eslint.config.mjs
├── package.json
├── public
├── banner.jpg
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon.ico
├── favicon.svg
├── fonts
│ └── MonaspaceNeon-Regular-v1.101.woff2
├── primitives
│ ├── accordion.jpg
│ ├── dialog.jpg
│ ├── disclosure.jpg
│ ├── drawer.jpg
│ ├── otp-field.jpg
│ ├── popover.jpg
│ ├── resizable.jpg
│ └── tooltip.jpg
├── readme
│ ├── arara.png
│ ├── solid-dismissible.png
│ ├── solid-focus-trap.png
│ ├── solid-list.png
│ ├── solid-persistent.png
│ ├── solid-presence.png
│ ├── solid-prevent-scroll.png
│ └── solid-transition-size.png
└── robots.txt
├── src
├── assets
│ ├── ararajs_banner.png
│ ├── examples
│ │ ├── arara_blue.jpg
│ │ ├── arara_red.jpg
│ │ └── arara_red_2.jpg
│ ├── global.css
│ ├── logo_dark.svg
│ ├── logo_light.svg
│ ├── res
│ │ ├── html_logo.png
│ │ ├── solidjs_logo.svg
│ │ ├── threejs_logo.svg
│ │ └── threejs_logo_dark.svg
│ ├── shiki.css
│ ├── title_with_logo_dark.svg
│ └── title_with_logo_light.svg
├── components
│ ├── Drawer.tsx
│ ├── Fonts.astro
│ ├── Head.astro
│ ├── HeaderLogo.astro
│ ├── ThemeScript.astro
│ ├── ThemeSelect.tsx
│ ├── Topbar.astro
│ └── docs
│ │ ├── Features.astro
│ │ ├── KeyboardNavigation.astro
│ │ ├── Link.astro
│ │ ├── PackageInfo.astro
│ │ ├── TableOfContents.tsx
│ │ ├── api
│ │ ├── ApiReference.astro
│ │ ├── items
│ │ │ ├── ApiItemChildrenProps.astro
│ │ │ ├── ApiItemComponent.astro
│ │ │ ├── ApiItemContext.astro
│ │ │ ├── ApiItemFunction.astro
│ │ │ ├── ApiItemInheritedComponent.astro
│ │ │ ├── ApiItemInheritedContext.astro
│ │ │ └── ApiItemSimple.astro
│ │ └── lists
│ │ │ ├── ApiProps.astro
│ │ │ ├── ApiReturns.astro
│ │ │ └── ApiTag.astro
│ │ ├── code
│ │ ├── Code.astro
│ │ ├── CopyToClipboard.tsx
│ │ └── RawCode.astro
│ │ ├── headings
│ │ ├── H2.astro
│ │ └── H3.astro
│ │ ├── nav
│ │ ├── NavLink.astro
│ │ └── Navigation.astro
│ │ ├── primitives
│ │ ├── Accordion.svg
│ │ ├── Dialog.svg
│ │ ├── Disclosure.svg
│ │ ├── Drawer.svg
│ │ ├── OtpField.svg
│ │ ├── Popover.svg
│ │ ├── PrimitivesOverview.astro
│ │ ├── Resizable.svg
│ │ └── Tooltip.svg
│ │ ├── search
│ │ ├── Search.tsx
│ │ ├── SearchDialog.tsx
│ │ ├── SearchDrawer.tsx
│ │ └── SearchItem.tsx
│ │ └── utilities
│ │ ├── UtilitiesOverview.astro
│ │ └── Utility.svg
├── env.d.ts
├── examples
│ ├── ExampleWrapper.tsx
│ ├── hover-card
│ │ ├── HoverCard.tsx
│ │ ├── createMouseDistanceFromCenter.ts
│ │ └── getDOMRootPosition.ts
│ ├── primitives
│ │ └── tailwind.js
│ ├── sine-wave
│ │ ├── SineWave2DDemo.tsx
│ │ ├── SineWaveDemo.tsx
│ │ └── SliderSineWaveDemo.tsx
│ ├── spring
│ │ ├── Spring2DDemo.tsx
│ │ ├── SpringDemo.tsx
│ │ ├── createMouseDistanceFromCenter.ts
│ │ └── getDOMRootPosition.ts
│ └── utilities
│ │ └── list
│ │ └── multi.tsx
├── layouts
│ └── Docs.astro
├── lib
│ ├── cn.ts
│ ├── packageMetas.ts
│ └── typedoc
│ │ ├── headings.ts
│ │ ├── libraries.ts
│ │ ├── resolve
│ │ ├── lib.ts
│ │ ├── resolve.ts
│ │ ├── resolveChildrenProps.ts
│ │ ├── resolveComponent.ts
│ │ ├── resolveContext.ts
│ │ ├── resolveFunction.ts
│ │ ├── resolveInheritedComponent.ts
│ │ ├── resolveInheritedContext.ts
│ │ └── resolveSimple.ts
│ │ └── types
│ │ ├── apiReferences.ts
│ │ ├── specifications.ts
│ │ └── typedoc.ts
└── pages
│ ├── 404.astro
│ ├── docs
│ ├── animation
│ │ ├── animation-callback.mdx
│ │ ├── animation-controller.mdx
│ │ ├── body.mdx
│ │ └── overview.mdx
│ ├── examples
│ │ └── hover-card.mdx
│ ├── getting-started.mdx
│ ├── index.mdx
│ ├── overview.mdx
│ └── primitives
│ │ ├── sine-wave.mdx
│ │ └── spring.mdx
│ └── index.astro
├── tailwind.config.js
├── tsconfig.json
├── typesense.config.json
└── vercel.json
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: FelipeEmos
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: '🐛 Bug report'
2 | description: Create a report to help improve arara
3 | title: "[Bug]: "
4 | labels: ["bug (unverified)"]
5 | assignees:
6 | - GiyoMoon
7 | body:
8 | - type: markdown
9 | attributes:
10 | value: |
11 | Thank you for reporting a bug to arara :pray::purple_heart:
12 |
13 | Before submitting a new bug/issue, please make sure to search for existing issues to avoid submitting duplicates:
14 |
15 | - [open issues](https://github.com/FelipeEmos/arara/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated)
16 | - [closed issues](https://github.com/FelipeEmos/arara/issues?q=is%3Aissue+is%3Aclosed+sort%3Aupdated+)
17 | - [arara discussions](https://github.com/FelipeEmos/arara/discussions)
18 | - type: textarea
19 | id: description
20 | attributes:
21 | label: Bug description
22 | description: Please provide a clear description of what the bug is.
23 | validations:
24 | required: true
25 | - type: input
26 | id: link
27 | attributes:
28 | label: Reproduction Link
29 |
30 | description: |
31 | Provide a link to a live example, or a repository that can reproduce the bug. We can analyze and fix the bug much faster if you provide a clear and minimal example.
32 | # CORVU description
33 | # description: |
34 | # Provide a link to a live example, or a repository that can reproduce the bug. We can analyze and fix the bug much faster if you provide a clear and minimal example. Please read these tips for providing a minimal example: https://stackoverflow.com/help/mcve.
35 |
36 | # You can use [Stackblitz](https://stackblitz.com/) to create a sharable reproduction. [Here](https://stackblitz.com/edit/arara?file=src%2FApp.tsx) is a template with `solid` and `arara` already set up which you can use. A public GitHub repository also works.
37 | placeholder: |
38 | https://stackblitz.com/edit/... or a public GitHub repository
39 | validations:
40 | required: true
41 | - type: textarea
42 | id: steps
43 | attributes:
44 | label: Reproduction Steps
45 | description: Describe how we can reproduce the bug with the provided link.
46 | placeholder: |
47 | 1. Click on ...
48 | 2. Open the console
49 | 3. See the error message
50 | validations:
51 | required: true
52 | - type: textarea
53 | id: expected
54 | attributes:
55 | label: Expected behavior
56 | description: Provide a clear description of what you expected to happen.
57 | placeholder: |
58 | I expected ... but ... is happening instead.
59 | validations:
60 | required: true
61 | - type: textarea
62 | id: additional
63 | attributes:
64 | label: Additional context
65 | description: Add any other context about the bug here.
66 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: true
2 |
--------------------------------------------------------------------------------
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: docs
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | docs:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Check out
13 | uses: actions/checkout@v4
14 |
15 | - name: Install Vercel CLI
16 | run: npm install -g vercel@37.8.0
17 |
18 | - name: Deploy to vercel
19 | uses: BetaHuhn/deploy-to-vercel-action@v1
20 | with:
21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22 | VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
23 | VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
24 | VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
25 |
26 | - name: Typesense scraper
27 | uses: celsiusnarhwal/typesense-scraper@v2
28 | with:
29 | api-key: ${{ secrets.TYPESENSE_API_KEY }}
30 | host: search.ararajs.vercel.app
31 | port: 443
32 | protocol: https
33 | config: ./web/typesense.config.json
34 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: lint
2 |
3 | on:
4 | push:
5 | branches:
6 | - '**'
7 | pull_request:
8 |
9 | concurrency: ${{ github.workflow }}-${{ github.ref }}
10 |
11 | jobs:
12 | lint:
13 | name: Lint
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Check out
17 | uses: actions/checkout@v4
18 |
19 | - name: Install nodejs
20 | uses: actions/setup-node@v4
21 | with:
22 | node-version: lts/*
23 |
24 | - name: Install pnpm
25 | uses: pnpm/action-setup@v3
26 | with:
27 | version: latest
28 | run_install: false
29 |
30 | - name: Get pnpm store dir
31 | id: pnpm-cache
32 | shell: bash
33 | run: |
34 | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
35 |
36 | - name: pnpm cache
37 | uses: actions/cache@v4
38 | with:
39 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
40 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
41 | restore-keys: |
42 | ${{ runner.os }}-pnpm-store-
43 |
44 | - name: Install dependencies
45 | run: |
46 | pnpm install
47 |
48 | - name: Lint
49 | run: |
50 | pnpm lint
51 |
--------------------------------------------------------------------------------
/.github/workflows/release-please.yml:
--------------------------------------------------------------------------------
1 | name: release-please
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | permissions:
9 | packages: write
10 | contents: write
11 | pull-requests: write
12 | actions: read
13 |
14 | jobs:
15 | release-please:
16 | runs-on: ubuntu-latest
17 | steps:
18 | - name: release-please
19 | id: release-please
20 | # uses: google-github-actions/release-please-action@v4
21 | uses: googleapis/release-please-action@v4
22 |
23 | - name: Check out
24 | if: ${{ steps.release-please.outputs.releases_created == 'true' }}
25 | uses: actions/checkout@v4
26 |
27 | - name: Install nodejs
28 | if: ${{ steps.release-please.outputs.releases_created == 'true' }}
29 | uses: actions/setup-node@v4
30 | with:
31 | node-version: lts/*
32 |
33 | - name: Install pnpm
34 | if: ${{ steps.release-please.outputs.releases_created == 'true' }}
35 | uses: pnpm/action-setup@v3
36 | with:
37 | version: latest
38 | run_install: false
39 |
40 | - name: Get pnpm store dir
41 | if: ${{ steps.release-please.outputs.releases_created == 'true' }}
42 | id: pnpm-cache
43 | shell: bash
44 | run: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
45 |
46 | - name: pnpm cache
47 | if: ${{ steps.release-please.outputs.releases_created == 'true' }}
48 | uses: actions/cache@v4
49 | with:
50 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
51 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
52 | restore-keys: |
53 | ${{ runner.os }}-pnpm-store-
54 |
55 | - name: Install dependencies
56 | if: ${{ steps.release-please.outputs.releases_created == 'true' }}
57 | run: pnpm install --frozen-lockfile
58 |
59 | - name: Set pnpm auth token
60 | if: ${{ steps.release-please.outputs.releases_created == 'true' }}
61 | run: pnpm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
62 | env:
63 | NPM_TOKEN: ${{secrets.NPM_TOKEN}}
64 |
65 | - name: Publish to npm
66 | if: ${{ steps.release-please.outputs.releases_created == 'true' }}
67 | run: pnpm ci:publish
68 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .astro
2 | .env
3 | .turbo
4 | dist
5 | node_modules
6 | packages/*/api.json
7 | tsup.config.*.mjs
8 |
--------------------------------------------------------------------------------
/.release-please-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages/core": "0.0.1"
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023-2024 Felipe Emos
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
12 |
13 | **[Documentation](https://ararajs.vercel.app) • [Discussions](https://github.com/FelipeEmos/ararajs/discussions) • [Discord](https://discord.com/invite/solidjs)**
14 |
15 |
16 |
17 | ## About
18 |
19 | AraraJs is a highly reactive signals-based Animation Library for SolidJS
20 |
21 | - 📡 Signal Based Primitive Animations
22 | - 🧩 Compose primitives to create new animations and emergent behaviors
23 | - 🫥 Render Agonostic
24 | - DOM
25 | - Three JS scenes
26 | - Any Javascript code
27 | - 🏗️ Under the hood it
28 | - Uses `gl-matrix` for vector math
29 | - Uses the best performant web api for this animation use case: RAF (_request animation frame_)
30 |
31 | Read more at [ararajs.vercel.app](https://ararajs.vercel.app).
32 |
33 |
55 |
--------------------------------------------------------------------------------
/dev/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import pluginPrettier from 'eslint-plugin-prettier/recommended'
2 | import pluginSolid from 'eslint-plugin-solid'
3 | import pluginTailwind from 'eslint-plugin-tailwindcss'
4 | import pluginTypescript from 'typescript-eslint'
5 | import tsParser from '@typescript-eslint/parser'
6 |
7 | export default [
8 | {
9 | ignores: ['dist/'],
10 | },
11 | ...pluginTypescript.configs.recommended,
12 | ...pluginTailwind.configs['flat/recommended'],
13 | pluginPrettier,
14 | {
15 | files: ['**/*.{ts,tsx}'],
16 | ...pluginSolid.configs['flat/typescript'],
17 | languageOptions: {
18 | parser: tsParser,
19 | parserOptions: {
20 | project: true,
21 | },
22 | },
23 | },
24 | {
25 | languageOptions: {
26 | parser: tsParser,
27 | },
28 | rules: {
29 | 'no-console': 'warn',
30 |
31 | 'sort-imports': [
32 | 'warn',
33 | {
34 | ignoreCase: true,
35 | },
36 | ],
37 |
38 | '@typescript-eslint/no-unused-vars': [
39 | 'warn',
40 | {
41 | argsIgnorePattern: '^_',
42 | varsIgnorePattern: '^_',
43 | caughtErrorsIgnorePattern: '^_',
44 | },
45 | ],
46 |
47 | '@typescript-eslint/method-signature-style': 'error',
48 | '@typescript-eslint/no-empty-object-type': 'off',
49 | '@typescript-eslint/no-wrapper-object-types': 'error',
50 | '@typescript-eslint/triple-slash-reference': 'off',
51 |
52 | '@typescript-eslint/no-unused-expressions': [
53 | 'error',
54 | {
55 | allowShortCircuit: true,
56 | },
57 | ],
58 |
59 | 'prettier/prettier': [
60 | 'error',
61 | {
62 | singleQuote: true,
63 | jsxSingleQuote: false,
64 | semi: false,
65 | },
66 | ],
67 |
68 | 'tailwindcss/classnames-order': 'error',
69 | 'tailwindcss/enforces-negative-arbitrary-values': 'error',
70 | 'tailwindcss/enforces-shorthand': 'error',
71 | 'tailwindcss/migration-from-tailwind-2': 'error',
72 | 'tailwindcss/no-custom-classname': 'error',
73 |
74 | 'solid/reactivity': 'off',
75 | },
76 | },
77 | {
78 | files: ['**/*.{ts,tsx}'],
79 | languageOptions: {
80 | parserOptions: {
81 | project: true,
82 | },
83 | },
84 | rules: {
85 | '@typescript-eslint/no-unnecessary-condition': 'error',
86 | '@typescript-eslint/strict-boolean-expressions': 'error',
87 | },
88 | },
89 | ]
90 |
--------------------------------------------------------------------------------
/dev/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | arara
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/dev/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@arara/dev",
3 | "private": true,
4 | "description": "arara development environment",
5 | "homepage": "https://ararajs.vercel.app/",
6 | "license": "MIT",
7 | "author": {
8 | "name": "Felipe Emos",
9 | "email": "felipe.emos.computacao@gmail.com",
10 | "url": "https://github.com/FelipeEmos"
11 | },
12 | "type": "module",
13 | "scripts": {
14 | "build": "vite build",
15 | "clean": "rm -rf .turbo dist node_modules",
16 | "dev": "vite dev",
17 | "lint": "eslint --max-warnings=0 .",
18 | "preview": "vite preview"
19 | },
20 | "dependencies": {
21 | "clsx": "^2.1.1",
22 | "solid-js": "^1.9.2"
23 | },
24 | "devDependencies": {
25 | "@typescript-eslint/parser": "^8.9.0",
26 | "@corvu/tailwind": "^0.1.5",
27 | "autoprefixer": "^10.4.20",
28 | "eslint": "^9.12.0",
29 | "eslint-config-prettier": "^9.1.0",
30 | "eslint-plugin-prettier": "^5.2.1",
31 | "eslint-plugin-solid": "^0.14.3",
32 | "eslint-plugin-tailwindcss": "^3.17.5",
33 | "postcss": "^8.4.47",
34 | "prettier": "^3.3.3",
35 | "tailwindcss": "^3.4.14",
36 | "typescript-eslint": "^8.9.0",
37 | "vite": "^5.4.9",
38 | "vite-plugin-solid": "^2.10.2",
39 | "vite-tsconfig-paths": "^5.0.1"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/dev/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/dev/src/AraraLab.tsx:
--------------------------------------------------------------------------------
1 | export default () => null
2 |
--------------------------------------------------------------------------------
/dev/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/dev/src/index.tsx:
--------------------------------------------------------------------------------
1 | import './index.css'
2 | import AraraLab from './AraraLab'
3 | import { render } from 'solid-js/web'
4 |
5 | render(() => , document.getElementById('root')!)
6 |
--------------------------------------------------------------------------------
/dev/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "ESNext",
4 | "target": "ESNext",
5 | "newLine": "LF",
6 | "lib": [
7 | "DOM",
8 | "DOM.Iterable",
9 | "ESNext"
10 | ],
11 | "moduleResolution": "Bundler",
12 | "noEmit": true,
13 | "allowSyntheticDefaultImports": true,
14 | "strict": true,
15 | "strictNullChecks": true,
16 | "esModuleInterop": true,
17 | "isolatedModules": true,
18 | "forceConsistentCasingInFileNames": true,
19 | "noUncheckedIndexedAccess": true,
20 | "skipLibCheck": true,
21 | "jsx": "preserve",
22 | "jsxImportSource": "solid-js",
23 | },
24 | "exclude": [
25 | "node_modules",
26 | "dist"
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/dev/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import solid from 'vite-plugin-solid'
3 | import tsConfigPaths from 'vite-tsconfig-paths'
4 |
5 | export default defineConfig({
6 | plugins: [solid(), tsConfigPaths()],
7 | })
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ararajs/monorepo",
3 | "private": true,
4 | "description": "SolidJS animation primitives using signals, useful for procedural and highly reactive animations",
5 | "homepage": "https://ararajs.vercel.app/",
6 | "license": "MIT",
7 | "author": {
8 | "name": "Felipe Emos",
9 | "email": "felipe.emos.computacao@gmail.com",
10 | "url": "https://github.com/FelipeEmos"
11 | },
12 | "scripts": {
13 | "build": "turbo run build",
14 | "ci:publish": "pnpm build && pnpm publish -r --access public",
15 | "clean": "turbo run clean && rm -rf .turbo node_modules",
16 | "dev:arara": "turbo watch dev --filter=@ararajs/dev",
17 | "dev:web": "turbo watch dev --filter=@ararajs/web",
18 | "lint": "turbo run lint",
19 | "preview:web": "turbo run preview --filter=@ararajs/web",
20 | "rp": "release-please"
21 | },
22 | "devDependencies": {
23 | "release-please": "^16.14.3",
24 | "turbo": "^2.1.3"
25 | },
26 | "packageManager": "pnpm@9.3.0"
27 | }
28 |
--------------------------------------------------------------------------------
/packages/core/README.md:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
12 |
13 | **[Documentation](https://ararajs.vercel.app) • [Discussions](https://github.com/FelipeEmos/ararajs/discussions) • [Discord](https://discord.com/invite/solidjs)**
14 |
15 |
16 |
17 | ## About
18 |
19 | AraraJs is a highly reactive signals-based Animation Library for SolidJS
20 |
21 | - 📡 Signal Based Primitive Animations
22 | - 🧩 Compose primitives to create new animations and emergent behaviors
23 | - 🫥 Render Agonostic
24 | - DOM
25 | - Three JS scenes
26 | - Any Javascript code
27 | - 🏗️ Under the hood it
28 | - Uses `gl-matrix` for vector math
29 | - Uses the best performant web api for this animation use case: RAF (_request animation frame_)
30 |
31 | Read more at [ararajs.vercel.app](https://ararajs.vercel.app).
32 |
33 |
55 |
--------------------------------------------------------------------------------
/packages/core/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import pluginPrettier from 'eslint-plugin-prettier/recommended'
2 | import pluginSolid from 'eslint-plugin-solid'
3 | import pluginTypescript from 'typescript-eslint'
4 | import tsParser from '@typescript-eslint/parser'
5 |
6 | export default [
7 | {
8 | ignores: ['dist/', 'tsup.config.*.mjs'],
9 | },
10 | ...pluginTypescript.configs.recommended,
11 | pluginPrettier,
12 | {
13 | files: ['**/*.{ts,tsx}'],
14 | ...pluginSolid.configs['flat/typescript'],
15 | languageOptions: {
16 | parser: tsParser,
17 | parserOptions: {
18 | project: true,
19 | },
20 | },
21 | },
22 | {
23 | languageOptions: {
24 | parser: tsParser,
25 | },
26 | rules: {
27 | 'no-console': 'warn',
28 |
29 | 'sort-imports': [
30 | 'warn',
31 | {
32 | ignoreCase: true,
33 | },
34 | ],
35 |
36 | '@typescript-eslint/no-unused-vars': [
37 | 'warn',
38 | {
39 | argsIgnorePattern: '^_',
40 | varsIgnorePattern: '^_',
41 | caughtErrorsIgnorePattern: '^_',
42 | },
43 | ],
44 |
45 | '@typescript-eslint/method-signature-style': 'error',
46 | '@typescript-eslint/no-empty-object-type': 'error',
47 | '@typescript-eslint/no-wrapper-object-types': 'error',
48 | '@typescript-eslint/triple-slash-reference': 'off',
49 |
50 | '@typescript-eslint/no-unused-expressions': [
51 | 'error',
52 | {
53 | allowShortCircuit: true,
54 | },
55 | ],
56 |
57 | 'prettier/prettier': [
58 | 'error',
59 | {
60 | singleQuote: true,
61 | jsxSingleQuote: false,
62 | semi: false,
63 | },
64 | ],
65 |
66 | 'solid/reactivity': 'off',
67 | },
68 | },
69 | {
70 | files: ['**/*.{ts,tsx}'],
71 | languageOptions: {
72 | parserOptions: {
73 | project: true,
74 | },
75 | },
76 | rules: {
77 | '@typescript-eslint/no-unnecessary-condition': 'error',
78 | '@typescript-eslint/strict-boolean-expressions': 'error',
79 | },
80 | },
81 | ]
82 |
--------------------------------------------------------------------------------
/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ararajs",
3 | "version": "0.0.1",
4 | "private": false,
5 | "description": "SolidJS animation primitives using signals, useful for procedural and highly reactive animations",
6 | "keywords": [
7 | "solid",
8 | "solidjs",
9 | "animation",
10 | "procedural",
11 | "physics",
12 | "primitives"
13 | ],
14 | "homepage": "https://ararajs.vercel.app/",
15 | "bugs": {
16 | "url": "https://github.com/FelipeEmos/arara/issues",
17 | "email": "felipe.emos.computacao@gmail.com"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/FelipeEmos/arara.git"
22 | },
23 | "license": "MIT",
24 | "author": {
25 | "name": "Felipe Emos",
26 | "email": "felipe.emos.computacao@gmail.com",
27 | "url": "https://github.com/FelipeEmos"
28 | },
29 | "sideEffects": false,
30 | "type": "module",
31 | "exports": {
32 | ".": {
33 | "types": "./dist/index.d.ts",
34 | "solid": "./dist/index.jsx",
35 | "default": "./dist/index.js"
36 | },
37 | "./*": {
38 | "types": "./dist/*.d.ts",
39 | "solid": "./dist/*.jsx",
40 | "default": "./dist/*.js"
41 | }
42 | },
43 | "typesVersions": {
44 | "*": {
45 | "*": [
46 | "./dist/*.d.ts"
47 | ]
48 | }
49 | },
50 | "files": [
51 | "dist"
52 | ],
53 | "scripts": {
54 | "build": "tsup",
55 | "clean": "rm -rf .turbo dist node_modules",
56 | "dev": "tsup --watch",
57 | "lint": "eslint --max-warnings=0 .",
58 | "lint:fix": "eslint --fix 'src/**/*.{js,cjs,mjs,ts,tsx}'",
59 | "typedoc": "typedoc --json api.json --entryPoints ./src/index.ts"
60 | },
61 | "dependencies": {
62 | "gl-matrix": "^3.4.3"
63 | },
64 | "devDependencies": {
65 | "@typescript-eslint/parser": "^8.9.0",
66 | "esbuild-plugin-solid": "^0.6.0",
67 | "eslint": "^9.12.0",
68 | "eslint-config-prettier": "^9.1.0",
69 | "eslint-plugin-prettier": "^5.2.1",
70 | "eslint-plugin-solid": "^0.14.3",
71 | "prettier": "^3.3.3",
72 | "solid-js": "^1.9.2",
73 | "tsup": "^8.3.0",
74 | "typedoc": "^0.26.10",
75 | "typescript": "^5.6.3",
76 | "typescript-eslint": "^8.9.0"
77 | },
78 | "peerDependencies": {
79 | "solid-js": "^1.8"
80 | }
81 | }
--------------------------------------------------------------------------------
/packages/core/src/animation/index.ts:
--------------------------------------------------------------------------------
1 | export * from './animation'
2 |
--------------------------------------------------------------------------------
/packages/core/src/composers/additive-pass.ts:
--------------------------------------------------------------------------------
1 | import { vec2, vec3 } from 'gl-matrix'
2 | import { type Body2DAnimationPass } from '../physics/body-2d-animation'
3 | import { type Body3DAnimationPass } from '../physics/body-3d-animation'
4 | import { type BodyAnimationPass } from '../physics/body-animation'
5 |
6 | export function additivePass(rawPass: BodyAnimationPass): BodyAnimationPass {
7 | return (world) => {
8 | const { body } = world
9 | const rawResult = rawPass(world)
10 | return {
11 | position: body.position + rawResult.position,
12 | velocity: body.velocity + rawResult.velocity,
13 | acceleration: body.acceleration + rawResult.acceleration,
14 | }
15 | }
16 | }
17 |
18 | export function additive2DPass(
19 | rawPass: Body2DAnimationPass,
20 | ): Body2DAnimationPass {
21 | return (world) => {
22 | const { body } = world
23 | const rawResult = rawPass(world)
24 | return {
25 | position: vec2.add([0, 0], body.position, rawResult.position),
26 | velocity: vec2.add([0, 0], body.velocity, rawResult.velocity),
27 | acceleration: vec2.add([0, 0], body.acceleration, rawResult.acceleration),
28 | }
29 | }
30 | }
31 |
32 | export function additive3DPass(
33 | rawPass: Body3DAnimationPass,
34 | ): Body3DAnimationPass {
35 | return (world) => {
36 | const { body } = world
37 | const rawResult = rawPass(world)
38 | return {
39 | position: vec3.add([0, 0, 0], body.position, rawResult.position),
40 | velocity: vec3.add([0, 0, 0], body.velocity, rawResult.velocity),
41 | acceleration: vec3.add(
42 | [0, 0, 0],
43 | body.acceleration,
44 | rawResult.acceleration,
45 | ),
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/core/src/composers/dimension-pass.ts:
--------------------------------------------------------------------------------
1 | import { type Body, sliceDimension } from '../physics/physics'
2 | import { type Body2DAnimationPass } from '../physics/body-2d-animation'
3 | import { type Body3DAnimationPass } from '../physics/body-3d-animation'
4 | import { type BodyAnimationPass } from '../physics/body-animation'
5 |
6 | const BLANK_BODY = {
7 | position: 0,
8 | velocity: 0,
9 | acceleration: 0,
10 | } as const satisfies Body
11 |
12 | export function compose2DPass({
13 | x: xPass,
14 | y: yPass,
15 | }: {
16 | x?: BodyAnimationPass
17 | y?: BodyAnimationPass
18 | }): Body2DAnimationPass {
19 | return (world) => {
20 | const xRes =
21 | xPass?.({
22 | ...world,
23 | body: sliceDimension('x', world.body),
24 | }) ?? BLANK_BODY
25 |
26 | const yRes =
27 | yPass?.({
28 | ...world,
29 | body: sliceDimension('y', world.body),
30 | }) ?? BLANK_BODY
31 |
32 | return {
33 | position: [xRes.position, yRes.position],
34 | velocity: [xRes.velocity, yRes.velocity],
35 | acceleration: [xRes.acceleration, yRes.acceleration],
36 | }
37 | }
38 | }
39 |
40 | export function compose3DPass({
41 | x: xPass,
42 | y: yPass,
43 | z: zPass,
44 | }: {
45 | x?: BodyAnimationPass
46 | y?: BodyAnimationPass
47 | z?: BodyAnimationPass
48 | }): Body3DAnimationPass {
49 | return (world) => {
50 | const xRes =
51 | xPass?.({
52 | ...world,
53 | body: sliceDimension('x', world.body),
54 | }) ?? BLANK_BODY
55 |
56 | const yRes =
57 | yPass?.({
58 | ...world,
59 | body: sliceDimension('y', world.body),
60 | }) ?? BLANK_BODY
61 |
62 | const zRes =
63 | zPass?.({
64 | ...world,
65 | body: sliceDimension('z', world.body),
66 | }) ?? BLANK_BODY
67 |
68 | return {
69 | position: [xRes.position, yRes.position, zRes.position],
70 | velocity: [xRes.velocity, yRes.velocity, zRes.velocity],
71 | acceleration: [xRes.acceleration, yRes.acceleration, zRes.acceleration],
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/packages/core/src/composers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './additive-pass'
2 | export * from './subctractive-pass'
3 | export * from './dimension-pass'
4 |
--------------------------------------------------------------------------------
/packages/core/src/composers/subctractive-pass.ts:
--------------------------------------------------------------------------------
1 | import { vec2, vec3 } from 'gl-matrix'
2 | import { type Body2DAnimationPass } from '../physics/body-2d-animation'
3 | import { type Body3DAnimationPass } from '../physics/body-3d-animation'
4 | import { type BodyAnimationPass } from '../physics/body-animation'
5 |
6 | export function subtractivePass(rawPass: BodyAnimationPass): BodyAnimationPass {
7 | return (world) => {
8 | const { body } = world
9 | const rawResult = rawPass(world)
10 | return {
11 | position: body.position - rawResult.position,
12 | velocity: body.velocity - rawResult.velocity,
13 | acceleration: body.acceleration - rawResult.acceleration,
14 | }
15 | }
16 | }
17 |
18 | export function subtractive2DPass(
19 | rawPass: Body2DAnimationPass,
20 | ): Body2DAnimationPass {
21 | return (world) => {
22 | const { body } = world
23 | const rawResult = rawPass(world)
24 | return {
25 | position: vec2.sub([0, 0], body.position, rawResult.position),
26 | velocity: vec2.sub([0, 0], body.velocity, rawResult.velocity),
27 | acceleration: vec2.sub([0, 0], body.acceleration, rawResult.acceleration),
28 | }
29 | }
30 | }
31 |
32 | export function subtractive3DPass(
33 | rawPass: Body3DAnimationPass,
34 | ): Body3DAnimationPass {
35 | return (world) => {
36 | const { body } = world
37 | const rawResult = rawPass(world)
38 | return {
39 | position: vec3.sub([0, 0, 0], body.position, rawResult.position),
40 | velocity: vec3.sub([0, 0, 0], body.velocity, rawResult.velocity),
41 | acceleration: vec3.sub(
42 | [0, 0, 0],
43 | body.acceleration,
44 | rawResult.acceleration,
45 | ),
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/core/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './animation'
2 | export * from './physics'
3 | export * from './composers'
4 |
5 | // Primitives
6 | export * from './primitives/sine-wave'
7 | export * from './primitives/spring'
8 |
9 | // Utilities
10 | export * from './utils/interpolation'
11 |
12 | export { vec2, vec3 } from 'gl-matrix'
13 |
--------------------------------------------------------------------------------
/packages/core/src/physics/body-2d-animation.ts:
--------------------------------------------------------------------------------
1 | import {
2 | type AnimationCallback,
3 | type AnimationController,
4 | type AnimationControllerGenerator,
5 | useOrCreateAnimationController,
6 | VOID_ANIMATION_CONTROLLER,
7 | } from '../animation/animation'
8 | import { createEffect, createMemo, untrack } from 'solid-js'
9 | import { createStore, unwrap } from 'solid-js/store'
10 | import type { Body2D } from './physics'
11 |
12 | export type Body2DAnimationPass = (world: {
13 | body: Body2D
14 | currentTime: number
15 | deltaTime: number
16 | }) => Body2D
17 |
18 | export type Body2DAnimationOptions = {
19 | initialConditions?: Body2D
20 | animationController?: AnimationControllerGenerator
21 | }
22 |
23 | export function createBody2DAnimation(
24 | kinematicAnimationPasses: () => Body2DAnimationPass[],
25 | options?: () => Body2DAnimationOptions,
26 | ) {
27 | const [body, setBody2D] = createStore({
28 | position: [0, 0],
29 | velocity: [0, 0],
30 | acceleration: [0, 0],
31 | // TODO: subscribe to Stop event and reset the body to intialConditions
32 | ...options?.().initialConditions,
33 | })
34 |
35 | const animationCallbacks = createMemo(() => {
36 | const list: AnimationCallback[] = []
37 |
38 | for (const pass of kinematicAnimationPasses()) {
39 | const callback: AnimationCallback = (world) => {
40 | const beforePass = unwrap(body)
41 | const afterPass = pass({ body: beforePass, ...world })
42 | setBody2D(afterPass)
43 | }
44 | list.push(callback)
45 | }
46 |
47 | return list
48 | })
49 |
50 | const animationController = useOrCreateAnimationController(
51 | options?.().animationController,
52 | )
53 |
54 | type SyncEffectDependencies = {
55 | controller: AnimationController
56 | callbacks: AnimationCallback[]
57 | }
58 |
59 | createEffect(
60 | (lastEffect) => {
61 | const { controller: lastController, callbacks: lastCallbacks } =
62 | lastEffect
63 | untrack(() => {
64 | lastCallbacks.forEach((cb) => {
65 | lastController.remove(cb)
66 | })
67 | })
68 |
69 | const controller = animationController()
70 | const callbacks = animationCallbacks()
71 | untrack(() => {
72 | callbacks.forEach((cb) => {
73 | controller.add(cb)
74 | })
75 | })
76 | return { controller, callbacks }
77 | },
78 | {
79 | controller: VOID_ANIMATION_CONTROLLER,
80 | callbacks: [],
81 | },
82 | )
83 |
84 | return [body, animationController] as const
85 | }
86 |
--------------------------------------------------------------------------------
/packages/core/src/physics/body-3d-animation.ts:
--------------------------------------------------------------------------------
1 | import {
2 | type AnimationCallback,
3 | type AnimationController,
4 | type AnimationControllerGenerator,
5 | useOrCreateAnimationController,
6 | VOID_ANIMATION_CONTROLLER,
7 | } from '../animation/animation'
8 | import { createEffect, createMemo, untrack } from 'solid-js'
9 | import { createStore, unwrap } from 'solid-js/store'
10 | import { type Body3D } from './physics'
11 |
12 | export type Body3DAnimationPass = (world: {
13 | body: Body3D
14 | currentTime: number
15 | deltaTime: number
16 | }) => Body3D
17 |
18 | export type Body3DAnimationOptions = {
19 | initialConditions?: Body3D
20 | animationController?: AnimationControllerGenerator
21 | }
22 |
23 | export function createBody3DAnimation(
24 | kinematicAnimationPasses: () => Body3DAnimationPass[],
25 | options?: () => Body3DAnimationOptions,
26 | ) {
27 | const [body, setBody3D] = createStore({
28 | position: [0, 0, 0],
29 | velocity: [0, 0, 0],
30 | acceleration: [0, 0, 0],
31 | // TODO: subscribe to Stop event and reset the body to intialConditions
32 | ...options?.().initialConditions,
33 | })
34 |
35 | const animationCallbacks = createMemo(() => {
36 | const list: AnimationCallback[] = []
37 |
38 | for (const pass of kinematicAnimationPasses()) {
39 | const callback: AnimationCallback = (world) => {
40 | const beforePass = unwrap(body)
41 | const afterPass = pass({ body: beforePass, ...world })
42 | setBody3D(afterPass)
43 | }
44 | list.push(callback)
45 | }
46 |
47 | return list
48 | })
49 |
50 | const animationController = useOrCreateAnimationController(
51 | options?.().animationController,
52 | )
53 |
54 | type SyncEffectDependencies = {
55 | controller: AnimationController
56 | callbacks: AnimationCallback[]
57 | }
58 |
59 | createEffect(
60 | (lastEffect) => {
61 | const { controller: lastController, callbacks: lastCallbacks } =
62 | lastEffect
63 | untrack(() => {
64 | lastCallbacks.forEach((cb) => {
65 | lastController.remove(cb)
66 | })
67 | })
68 |
69 | const controller = animationController()
70 | const callbacks = animationCallbacks()
71 | untrack(() => {
72 | callbacks.forEach((cb) => {
73 | controller.add(cb)
74 | })
75 | })
76 | return { controller, callbacks }
77 | },
78 | {
79 | controller: VOID_ANIMATION_CONTROLLER,
80 | callbacks: [],
81 | },
82 | )
83 |
84 | return [body, animationController] as const
85 | }
86 |
--------------------------------------------------------------------------------
/packages/core/src/physics/body-animation.ts:
--------------------------------------------------------------------------------
1 | import {
2 | type AnimationCallback,
3 | type AnimationController,
4 | type AnimationControllerGenerator,
5 | useOrCreateAnimationController,
6 | VOID_ANIMATION_CONTROLLER,
7 | } from '../animation/animation'
8 | import { createEffect, createMemo, untrack } from 'solid-js'
9 | import { createStore, unwrap } from 'solid-js/store'
10 | import { type Body } from './physics'
11 |
12 | export type BodyAnimationPass = (world: {
13 | body: Body
14 | currentTime: number
15 | deltaTime: number
16 | }) => Body
17 |
18 | export type BodyAnimationOptions = {
19 | initialConditions?: Body
20 | animationController?: AnimationControllerGenerator
21 | }
22 |
23 | export function createBodyAnimation(
24 | kinematicAnimationPasses: () => BodyAnimationPass[],
25 | options?: () => BodyAnimationOptions,
26 | ) {
27 | const [body, setBody] = createStore({
28 | position: 0,
29 | velocity: 0,
30 | acceleration: 0,
31 | // TODO: subscribe to Stop event and reset the body to intialConditions
32 | ...options?.().initialConditions,
33 | })
34 |
35 | const animationCallbacks = createMemo(() => {
36 | const list: AnimationCallback[] = []
37 |
38 | for (const pass of kinematicAnimationPasses()) {
39 | const callback: AnimationCallback = (world) => {
40 | const beforePass = unwrap(body)
41 | const afterPass = pass({ body: beforePass, ...world })
42 | setBody(afterPass)
43 | }
44 | list.push(callback)
45 | }
46 |
47 | return list
48 | })
49 |
50 | const animationController = useOrCreateAnimationController(
51 | options?.().animationController,
52 | )
53 |
54 | type SyncEffectDependencies = {
55 | controller: AnimationController
56 | callbacks: AnimationCallback[]
57 | }
58 |
59 | createEffect(
60 | (lastEffect) => {
61 | const { controller: lastController, callbacks: lastCallbacks } =
62 | lastEffect
63 | untrack(() => {
64 | lastCallbacks.forEach((cb) => {
65 | lastController.remove(cb)
66 | })
67 | })
68 |
69 | const controller = animationController()
70 | const callbacks = animationCallbacks()
71 | untrack(() => {
72 | callbacks.forEach((cb) => {
73 | controller.add(cb)
74 | })
75 | })
76 | return { controller, callbacks }
77 | },
78 | {
79 | controller: VOID_ANIMATION_CONTROLLER,
80 | callbacks: [],
81 | },
82 | )
83 |
84 | return [body, animationController] as const
85 | }
86 |
--------------------------------------------------------------------------------
/packages/core/src/physics/index.ts:
--------------------------------------------------------------------------------
1 | export * from './body-animation'
2 | export * from './body-2d-animation'
3 | export * from './body-3d-animation'
4 |
--------------------------------------------------------------------------------
/packages/core/src/physics/physics.ts:
--------------------------------------------------------------------------------
1 | import { vec2, vec3 } from 'gl-matrix'
2 |
3 | export type Body = {
4 | position: number
5 | velocity: number
6 | acceleration: number
7 | }
8 |
9 | export type Body2D = {
10 | position: vec2
11 | velocity: vec2
12 | acceleration: vec2
13 | }
14 |
15 | export type Body3D = {
16 | position: vec3
17 | velocity: vec3
18 | acceleration: vec3
19 | }
20 |
21 | export type Dimension = 0 | 1 | 2 | 'x' | 'y' | 'z'
22 | export function getAxis(dimension: Dimension): number {
23 | if (typeof dimension === 'number') return dimension
24 | if (dimension === 'x') return 0
25 | if (dimension === 'y') return 1
26 | return 2
27 | }
28 |
29 | export function sliceDimension(dimension: Dimension, body: Body2D): Body
30 | export function sliceDimension(dimension: Dimension, body: Body3D): Body
31 | export function sliceDimension(
32 | dimension: Dimension,
33 | body: Body3D | Body2D,
34 | ): Body {
35 | const axis = getAxis(dimension)
36 | return {
37 | position: body.position[axis]!,
38 | velocity: body.velocity[axis]!,
39 | acceleration: body.acceleration[axis]!,
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/packages/core/src/primitives/sine-wave/index.ts:
--------------------------------------------------------------------------------
1 | export * from './sine-wave'
2 | export * from './sine-wave2d'
3 | export * from './sine-wave3d'
4 |
--------------------------------------------------------------------------------
/packages/core/src/primitives/sine-wave/sine-wave.ts:
--------------------------------------------------------------------------------
1 | import {
2 | type BodyAnimationOptions,
3 | type BodyAnimationPass,
4 | createBodyAnimation,
5 | } from '../../physics/body-animation'
6 | import { type Accessor } from 'solid-js'
7 |
8 | export type SineWaveOptions = {
9 | offset: number
10 | frequency: number
11 | amplitude: number
12 | phase: number
13 | }
14 |
15 | export const defaultSineWaveOptions = {
16 | offset: 0,
17 | frequency: 1,
18 | amplitude: 1,
19 | phase: 0,
20 | } as const satisfies SineWaveOptions
21 |
22 | export function sineWavePass(
23 | options?: Partial | Accessor>,
24 | ): BodyAnimationPass {
25 | return ({ currentTime }) => {
26 | const opts = typeof options === 'function' ? options() : options
27 | const { offset, frequency, amplitude, phase } = {
28 | ...defaultSineWaveOptions,
29 | ...opts,
30 | }
31 |
32 | const omega = 2 * Math.PI * frequency
33 | const sine = Math.sin((omega * currentTime) / 1000 + phase)
34 | const cosine = Math.cos((omega * currentTime) / 1000 + phase)
35 |
36 | return {
37 | position: offset + amplitude * sine,
38 | velocity: amplitude * omega * cosine,
39 | acceleration: -amplitude * omega * omega * cosine,
40 | }
41 | }
42 | }
43 |
44 | export function createSineWave(
45 | options?: Partial | Accessor>,
46 | bodyAnimationOptions?: () => BodyAnimationOptions,
47 | ) {
48 | return createBodyAnimation(
49 | () => [sineWavePass(options)],
50 | bodyAnimationOptions,
51 | )
52 | }
53 |
--------------------------------------------------------------------------------
/packages/core/src/primitives/sine-wave/sine-wave2d.ts:
--------------------------------------------------------------------------------
1 | import {
2 | type Body2DAnimationOptions,
3 | type Body2DAnimationPass,
4 | createBody2DAnimation,
5 | } from '../../physics/body-2d-animation'
6 | import {
7 | defaultSineWaveOptions,
8 | type SineWaveOptions,
9 | sineWavePass,
10 | } from './sine-wave'
11 | import { type Dimension, getAxis } from '../../physics/physics'
12 | import { type Accessor } from 'solid-js'
13 | import { compose2DPass } from '../../composers/dimension-pass'
14 | import { vec2 } from 'gl-matrix'
15 |
16 | export type Sine2DWaveOptions = {
17 | offset: vec2
18 | frequency: vec2
19 | amplitude: vec2
20 | phase: vec2
21 | }
22 |
23 | function getOptionsInDimention(
24 | options:
25 | | Partial
26 | | Accessor>
27 | | undefined,
28 | dimension: Dimension,
29 | ): Partial {
30 | const opts = typeof options === 'function' ? options() : options
31 | if (!opts) {
32 | return {}
33 | }
34 | const axis = getAxis(dimension)
35 | const result: Partial = {}
36 | for (const k of Object.keys(opts)) {
37 | const key = k as keyof SineWaveOptions
38 | const value = opts[key]
39 |
40 | if (value) {
41 | result[key] = value[axis]
42 | }
43 | }
44 | return result
45 | }
46 |
47 | export function sineWave2DPass(
48 | options?: Partial | Accessor>,
49 | ): Body2DAnimationPass {
50 | return compose2DPass({
51 | x: sineWavePass({
52 | ...defaultSineWaveOptions,
53 | ...getOptionsInDimention(options, 'x'),
54 | }),
55 | y: sineWavePass({
56 | ...defaultSineWaveOptions,
57 | ...getOptionsInDimention(options, 'y'),
58 | }),
59 | })
60 | }
61 |
62 | export function createSineWave2D(
63 | options?: Partial | Accessor>,
64 | bodyAnimationOptions?: () => Body2DAnimationOptions,
65 | ) {
66 | return createBody2DAnimation(
67 | () => [sineWave2DPass(options)],
68 | bodyAnimationOptions,
69 | )
70 | }
71 |
--------------------------------------------------------------------------------
/packages/core/src/primitives/sine-wave/sine-wave3d.ts:
--------------------------------------------------------------------------------
1 | import {
2 | type Body3DAnimationOptions,
3 | type Body3DAnimationPass,
4 | createBody3DAnimation,
5 | } from '../../physics/body-3d-animation'
6 | import {
7 | defaultSineWaveOptions,
8 | type SineWaveOptions,
9 | sineWavePass,
10 | } from './sine-wave'
11 | import { type Dimension, getAxis } from '../../physics/physics'
12 | import { type Accessor } from 'solid-js'
13 | import { compose3DPass } from '../../composers/dimension-pass'
14 | import { vec3 } from 'gl-matrix'
15 |
16 | export type SineWave3DOptions = {
17 | offset: vec3
18 | frequency: vec3
19 | amplitude: vec3
20 | phase: vec3
21 | }
22 |
23 | function getOptionsInDimention(
24 | options:
25 | | Partial
26 | | Accessor>
27 | | undefined,
28 | dimension: Dimension,
29 | ): Partial {
30 | const opts = typeof options === 'function' ? options() : options
31 | if (!opts) {
32 | return {}
33 | }
34 | const axis = getAxis(dimension)
35 | const result: Partial = {}
36 | for (const k of Object.keys(opts)) {
37 | const key = k as keyof SineWaveOptions
38 | const value = opts[key]
39 |
40 | if (value) {
41 | result[key] = value[axis]
42 | }
43 | }
44 | return result
45 | }
46 |
47 | export function sineWave3DPass(
48 | options?: Partial | Accessor>,
49 | ): Body3DAnimationPass {
50 | return compose3DPass({
51 | x: sineWavePass({
52 | ...defaultSineWaveOptions,
53 | ...getOptionsInDimention(options, 'x'),
54 | }),
55 | y: sineWavePass({
56 | ...defaultSineWaveOptions,
57 | ...getOptionsInDimention(options, 'y'),
58 | }),
59 | z: sineWavePass({
60 | ...defaultSineWaveOptions,
61 | ...getOptionsInDimention(options, 'z'),
62 | }),
63 | })
64 | }
65 |
66 | export function createSineWave3D(
67 | options?: Partial | Accessor>,
68 | bodyAnimationOptions?: () => Body3DAnimationOptions,
69 | ) {
70 | return createBody3DAnimation(
71 | () => [sineWave3DPass(options)],
72 | bodyAnimationOptions,
73 | )
74 | }
75 |
--------------------------------------------------------------------------------
/packages/core/src/primitives/spring/index.ts:
--------------------------------------------------------------------------------
1 | export * from './spring'
2 | export * from './spring2d'
3 | export * from './spring3d'
4 |
--------------------------------------------------------------------------------
/packages/core/src/primitives/spring/spring.ts:
--------------------------------------------------------------------------------
1 | import {
2 | type BodyAnimationOptions,
3 | type BodyAnimationPass,
4 | createBodyAnimation,
5 | } from '../../physics/body-animation'
6 | import { type Accessor } from 'solid-js'
7 |
8 | export type SpringOptions = {
9 | mass: number
10 | target: number
11 | targetThreshold?: number
12 | damping: number
13 | stiffness: number
14 | }
15 |
16 | export const defaultSpringOptions = {
17 | mass: 1,
18 | target: 1,
19 | targetThreshold: 0.001,
20 | damping: 0.5,
21 | stiffness: 0.5,
22 | } as const satisfies SpringOptions
23 |
24 | export function springPass(
25 | options?: Partial | Accessor>,
26 | ): BodyAnimationPass {
27 | return ({ body, deltaTime }) => {
28 | const opts = typeof options === 'function' ? options() : options
29 | const { target, targetThreshold, damping, stiffness, mass } = {
30 | ...defaultSpringOptions,
31 | ...opts,
32 | }
33 | const threshold = target - body.position
34 | if (Math.abs(threshold) < Math.abs(targetThreshold)) {
35 | return {
36 | position: target,
37 | velocity: 0,
38 | acceleration: 0,
39 | }
40 | }
41 |
42 | if (mass === 0) {
43 | // FIXME: Is this the best way to handle this in library code?
44 | throw new Error('Cannot create a spring animation with mass 0')
45 | }
46 |
47 | const delta = target - body.position
48 | const elasticForce = delta * stiffness
49 | const fricctionForce = -damping * body.velocity
50 |
51 | const acceleration = (elasticForce + fricctionForce) / mass
52 | const velocity = body.velocity + (acceleration * deltaTime) / 1000
53 | const position = body.position + (velocity * deltaTime) / 1000
54 |
55 | return {
56 | position,
57 | velocity,
58 | acceleration,
59 | }
60 | }
61 | }
62 |
63 | export function createSpring(
64 | options?: Partial | Accessor>,
65 | bodyAnimationOptions?: () => BodyAnimationOptions,
66 | ) {
67 | return createBodyAnimation(() => [springPass(options)], bodyAnimationOptions)
68 | }
69 |
--------------------------------------------------------------------------------
/packages/core/src/primitives/spring/spring2d.ts:
--------------------------------------------------------------------------------
1 | import {
2 | type Body2DAnimationOptions,
3 | type Body2DAnimationPass,
4 | createBody2DAnimation,
5 | } from '../../physics/body-2d-animation'
6 | import { type Accessor } from 'solid-js'
7 | import { vec2 } from 'gl-matrix'
8 |
9 | export type Spring2DOptions = {
10 | mass: number
11 | target: vec2
12 | initialPosition: vec2
13 | targetThreshold?: number
14 | damping: number
15 | stiffness: number
16 | }
17 |
18 | export const defaultSpring2DOptions = {
19 | mass: 1,
20 | target: [1, 1],
21 | targetThreshold: 0.001,
22 | initialPosition: [0, 0],
23 | damping: 0.5,
24 | stiffness: 0.5,
25 | } as const satisfies Spring2DOptions
26 |
27 | export function spring2DPass(
28 | options?: Partial | Accessor>,
29 | ): Body2DAnimationPass {
30 | const distanceVector = vec2.create()
31 | const fricctionForce = vec2.create()
32 | const elasticForce = vec2.create()
33 | const sumForces = vec2.create()
34 |
35 | return ({ body, deltaTime }) => {
36 | const opts = typeof options === 'function' ? options() : options
37 | const { target, targetThreshold, damping, stiffness, mass } = {
38 | ...defaultSpring2DOptions,
39 | ...opts,
40 | }
41 |
42 | const threshold = vec2.distance(target, body.position)
43 | if (Math.abs(threshold) < Math.abs(targetThreshold)) {
44 | return {
45 | position: target,
46 | velocity: [0, 0],
47 | acceleration: [0, 0],
48 | }
49 | }
50 |
51 | if (mass === 0) {
52 | // FIXME: Is this the best way to handle this in library code?
53 | throw new Error('Cannot create a spring2D animation with mass 0')
54 | }
55 |
56 | // Elastic
57 | vec2.sub(distanceVector, target, body.position)
58 | vec2.scale(elasticForce, distanceVector, stiffness)
59 |
60 | // Fricction
61 | vec2.scale(fricctionForce, body.velocity, -damping)
62 |
63 | // Resulting Force
64 | vec2.add(sumForces, elasticForce, fricctionForce)
65 |
66 | const acceleration = vec2.scale(vec2.create(), sumForces, 1 / mass)
67 | const velocity = vec2.scaleAndAdd(
68 | vec2.create(),
69 | body.velocity,
70 | acceleration,
71 | deltaTime / 1000,
72 | )
73 |
74 | const position = vec2.scaleAndAdd(
75 | vec2.create(),
76 | body.position,
77 | velocity,
78 | deltaTime / 1000,
79 | )
80 |
81 | return {
82 | position,
83 | velocity,
84 | acceleration,
85 | }
86 | }
87 | }
88 |
89 | export function createSpring2D(
90 | options?: Partial | Accessor>,
91 | bodyAnimationOptions?: () => Body2DAnimationOptions,
92 | ) {
93 | return createBody2DAnimation(
94 | () => [spring2DPass(options)],
95 | bodyAnimationOptions,
96 | )
97 | }
98 |
--------------------------------------------------------------------------------
/packages/core/src/primitives/spring/spring3d.ts:
--------------------------------------------------------------------------------
1 | import {
2 | type Body3DAnimationOptions,
3 | type Body3DAnimationPass,
4 | createBody3DAnimation,
5 | } from '../../physics/body-3d-animation'
6 | import { type Accessor } from 'solid-js'
7 | import { vec3 } from 'gl-matrix'
8 |
9 | export type Spring3DOptions = {
10 | mass: number
11 | target: vec3
12 | targetThreshold?: number
13 | damping: number
14 | stiffness: number
15 | }
16 |
17 | export const defaultSpring3DOptions = {
18 | mass: 1,
19 | target: [1, 1, 1],
20 | targetThreshold: 0.001,
21 | damping: 0.5,
22 | stiffness: 0.5,
23 | } as const satisfies Spring3DOptions
24 |
25 | export function spring3DPass(
26 | options?: Partial | Accessor>,
27 | ): Body3DAnimationPass {
28 | const distanceVector = vec3.create()
29 | const fricctionForce = vec3.create()
30 | const elasticForce = vec3.create()
31 | const sumForces = vec3.create()
32 |
33 | return ({ body, deltaTime }) => {
34 | const opts = typeof options === 'function' ? options() : options
35 | const { target, targetThreshold, damping, stiffness, mass } = {
36 | ...defaultSpring3DOptions,
37 | ...opts,
38 | }
39 |
40 | const threshold = vec3.distance(target, body.position)
41 | if (Math.abs(threshold) < Math.abs(targetThreshold)) {
42 | return {
43 | position: target,
44 | velocity: [0, 0, 0],
45 | acceleration: [0, 0, 0],
46 | }
47 | }
48 |
49 | if (mass === 0) {
50 | // FIXME: Is this the best way to handle this in library code?
51 | throw new Error('Cannot create a spring3D animation with mass 0')
52 | }
53 |
54 | // Elastic
55 | vec3.sub(distanceVector, target, body.position)
56 | vec3.scale(elasticForce, distanceVector, stiffness)
57 |
58 | // Fricction
59 | vec3.scale(fricctionForce, body.velocity, -damping)
60 |
61 | // Resulting Force
62 | vec3.add(sumForces, elasticForce, fricctionForce)
63 |
64 | const acceleration = vec3.scale(vec3.create(), sumForces, 1 / mass)
65 | const velocity = vec3.scaleAndAdd(
66 | vec3.create(),
67 | body.velocity,
68 | acceleration,
69 | deltaTime / 1000,
70 | )
71 | const position = vec3.scaleAndAdd(
72 | vec3.create(),
73 | body.position,
74 | velocity,
75 | deltaTime / 1000,
76 | )
77 |
78 | return {
79 | position,
80 | velocity,
81 | acceleration,
82 | }
83 | }
84 | }
85 |
86 | export function createSpring3D(
87 | options?: Partial | Accessor>,
88 | bodyAnimationOptions?: () => Body3DAnimationOptions,
89 | ) {
90 | return createBody3DAnimation(
91 | () => [spring3DPass(options)],
92 | bodyAnimationOptions,
93 | )
94 | }
95 |
--------------------------------------------------------------------------------
/packages/core/src/utils/interpolation.ts:
--------------------------------------------------------------------------------
1 | export function lerp(t: number, start: number, end: number): number {
2 | if (t < 0) {
3 | return start
4 | }
5 | if (t > 1) {
6 | return end
7 | }
8 | return start + (end - start) * t
9 | }
10 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "ESNext",
4 | "target": "ESNext",
5 | "newLine": "LF",
6 | "lib": [
7 | "DOM",
8 | "DOM.Iterable",
9 | "ESNext"
10 | ],
11 | "moduleResolution": "Bundler",
12 | "noEmit": true,
13 | "allowSyntheticDefaultImports": true,
14 | "strict": true,
15 | "strictNullChecks": true,
16 | "esModuleInterop": true,
17 | "isolatedModules": true,
18 | "forceConsistentCasingInFileNames": true,
19 | "noUncheckedIndexedAccess": true,
20 | "skipLibCheck": true,
21 | "jsx": "preserve",
22 | "jsxImportSource": "solid-js",
23 | "verbatimModuleSyntax": true
24 | },
25 | "exclude": [
26 | "node_modules",
27 | "dist"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/packages/core/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup'
2 | import type { Options } from 'tsup'
3 | import { solidPlugin } from 'esbuild-plugin-solid'
4 |
5 | function generateConfig(format: 'esm' | 'cjs', jsx: boolean): Options {
6 | return {
7 | target: 'esnext',
8 | platform: 'browser',
9 | format,
10 | clean: true,
11 | dts: format === 'esm' && !jsx,
12 | entry: ['src/*.ts'],
13 | outDir: 'dist/',
14 | treeshake: { preset: 'smallest' },
15 | replaceNodeEnv: true,
16 | esbuildOptions(options) {
17 | if (jsx) {
18 | options.jsx = 'preserve'
19 | }
20 | options.chunkNames = '[name]/[hash]'
21 | options.drop = ['console', 'debugger']
22 | },
23 | outExtension() {
24 | if (jsx) {
25 | return { js: '.jsx' }
26 | } else {
27 | return {}
28 | }
29 | },
30 | esbuildPlugins: !jsx ? [solidPlugin({ solid: { generate: 'dom' } })] : [],
31 | }
32 | }
33 |
34 | export default defineConfig([
35 | generateConfig('esm', false),
36 | generateConfig('esm', true),
37 | ])
38 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'packages/*'
3 | - 'web'
4 | - 'dev'
5 |
--------------------------------------------------------------------------------
/public/html5.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/public/threejs.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/release-please-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "bootstrap-sha": "f7b9e4e0022bf1f26520a013efc37e7054e863cd",
3 | "last-release-sha": "7a53e105af7762b25154465b9ac4ea5ab97b489d",
4 | "bump-minor-pre-major": true,
5 | "bump-patch-for-minor-pre-major": true,
6 | "tag-separator": "@",
7 | "include-component-in-tag": true,
8 | "include-v-in-tag": false,
9 | "packages": {
10 | "packages/arara": {
11 | "component": "arara"
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "tasks": {
4 | "build": {
5 | "dependsOn": [
6 | "^build",
7 | "^typedoc"
8 | ],
9 | "outputs": [
10 | "dist/**"
11 | ]
12 | },
13 | "clean": {
14 | "cache": false
15 | },
16 | "dev": {
17 | "dependsOn": [
18 | "^build"
19 | ],
20 | "persistent": true
21 | },
22 | "@arara/web#dev": {
23 | "dependsOn": [
24 | "^build",
25 | "^typedoc"
26 | ],
27 | "persistent": true
28 | },
29 | "lint": {
30 | "dependsOn": [
31 | "^build"
32 | ]
33 | },
34 | "preview": {
35 | "dependsOn": [
36 | "build"
37 | ]
38 | },
39 | "typedoc": {
40 | "dependsOn": [
41 | "^build"
42 | ],
43 | "outputs": [
44 | "api.json"
45 | ]
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/web/.env.example:
--------------------------------------------------------------------------------
1 | PUBLIC_SEARCH_API_URL=
2 | PUBLIC_SEARCH_API_KEY=
3 |
--------------------------------------------------------------------------------
/web/.prettierrc.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import("prettier").Config} */
2 | export default {
3 | plugins: ['prettier-plugin-astro'],
4 | singleQuote: true,
5 | jsxSingleQuote: false,
6 | semi: false,
7 | htmlWhitespaceSensitivity: 'strict',
8 | overrides: [
9 | {
10 | files: '*.astro',
11 | options: {
12 | parser: 'astro',
13 | singleQuote: true,
14 | jsxSingleQuote: false,
15 | semi: false,
16 | htmlWhitespaceSensitivity: 'strict',
17 | },
18 | },
19 | ],
20 | }
21 |
--------------------------------------------------------------------------------
/web/astro.config.ts:
--------------------------------------------------------------------------------
1 | import sitemap, { ChangeFreqEnum } from '@astrojs/sitemap'
2 | import { defineConfig } from 'astro/config'
3 | import mdx from '@astrojs/mdx'
4 | import solid from '@astrojs/solid-js'
5 | import tailwind from '@astrojs/tailwind'
6 |
7 | // https://astro.build/config
8 | export default defineConfig({
9 | integrations: [
10 | mdx(),
11 | sitemap({
12 | serialize(item) {
13 | if (item.url === 'https://ararajs.vercel.app/') {
14 | item.priority = 1
15 | } else {
16 | item.priority = 0.9
17 | }
18 | item.changefreq = ChangeFreqEnum.DAILY
19 | item.lastmod = new Date().toISOString()
20 | return item
21 | },
22 | }),
23 | tailwind(),
24 | solid(),
25 | ],
26 | prefetch: {
27 | prefetchAll: true,
28 | },
29 | redirects: {
30 | '/docs/usage/': {
31 | status: 307,
32 | destination: '/docs/state/',
33 | },
34 | '/docs/polymorphic/': {
35 | status: 307,
36 | destination: '/docs/dynamic-components/',
37 | },
38 | '/docs/polymorphism/': {
39 | status: 307,
40 | destination: '/docs/dynamic-components/',
41 | },
42 | '/docs/primitives/': {
43 | status: 307,
44 | destination: '/docs/overview/',
45 | },
46 | '/docs/utilities/': {
47 | status: 307,
48 | destination: '/docs/overview/',
49 | },
50 | },
51 | markdown: {
52 | syntaxHighlight: false,
53 | },
54 | site: 'https://ararajs.vercel.app',
55 | trailingSlash: 'always',
56 | })
57 |
--------------------------------------------------------------------------------
/web/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import astroParser from 'astro-eslint-parser'
2 | import pluginAstro from 'eslint-plugin-astro'
3 | import pluginPrettier from 'eslint-plugin-prettier/recommended'
4 | import pluginSolid from 'eslint-plugin-solid'
5 | import pluginTailwind from 'eslint-plugin-tailwindcss'
6 | import pluginTypescript from 'typescript-eslint'
7 | import tsParser from '@typescript-eslint/parser'
8 |
9 | export default [
10 | {
11 | ignores: ['dist/'],
12 | },
13 | ...pluginTypescript.configs.recommended,
14 | ...pluginTailwind.configs['flat/recommended'],
15 | pluginPrettier,
16 | ...pluginAstro.configs.recommended,
17 | {
18 | files: ['**/*.{ts,tsx}'],
19 | ...pluginSolid.configs['flat/typescript'],
20 | languageOptions: {
21 | parser: tsParser,
22 | parserOptions: {
23 | project: true,
24 | },
25 | },
26 | },
27 | {
28 | languageOptions: {
29 | parser: tsParser,
30 | },
31 | rules: {
32 | 'no-console': 'warn',
33 |
34 | 'sort-imports': [
35 | 'warn',
36 | {
37 | ignoreCase: true,
38 | },
39 | ],
40 |
41 | '@typescript-eslint/no-unused-vars': [
42 | 'warn',
43 | {
44 | argsIgnorePattern: '^_',
45 | varsIgnorePattern: '^_',
46 | caughtErrorsIgnorePattern: '^_',
47 | },
48 | ],
49 |
50 | '@typescript-eslint/method-signature-style': 'error',
51 | '@typescript-eslint/no-empty-object-type': 'off',
52 | '@typescript-eslint/no-wrapper-object-types': 'error',
53 | '@typescript-eslint/triple-slash-reference': 'off',
54 |
55 | '@typescript-eslint/no-unused-expressions': [
56 | 'error',
57 | {
58 | allowShortCircuit: true,
59 | },
60 | ],
61 |
62 | 'tailwindcss/classnames-order': 'error',
63 | 'tailwindcss/enforces-negative-arbitrary-values': 'error',
64 | 'tailwindcss/enforces-shorthand': 'error',
65 | 'tailwindcss/migration-from-tailwind-2': 'error',
66 | 'tailwindcss/no-custom-classname': 'error',
67 |
68 | 'solid/reactivity': 'off',
69 | },
70 | },
71 | {
72 | files: ['**/*.{ts,tsx,astro}'],
73 | languageOptions: {
74 | parserOptions: {
75 | project: true,
76 | },
77 | },
78 | rules: {
79 | '@typescript-eslint/no-unnecessary-condition': 'error',
80 | '@typescript-eslint/strict-boolean-expressions': 'error',
81 | },
82 | },
83 | {
84 | files: ['**/*.astro'],
85 | languageOptions: {
86 | parser: astroParser,
87 | parserOptions: {
88 | parser: tsParser,
89 | extraFileExtensions: ['.astro'],
90 | },
91 | },
92 | },
93 | ]
94 |
--------------------------------------------------------------------------------
/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ararajs/web",
3 | "private": true,
4 | "description": "ararajs.vercel.app website",
5 | "homepage": "https://ararajs.vercel.app/",
6 | "license": "MIT",
7 | "author": {
8 | "name": "Felipe Emos",
9 | "email": "felipe.emos.computacao@gmail.com",
10 | "url": "https://github.com/FelipeEmos"
11 | },
12 | "type": "module",
13 | "scripts": {
14 | "build": "astro build",
15 | "clean": "rm -rf .turbo dist node_modules",
16 | "dev": "astro dev",
17 | "lint": "eslint --max-warnings=0 .",
18 | "preview": "astro preview"
19 | },
20 | "dependencies": {
21 | "@astrojs/mdx": "4.0.0-beta.2",
22 | "@astrojs/sitemap": "^3.2.1",
23 | "@astrojs/solid-js": "^4.4.2",
24 | "@astrojs/tailwind": "6.0.0-alpha.0",
25 | "@corvu/accordion": "^0.2.3",
26 | "@corvu/dialog": "^0.2.3",
27 | "@corvu/disclosure": "^0.2.0",
28 | "@corvu/drawer": "^0.2.2",
29 | "@corvu/otp-field": "^0.1.3",
30 | "@corvu/popover": "^0.2.0",
31 | "@corvu/resizable": "^0.2.3",
32 | "@corvu/tailwind": "^0.1.5",
33 | "@corvu/tooltip": "^0.2.1",
34 | "@kobalte/core": "^0.13.7",
35 | "@solid-primitives/mouse": "^2.0.20",
36 | "@solid-primitives/resize-observer": "^2.0.26",
37 | "@solid-primitives/storage": "^4.2.1",
38 | "@tailwindcss/aspect-ratio": "^0.4.2",
39 | "@tailwindcss/container-queries": "^0.1.1",
40 | "@tailwindcss/forms": "^0.5.9",
41 | "@tailwindcss/typography": "^0.5.15",
42 | "@typescript-eslint/parser": "^8.9.0",
43 | "ararajs": "workspace:*",
44 | "astro": "5.0.0-beta.5",
45 | "astro-eslint-parser": "^1.0.3",
46 | "clsx": "^2.1.1",
47 | "eslint": "^9.12.0",
48 | "eslint-config-prettier": "^9.1.0",
49 | "eslint-plugin-astro": "^1.3.0",
50 | "eslint-plugin-prettier": "^5.2.1",
51 | "eslint-plugin-solid": "^0.14.3",
52 | "eslint-plugin-tailwindcss": "^3.17.5",
53 | "prettier": "^3.3.3",
54 | "prettier-plugin-astro": "^0.14.1",
55 | "sharp": "^0.33.5",
56 | "shiki": "^1.21.0",
57 | "solid-js": "^1.9.2",
58 | "solid-list": "^0.3.0",
59 | "solid-persistent": "^0.1.0",
60 | "tailwind-merge": "^2.5.4",
61 | "tailwindcss": "^3.4.14",
62 | "typescript": "^5.6.3",
63 | "typescript-eslint": "^8.9.0"
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/web/public/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/banner.jpg
--------------------------------------------------------------------------------
/web/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/favicon-16x16.png
--------------------------------------------------------------------------------
/web/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/favicon-32x32.png
--------------------------------------------------------------------------------
/web/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/favicon.ico
--------------------------------------------------------------------------------
/web/public/fonts/MonaspaceNeon-Regular-v1.101.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/fonts/MonaspaceNeon-Regular-v1.101.woff2
--------------------------------------------------------------------------------
/web/public/primitives/accordion.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/primitives/accordion.jpg
--------------------------------------------------------------------------------
/web/public/primitives/dialog.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/primitives/dialog.jpg
--------------------------------------------------------------------------------
/web/public/primitives/disclosure.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/primitives/disclosure.jpg
--------------------------------------------------------------------------------
/web/public/primitives/drawer.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/primitives/drawer.jpg
--------------------------------------------------------------------------------
/web/public/primitives/otp-field.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/primitives/otp-field.jpg
--------------------------------------------------------------------------------
/web/public/primitives/popover.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/primitives/popover.jpg
--------------------------------------------------------------------------------
/web/public/primitives/resizable.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/primitives/resizable.jpg
--------------------------------------------------------------------------------
/web/public/primitives/tooltip.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/primitives/tooltip.jpg
--------------------------------------------------------------------------------
/web/public/readme/arara.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/readme/arara.png
--------------------------------------------------------------------------------
/web/public/readme/solid-dismissible.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/readme/solid-dismissible.png
--------------------------------------------------------------------------------
/web/public/readme/solid-focus-trap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/readme/solid-focus-trap.png
--------------------------------------------------------------------------------
/web/public/readme/solid-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/readme/solid-list.png
--------------------------------------------------------------------------------
/web/public/readme/solid-persistent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/readme/solid-persistent.png
--------------------------------------------------------------------------------
/web/public/readme/solid-presence.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/readme/solid-presence.png
--------------------------------------------------------------------------------
/web/public/readme/solid-prevent-scroll.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/readme/solid-prevent-scroll.png
--------------------------------------------------------------------------------
/web/public/readme/solid-transition-size.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/public/readme/solid-transition-size.png
--------------------------------------------------------------------------------
/web/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /
3 | Sitemap: https://ararajs.vercel.app/sitemap-index.xml
4 |
--------------------------------------------------------------------------------
/web/src/assets/ararajs_banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/src/assets/ararajs_banner.png
--------------------------------------------------------------------------------
/web/src/assets/examples/arara_blue.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/src/assets/examples/arara_blue.jpg
--------------------------------------------------------------------------------
/web/src/assets/examples/arara_red.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/src/assets/examples/arara_red.jpg
--------------------------------------------------------------------------------
/web/src/assets/examples/arara_red_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/src/assets/examples/arara_red_2.jpg
--------------------------------------------------------------------------------
/web/src/assets/global.css:
--------------------------------------------------------------------------------
1 | html {
2 | --arara-text-dark: 207 43% 4%;
3 | --arara-text: 207 43% 4%;
4 | --arara-bg: 202 52% 96%;
5 | --arara-article-bg: 206 100% 100%;
6 | --arara-100: 206 94% 94%;
7 | --arara-200: 205 78% 87%;
8 | --arara-300: 202 72% 80%;
9 | --arara-400: 202 62% 70%;
10 | --arara-blue: 203 80% 82%;
11 | --arara-pink: 303 80% 82%;
12 | --arara-link: 205 91% 35%;
13 | --arara-link-hover: 205 95% 50%;
14 |
15 | scrollbar-color: hsl(var(--arara-300)) hsl(var(--arara-100));
16 | }
17 |
18 | html.dark {
19 | color-scheme: dark;
20 | --arara-text: 203 29% 95%;
21 | --arara-bg: 209 32% 13%;
22 | --arara-article-bg: 209 32% 8;
23 | --arara-100: 205 94% 15%;
24 | --arara-200: 205 84% 20%;
25 | --arara-300: 210 32% 55%;
26 | --arara-400: 210 32% 55%;
27 | --arara-blue: 210 54% 30%;
28 | --arara-pink: 310 44% 40%;
29 | --arara-link: 205 85% 65%;
30 | --arara-link-hover: 205 85% 95%;
31 | }
32 |
33 | .changing-theme,
34 | .changing-theme * {
35 | transition: none !important;
36 | }
37 |
38 | :root {
39 | --shadow-color: var(--arara-article-bg);
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/web/src/assets/res/html_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FelipeEmos/ararajs/59bdaf8d000577414c1b77650534711cd43a055b/web/src/assets/res/html_logo.png
--------------------------------------------------------------------------------
/web/src/assets/res/solidjs_logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/src/assets/res/threejs_logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/web/src/assets/res/threejs_logo_dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/web/src/assets/shiki.css:
--------------------------------------------------------------------------------
1 | html {
2 | --shiki-background: hsl(var(--arara-100));
3 | --shiki-foreground: hsl(249, 87%, 6%);
4 | --shiki-token-comment: hsl(268, 21%, 45%);
5 | --shiki-token-constant: hsl(278, 80%, 47%);
6 | --shiki-token-string-expression: hsl(138, 70%, 25%);
7 |
8 | --shiki-token-keyword: hsl(206, 75%, 35%);
9 | --shiki-token-punctuation: hsl(206, 75%, 35%);
10 | --shiki-token-function: hsl(0, 70%, 46%);
11 |
12 | /* Seem to get never used */
13 | --shiki-token-string: hsl(249, 87%, 6%);
14 | --shiki-token-link: hsl(249, 87%, 6%);
15 | --shiki-token-parameter: hsl(249, 87%, 6%);
16 | }
17 |
18 | html.dark {
19 | --shiki-foreground: hsl(253, 26%, 83%);
20 | --shiki-token-comment: hsl(253, 26%, 63%);
21 | --shiki-token-constant: hsl(278, 100%, 87%);
22 | --shiki-token-string-expression: hsl(138, 61%, 75%);
23 |
24 | --shiki-token-keyword: hsl(206, 65%, 75%);
25 | --shiki-token-punctuation: hsl(206, 65%, 75%);
26 | --shiki-token-function: hsl(0, 100%, 86%);
27 |
28 | /* Seem to get never used */
29 | --shiki-token-string: hsl(253, 26%, 83%);
30 | --shiki-token-link: hsl(253, 26%, 83%);
31 | --shiki-token-parameter: hsl(253, 26%, 83%);
32 | }
33 |
--------------------------------------------------------------------------------
/web/src/components/Drawer.tsx:
--------------------------------------------------------------------------------
1 | import Drawer from '@corvu/drawer'
2 | import type { FlowComponent } from 'solid-js'
3 |
4 | const NavDrawer: FlowComponent = (props) => {
5 | return (
6 |
7 | {(drawerProps) => (
8 | <>
9 |
10 | Open navigation
11 |
17 |
18 |
19 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
41 |
42 |
43 | {props.children}
44 |
45 |
46 |
47 | >
48 | )}
49 |
50 | )
51 | }
52 |
53 | export default NavDrawer
54 |
--------------------------------------------------------------------------------
/web/src/components/Fonts.astro:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/web/src/components/Head.astro:
--------------------------------------------------------------------------------
1 | ---
2 | interface Props {
3 | title: string
4 | description: string
5 | image: string
6 | }
7 |
8 | const { title, description, image } = Astro.props
9 | ---
10 |
11 | {title}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/web/src/components/HeaderLogo.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import HeaderLogoDark from '@assets/title_with_logo_dark.svg'
3 | import HeaderLogoLight from '@assets/title_with_logo_light.svg'
4 | import LogoDark from '@assets/logo_dark.svg'
5 | import LogoLight from '@assets/logo_light.svg'
6 | import { cn } from '@lib/cn'
7 |
8 | interface Props {
9 | tiny?: boolean
10 | className?: string
11 | }
12 |
13 | const { tiny = false, className } = Astro.props
14 | ---
15 |
16 |
35 |
--------------------------------------------------------------------------------
/web/src/components/ThemeScript.astro:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/web/src/components/Topbar.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Drawer from '@components/Drawer'
3 | import HeaderLogo from '@components/HeaderLogo.astro'
4 | import Navigation from '@components/docs/nav/Navigation.astro'
5 | import SearchDialog from './docs/search/SearchDialog'
6 | import SearchDrawer from './docs/search/SearchDrawer'
7 | import ThemeSelect from '@components/ThemeSelect'
8 |
9 | interface Props {
10 | inDocs?: boolean
11 | }
12 |
13 | const { inDocs } = Astro.props
14 | ---
15 |
16 |
22 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
83 |
84 |
85 |
92 |
93 |
--------------------------------------------------------------------------------
/web/src/components/docs/Features.astro:
--------------------------------------------------------------------------------
1 | ---
2 | interface Props {
3 | features: string[]
4 | }
5 |
6 | const { features } = Astro.props
7 | ---
8 |
9 |
10 | {
11 | features.map((feature) => (
12 |
13 | 🐦️
14 | {/*
20 | <>
21 |
22 | <>
23 |
27 |
31 | >
32 |
33 |
34 |
35 |
36 |
37 |
38 | >
39 | */}
40 | {feature}
41 |
42 | ))
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/web/src/components/docs/KeyboardNavigation.astro:
--------------------------------------------------------------------------------
1 | ---
2 | interface Props {
3 | keys: {
4 | key: string
5 | behavior: string
6 | }[]
7 | }
8 |
9 | const { keys } = Astro.props
10 | ---
11 |
12 |
13 |
14 |
15 | Key
16 | Behavior
17 |
18 | {
19 | keys.map((key) => (
20 |
21 |
22 |
23 | {key.key}
24 |
25 |
26 | {key.behavior}
27 |
28 | ))
29 | }
30 |
31 |
32 |
--------------------------------------------------------------------------------
/web/src/components/docs/Link.astro:
--------------------------------------------------------------------------------
1 | ---
2 | interface Props {
3 | href: string
4 | newTab?: boolean
5 | }
6 |
7 | const { href, newTab } = Astro.props
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/web/src/components/docs/PackageInfo.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import packageMetas from '@lib/packageMetas'
3 |
4 | interface Props {
5 | packageName: string
6 | }
7 |
8 | const { packageName } = Astro.props
9 | const packageMeta = packageMetas[packageName]
10 | ---
11 |
12 |
62 |
--------------------------------------------------------------------------------
/web/src/components/docs/TableOfContents.tsx:
--------------------------------------------------------------------------------
1 | import { createEffect, createSignal, For, onCleanup } from 'solid-js'
2 | import clsx from 'clsx'
3 | import type { VoidComponent } from 'solid-js'
4 |
5 | type Heading = {
6 | depth: number
7 | text: string
8 | slug: string
9 | subheadings: Heading[]
10 | }
11 |
12 | const TableOfContents: VoidComponent<{
13 | headings: {
14 | depth: number
15 | text: string
16 | slug: string
17 | }[]
18 | apiHeadings: {
19 | text: string
20 | slug: string
21 | }[]
22 | }> = (props) => {
23 | const tableOfContents: Heading[] = []
24 | const parentHeadings = new Map()
25 |
26 | const [headingsInView, setHeadingsInView] = createSignal([])
27 |
28 | props.headings
29 | .filter((h) => h.depth !== 1)
30 | .forEach((h) => {
31 | const heading = { ...h, subheadings: [] }
32 | parentHeadings.set(heading.depth, heading)
33 | if (heading.depth === 2) {
34 | tableOfContents.push(heading)
35 | } else {
36 | parentHeadings.get(heading.depth - 1).subheadings.push(heading)
37 | }
38 | })
39 |
40 | if (props.apiHeadings.length > 0) {
41 | const apiTitle = tableOfContents.find((h) => h.slug === 'api-reference')
42 | if (apiTitle) {
43 | apiTitle.subheadings = props.apiHeadings.map((h) => {
44 | return {
45 | depth: 3,
46 | text: h.text,
47 | slug: h.slug,
48 | subheadings: [],
49 | }
50 | })
51 | }
52 | }
53 |
54 | createEffect(() => {
55 | const observer = new IntersectionObserver((sections) => {
56 | sections.forEach((section) => {
57 | const id = section.target.getAttribute('id')
58 | if (id === null) return
59 |
60 | if (section.isIntersecting && !headingsInView().includes(id)) {
61 | setHeadingsInView([...headingsInView(), id])
62 | return
63 | }
64 | if (!section.isIntersecting && headingsInView().includes(id)) {
65 | setHeadingsInView(headingsInView().filter((h) => h !== id))
66 | return
67 | }
68 | })
69 | })
70 |
71 | document.querySelectorAll('h2, h3, h4').forEach((section) => {
72 | observer.observe(section)
73 | })
74 |
75 | onCleanup(() => observer.disconnect())
76 | })
77 |
78 | return (
79 |
80 | {tableOfContents.length > 0 && (
81 | On this page
82 | )}
83 |
84 |
85 | {(heading) => {
86 | return (
87 |
88 | )
89 | }}
90 |
91 |
92 |
93 | )
94 | }
95 |
96 | const TocItem: VoidComponent<{ heading: Heading; headingsInView: string[] }> = (
97 | props,
98 | ) => {
99 | return (
100 |
101 | {
112 | e.preventDefault()
113 | document
114 | .getElementById(props.heading.slug)
115 | ?.scrollIntoView({ behavior: 'smooth' })
116 | history.pushState({}, '', `#${props.heading.slug}`)
117 | }}
118 | >
119 | {props.heading.text}
120 |
121 | {props.heading.subheadings.length > 0 && (
122 |
123 |
124 | {(heading) => {
125 | return (
126 |
130 | )
131 | }}
132 |
133 |
134 | )}
135 |
136 | )
137 | }
138 |
139 | export default TableOfContents
140 |
--------------------------------------------------------------------------------
/web/src/components/docs/api/ApiReference.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import ApiItemChildrenProps from '@components/docs/api/items/ApiItemChildrenProps.astro'
3 | import ApiItemComponent from '@components/docs/api/items/ApiItemComponent.astro'
4 | import ApiItemContext from '@components/docs/api/items/ApiItemContext.astro'
5 | import ApiItemFunction from '@components/docs/api/items/ApiItemFunction.astro'
6 | import ApiItemInheritedComponent from '@components/docs/api/items/ApiItemInheritedComponent.astro'
7 | import ApiItemInheritedContext from '@components/docs/api/items/ApiItemInheritedContext.astro'
8 | import ApiItemSimple from '@components/docs/api/items/ApiItemSimple.astro'
9 | import type { Library } from '@lib/typedoc/types/specifications'
10 | import resolveLibrary from '@lib/typedoc/resolve/resolve'
11 |
12 | interface Props {
13 | library: Library
14 | }
15 |
16 | const { library } = Astro.props
17 |
18 | const apiReferences = resolveLibrary(library)
19 | ---
20 |
21 |
22 | {
23 | apiReferences.map((apiReference) => (
24 | <>
25 | {apiReference.kind === 'component' && (
26 |
27 | )}
28 | {apiReference.kind === 'inherited-component' && (
29 |
30 | )}
31 | {apiReference.kind === 'context' && (
32 |
33 | )}
34 | {apiReference.kind === 'inherited-context' && (
35 |
36 | )}
37 | {apiReference.kind === 'childrenProps' && (
38 |
39 | )}
40 | {apiReference.kind === 'simple' &&
}
41 | {apiReference.kind === 'function' && (
42 |
43 | )}
44 | >
45 | ))
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/web/src/components/docs/api/items/ApiItemChildrenProps.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import ApiReturns from '@components/docs/api/lists/ApiReturns.astro'
3 | import type { ChildrenPropsApiReference } from '@lib/typedoc/types/apiReferences'
4 |
5 | interface Props extends ChildrenPropsApiReference {}
6 |
7 | const { name, descriptionHtml, inherits, props } = Astro.props
8 | ---
9 |
10 |
56 |
--------------------------------------------------------------------------------
/web/src/components/docs/api/items/ApiItemComponent.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import ApiProps from '@components/docs/api/lists/ApiProps.astro'
3 | import ApiTag from '@components/docs/api/lists/ApiTag.astro'
4 | import type { ComponentApiReference } from '@lib/typedoc/types/apiReferences'
5 |
6 | interface Props extends ComponentApiReference {}
7 |
8 | const { name, descriptionHtml, props, inherits, data, css } = Astro.props
9 | ---
10 |
11 |
12 |
52 |
53 |
54 |
55 | {data.length > 0 &&
}
56 | {css.length > 0 &&
}
57 |
58 |
59 |
--------------------------------------------------------------------------------
/web/src/components/docs/api/items/ApiItemContext.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import ApiReturns from '@components/docs/api/lists/ApiReturns.astro'
3 | import type { ContextApiReference } from '@lib/typedoc/types/apiReferences'
4 |
5 | interface Props extends ContextApiReference {}
6 |
7 | const { name, descriptionHtml, returns } = Astro.props
8 | ---
9 |
10 |
56 |
--------------------------------------------------------------------------------
/web/src/components/docs/api/items/ApiItemFunction.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import ApiProps from '@components/docs/api/lists/ApiProps.astro'
3 | import ApiReturns from '@components/docs/api/lists/ApiReturns.astro'
4 | import type { FunctionApiReference } from '@lib/typedoc/types/apiReferences'
5 |
6 | interface Props extends FunctionApiReference {}
7 |
8 | const { name, props, returns } = Astro.props
9 | ---
10 |
11 |
12 |
52 |
53 |
54 | {returns &&
}
55 |
56 |
57 |
--------------------------------------------------------------------------------
/web/src/components/docs/api/items/ApiItemInheritedComponent.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import ApiTag from '@components/docs/api/lists/ApiTag.astro'
3 | import type { InheritedComponentApiReference } from '@lib/typedoc/types/apiReferences'
4 | import Link from '@components/docs/Link.astro'
5 |
6 | interface Props extends InheritedComponentApiReference {}
7 |
8 | const { name, descriptionHtml, inherits, data, css } = Astro.props
9 | ---
10 |
11 |
12 |
52 |
53 |
54 |
55 |
Props
56 |
57 | Inherits <{inherits.library.name}.{inherits.name} />
Props.
61 |
62 |
63 | {data.length > 0 &&
}
64 | {css.length > 0 &&
}
65 |
66 |
67 |
--------------------------------------------------------------------------------
/web/src/components/docs/api/items/ApiItemInheritedContext.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import type { InheritedContextApiReference } from '@lib/typedoc/types/apiReferences'
3 | import Link from '@components/docs/Link.astro'
4 |
5 | interface Props extends InheritedContextApiReference {}
6 |
7 | const { name, inherits } = Astro.props
8 | ---
9 |
10 |
11 |
51 |
52 |
53 | Inherited from <{inherits.library.name}.{inherits.name} />
.
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/web/src/components/docs/api/items/ApiItemSimple.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import RawCode from '@components/docs/code/RawCode.astro'
3 | import type { SimpleApiReference } from '@lib/typedoc/types/apiReferences'
4 |
5 | interface Props extends SimpleApiReference {}
6 |
7 | const { name, type } = Astro.props
8 | ---
9 |
10 |
55 |
--------------------------------------------------------------------------------
/web/src/components/docs/api/lists/ApiProps.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import type { Library } from '@lib/typedoc/types/specifications'
3 | import Link from '@components/docs/Link.astro'
4 | import type { PropType } from '@lib/typedoc/types/apiReferences'
5 |
6 | interface Props {
7 | title?: string
8 | descriptionHtml?: string
9 | props: PropType[]
10 | inherits: {
11 | library: Library
12 | name: string
13 | } | null
14 | }
15 |
16 | const { title, descriptionHtml, props, inherits } = Astro.props
17 | ---
18 |
19 |
20 |
{title ?? 'Props'}
21 | {
22 | descriptionHtml !== undefined && (
23 |
24 | )
25 | }
26 | {
27 | inherits && (
28 |
29 | Inherits {/* prettier-ignore */}
30 | <{inherits.library.name}.{inherits.name} />
31 | Props.
32 |
33 | )
34 | }
35 |
36 |
37 |
38 | Property
39 | Default
40 | Type/Description
41 |
42 | {
43 | props.map((prop) => (
44 |
45 |
46 |
55 | {prop.name}
56 |
57 |
58 |
59 | {prop.defaultHtml === null ? (
60 | -
61 | ) : (
62 |
66 | )}
67 |
68 |
69 |
70 | {prop.type}
71 |
72 |
76 |
77 |
78 | ))
79 | }
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/web/src/components/docs/api/lists/ApiReturns.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import type { Library } from '@lib/typedoc/types/specifications'
3 | import Link from '@components/docs/Link.astro'
4 | import type { ReturnType } from '@lib/typedoc/types/apiReferences'
5 |
6 | interface Props {
7 | title?: string
8 | descriptionHtml?: string
9 | returns: ReturnType[]
10 | inherits: {
11 | library: Library
12 | name: string
13 | } | null
14 | }
15 |
16 | const { title, descriptionHtml, returns, inherits } = Astro.props
17 | ---
18 |
19 |
20 |
{title ?? 'Returns'}
21 | {
22 | descriptionHtml !== undefined && (
23 |
24 | )
25 | }
26 | {
27 | inherits && (
28 |
29 | Inherits {/* prettier-ignore */}
30 | <{inherits.library.name}.{inherits.name} />
31 | Props.
32 |
33 | )
34 | }
35 |
36 |
37 |
38 | Property
39 | Type/Description
40 |
41 | {
42 | returns.map((returnProp) => (
43 |
44 |
45 |
54 | {returnProp.name}
55 |
56 |
57 |
58 |
59 | {returnProp.type}
60 |
61 |
65 |
66 |
67 | ))
68 | }
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/web/src/components/docs/api/lists/ApiTag.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import type { Tag } from '@lib/typedoc/types/apiReferences'
3 |
4 | interface Props {
5 | kind: 'data' | 'css'
6 | componentName: string
7 | tags: Tag[]
8 | }
9 |
10 | const { kind, componentName, tags } = Astro.props
11 | ---
12 |
13 |
14 |
15 | {kind === 'data' ? 'Data attributes' : 'CSS properties'}
16 |
17 | {
18 | kind === 'data' && (
19 |
20 | Data attributes present on <{componentName} />
{' '}
21 | components.
22 |
23 | )
24 | }
25 | {
26 | kind === 'css' && (
27 |
28 | CSS properties present on <{componentName} />
{' '}
29 | components.
30 |
31 | )
32 | }
33 |
34 |
35 |
36 | Property
37 | Description
38 |
39 | {
40 | tags.map((prop) => (
41 |
42 |
43 |
44 | {prop.name}
45 |
46 |
47 |
48 |
49 |
50 |
51 | ))
52 | }
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/web/src/components/docs/code/Code.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import CopyToClipboard from '@components/docs/code/CopyToClipboard'
3 | import RawCode from '@components/docs/code/RawCode.astro'
4 |
5 | interface Props {
6 | code: string
7 | lang: 'html' | 'tsx' | 'js' | 'css' | 'bash'
8 | copyButton?: boolean
9 | }
10 |
11 | const { code, lang, copyButton } = Astro.props
12 |
13 | const trimmedCode = code.trim()
14 | ---
15 |
16 |
17 |
18 | {copyButton === true && }
19 |
20 |
--------------------------------------------------------------------------------
/web/src/components/docs/code/CopyToClipboard.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | createEffect,
3 | createSignal,
4 | onCleanup,
5 | type VoidComponent,
6 | } from 'solid-js'
7 |
8 | const CopyToClipboard: VoidComponent<{ code: string }> = (props) => {
9 | const [copied, setCopied] = createSignal(false)
10 |
11 | const copyToClipboard = async () => {
12 | await navigator.clipboard.writeText(props.code)
13 | setCopied(true)
14 | }
15 |
16 | createEffect(() => {
17 | let timeout: NodeJS.Timeout | undefined
18 | if (copied()) {
19 | timeout = setTimeout(() => setCopied(false), 2000)
20 | }
21 | onCleanup(() => clearTimeout(timeout))
22 | })
23 |
24 | return (
25 | <>
26 | {copied() && (
27 |
28 | copied!
29 |
30 | )}
31 |
35 | {copied() ? 'Code copied' : 'Copy code'}
36 | {copied() ? (
37 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | ) : (
52 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | )}
66 |
67 | >
68 | )
69 | }
70 |
71 | export default CopyToClipboard
72 |
--------------------------------------------------------------------------------
/web/src/components/docs/code/RawCode.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import { codeToHtml, createCssVariablesTheme } from 'shiki'
3 | import CopyToClipboard from '@components/docs/code/CopyToClipboard'
4 |
5 | interface Props {
6 | code: string
7 | lang: 'html' | 'tsx' | 'ts' | 'js' | 'css' | 'bash'
8 | copyButton?: boolean
9 | }
10 |
11 | const { code, lang, copyButton } = Astro.props
12 |
13 | const trimmedCode = code.trim()
14 |
15 | // const theme = createCssVariablesTheme({
16 | // name: 'css-variables',
17 | // variablePrefix: '--shiki-',
18 | // variableDefaults: {},
19 | // fontStyle: true,
20 | // })
21 |
22 | const codeHtml = await codeToHtml(trimmedCode, {
23 | lang,
24 | // theme: theme,
25 | theme: 'nord',
26 | })
27 | ---
28 |
29 |
33 | {copyButton === true && }
34 |
--------------------------------------------------------------------------------
/web/src/components/docs/headings/H2.astro:
--------------------------------------------------------------------------------
1 | ---
2 | const props = Astro.props
3 | ---
4 |
5 |
36 |
--------------------------------------------------------------------------------
/web/src/components/docs/headings/H3.astro:
--------------------------------------------------------------------------------
1 | ---
2 | const props = Astro.props
3 | ---
4 |
5 |
36 |
--------------------------------------------------------------------------------
/web/src/components/docs/nav/NavLink.astro:
--------------------------------------------------------------------------------
1 | ---
2 | interface Props {
3 | name: string
4 | href: string
5 | }
6 |
7 | const { name, href } = Astro.props
8 |
9 | const isActive = href === Astro.url.pathname
10 | ---
11 |
12 |
21 |
22 |
{name}
23 |
26 | {name}
27 |
28 |
29 |
32 | 🐦️
33 |
34 |
35 |
--------------------------------------------------------------------------------
/web/src/components/docs/nav/Navigation.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import NavLink from '@components/docs/nav/NavLink.astro'
3 | ---
4 |
5 |
6 |
7 |
Overview
11 |
12 |
13 |
14 |
15 |
16 |
Animation
20 |
21 |
22 |
23 |
24 |
25 |
26 |
Primitives
30 |
31 |
32 |
33 |
40 |
41 |
--------------------------------------------------------------------------------
/web/src/components/docs/primitives/Dialog.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/web/src/components/docs/primitives/Disclosure.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/web/src/components/docs/primitives/Drawer.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/web/src/components/docs/primitives/PrimitivesOverview.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import AccordionSvg from '@components/docs/primitives/Accordion.svg?raw'
3 | import DialogSvg from '@components/docs/primitives/Dialog.svg?raw'
4 | import DisclosureSvg from '@components/docs/primitives/Disclosure.svg?raw'
5 | import DrawerSvg from '@components/docs/primitives/Drawer.svg?raw'
6 | import OtpFieldSvg from '@components/docs/primitives/OtpField.svg?raw'
7 | import PopoverSvg from '@components/docs/primitives/Popover.svg?raw'
8 | import ResizableSvg from '@components/docs/primitives/Resizable.svg?raw'
9 | import TooltipSvg from '@components/docs/primitives/Tooltip.svg?raw'
10 |
11 | const primitives = [
12 | {
13 | illustration: AccordionSvg,
14 | name: 'Accordion',
15 | href: '/docs/primitives/accordion/',
16 | },
17 | {
18 | illustration: DialogSvg,
19 | name: 'Dialog',
20 | href: '/docs/primitives/dialog/',
21 | },
22 | {
23 | illustration: DisclosureSvg,
24 | name: 'Disclosure',
25 | href: '/docs/primitives/disclosure/',
26 | },
27 | {
28 | illustration: DrawerSvg,
29 | name: 'Drawer',
30 | href: '/docs/primitives/drawer/',
31 | },
32 | {
33 | illustration: OtpFieldSvg,
34 | name: 'OTP Field',
35 | href: '/docs/primitives/otp-field/',
36 | },
37 | {
38 | illustration: PopoverSvg,
39 | name: 'Popover',
40 | href: '/docs/primitives/popover/',
41 | },
42 | {
43 | illustration: ResizableSvg,
44 | name: 'Resizable',
45 | href: '/docs/primitives/resizable/',
46 | },
47 | {
48 | illustration: TooltipSvg,
49 | name: 'Tooltip',
50 | href: '/docs/primitives/tooltip/',
51 | },
52 | ]
53 | ---
54 |
55 |
72 |
--------------------------------------------------------------------------------
/web/src/components/docs/primitives/Resizable.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/web/src/components/docs/primitives/Tooltip.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/web/src/components/docs/search/SearchDialog.tsx:
--------------------------------------------------------------------------------
1 | import { createEffect, createSignal, onCleanup } from 'solid-js'
2 | import Search, { type SearchResult } from '@components/docs/search/Search'
3 | import clsx from 'clsx'
4 | import Dialog from '@corvu/dialog'
5 |
6 | const SearchDialog = () => {
7 | const [open, setOpen] = createSignal(false)
8 | const [searchValue, setSearchValue] = createSignal('')
9 | const [result, setResult] = createSignal(null)
10 |
11 | createEffect(() => {
12 | const urlParams = new URLSearchParams(window.location.search)
13 | if (urlParams.has('s')) {
14 | setOpen(true)
15 | setSearchValue(urlParams.get('s')!)
16 | urlParams.delete('s')
17 | window.history.replaceState(
18 | null,
19 | document.title,
20 | window.location.pathname + urlParams.toString(),
21 | )
22 | }
23 | })
24 |
25 | createEffect(() => {
26 | const handleKeyDown = (e: KeyboardEvent) => {
27 | if (e.metaKey && e.key === 'k') {
28 | setOpen((open) => !open)
29 | }
30 | }
31 |
32 | window.addEventListener('keydown', handleKeyDown)
33 |
34 | onCleanup(() => {
35 | window.removeEventListener('keydown', handleKeyDown)
36 | })
37 | })
38 |
39 | return (
40 | {
43 | setOpen(open)
44 | if (open) return
45 | setSearchValue('')
46 | setResult({})
47 | }}
48 | restoreScrollPosition={false}
49 | >
50 | {(props) => (
51 | <>
52 |
53 |
59 |
60 |
61 | Search
62 |
63 | ⌘K
64 |
65 |
66 |
67 |
68 |
69 | props.setOpen(false)}
75 | />
76 |
77 |
78 | select
79 |
80 | prev/next
81 |
82 | exit
83 |
84 |
85 |
86 | >
87 | )}
88 |
89 | )
90 | }
91 |
92 | const KeyboardShortcut = (props: { key: string; class?: string }) => {
93 | return (
94 |
100 | {props.key}
101 |
102 | )
103 | }
104 |
105 | export default SearchDialog
106 |
--------------------------------------------------------------------------------
/web/src/components/docs/search/SearchDrawer.tsx:
--------------------------------------------------------------------------------
1 | import { createEffect, createSignal } from 'solid-js'
2 | import Search, { type SearchResult } from '@components/docs/search/Search'
3 | import Drawer from '@corvu/drawer'
4 |
5 | const SearchDrawer = () => {
6 | const [open, setOpen] = createSignal(false)
7 | const [searchValue, setSearchValue] = createSignal('')
8 | const [result, setResult] = createSignal(null)
9 |
10 | createEffect(() => {
11 | const urlParams = new URLSearchParams(window.location.search)
12 | if (urlParams.has('s')) {
13 | setOpen(true)
14 | setSearchValue(urlParams.get('s')!)
15 | urlParams.delete('s')
16 | window.history.replaceState(
17 | null,
18 | document.title,
19 | window.location.pathname + urlParams.toString(),
20 | )
21 | }
22 | })
23 |
24 | return (
25 | {
28 | setOpen(open)
29 | if (open) return
30 | setSearchValue('')
31 | setResult({})
32 | }}
33 | restoreScrollPosition={false}
34 | >
35 | {(props) => (
36 | <>
37 |
38 |
44 |
45 |
46 | Search docs
47 |
48 |
49 |
57 |
58 | props.setOpen(false)}
64 | />
65 |
66 |
67 | >
68 | )}
69 |
70 | )
71 | }
72 |
73 | export default SearchDrawer
74 |
--------------------------------------------------------------------------------
/web/src/components/docs/search/SearchItem.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx'
2 | import type { SearchItemType } from '@components/docs/search/Search'
3 |
4 | const SearchItem = (props: {
5 | item: SearchItemType
6 | onMouseMove: () => void
7 | isActive: boolean
8 | closeSearch: () => void
9 | }) => {
10 | return (
11 |
12 |
21 | {props.item.hierarchy}
22 |
27 |
28 |
29 | )
30 | }
31 |
32 | export default SearchItem
33 |
--------------------------------------------------------------------------------
/web/src/components/docs/utilities/UtilitiesOverview.astro:
--------------------------------------------------------------------------------
1 | ---
2 | // import UtilitySvg from '@components/docs/utilities/Utility.svg?raw'
3 |
4 | const utilities = [
5 | {
6 | emoji: '🛠️',
7 | name: 'dismissible',
8 | href: '/docs/utilities/dismissible/',
9 | },
10 | {
11 | emoji: '🛠️',
12 | name: 'focusTrap',
13 | href: '/docs/utilities/focus-trap/',
14 | },
15 | {
16 | emoji: '🛠️',
17 | name: 'list',
18 | href: '/docs/utilities/list/',
19 | },
20 | {
21 | emoji: '🛠️',
22 | name: 'persistent',
23 | href: '/docs/utilities/persistent/',
24 | },
25 | {
26 | emoji: '🛠️',
27 | name: 'presence',
28 | href: '/docs/utilities/presence/',
29 | },
30 | {
31 | emoji: '🛠️',
32 | name: 'preventScroll',
33 | href: '/docs/utilities/prevent-scroll/',
34 | },
35 | {
36 | emoji: '🛠️',
37 | name: 'transitionSize',
38 | href: '/docs/utilities/transition-size/',
39 | },
40 | ]
41 | ---
42 |
43 |
61 |
--------------------------------------------------------------------------------
/web/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/web/src/examples/hover-card/createMouseDistanceFromCenter.ts:
--------------------------------------------------------------------------------
1 | import { createElementSize } from '@solid-primitives/resize-observer'
2 | import { createMousePosition } from '@solid-primitives/mouse'
3 |
4 | import { getDOMRootPosition } from './getDOMRootPosition'
5 | import { vec2 } from 'ararajs'
6 | import { type Accessor } from 'solid-js'
7 |
8 | export function createMouseDistanceFromCenter(
9 | container: Accessor,
10 | ) {
11 | const containerSize = createElementSize(container)
12 | const mouse = createMousePosition()
13 |
14 | const mouseDistanceFromCenter = (): vec2 => {
15 | const origin = getDOMRootPosition(container())
16 |
17 | const rawTargetX = mouse.x - origin.left
18 | const minX = 0
19 | const maxX = containerSize.width ?? 0
20 | const targetX = Math.max(minX, Math.min(rawTargetX, maxX))
21 |
22 | const rawTargetY = mouse.y - origin.top
23 | const minY = 0
24 | const maxY = containerSize.height ?? 0
25 | const targetY = Math.max(minY, Math.min(rawTargetY, maxY))
26 |
27 | const relativeX = targetX - (containerSize.width ?? 0) / 2
28 | const relativeY = targetY - (containerSize.height ?? 0) / 2
29 |
30 | return [relativeX, relativeY]
31 | }
32 |
33 | return mouseDistanceFromCenter
34 | }
35 |
--------------------------------------------------------------------------------
/web/src/examples/hover-card/getDOMRootPosition.ts:
--------------------------------------------------------------------------------
1 | export function getDOMRootPosition(element: HTMLElement | null | undefined): {
2 | top: number
3 | left: number
4 | } {
5 | if (!element) {
6 | return { top: 0, left: 0 }
7 | }
8 | const parentPosition = getDOMRootPosition(element.offsetParent as HTMLElement)
9 | return {
10 | top: element.offsetTop + parentPosition.top,
11 | left: element.offsetLeft + parentPosition.left,
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/web/src/examples/primitives/tailwind.js:
--------------------------------------------------------------------------------
1 | import animatePlugin from 'tailwindcss-animate'
2 | import araraPlugin from '@corvu/tailwind'
3 | import formsPlugin from '@tailwindcss/forms'
4 |
5 | /** @type {import('tailwindcss').Config} */
6 | export default {
7 | content: ['./src/**/*.{ts,tsx}'],
8 | theme: {
9 | extend: {
10 | colors: {
11 | arara: {
12 | bg: '#f3f1fe',
13 | 100: '#e6e2fd',
14 | 200: '#d4cbfb',
15 | 300: '#bcacf6',
16 | 400: '#a888f1',
17 | text: '#180f24',
18 | },
19 | },
20 | },
21 | },
22 | plugins: [animatePlugin, araraPlugin, formsPlugin],
23 | }
24 |
--------------------------------------------------------------------------------
/web/src/examples/sine-wave/SineWave2DDemo.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from '@lib/cn'
2 | import { createElementSize } from '@solid-primitives/resize-observer'
3 | import { createSignal } from 'solid-js'
4 | import { createSineWave2D } from 'ararajs'
5 |
6 | export function SineWave2DDemo(props: { class?: string; ballClass?: string }) {
7 | const [container, setContainer] = createSignal()
8 | const containerSize = createElementSize(container)
9 |
10 | const [ball, setBall] = createSignal()
11 | const ballSize = createElementSize(ball)
12 |
13 | const radius = 90
14 | const freq = 1
15 | const [body] = createSineWave2D({
16 | amplitude: [radius, radius],
17 | frequency: [freq, freq],
18 | phase: [Math.PI / 2, 0],
19 | })
20 |
21 | const xOffset = () => {
22 | return (
23 | body.position[0] +
24 | -(ballSize.width ?? 0) / 2 +
25 | (containerSize.width ?? 0) / 2
26 | )
27 | }
28 |
29 | const yOffset = () => {
30 | return (
31 | body.position[1] +
32 | -(ballSize.height ?? 0) / 2 +
33 | (containerSize.height ?? 0) / 2
34 | )
35 | }
36 |
37 | const paddingY = 32
38 | return (
39 |
60 | )
61 | }
62 |
--------------------------------------------------------------------------------
/web/src/examples/sine-wave/SineWaveDemo.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from '@lib/cn'
2 | import { createElementSize } from '@solid-primitives/resize-observer'
3 | import { createSignal } from 'solid-js'
4 | import { createSineWave } from 'ararajs'
5 |
6 | export function SineWaveDemo(props: {
7 | class?: string
8 | ballClass?: string
9 | frequency?: () => number
10 | }) {
11 | const [body] = createSineWave(() => ({
12 | amplitude: 90,
13 | frequency: props.frequency?.() ?? 1,
14 | }))
15 |
16 | const [container, setContainer] = createSignal()
17 | const containerSize = createElementSize(container)
18 | const [ball, setBall] = createSignal()
19 | const ballSize = createElementSize(ball)
20 |
21 | const xOffset = () => {
22 | return (
23 | body.position +
24 | -(ballSize.width ?? 0) / 2 +
25 | (containerSize.width ?? 0) / 2
26 | )
27 | }
28 |
29 | return (
30 |
48 | )
49 | }
50 |
--------------------------------------------------------------------------------
/web/src/examples/sine-wave/SliderSineWaveDemo.tsx:
--------------------------------------------------------------------------------
1 | import { createSignal } from 'solid-js'
2 | import { SineWaveDemo } from './SineWaveDemo'
3 | import { Slider } from '@kobalte/core/slider'
4 |
5 | export function SliderSineWaveDemo(props: {
6 | class?: string
7 | ballClass?: string
8 | }) {
9 | const [value, setValue] = createSignal([1])
10 | const frequency = () => value()[0]
11 |
12 | return (
13 | <>
14 |
15 |
23 |
24 | Frequency
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | >
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/web/src/examples/spring/Spring2DDemo.tsx:
--------------------------------------------------------------------------------
1 | import { createElementSize } from '@solid-primitives/resize-observer'
2 | import { createSignal } from 'solid-js'
3 | import { createSpring2D, vec2 } from 'ararajs'
4 | import { cn } from '@lib/cn'
5 | import { createMousePosition } from '@solid-primitives/mouse'
6 | import { getDOMRootPosition } from './getDOMRootPosition'
7 |
8 | export function Spring2DDemo() {
9 | const [container, setContainer] = createSignal()
10 | const containerSize = createElementSize(container)
11 |
12 | const [ball, setBall] = createSignal()
13 | const ballSize = createElementSize(ball)
14 | const padding = 32 // px
15 |
16 | const mouse = createMousePosition()
17 | const target = (): vec2 => {
18 | const origin = getDOMRootPosition(container())
19 |
20 | const rawTargetX =
21 | mouse.x - origin.left - padding - (ballSize.width ?? 0) / 2
22 | const minX = 0
23 | const maxX =
24 | (containerSize.width ?? 0) - (ballSize.width ?? 0) - 2 * padding
25 | const targetX = Math.max(minX, Math.min(rawTargetX, maxX))
26 |
27 | const rawTargetY =
28 | mouse.y - origin.top - padding - (ballSize.height ?? 0) / 2
29 | const minY = 0
30 | const maxY =
31 | (containerSize.height ?? 0) - (ballSize.height ?? 0) - 2 * padding
32 | const targetY = Math.max(minY, Math.min(rawTargetY, maxY))
33 |
34 | return [targetX, targetY]
35 | }
36 |
37 | const [body] = createSpring2D(() => ({
38 | target: target(),
39 | stiffness: 32,
40 | damping: 16,
41 | }))
42 |
43 | return (
44 |
65 | )
66 | }
67 |
68 | function MoveMouseOverlay() {
69 | return (
70 |
71 |
72 |
73 | Move Mouse
74 |
75 |
76 | )
77 | }
78 |
79 | function PointerSVG(props: { class?: string }) {
80 | return (
81 |
91 |
92 |
93 |
94 | )
95 | }
96 |
--------------------------------------------------------------------------------
/web/src/examples/spring/SpringDemo.tsx:
--------------------------------------------------------------------------------
1 | import { createElementSize } from '@solid-primitives/resize-observer'
2 | import { createSignal } from 'solid-js'
3 | import { createSpring } from 'ararajs'
4 |
5 | export function SpringDemo() {
6 | const [container, setContainer] = createSignal()
7 | const containerSize = createElementSize(container)
8 |
9 | const [ball, setBall] = createSignal()
10 | const ballSize = createElementSize(ball)
11 |
12 | const [targetState, setTargetState] = createSignal(false)
13 | const target = () =>
14 | targetState() ? (ballSize.width ?? 0) : (containerSize.width ?? 0) - 120
15 |
16 | const [body] = createSpring(() => ({
17 | target: target(),
18 | stiffness: 66,
19 | damping: 16,
20 | }))
21 |
22 | return (
23 |
24 |
42 |
setTargetState(!targetState())}
44 | class="flex w-fit flex-row items-center gap-4 rounded-lg bg-arara-300 px-4 py-3 text-lg font-medium transition-all duration-100 active:translate-y-0.5"
45 | >
46 |
Change Target
47 |
48 |
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/web/src/examples/spring/createMouseDistanceFromCenter.ts:
--------------------------------------------------------------------------------
1 | import { createElementSize } from '@solid-primitives/resize-observer'
2 | import { createMousePosition } from '@solid-primitives/mouse'
3 |
4 | import { getDOMRootPosition } from './getDOMRootPosition'
5 | import { vec2 } from 'ararajs'
6 | import { type Accessor } from 'solid-js'
7 |
8 | export function createMouseDistanceFromCenter(
9 | container: Accessor,
10 | ) {
11 | const containerSize = createElementSize(container)
12 | const mouse = createMousePosition()
13 |
14 | const mouseDistanceFromCenter = (): vec2 => {
15 | const origin = getDOMRootPosition(container())
16 |
17 | const rawTargetX = mouse.x - origin.left
18 | const minX = 0
19 | const maxX = containerSize.width ?? 0
20 | const targetX = Math.max(minX, Math.min(rawTargetX, maxX))
21 |
22 | const rawTargetY = mouse.y - origin.top
23 | const minY = 0
24 | const maxY = containerSize.height ?? 0
25 | const targetY = Math.max(minY, Math.min(rawTargetY, maxY))
26 |
27 | const relativeX = targetX - (containerSize.width ?? 0) / 2
28 | const relativeY = targetY - (containerSize.height ?? 0) / 2
29 |
30 | return [relativeX, relativeY]
31 | }
32 |
33 | return mouseDistanceFromCenter
34 | }
35 |
--------------------------------------------------------------------------------
/web/src/examples/spring/getDOMRootPosition.ts:
--------------------------------------------------------------------------------
1 | export function getDOMRootPosition(element: HTMLElement | null | undefined): {
2 | top: number
3 | left: number
4 | } {
5 | if (!element) {
6 | return { top: 0, left: 0 }
7 | }
8 | const parentPosition = getDOMRootPosition(element.offsetParent as HTMLElement)
9 | return {
10 | top: element.offsetTop + parentPosition.top,
11 | left: element.offsetLeft + parentPosition.left,
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/web/src/examples/utilities/list/multi.tsx:
--------------------------------------------------------------------------------
1 | import { createEffect, createSignal, For, onCleanup } from 'solid-js'
2 | import clsx from 'clsx'
3 | import { createMultiList } from 'solid-list'
4 |
5 | const ITEMS = ['Rook', 'Chough', 'Raven', 'Jackdaw', 'Magpie', 'Jay']
6 |
7 | const MultiListExample = () => {
8 | const [refs, setRefs] = createSignal([])
9 |
10 | const {
11 | cursor,
12 | active,
13 | setCursorActive,
14 | selected,
15 | toggleSelected,
16 | onKeyDown,
17 | } = createMultiList({
18 | items: ITEMS,
19 | vimMode: true,
20 | onCursorChange: (item) => {
21 | if (item === null) return
22 | refs()[ITEMS.indexOf(item)]?.focus()
23 | },
24 | })
25 |
26 | createEffect(() => {
27 | document.addEventListener('mousedown', onMouseDown)
28 | onCleanup(() => document.removeEventListener('mousedown', onMouseDown))
29 | })
30 | const onMouseDown = () => setCursorActive(null)
31 |
32 | return (
33 |
34 |
35 | {(crow) => (
36 | setRefs((refs) => [...refs, ref])}
38 | role="checkbox"
39 | aria-checked={selected().includes(crow)}
40 | tabindex="0"
41 | onFocus={() => {
42 | if (cursor() !== null) return
43 | setCursorActive(crow)
44 | }}
45 | onKeyDown={(e) => {
46 | if (e.key === 'x' || e.key === ' ' || e.key === 'Enter') {
47 | toggleSelected(crow)
48 | e.preventDefault()
49 | return
50 | }
51 | onKeyDown(e)
52 | }}
53 | onClick={() => {
54 | setCursorActive(crow)
55 | toggleSelected(crow)
56 | }}
57 | class={clsx(
58 | 'flex w-full max-w-80 cursor-pointer items-center space-x-2 rounded-md px-4 py-2 transition-all hover:scale-[1.01] hover:bg-arara-200 focus:outline-none',
59 | {
60 | 'bg-arara-bg': !active().includes(crow),
61 | 'bg-arara-200 scale-[1.01]': active().includes(crow),
62 | },
63 | )}
64 | >
65 |
81 |
{crow}
82 |
83 | )}
84 |
85 |
86 | )
87 | }
88 |
89 | export default MultiListExample
90 |
--------------------------------------------------------------------------------
/web/src/layouts/Docs.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import '@assets/global.css'
3 | import '@assets/shiki.css'
4 | import getApiReferenceHeadings from '@lib/typedoc/headings'
5 | import Head from '@components/Head.astro'
6 | import Navigation from '@components/docs/nav/Navigation.astro'
7 | import TableOfContents from '@components/docs/TableOfContents'
8 | import ThemeScript from '@components/ThemeScript.astro'
9 | import Topbar from '@components/Topbar.astro'
10 |
11 | interface Props {
12 | frontmatter: {
13 | title: string
14 | description: string
15 | image: string
16 | }
17 | headings: {
18 | depth: number
19 | text: string
20 | slug: string
21 | }[]
22 | }
23 |
24 | const { frontmatter, headings } = Astro.props
25 | ---
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
43 |
44 |
45 |
46 |
47 |
48 |
51 |
52 |
53 |
60 |
67 |
68 |
69 |
71 |
--------------------------------------------------------------------------------
/web/src/lib/cn.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from 'clsx'
2 | import { twMerge } from 'tailwind-merge'
3 |
4 | export type ClassProps = {
5 | class?: string
6 | }
7 |
8 | export function cn(...inputs: ClassValue[]) {
9 | return twMerge(clsx(inputs))
10 | }
11 |
--------------------------------------------------------------------------------
/web/src/lib/packageMetas.ts:
--------------------------------------------------------------------------------
1 | import core from '../../../packages/core/package.json'
2 |
3 | const packageMetas: {
4 | [library: string]: {
5 | version: string
6 | npmHref: string
7 | sourceHref: string
8 | }
9 | } = {
10 | core: {
11 | version: core.version,
12 | npmHref: 'https://www.npmjs.com/package/ararajs',
13 | sourceHref: 'https://github.com/FelipeEmos/ararajs/tree/main/packages/core',
14 | },
15 | }
16 |
17 | export default packageMetas
18 |
--------------------------------------------------------------------------------
/web/src/lib/typedoc/headings.ts:
--------------------------------------------------------------------------------
1 | import libraries from '@lib/typedoc/libraries'
2 |
3 | const getApiReferenceHeadings = (libraryName: string) => {
4 | const library = libraries.find((lib) => lib.name === libraryName)
5 | if (!library) return []
6 |
7 | const headings: {
8 | text: string
9 | slug: string
10 | }[] = []
11 |
12 | for (const [name, item] of Object.entries(library.items)) {
13 | headings.push({
14 | text:
15 | item.kind === 'component' || item.kind === 'inherited-component'
16 | ? `<${name} />`
17 | : name,
18 | slug: encodeURIComponent(name),
19 | })
20 | }
21 |
22 | return headings
23 | }
24 |
25 | export default getApiReferenceHeadings
26 |
--------------------------------------------------------------------------------
/web/src/lib/typedoc/libraries.ts:
--------------------------------------------------------------------------------
1 | import type { ApiDeclaration } from '@lib/typedoc/types/typedoc'
2 | import coreTypedoc from '../../../../packages/core/api.json'
3 | import type { Library } from '@lib/typedoc/types/specifications'
4 |
5 | export const Typedoc: { [key: string]: ApiDeclaration } = {
6 | ararajs: coreTypedoc as ApiDeclaration,
7 | }
8 |
9 | const AraraJsAPI: Library = {
10 | api: Typedoc['ararajs'],
11 | name: 'AraraJS',
12 | items: {
13 | Root: {
14 | kind: 'component',
15 | sorting: [
16 | 'expanded',
17 | 'onExpandedChange',
18 | 'initialExpanded',
19 | 'collapseBehavior',
20 | 'disclosureId',
21 | 'contextId',
22 | ],
23 | },
24 | Trigger: {
25 | kind: 'component',
26 | sorting: ['as', 'contextId'],
27 | },
28 | Content: {
29 | kind: 'component',
30 | sorting: ['as', 'forceMount', 'contextId'],
31 | },
32 | useContext: {
33 | kind: 'context',
34 | sorting: [
35 | 'expanded',
36 | 'setExpanded',
37 | 'collapseBehavior',
38 | 'disclosureId',
39 | 'contentPresent',
40 | 'contentRef',
41 | 'contentSize',
42 | ],
43 | },
44 | RootChildrenProps: {
45 | kind: 'childrenProps',
46 | sorting: [
47 | 'expanded',
48 | 'setExpanded',
49 | 'collapseBehavior',
50 | 'disclosureId',
51 | 'contentPresent',
52 | 'contentRef',
53 | 'contentSize',
54 | ],
55 | },
56 | },
57 | }
58 |
59 | console.log("CRAZY", AraraJsAPI)
60 |
61 | export { AraraJsAPI }
62 |
63 | export default [AraraJsAPI]
64 |
--------------------------------------------------------------------------------
/web/src/lib/typedoc/resolve/resolve.ts:
--------------------------------------------------------------------------------
1 | import type { ApiReference } from '@lib/typedoc/types/apiReferences'
2 | import type { Library } from '@lib/typedoc/types/specifications'
3 | import resolveChildrenProps from '@lib/typedoc/resolve/resolveChildrenProps'
4 | import resolveComponent from '@lib/typedoc/resolve/resolveComponent'
5 | import resolveContext from '@lib/typedoc/resolve/resolveContext'
6 | import resolveFunction from '@lib/typedoc/resolve/resolveFunction'
7 | import resolveInheritedComponent from '@lib/typedoc/resolve/resolveInheritedComponent'
8 | import resolveInheritedContext from '@lib/typedoc/resolve/resolveInheritedContext'
9 | import resolveSimple from '@lib/typedoc/resolve/resolveSimple'
10 |
11 | const resolveLibrary = (library: Library): ApiReference[] => {
12 | const apiReferences: ApiReference[] = []
13 | // FIXME: this is a temporary fix for the first deploy
14 | // for (const [name, item] of Object.entries(library.items)) {
15 | // switch (item.kind) {
16 | // case 'component':
17 | // apiReferences.push(resolveComponent(library.api, name, item))
18 | // break
19 | // case 'inherited-component':
20 | // apiReferences.push(resolveInheritedComponent(library.api, name, item))
21 | // break
22 | // case 'context':
23 | // apiReferences.push(resolveContext(library.api, name, item))
24 | // break
25 | // case 'inherited-context':
26 | // apiReferences.push(resolveInheritedContext(library.api, name, item))
27 | // break
28 | // case 'childrenProps':
29 | // apiReferences.push(resolveChildrenProps(library.api, name, item))
30 | // break
31 | // case 'simple':
32 | // apiReferences.push(resolveSimple(library.api, name))
33 | // break
34 | // case 'function':
35 | // apiReferences.push(resolveFunction(library.api, name, item))
36 | // break
37 | // case 'temporary':
38 | // switch (name) {
39 | // case 'createPersistent':
40 | // apiReferences.push({
41 | // name,
42 | // kind: 'function',
43 | // props: [
44 | // {
45 | // name: 'component',
46 | // defaultHtml: null,
47 | // type: '() => JSX.Element',
48 | // descriptionHtml: '',
49 | // isFunction: true,
50 | // },
51 | // ],
52 | // returns: [
53 | // {
54 | // name: 'persistedComponent',
55 | // type: 'Accessor',
56 | // descriptionHtml: '',
57 | // isFunction: true,
58 | // },
59 | // ],
60 | // })
61 | // break
62 | // }
63 | // }
64 | // }
65 |
66 | return apiReferences
67 | }
68 |
69 | export default resolveLibrary
70 |
--------------------------------------------------------------------------------
/web/src/lib/typedoc/resolve/resolveInheritedComponent.ts:
--------------------------------------------------------------------------------
1 | import type { ApiDeclaration, Comment } from '@lib/typedoc/types/typedoc'
2 | import type { ApiReference, Tag } from '@lib/typedoc/types/apiReferences'
3 | import { formatText } from '@lib/typedoc/resolve/lib'
4 | import type { InheritedComponentTypeSpecification } from '@lib/typedoc/types/specifications'
5 |
6 | const resolveInheritedComponent = (
7 | api: ApiDeclaration,
8 | name: string,
9 | inheritedComponent: InheritedComponentTypeSpecification,
10 | ): ApiReference => {
11 | const componentDeclaration = api.children.find((child) => child.name === name)
12 | if (!componentDeclaration) {
13 | throw new Error(`Component declaration not found: ${name}`)
14 | }
15 | const dataTags = getTags('data', componentDeclaration.comment)
16 | const cssTags = getTags('css', componentDeclaration.comment)
17 |
18 | return {
19 | name,
20 | kind: 'inherited-component',
21 | descriptionHtml: formatText(componentDeclaration.comment?.summary),
22 | inherits: inheritedComponent.inherits,
23 | data: dataTags,
24 | css: cssTags,
25 | }
26 | }
27 |
28 | const getTags = (name: 'data' | 'css', comment?: Comment): Tag[] => {
29 | if (!comment || !comment.blockTags) {
30 | return []
31 | }
32 | const dataTags = comment.blockTags.filter((tag) => tag.tag === `@${name}`)
33 | return dataTags.map((dataTag) => {
34 | return {
35 | name: dataTag.content[0].text.slice(1, -1),
36 | descriptionHtml: formatText(dataTag.content.slice(1)).replace(' - ', ''),
37 | }
38 | })
39 | }
40 |
41 | export default resolveInheritedComponent
42 |
--------------------------------------------------------------------------------
/web/src/lib/typedoc/resolve/resolveInheritedContext.ts:
--------------------------------------------------------------------------------
1 | import type { ApiDeclaration } from '@lib/typedoc/types/typedoc'
2 | import type { ApiReference } from '@lib/typedoc/types/apiReferences'
3 | import { formatText } from '@lib/typedoc/resolve/lib'
4 | import type { InheritedContextTypeSpecification } from '@lib/typedoc/types/specifications'
5 |
6 | const resolveInheritedContext = (
7 | api: ApiDeclaration,
8 | name: string,
9 | inheritedContext: InheritedContextTypeSpecification,
10 | ): ApiReference => {
11 | const contextDeclaration = api.children.find((child) => child.name === name)
12 | if (!contextDeclaration) {
13 | throw new Error(`Context declaration not found: ${name}`)
14 | }
15 | return {
16 | name,
17 | kind: 'inherited-context',
18 | descriptionHtml: formatText(contextDeclaration.comment?.summary),
19 | inherits: inheritedContext.inherits,
20 | }
21 | }
22 |
23 | export default resolveInheritedContext
24 |
--------------------------------------------------------------------------------
/web/src/lib/typedoc/resolve/resolveSimple.ts:
--------------------------------------------------------------------------------
1 | import type { ApiDeclaration } from '@lib/typedoc/types/typedoc'
2 | import type { ApiReference } from '@lib/typedoc/types/apiReferences'
3 | import { resolveTypeTopLevel } from '@lib/typedoc/resolve/lib'
4 |
5 | const resolveSimple = (api: ApiDeclaration, name: string): ApiReference => {
6 | const simpleDeclaration = api.children.find((child) => child.name === name)
7 | if (
8 | !simpleDeclaration ||
9 | (!simpleDeclaration.type && !simpleDeclaration.signatures?.[0].type)
10 | ) {
11 | throw new Error(`Simple declaration not found: ${name}`)
12 | }
13 |
14 | let type
15 | if (simpleDeclaration.type) {
16 | type = resolveTypeTopLevel(simpleDeclaration.type)
17 | } else {
18 | type = resolveTypeTopLevel(
19 | simpleDeclaration.signatures![0].type,
20 | undefined,
21 | [],
22 | )
23 | }
24 |
25 | return {
26 | name,
27 | kind: 'simple',
28 | type: `type ${name} = ${type}`,
29 | }
30 | }
31 |
32 | export default resolveSimple
33 |
--------------------------------------------------------------------------------
/web/src/lib/typedoc/types/apiReferences.ts:
--------------------------------------------------------------------------------
1 | import type { Library } from '@lib/typedoc/types/specifications'
2 |
3 | export type ApiReference =
4 | | ComponentApiReference
5 | | InheritedComponentApiReference
6 | | ContextApiReference
7 | | InheritedContextApiReference
8 | | ChildrenPropsApiReference
9 | | FunctionApiReference
10 | | SimpleApiReference
11 |
12 | export type ComponentApiReference = {
13 | name: string
14 | kind: 'component'
15 | descriptionHtml: string
16 | inherits: {
17 | library: Library
18 | name: string
19 | } | null
20 | props: PropType[]
21 | data: Tag[]
22 | css: Tag[]
23 | }
24 |
25 | export type InheritedComponentApiReference = {
26 | name: string
27 | kind: 'inherited-component'
28 | descriptionHtml: string
29 | inherits: {
30 | library: Library
31 | name: string
32 | }
33 | data: Tag[]
34 | css: Tag[]
35 | }
36 |
37 | export type ContextApiReference = {
38 | name: string
39 | kind: 'context'
40 | descriptionHtml: string
41 | returns: ReturnType[]
42 | }
43 |
44 | export type InheritedContextApiReference = {
45 | name: string
46 | kind: 'inherited-context'
47 | descriptionHtml: string
48 | inherits: {
49 | library: Library
50 | name: string
51 | }
52 | }
53 |
54 | export type ChildrenPropsApiReference = {
55 | name: string
56 | kind: 'childrenProps'
57 | descriptionHtml: string
58 | inherits: {
59 | library: Library
60 | name: string
61 | } | null
62 | props: ReturnType[]
63 | }
64 |
65 | export type FunctionApiReference = {
66 | name: string
67 | kind: 'function'
68 | props: PropType[]
69 | returns: ReturnType[] | null
70 | }
71 |
72 | export type SimpleApiReference = {
73 | name: string
74 | kind: 'simple'
75 | type: string
76 | }
77 |
78 | export type PropType = {
79 | name: string
80 | defaultHtml: string | null
81 | type: string
82 | descriptionHtml: string
83 | isFunction: boolean
84 | }
85 |
86 | export type Tag = {
87 | name: string
88 | descriptionHtml: string
89 | }
90 |
91 | export type ReturnType = {
92 | name: string
93 | type: string
94 | descriptionHtml: string
95 | isFunction: boolean
96 | }
97 |
--------------------------------------------------------------------------------
/web/src/lib/typedoc/types/specifications.ts:
--------------------------------------------------------------------------------
1 | import type { ApiDeclaration } from '@lib/typedoc/types/typedoc'
2 |
3 | export type Library = {
4 | api: ApiDeclaration
5 | name: string
6 | items: { [name: string]: TypeSpecification }
7 | }
8 |
9 | export type TypeSpecification =
10 | | ComponentTypeSpecification
11 | | InheritedComponentTypeSpecification
12 | | ContextTypeSpecification
13 | | InheritedContextTypeSpecification
14 | | ChildrenPropsTypeSpecification
15 | | FunctionTypeSpecification
16 | | SimpleTypeSpecification
17 | | TemporaryTypeSpecification
18 |
19 | export type ComponentTypeSpecification = {
20 | isDefaultExport?: boolean
21 | kind: 'component'
22 | sorting: string[]
23 | inherits?: {
24 | library: Library
25 | name: string
26 | }
27 | }
28 |
29 | export type InheritedComponentTypeSpecification = {
30 | kind: 'inherited-component'
31 | inherits: {
32 | library: Library
33 | name: string
34 | }
35 | }
36 |
37 | export type ContextTypeSpecification = {
38 | kind: 'context'
39 | sorting: string[]
40 | }
41 |
42 | export type InheritedContextTypeSpecification = {
43 | kind: 'inherited-context'
44 | inherits: {
45 | library: Library
46 | name: string
47 | }
48 | }
49 |
50 | export type ChildrenPropsTypeSpecification = {
51 | kind: 'childrenProps'
52 | sorting: string[]
53 | inherits?: {
54 | library: Library
55 | name: string
56 | }
57 | }
58 |
59 | export type FunctionTypeSpecification = {
60 | isDefaultExport?: boolean
61 | kind: 'function'
62 | propsSorting: string[]
63 | returnsSorting?: string[]
64 | }
65 |
66 | export type SimpleTypeSpecification = {
67 | kind: 'simple'
68 | }
69 |
70 | export type TemporaryTypeSpecification = {
71 | kind: 'temporary'
72 | }
73 |
--------------------------------------------------------------------------------
/web/src/pages/404.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import '@assets/global.css'
3 | import Fonts from '@components/Fonts.astro'
4 | import LogoDark from '@assets/logo_dark.svg'
5 | import LogoLight from '@assets/logo_light.svg'
6 | import ThemeScript from '@components/ThemeScript.astro'
7 | import Topbar from '@components/Topbar.astro'
8 | ---
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | 404 - arara
18 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
28 |
29 |
34 |
39 |
40 |
41 | 404
44 | Page not found
45 |
46 |
47 |
48 |
49 |
61 |
62 |
64 |
--------------------------------------------------------------------------------
/web/src/pages/docs/animation/animation-callback.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | layout: '@layouts/Docs.astro'
3 | title: Flexible state management of SolidJS UI primitives
4 | description: Learn how to manage the state of arara UI primitives for SolidJS.
5 | image: https://ararajs.vercel.app/banner.jpg
6 | ---
7 |
8 | import Code from '@components/docs/code/Code.astro'
9 | import H2 from '@components/docs/headings/H2.astro'
10 | import H3 from '@components/docs/headings/H3.astro'
11 | import Features from '@components/docs/Features.astro'
12 | export const components = { h2: H2, h3: H3 }
13 |
14 | # Animation Callback
15 |
16 | The primitive `createAnimation` asks you to pass an `AnimationCallback` as an argument. This primitive will start a `RAF` loop and run the callback you passed onto it on every frame. A manual first approach to this API could be to set your own signals inside the loop.
17 |
18 | {
25 | setX(prev => prev + 100 * deltaTime)
26 | })
27 |
28 | function MovingBox() {
29 | return (
30 |
36 | )
37 | }
38 | `} lang='tsx' />
39 |
40 | That is close to how other primitives are made:
41 | number) {
46 | const [pos, setPos] = createSignal(0)
47 | createAnimation(({ deltaTime }) => {
48 | setPos(prev => prev + (velocity?.() ?? 100) * deltaTime)
49 | })
50 | return pos
51 | }
52 |
53 | function MovingBox() {
54 | const x = createLinearMovement()
55 |
56 | return (
57 |
63 | )
64 | }
65 | `} lang='tsx' />
66 |
67 |
68 | Here's the AnimationCallback reference API:
69 | void
74 | `} lang='tsx' />
--------------------------------------------------------------------------------
/web/src/pages/docs/animation/animation-controller.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | layout: '@layouts/Docs.astro'
3 | title: Flexible state management of SolidJS UI primitives
4 | description: Learn how to manage the state of arara UI primitives for SolidJS.
5 | image: https://ararajs.vercel.app/banner.jpg
6 | ---
7 |
8 | import Code from '@components/docs/code/Code.astro'
9 | import H2 from '@components/docs/headings/H2.astro'
10 | import H3 from '@components/docs/headings/H3.astro'
11 | import Features from '@components/docs/Features.astro'
12 | export const components = { h2: H2, h3: H3 }
13 |
14 | # Animation Controller
15 |
16 | The animation controller is the heart of the system. It manages the animation loop using requestAnimationFrame and provides precise timing information to your animations.
17 |
18 |
30 |
31 |
32 | The `createAnimation` primitive also returns an `AnimationController` that can be used to control the animation loop.
33 |
34 | {
37 | console.log(currentTime)
38 | })
39 |
40 | Start
41 | Pause
42 | Stop
43 | `}
44 | lang="tsx"
45 | />
46 |
47 | Say you want to have two animations running at the same time. You can pass a `controller` as the **second** argument of the `createAnimation` primitive, it will use that instead of creating a new controller.
48 |
49 | {
54 | console.log("FIRST ANIMATION", currentTime)
55 | }, () => controller)
56 | createAnimation(({ currentTime }) => {
57 | console.log("SECOND ANIMATION: ", currentTime)
58 | }, () => controller)
59 | createAnimation(({ currentTime }) => {
60 | console.log("THIRD ANIMATION: ", currentTime)
61 | }, () => controller)
62 |
63 | // These controls will affect all three animations
64 | controller.start()
65 | controller.pause()
66 | controller.stop()
67 | `}
68 | lang="tsx"
69 | />
70 |
71 | Each controller provides:
72 |
73 | - `currentTime` since animation start
74 | - `deltaTime` between frames
75 | - Running state management
76 | - Automatic cleanup on component unmount
77 |
--------------------------------------------------------------------------------
/web/src/pages/docs/animation/body.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | layout: '@layouts/Docs.astro'
3 | title: Flexible state management of SolidJS UI primitives
4 | description: Learn how to manage the state of arara UI primitives for SolidJS.
5 | image: https://ararajs.vercel.app/banner.jpg
6 | ---
7 |
8 | import Link from '@components/docs/Link.astro'
9 | import Code from '@components/docs/code/Code.astro'
10 | import H2 from '@components/docs/headings/H2.astro'
11 | import H3 from '@components/docs/headings/H3.astro'
12 | import Features from '@components/docs/Features.astro'
13 | import RawCode from '@components/docs/code/RawCode.astro'
14 | import ExampleWrapper from '@examples/ExampleWrapper'
15 | import { SineWave2DDemo } from '@examples/sine-wave/SineWave2DDemo'
16 | import RawSineWave2DDemo from '@examples/sine-wave/SineWave2DDemo?raw'
17 | export const components = { h2: H2, h3: H3 }
18 |
19 | # Body
20 |
21 | Bodies are very simple. They are just the AraraJS way of outputting animated signals. In case you are interested in the speed in which your signal is changing, for example, you can access the `velocity` and `acceleration` properties of the `Body` object.
22 |
23 |
33 |
34 | ## 2D and 3D variants
35 |
36 | For your convenience, AraraJS provides primitives for 2D and 3D animations as well. For example `createSpring2D` will return an accessor for a `Body2D`
37 |
38 |
46 |
47 |
62 |
63 |
64 |
65 |
66 | The 2D and 3D variants use vectors from the gl-matrix library.
67 | > These types `vec2` and `vec3` are exposed to you by directly importing from AraraJS.
68 |
69 |
--------------------------------------------------------------------------------
/web/src/pages/docs/animation/overview.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | layout: '@layouts/Docs.astro'
3 | title: AraraJS Animation Execution Model Overview
4 | description: Animation Overview
5 | image: https://ararajs.vercel.app/banner.jpg
6 | ---
7 |
8 | import Code from '@components/docs/code/Code.astro'
9 | import H2 from '@components/docs/headings/H2.astro'
10 | import H3 from '@components/docs/headings/H3.astro'
11 | import Features from '@components/docs/Features.astro'
12 |
13 | import RawCode from '@components/docs/code/RawCode.astro'
14 | import ExampleWrapper from '@examples/ExampleWrapper'
15 | import { SineWaveDemo } from '@examples/sine-wave/SineWaveDemo'
16 | import RawSineWaveDemo from '@examples/sine-wave/SineWaveDemo?raw'
17 |
18 | export const components = { h2: H2, h3: H3 }
19 |
20 | # Animation Overview
21 |
22 | AraraJS's execution model is supposed to be minimal, powerful and flexible.
23 | Some of it's features include:
24 |
25 |
33 |
34 | ## Bare Metal API
35 |
36 | The animation system is built around some basic building blocks:
37 |
38 | - Animation Callbacks - Just a function that is called on each frame.
39 | - Animation Controller - Manages the animation loop and timing.
40 |
41 | These basic building blocks don't output a signal for you, they just manage timing and possibly orchestration of your animations.
42 |
43 | ## Body Animations
44 |
45 | To get an output signal in a more convenient way, you may want to create a `Body` animation. Maybe you are not interested in how your signal change, which is the concept of `velocity`, but keep in mind that both `velocity` and `acceleration` are present in the `Body` object returned by the signal in case you need them in some point.
46 |
47 | Maybe you won't use them all, but the primitives come with them.
48 |
49 | ## Composition and Other Primitives
50 |
51 | Some common body animation primitives, like `spring` and `sine wave`, are available to you by the library. They come both in the form of just a callback, a `BodyAnimationPass`, which is a callback that applies changes to the body on every frame, and they also come in more convenient `createSpring`, `createSine`, ... variants.
52 |
53 | [
57 | sineWavePass()
58 | ])
59 | `} lang='tsx' />
60 |
61 |
76 |
77 |
78 |
79 |
80 | Using the `BodyAnimationPass` will give you more flexibility because you can input multiple passes in a `createBodyAnimation` and these passes are applied in order. It's easy to combine multiple Sines and Springs with that, which gives you easings and periodic behaviours.
--------------------------------------------------------------------------------
/web/src/pages/docs/getting-started.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | layout: '@layouts/Docs.astro'
3 | title: Getting Started
4 | description: Installation instructions and basic usage of the library.
5 | image: https://ararajs.vercel.app/banner.jpg
6 | ---
7 | import Code from '@components/docs/code/Code.astro'
8 | import H2 from '@components/docs/headings/H2.astro'
9 | import H3 from '@components/docs/headings/H3.astro'
10 | export const components = { h2: H2, h3: H3 }
11 |
12 | # Getting Started
13 | To install the library
14 |
17 |
--------------------------------------------------------------------------------
/web/src/pages/docs/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | layout: '@layouts/Docs.astro'
3 | title: Introduction to arara
4 | description: An overview of arara and its features.
5 | image: https://ararajs.vercel.app/banner.jpg
6 | ---
7 | import Link from '@components/docs/Link.astro'
8 | import H2 from '@components/docs/headings/H2.astro'
9 | import H3 from '@components/docs/headings/H3.astro'
10 | export const components = { h2: H2, h3: H3 }
11 |
12 | import araraBlueImage from '@assets/examples/arara_blue.jpg'
13 |
14 | import { HoverCard } from '@examples/hover-card/HoverCard'
15 |
16 | # Introduction
17 |
18 | AraraJS is a unique take on animation for SolidJS that is basically asking the question:
19 |
20 | - What if I want to animate the signal value?
21 |
22 | Since SolidJS rendering and reactivity are very efficient, this is an exciting uncharted territory.
23 |
24 | > **🚧 Careful**: This is experimental, please be aware of that. Suggestions are very welcome and appreciated, please reach out in SolidJS' Discord Channel or write an Issue if you think its' more suited.
25 |
26 | ## Is it for you?
27 |
28 | Animation libraries are either very high level (which is great when you're following a common use case) or they are low level enough that it forces the programmer to leave the "reactive / declarative" mental model to deal with the animation's "imperative / sequential" nature.
29 |
30 | You can think of AraraJS as a middle ground between the two. It's potentially more flexible than a high level solution but you won't leave the "reactive / declarative" mental model because you are still in signals land.
31 |
32 | Some applications that may benefit grom this approach include:
33 | - 🏃 Highly Reactive animations ( like reacting to mouse position )
34 | - 🧪 Mocking signal values
35 | - 🍎 Physics based & procedural animations
36 |
37 | ### Emergent Behavior
38 |
39 | Combining signals into derived signals is a remarkable DX from SolidJS, it's simple, it's powerful, it scales.
40 |
41 | AraraJS's signals can also be derived and combined in cleaver ways. A simple combination can emerge new exciting behavior which you'll have full control of. Look at what is possible with only spring and sine wave primitives combined together (with mouse position) in this Hover Card.
42 |
43 |
44 |
45 |
46 |
47 |
48 | ### Alternatives
49 | - Motion - motion.dev
50 | - Solid MotionOne - SolidJS Motion wrapper
51 | - AnimeJS - animejs.com
52 | - GSAP - gsap.com
53 | - Popmotion - popmotion.io
54 |
55 | > 📖 Recommended reading : This article from Motion is a feature comparison of some animation libraries. It also explores their different Web API strategies and how it affect's the user. In case you are wondering, AraraJS is using the api `requestAnimationFrame` (RAF) under the hood because it operates exclusively on **signals / javascript** land. A hybrid WAAPI with RAF approach is being considered for the future.
56 |
57 |
58 | ### Additional thanks
59 | - Jasmin GiyoMoon for the awesome Corvu UI project, which was used as a template for this project.
60 | - Solid Primitives for awesome, high-quality reactivity primitives which make working with SolidJS a breeze
61 | - Ryan Carniato for his highly educational Friday streams!
62 | - The Solid Community for being so welcoming and inspiring!
63 |
--------------------------------------------------------------------------------
/web/src/pages/docs/overview.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | layout: '@layouts/Docs.astro'
3 | title: SolidJS UI primitives and utilities
4 | description: An overview over all ui primitives and utilities arara provides for SolidJS.
5 | image: https://ararajs.vercel.app/banner.jpg
6 | ---
7 | import PrimitivesOverview from '@components/docs/primitives/PrimitivesOverview.astro'
8 | import UtilitiesOverview from '@components/docs/utilities/UtilitiesOverview.astro'
9 | import H2 from '@components/docs/headings/H2.astro'
10 | import H3 from '@components/docs/headings/H3.astro'
11 | export const components = { h2: H2, h3: H3 }
12 |
13 | # Overview
14 |
15 | > TODO: 🏗️ under construction...
16 |
17 | {/* ## Primitives
18 |
19 | arara's growing list of UI primitives for SolidJS. Accessible, customizable and ready to use in your project!
20 |
21 |
22 |
23 | ## Utilities
24 |
25 | A set of commonly needed, low-level patterns that you might need in the modern, accessible web. They are exported as separate packages and can be used independently from arara.
26 |
27 |
28 | */}
29 |
--------------------------------------------------------------------------------
/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "astro/tsconfigs/strict",
3 | "compilerOptions": {
4 | "jsx": "preserve",
5 | "jsxImportSource": "solid-js",
6 | "baseUrl": ".",
7 | "paths": {
8 | "@components/*": [
9 | "./src/components/*"
10 | ],
11 | "@examples/*": [
12 | "./src/examples/*"
13 | ],
14 | "@assets/*": [
15 | "./src/assets/*"
16 | ],
17 | "@layouts/*": [
18 | "./src/layouts/*"
19 | ],
20 | "@lib/*": [
21 | "./src/lib/*"
22 | ]
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/web/typesense.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "index_name": "arara",
3 | "start_urls": [
4 | "https://ararajs.vercel.app/docs/"
5 | ],
6 | "sitemap_urls": [
7 | "https://ararajs.vercel.app/sitemap-index.xml"
8 | ],
9 | "selectors": {
10 | "lvl0": "article h1",
11 | "lvl1": "article h2",
12 | "lvl2": "article h3",
13 | "lvl3": "article h4",
14 | "text": "article p, article li"
15 | },
16 | "selectors_exclude": ["[data-typesense-ignore]"],
17 | "nb_hits": 885
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/web/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [
3 | {
4 | "source": "/plausible/js/script.js",
5 | "destination": "https://plausible.io/js/script.js"
6 | },
7 | {
8 | "source": "/plausible/api/event/",
9 | "destination": "https://plausible.io/api/event"
10 | }
11 | ],
12 | "trailingSlash": true,
13 | "headers": [
14 | {
15 | "source": "/fonts/MonaspaceNeon-Regular-v1.101.woff2",
16 | "headers": [
17 | {
18 | "key": "Cache-Control",
19 | "value": "public, max-age=31536000, immutable"
20 | }
21 | ]
22 | }
23 | ],
24 | "git": {
25 | "deploymentEnabled": {
26 | "main": false
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------