├── .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 |
2 | 3 | arara banner 4 | 5 |
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 |
2 | 3 | arara banner 4 | 5 |
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 | 16 | 17 | 18 | { 19 | keys.map((key) => ( 20 | 21 | 26 | 27 | 28 | )) 29 | } 30 |
KeyBehavior
22 | 23 | {key.key} 24 | 25 | {key.behavior}
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 |
13 |

16 | 17 | 27 | 28 | {packageMeta.version} 29 |

30 | npm 44 | 45 | source 60 | 61 |
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 | 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 |
    11 |
    12 |

    16 | {name} 17 |

    20 | Type 21 |

    22 | 27 | 32 | 33 | 40 | 47 | 48 | Section titled {name} 49 | 50 |
    51 |

    52 |

    53 | 54 |
    55 |
    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 |
    13 |

    17 | <{name} /> 18 |

    21 | Component 22 |

    23 | 28 | 33 | 34 | 41 | 48 | 49 | Section titled {name} 50 | 51 |
    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 |
    11 |
    12 |

    16 | {name} 17 |

    20 | Context 21 |

    22 | 27 | 32 | 33 | 40 | 47 | 48 | Section titled {name} 49 | 50 |
    51 |

    52 |

    53 | 54 |
    55 |
    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 |
    13 |

    17 | {name} 18 |

    21 | Function 22 |

    23 | 28 | 33 | 34 | 41 | 48 | 49 | Section titled {name} 50 | 51 |
    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 |
    13 |

    17 | <{name} /> 18 |

    21 | Component 22 |

    23 | 28 | 33 | 34 | 41 | 48 | 49 | Section titled {name} 50 | 51 |
    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 |
    12 |

    16 | {name} 17 |

    20 | Context 21 |

    22 | 27 | 32 | 33 | 40 | 47 | 48 | Section titled {name} 49 | 50 |
    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 |
    11 |
    12 |

    16 | {name} 17 |

    20 | Type 21 |

    22 | 27 | 32 | 33 | 40 | 47 | 48 | Section titled {name} 49 | 50 |
    51 |
    52 | 53 |
    54 |
    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 | 39 | 40 | 41 | 42 | { 43 | props.map((prop) => ( 44 | 45 | 58 | 68 | 77 | 78 | )) 79 | } 80 |
    PropertyDefaultType/Description
    46 |

    55 | {prop.name} 56 |

    57 |
    59 | {prop.defaultHtml === null ? ( 60 |

    -

    61 | ) : ( 62 |
    66 | )} 67 |
    69 |

    70 | {prop.type} 71 |

    72 |
    76 |
    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 | 39 | 40 | 41 | { 42 | returns.map((returnProp) => ( 43 | 44 | 57 | 66 | 67 | )) 68 | } 69 |
    PropertyType/Description
    45 |

    54 | {returnProp.name} 55 |

    56 |
    58 |

    59 | {returnProp.type} 60 |

    61 |
    65 |
    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 | 37 | 38 | 39 | { 40 | tags.map((prop) => ( 41 | 42 | 47 | 50 | 51 | )) 52 | } 53 |
    PropertyDescription
    43 |

    44 | {prop.name} 45 |

    46 |
    48 |
    49 |
    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 | 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 |

    6 | 7 | 12 | 17 | 18 | 25 | 32 | 33 | Section titled 34 | 35 |

    36 | -------------------------------------------------------------------------------- /web/src/components/docs/headings/H3.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const props = Astro.props 3 | --- 4 | 5 |

    6 | 7 | 12 | 17 | 18 | 25 | 32 | 33 | Section titled 34 | 35 |

    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 | 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 |
    56 |
    57 | { 58 | primitives.map((primitive) => ( 59 | 63 | 64 | 65 | {primitive.name} 66 | 67 | 68 | )) 69 | } 70 |
    71 |
    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 | 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 |
    44 |
    45 | { 46 | utilities.map((utility) => ( 47 | 51 | {/* */} 52 | {utility.emoji} 53 | 54 | {utility.name} 55 | 56 | 57 | )) 58 | } 59 |
    60 |
    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 |
    49 |
    59 |
    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 |
    37 |
    47 |
    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 |
    48 | 49 | 50 |
    57 |
    64 |
    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 |
    28 |
    35 |
    41 |
    42 | 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 |
    66 | 75 | 79 | 80 |
    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 | 53 |
    54 |
    57 | 58 |
    59 |
    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 | arara logo dark 34 | 39 | 40 |

    41 | 404 44 | Page not found 45 |

    46 |
    47 |
    48 | 49 |
    50 |
    53 |
    54 | 58 | Go back home 59 | 60 |
    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 | 41 | 42 | 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 | --------------------------------------------------------------------------------