├── .eslintignore
├── .eslintrc.js
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug-report.yml
│ └── config.yml
├── release-drafter.yaml
└── workflows
│ ├── deploy-docs.yaml
│ ├── release-drafter.yaml
│ └── update-changelog.yaml
├── .gitignore
├── .prettierrc.js
├── CHANGELOG.md
├── LICENSE
├── README.md
├── assets
├── stacks-logo.png
└── stacks-preview.png
├── bun.lockb
├── docs
├── .eslintrc.js
├── .gitignore
├── .prettierrc.js
├── babel.config.js
├── bun.lockb
├── components
│ ├── Components.module.css
│ ├── Components.tsx
│ ├── Device.module.css
│ ├── Device.tsx
│ ├── Divider.module.css
│ ├── Divider.tsx
│ ├── Example.module.css
│ ├── Example.tsx
│ ├── Placeholder.module.css
│ ├── Placeholder.tsx
│ └── svg
│ │ ├── icon-design.svg
│ │ ├── icon-glasses.svg
│ │ ├── icon-grid.svg
│ │ ├── icon-multiple-devices.svg
│ │ ├── icon-smartphone-tablet.svg
│ │ └── stacks-logo.svg
├── examples
│ ├── bleed
│ │ └── bleed.tsx
│ ├── box
│ │ ├── align-x.tsx
│ │ ├── align-y.tsx
│ │ ├── flex.tsx
│ │ ├── gap.tsx
│ │ ├── margin.tsx
│ │ ├── padding.tsx
│ │ └── size.tsx
│ ├── columns
│ │ ├── align-x.tsx
│ │ ├── align-y.tsx
│ │ ├── collapse-below.tsx
│ │ ├── flex.tsx
│ │ └── reverse.tsx
│ ├── float-box
│ │ └── float-box.tsx
│ ├── grid
│ │ └── grid.tsx
│ ├── inline
│ │ ├── align-x.tsx
│ │ ├── align-y.tsx
│ │ └── space.tsx
│ ├── inset
│ │ └── inset.tsx
│ ├── rows
│ │ ├── align-x.tsx
│ │ ├── align-y.tsx
│ │ ├── flex.tsx
│ │ └── rows.tsx
│ ├── stack
│ │ ├── align.tsx
│ │ ├── divider.tsx
│ │ ├── horizontal.tsx
│ │ └── space.tsx
│ └── tiles
│ │ ├── columns.tsx
│ │ ├── fill.tsx
│ │ └── space.tsx
├── next-env.d.ts
├── next.config.js
├── package.json
├── pages
│ ├── _app.mdx
│ ├── _meta.json
│ ├── about.mdx
│ ├── docs
│ │ ├── _meta.json
│ │ ├── components.mdx
│ │ ├── components
│ │ │ ├── _meta.json
│ │ │ ├── bleed.mdx
│ │ │ ├── box.mdx
│ │ │ ├── columns.mdx
│ │ │ ├── float-box.mdx
│ │ │ ├── grid.mdx
│ │ │ ├── hidden.mdx
│ │ │ ├── inline.mdx
│ │ │ ├── inset.mdx
│ │ │ ├── rows.mdx
│ │ │ ├── stack.mdx
│ │ │ └── tiles.mdx
│ │ ├── getting-started.mdx
│ │ ├── hooks.mdx
│ │ ├── index.mdx
│ │ ├── migration-guide.mdx
│ │ ├── types.mdx
│ │ └── usage.mdx
│ └── index.mdx
├── postcss.config.js
├── public
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── apple-touch-icon.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon.ico
│ ├── og-slogan.png
│ ├── og-website.png
│ └── stacks-preview.png
├── styles.css
├── tailwind.config.js
├── theme.config.jsx
├── tsconfig.json
└── unistyles.ts
├── example
├── .gitignore
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ ├── debug.keystore
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── anonymous
│ │ │ │ └── stacks
│ │ │ │ ├── MainActivity.kt
│ │ │ │ └── MainApplication.kt
│ │ │ └── res
│ │ │ ├── drawable-hdpi
│ │ │ └── splashscreen_image.png
│ │ │ ├── drawable-mdpi
│ │ │ └── splashscreen_image.png
│ │ │ ├── drawable-xhdpi
│ │ │ └── splashscreen_image.png
│ │ │ ├── drawable-xxhdpi
│ │ │ └── splashscreen_image.png
│ │ │ ├── drawable-xxxhdpi
│ │ │ └── splashscreen_image.png
│ │ │ ├── drawable
│ │ │ ├── rn_edit_text_material.xml
│ │ │ └── splashscreen.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── values-night
│ │ │ └── colors.xml
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── settings.gradle
├── app.json
├── app
│ ├── _layout.tsx
│ ├── bleed
│ │ └── index.tsx
│ ├── columns
│ │ └── index.tsx
│ ├── components
│ │ ├── Divider.tsx
│ │ ├── Placeholder.tsx
│ │ └── Screen.tsx
│ ├── float-box
│ │ └── index.tsx
│ ├── grid
│ │ └── index.tsx
│ ├── index.tsx
│ ├── inline
│ │ └── index.tsx
│ ├── inset
│ │ └── index.tsx
│ ├── rows
│ │ └── index.tsx
│ ├── stack
│ │ └── index.tsx
│ ├── styles
│ │ └── unistyles.ts
│ └── tiles
│ │ └── index.tsx
├── assets
│ ├── adaptive-icon.png
│ ├── favicon.png
│ ├── icon.png
│ └── splash.png
├── babel.config.js
├── bun.lockb
├── ios
│ ├── .gitignore
│ ├── .xcode.env
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Podfile.properties.json
│ ├── stacks.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── stacks.xcscheme
│ ├── stacks.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── stacks
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.mm
│ │ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── App-Icon-1024x1024@1x.png
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── SplashScreen.imageset
│ │ │ ├── Contents.json
│ │ │ └── image.png
│ │ └── SplashScreenBackground.imageset
│ │ │ ├── Contents.json
│ │ │ └── image.png
│ │ ├── Info.plist
│ │ ├── SplashScreen.storyboard
│ │ ├── Supporting
│ │ └── Expo.plist
│ │ ├── main.m
│ │ ├── noop-file.swift
│ │ ├── stacks-Bridging-Header.h
│ │ └── stacks.entitlements
├── metro.config.js
├── package.json
└── tsconfig.json
├── package.json
├── plugin
└── index.js
├── src
├── components
│ ├── Bleed.tsx
│ ├── Box.tsx
│ ├── Column.tsx
│ ├── Columns.tsx
│ ├── FloatBox.tsx
│ ├── Grid.tsx
│ ├── Hidden.tsx
│ ├── Inline.tsx
│ ├── Inset.tsx
│ ├── Row.tsx
│ ├── Rows.tsx
│ ├── Stack.tsx
│ ├── Tiles.tsx
│ └── index.ts
├── hooks
│ ├── index.ts
│ ├── useBreakpointComparators.ts
│ ├── useDebugStyle.ts
│ ├── useResponsiveProp.ts
│ └── useSpacingHelpers.ts
├── index.ts
├── polymorphic.ts
├── types.ts
└── utils.ts
├── tsconfig.json
└── tsup.config.ts
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
3 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [require.resolve('@grapp/eslint-config/react-native')],
3 | }
4 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: mobily
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.yml:
--------------------------------------------------------------------------------
1 | name: Bug report
2 | description: File a bug report
3 | labels:
4 | - 'needs review'
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for taking the time to fill out this bug report!
10 |
11 | Before submitting a new bug/issue, please check the links below to see if there is a solution or question posted there already:
12 | - Issues tab: https://github.com/grapp-dev/stacks/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc
13 | - Closed issues tab: https://github.com/grapp-dev/stacks/issues?q=is%3Aissue+is%3Aclosed
14 | - Discussions tab: https://github.com/grapp-dev/stacks/discussions
15 | - type: markdown
16 | attributes:
17 | value: |
18 | ## Required information
19 | - type: textarea
20 | id: description
21 | attributes:
22 | label: Description
23 | description: Please provide a clear, concise and descriptive explanation of what the bug is. Include screenshots or a video if needed by drag and dropping them to the box below.
24 | validations:
25 | required: true
26 |
27 | - type: input
28 | id: repro
29 | attributes:
30 | label: Snack or a link to a repository (optional)
31 | description: |
32 | Please provide a Snack (https://snack.expo.io/) or a link to a repository on GitHub under your username that reproduces the issue.
33 | Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve.
34 | Issues without a reproduction are likely to stale.
35 | placeholder: Link to a Snack or a GitHub repository
36 |
37 | - type: input
38 | id: stacks-version
39 | attributes:
40 | label: Stacks version
41 | description: What version of Stacks are you using?
42 | placeholder: 3.0.0
43 | validations:
44 | required: true
45 |
46 | - type: input
47 | id: react-native-version
48 | attributes:
49 | label: React Native version
50 | description: What version of react-native are you using?
51 | placeholder: 0.73.0
52 | validations:
53 | required: true
54 |
55 | - type: dropdown
56 | id: platforms
57 | attributes:
58 | label: Platforms
59 | description: On what platform your application is running on?
60 | multiple: true
61 | options:
62 | - Android
63 | - iOS
64 | - React Native Web
65 | - SSR
66 | - React Native macOS
67 | validations:
68 | required: true
69 |
70 | - type: dropdown
71 | id: engine
72 | attributes:
73 | label: Engine
74 | description: Which engine do you use?
75 | multiple: false
76 | options:
77 | - Hermes
78 | - JSC
79 | validations:
80 | required: true
81 |
82 | - type: dropdown
83 | id: architecture
84 | attributes:
85 | label: Architecture
86 | description: Which architecture do you use?
87 | multiple: false
88 | options:
89 | - Paper (old)
90 | - Fabric (new)
91 | validations:
92 | required: true
93 |
94 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Question
4 | url: https://github.com/grapp-dev/stacks/discussions/categories/q-a
5 | about: Please ask and answer questions here.
6 | - name: Feature Request
7 | url: https://github.com/grapp-dev/stacks/discussions/categories/ideas
8 | about: Please submit feature requests, feedback and ideas here.
9 |
--------------------------------------------------------------------------------
/.github/release-drafter.yaml:
--------------------------------------------------------------------------------
1 | name-template: 'v$RESOLVED_VERSION'
2 | tag-template: 'v$RESOLVED_VERSION'
3 | categories:
4 | - title: 🛠 Breaking Changes
5 | labels:
6 | - 'breaking change'
7 | - title: 🚀 Features
8 | labels:
9 | - feature
10 | - enhancement
11 | - title: 🐛 Bug Fixes
12 | labels:
13 | - bugfix
14 | - fix
15 | - title: 🚩 Other Changes
16 | labels:
17 | - chore
18 | category-template: '#### $TITLE'
19 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
20 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
21 | version-resolver:
22 | major:
23 | labels:
24 | - 'major'
25 | minor:
26 | labels:
27 | - 'minor'
28 | patch:
29 | labels:
30 | - 'patch'
31 | default: patch
32 | template: |
33 | ### What's Changed
34 |
35 | $CHANGES
36 |
37 | autolabeler:
38 | - label: 'bugfix'
39 | branch:
40 | - '/fix\/.+/'
41 | title:
42 | - '/fix/i'
43 | - label: 'feature'
44 | branch:
45 | - '/(feat|feature)\/.+/'
46 | title:
47 | - '/feat/i'
48 | - label: 'chore'
49 | title:
50 | - '/(chore|docs)/i'
51 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-docs.yaml:
--------------------------------------------------------------------------------
1 | name: Build & Deploy docs
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - docs/**
9 |
10 | jobs:
11 | deploy:
12 | permissions:
13 | contents: write
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v4
17 | - uses: actions/setup-node@v4
18 | with:
19 | node-version: 18
20 | - uses: oven-sh/setup-bun@v1
21 | - uses: actions/cache@v4
22 | with:
23 | path: |
24 | ~/.npm
25 | ${{ github.workspace }}/docs/.next/cache
26 | key: ${{ runner.os }}-nextjs-${{ hashFiles('**/bun.lockb') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
27 | restore-keys: |
28 | ${{ runner.os }}-nextjs-${{ hashFiles('**/bun.lockb') }}-
29 | - run: |
30 | bun install
31 | cd docs
32 | bun install
33 | bun run build
34 | cd ..
35 | - uses: SamKirkland/FTP-Deploy-Action@v4.3.5
36 | with:
37 | server: ${{ secrets.GRAPP_FTP_SERVER_URL }}
38 | username: ${{ secrets.GRAPP_FTP_USERNAME }}
39 | password: ${{ secrets.GRAPP_FTP_PASSWORD }}
40 | local-dir: './docs/out/'
41 | server-dir: 'stacks/'
42 |
--------------------------------------------------------------------------------
/.github/workflows/release-drafter.yaml:
--------------------------------------------------------------------------------
1 | name: Release Drafter
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | types: [opened, reopened, synchronize]
9 | pull_request_target:
10 | types: [opened, reopened, synchronize]
11 |
12 | permissions:
13 | contents: read
14 |
15 | jobs:
16 | update_release_draft:
17 | permissions:
18 | contents: write
19 | pull-requests: write
20 | runs-on: ubuntu-latest
21 | steps:
22 | - uses: release-drafter/release-drafter@v5
23 | with:
24 | config-name: release-drafter.yaml
25 | env:
26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27 |
--------------------------------------------------------------------------------
/.github/workflows/update-changelog.yaml:
--------------------------------------------------------------------------------
1 | name: Update Changelog
2 |
3 | on:
4 | release:
5 | types: [released]
6 |
7 | jobs:
8 | update:
9 | runs-on: ubuntu-latest
10 |
11 | permissions:
12 | contents: write
13 |
14 | steps:
15 | - uses: actions/checkout@v4
16 | with:
17 | ref: ${{ github.event.release.target_commitish }}
18 |
19 | - uses: stefanzweifel/changelog-updater-action@v1
20 | with:
21 | latest-version: ${{ github.event.release.tag_name }}
22 | release-notes: ${{ github.event.release.body }}
23 |
24 | - uses: stefanzweifel/git-auto-commit-action@v5
25 | with:
26 | branch: ${{ github.event.release.target_commitish }}
27 | commit_message: 'chore: update CHANGELOG.md'
28 | file_pattern: CHANGELOG.md
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .buildlog/
8 | .history
9 | .svn/
10 |
11 | # Editors
12 | *.iml
13 | *.ipr
14 | *.iws
15 | .atom/
16 | .idea/
17 | .vscode/
18 |
19 | # Bun
20 | dist/
21 | node_modules/
22 | .mind/
23 | lib/
24 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = require('@grapp/prettier-config')
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## v3.1.0 - 2024-04-11
4 |
5 | ### What's Changed
6 |
7 | #### 🚀 Features
8 |
9 | - feat(Box): add support for `borderTopWidth`, `borderRightWidth`, `borderBottomWidth` and `borderLeftWidth` @mobily (#49)
10 |
11 | ## v3.0.3 - 2024-04-09
12 |
13 | ### What's Changed
14 |
15 | #### 🐛 Bug Fixes
16 |
17 | - fix: allow passing 0 to `multiply`/`divide` functions @mobily (#48)
18 |
19 | ## v3.0.2 - 2024-04-09
20 |
21 | ### What's Changed
22 |
23 | #### 🐛 Bug Fixes
24 |
25 | - fix: improve `Row.from` and `Column.from` type signatures @mobily (#47)
26 |
27 | ## v3.0.1 - 2024-04-08
28 |
29 | ### What's Changed
30 |
31 | #### 🐛 Bug Fixes
32 |
33 | - fix: 🐛 Export all props of the components @mobily (#46)
34 |
35 | ## v3.0.0 - 2024-04-08
36 |
37 | ### What's Changed
38 |
39 | `Stacks` is now published in the `@grapp` scope. So, you need to fix the imports first. Don't worry, it's a quick fix!
40 |
41 | ~`@mobily/stacks`~ → `@grapp/stacks`
42 |
43 | ### General
44 |
45 | The `Stacks` library has been completely rewritten in TypeScript, so it no longer supports ReScript.
46 | [React Native Unistyles](https://github.com/jpudysz/react-native-unistyles) is a core dependency.
47 | The `flex gap` values now define spaces between components (excluding `Columns`, see [this](https://github.com/Doist/reactist/pull/739#issuecomment-1373825792)).
48 |
49 | ### Components
50 |
51 | #### Provider
52 |
53 | `Stacks` now uses Unistyles, which means that you can remove `StacksProvider` from the React component tree and provide configuration values to the `Unistyles` theme object, as described [here](/docs/getting-started).
54 |
55 | #### Box
56 |
57 | Several new props have been added to the `Box` component, including `width`, `height`, `gap`, `rowGap`, `columnGap`, `backgroundColor`, `borderRadius`, `borderTopLeftRadius`, `borderTopRightRadius`, `borderBottomLeftRadius`, `borderBottomRightRadius`, `borderColor`, `borderWidth`, and `debuggable`.
58 |
59 | #### Columns
60 |
61 | The `defaultWidth` prop is now `defaultFlex`, and the `width` prop has been changed to `flex`.
62 | Use `Column.from` to create a custom `Column` component.
63 | The `markAsColumn` prop has been removed.
64 |
65 | #### FillView
66 |
67 | `FillView` has been renamed to `FloatBox`.
68 | The `unset` helper has been removed, and the positioning has been fixed if you don't provide all offset values.
69 |
70 | #### Hidden
71 |
72 | Experimental support for hiding elements by transforming the React component tree with the provided `Babel` plugin has been added.
73 |
74 | #### Inline
75 |
76 | The `spaceX` and `spaceY` props have been added.
77 |
78 | #### Rows
79 |
80 | The `defaultHeight` prop is now `defaultFlex`, and the `height` prop has been changed to `flex`.
81 | Use `Row.from` to create a custom `Row` component.
82 | The `markAsRow` prop has been removed.
83 |
84 | #### Tiles
85 |
86 | The `spaceX` and `spaceY` props have been added, and `empty` has been renamed to `fill`.
87 |
88 | ### Hooks
89 |
90 | - `useStacks` has been removed, as `StacksProvider` is no longer needed.
91 | - `useCurrentBreakpoint` has been removed. To get the current breakpoint name, you can use `useStyles` from Unistyles.
92 | - `useSpacing` has been removed. To achieve the same result, you can use `useSpacingHelpers` and `multiply`.
93 | - `useWindowDimensions` has been removed. You can now get the screen dimensions with `UnistylesRuntime.screen`.
94 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Marcin Dziewulski
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | 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, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Stacks · [](http://makeapullrequest.com) [](https://github.com/grapp-dev/stacks/blob/master/LICENSE) [](https://www.npmjs.com/package/@grapp/stacks)
2 |
3 |
4 |
5 | > A set of components for building layouts in React Native. Powered by [React Native Unistyles](https://github.com/jpudysz/react-native-unistyles).
6 |
7 | `Stacks` primarily aims to simplify building and maintaining layouts, making it quick and easy. It follows a design principle that suggests components should not include any surrounding white space. Instead, layout components should solely own spacing between elements.
8 |
9 | ## Documentation
10 |
11 | Full documentation can be found [here](https://stacks.grapp.dev).
12 |
13 | ## Installation
14 |
15 | To install Stacks, you should use your preferred package manager.
16 |
17 | ```shell
18 | bun add @grapp/stacks
19 | ```
20 |
21 | ```shell
22 | pnpm add @grapp/stacks
23 | ```
24 |
25 | ```shell
26 | yarn add @grapp/stacks
27 | ```
28 |
29 | ```shell
30 | npm install @grapp/stacks --save
31 | ```
32 |
33 | ## Discord
34 |
35 | Join [Discord](https://discord.gg/DhS6neVJBK) to get involved with the community, ask questions, and share tips.
36 |
37 | ## License
38 |
39 | The MIT License.
40 |
41 | See [LICENSE](LICENSE)
42 |
--------------------------------------------------------------------------------
/assets/stacks-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/assets/stacks-logo.png
--------------------------------------------------------------------------------
/assets/stacks-preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/assets/stacks-preview.png
--------------------------------------------------------------------------------
/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/bun.lockb
--------------------------------------------------------------------------------
/docs/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [require.resolve('@grapp/eslint-config/default')],
3 | };
4 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
2 |
3 | # Logs
4 |
5 | logs
6 | _.log
7 | npm-debug.log_
8 | yarn-debug.log*
9 | yarn-error.log*
10 | lerna-debug.log*
11 | .pnpm-debug.log*
12 |
13 | # Caches
14 |
15 | .cache
16 |
17 | # Diagnostic reports (https://nodejs.org/api/report.html)
18 |
19 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
20 |
21 | # Runtime data
22 |
23 | pids
24 | _.pid
25 | _.seed
26 | *.pid.lock
27 |
28 | # Directory for instrumented libs generated by jscoverage/JSCover
29 |
30 | lib-cov
31 |
32 | # Coverage directory used by tools like istanbul
33 |
34 | coverage
35 | *.lcov
36 |
37 | # nyc test coverage
38 |
39 | .nyc_output
40 |
41 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
42 |
43 | .grunt
44 |
45 | # Bower dependency directory (https://bower.io/)
46 |
47 | bower_components
48 |
49 | # node-waf configuration
50 |
51 | .lock-wscript
52 |
53 | # Compiled binary addons (https://nodejs.org/api/addons.html)
54 |
55 | build/Release
56 |
57 | # Dependency directories
58 |
59 | node_modules/
60 | jspm_packages/
61 |
62 | # Snowpack dependency directory (https://snowpack.dev/)
63 |
64 | web_modules/
65 |
66 | # TypeScript cache
67 |
68 | *.tsbuildinfo
69 |
70 | # Optional npm cache directory
71 |
72 | .npm
73 |
74 | # Optional eslint cache
75 |
76 | .eslintcache
77 |
78 | # Optional stylelint cache
79 |
80 | .stylelintcache
81 |
82 | # Microbundle cache
83 |
84 | .rpt2_cache/
85 | .rts2_cache_cjs/
86 | .rts2_cache_es/
87 | .rts2_cache_umd/
88 |
89 | # Optional REPL history
90 |
91 | .node_repl_history
92 |
93 | # Output of 'npm pack'
94 |
95 | *.tgz
96 |
97 | # Yarn Integrity file
98 |
99 | .yarn-integrity
100 |
101 | # dotenv environment variable files
102 |
103 | .env
104 | .env.development.local
105 | .env.test.local
106 | .env.production.local
107 | .env.local
108 |
109 | # parcel-bundler cache (https://parceljs.org/)
110 |
111 | .parcel-cache
112 |
113 | # Next.js build output
114 |
115 | .next
116 | out
117 |
118 | # Nuxt.js build / generate output
119 |
120 | .nuxt
121 | dist
122 |
123 | # Gatsby files
124 |
125 | # Comment in the public line in if your project uses Gatsby and not Next.js
126 |
127 | # https://nextjs.org/blog/next-9-1#public-directory-support
128 |
129 | # public
130 |
131 | # vuepress build output
132 |
133 | .vuepress/dist
134 |
135 | # vuepress v2.x temp and cache directory
136 |
137 | .temp
138 |
139 | # Docusaurus cache and generated files
140 |
141 | .docusaurus
142 |
143 | # Serverless directories
144 |
145 | .serverless/
146 |
147 | # FuseBox cache
148 |
149 | .fusebox/
150 |
151 | # DynamoDB Local files
152 |
153 | .dynamodb/
154 |
155 | # TernJS port file
156 |
157 | .tern-port
158 |
159 | # Stores VSCode versions used for testing VSCode extensions
160 |
161 | .vscode-test
162 |
163 | # yarn v2
164 |
165 | .yarn/cache
166 | .yarn/unplugged
167 | .yarn/build-state.yml
168 | .yarn/install-state.gz
169 | .pnp.*
170 |
171 | # IntelliJ based IDEs
172 | .idea
173 |
174 | # Finder (MacOS) folder config
175 | .DS_Store
176 | .htaccess
177 |
--------------------------------------------------------------------------------
/docs/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = require('@grapp/prettier-config')
2 |
--------------------------------------------------------------------------------
/docs/babel.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = api => {
4 | api.cache(true);
5 |
6 | return {
7 | presets: ['next/babel'],
8 | plugins: [
9 | [
10 | 'module-resolver',
11 | {
12 | alias: {
13 | 'react-native': path.join(__dirname, 'node_modules', 'react-native-web'),
14 | },
15 | },
16 | ],
17 | ],
18 | };
19 | };
20 |
--------------------------------------------------------------------------------
/docs/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/docs/bun.lockb
--------------------------------------------------------------------------------
/docs/components/Components.module.css:
--------------------------------------------------------------------------------
1 | .root {
2 | margin: 30px 0 20px;
3 | }
4 |
5 | .card {
6 | min-height: 175px;
7 | padding: 8px;
8 | }
9 |
10 | .link {
11 | color: unset;
12 | text-decoration: unset;
13 | }
14 |
15 | .title {
16 | padding: 0 8px 8px;
17 | text-align: center;
18 | font-size: 0.9rem;
19 | font-weight: 500;
20 | }
21 |
22 | .content {
23 | position: relative;
24 | height: 100%;
25 | background-color: #EAECEE;
26 | border-radius: 4px;
27 | padding: 12px;
28 | display: flex;
29 | flex-direction: column;
30 | flex: 1;
31 |
32 | :global(.dark) & {
33 | background-color: #191D27;
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/docs/components/Device.module.css:
--------------------------------------------------------------------------------
1 | .root {
2 | display: flex;
3 | flex-direction: row;
4 |
5 | @media (max-width: 480px) {
6 | transform: scale(0.8);
7 | }
8 |
9 | @media (max-width: 380px) {
10 | transform: scale(0.7);
11 | }
12 |
13 | @media (max-width: 330px) {
14 | transform: scale(0.6);
15 | }
16 |
17 | @media (max-width: 310px) {
18 | transform: scale(0.5);
19 | }
20 | }
21 |
22 | .title {
23 | margin-bottom: 16px;
24 | }
25 |
26 | .iphone {
27 | display: block;
28 | color: #fff;
29 | text-align: center;
30 | background-color: #fff;
31 | border: solid #111;
32 | margin-bottom: 2em;
33 | position: relative;
34 | box-shadow: 0 0.5em 2em 0.2em rgba(0, 0, 0, 0.33), 0 0 0 0.5px #000 inset;
35 | width: 380px;
36 | height: 818px;
37 | border-width: 24px;
38 | border-radius: 56px;
39 | }
40 |
41 | .iphoneX {
42 | position: relative;
43 | margin: 40px 0;
44 | width: 360px;
45 | height: 780px;
46 | background-color: #fff;
47 | border-radius: 40px;
48 | box-shadow: 0px 0px 0px 11px #1f1f1f, 0px 0px 0px 13px #191919, 0px 0px 0px 20px #111;
49 | display: flex;
50 | flex-direction: row;
51 | }
52 |
53 | :is(html[class~=dark]) {
54 | .iphoneX {
55 | background-color: #232731;
56 | }
57 | }
58 |
59 | .iphoneX:before, .iphoneX:after {
60 | content: "";
61 | position: absolute;
62 | z-index: 2;
63 | left: 50%;
64 | transform: translateX(-50%);
65 | }
66 |
67 | .iphoneX:after {
68 | bottom: 7px;
69 | width: 140px;
70 | height: 4px;
71 | background-color: #111;
72 | border-radius: 10px;
73 | }
74 |
75 | .iphoneX:before {
76 | top: 0px;
77 | width: 56%;
78 | height: 30px;
79 | background-color: #1f1f1f;
80 | border-radius: 0px 0px 40px 40px;
81 | }
82 |
83 | .iphoneX .speaker,
84 | .iphoneX .camera,
85 | .iphoneX .action-button {
86 | position: absolute;
87 | z-index: 2;
88 | display: block;
89 | color: transparent;
90 | }
91 |
92 | .iphoneX .speaker {
93 | top: 0px;
94 | left: 50%;
95 | transform: translate(-50%, 6px);
96 | height: 8px;
97 | width: 15%;
98 | background-color: #101010;
99 | border-radius: 8px;
100 | box-shadow: inset 0px -3px 3px 0px rgba(255, 255, 255, 0.2);
101 | }
102 |
103 | .iphoneX .camera {
104 | left: 10%;
105 | top: 0px;
106 | transform: translate(180px, 4px);
107 | width: 12px;
108 | height: 12px;
109 | background-color: #101010;
110 | border-radius: 12px;
111 | box-shadow: inset 0px -3px 2px 0px rgba(255, 255, 255, 0.2);
112 | }
113 |
114 | .iphoneX .camera:after {
115 | content: "";
116 | position: absolute;
117 | background-color: #2d4d76;
118 | width: 6px;
119 | height: 6px;
120 | top: 2px;
121 | left: 2px;
122 | top: 3px;
123 | left: 3px;
124 | display: block;
125 | border-radius: 4px;
126 | box-shadow: inset 0px -2px 2px rgba(0, 0, 0, 0.5);
127 | }
128 |
129 | .iphoneX .screen {
130 | padding: 30px 1px 24px;
131 | position: relative;
132 | display: flex;
133 | flex-direction: column;
134 | flex: 1;
135 | border-radius: 38px;
136 | overflow: hidden;
137 | }
138 |
139 | .iphoneX .content {
140 | padding: 16px;
141 | display: flex;
142 | flex: 1;
143 | flex-direction: column;
144 | }
145 |
--------------------------------------------------------------------------------
/docs/components/Device.tsx:
--------------------------------------------------------------------------------
1 | import styles from './Device.module.css';
2 |
3 | import * as React from 'react';
4 |
5 | type Props = React.PropsWithChildren<{
6 | readonly title?: string;
7 | }>;
8 |
9 | export const Device = (props: Props) => {
10 | const { children, title } = props;
11 |
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 | {title && (
20 |
23 | {title}
24 |
25 | )}
26 | {children}
27 |
28 |
29 |
30 |
31 | );
32 | };
33 |
--------------------------------------------------------------------------------
/docs/components/Divider.module.css:
--------------------------------------------------------------------------------
1 | .root {
2 | background-color: rgba(11, 15, 26, 0.14);
3 | height: 1px;
4 | }
5 |
6 | :is(html[class~=dark]) {
7 | .root {
8 | background-color: rgba(255, 137, 175, 0.3);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/docs/components/Divider.tsx:
--------------------------------------------------------------------------------
1 | import styles from './Divider.module.css';
2 |
3 | import * as React from 'react';
4 |
5 | export const Divider = () => {
6 | return ;
7 | };
8 |
9 | Divider.displayName = 'Divider';
10 |
--------------------------------------------------------------------------------
/docs/components/Placeholder.module.css:
--------------------------------------------------------------------------------
1 | .label {
2 | font-family: 'JetBrains Mono';
3 | font-size: 13px;
4 | color: rgba(11, 15, 26, 1.0);
5 | }
6 |
7 | :is(html[class~=dark]) {
8 | .label {
9 | color: #2BBBB2;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/docs/components/Placeholder.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box } from '@grapp/stacks';
4 |
5 | import { useTheme } from 'nextra-theme-docs';
6 |
7 | import styles from './Placeholder.module.css';
8 |
9 | type Props = {
10 | readonly width?: number;
11 | readonly height?: number;
12 | readonly label?: string;
13 | readonly flex?: 'fluid' | 'content';
14 | };
15 |
16 | export const Placeholder = (props: Props) => {
17 | const { label, width, height = 46, flex } = props;
18 |
19 | const theme = useTheme();
20 |
21 | return (
22 |
30 | {label && {label}}
31 |
32 | );
33 | };
34 |
35 | Placeholder.displayName = 'Placeholder';
36 |
--------------------------------------------------------------------------------
/docs/components/svg/icon-design.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/components/svg/icon-glasses.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/components/svg/icon-grid.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/components/svg/icon-multiple-devices.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/components/svg/icon-smartphone-tablet.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/components/svg/stacks-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/examples/bleed/bleed.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Bleed, Stack } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | );
38 | };
39 |
40 | export default Example;
41 |
--------------------------------------------------------------------------------
/docs/examples/box/align-x.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 | };
37 |
38 | export default Example;
39 |
--------------------------------------------------------------------------------
/docs/examples/box/align-y.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 | };
37 |
38 | export default Example;
39 |
--------------------------------------------------------------------------------
/docs/examples/box/flex.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | );
60 | };
61 |
62 | export default Example;
63 |
--------------------------------------------------------------------------------
/docs/examples/box/gap.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | );
26 | };
27 |
28 | export default Example;
29 |
--------------------------------------------------------------------------------
/docs/examples/box/margin.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 | };
34 |
35 | export default Example;
36 |
--------------------------------------------------------------------------------
/docs/examples/box/padding.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 | };
34 |
35 | export default Example;
36 |
--------------------------------------------------------------------------------
/docs/examples/box/size.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | );
24 | };
25 |
26 | export default Example;
27 |
--------------------------------------------------------------------------------
/docs/examples/columns/align-x.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Columns, Stack } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | );
47 | };
48 |
49 | export default Example;
50 |
--------------------------------------------------------------------------------
/docs/examples/columns/align-y.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Columns, Stack } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | );
32 | };
33 |
34 | export default Example;
35 |
--------------------------------------------------------------------------------
/docs/examples/columns/collapse-below.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Columns } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 | );
16 | };
17 |
18 | export default Example;
19 |
--------------------------------------------------------------------------------
/docs/examples/columns/flex.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Column, Columns, Stack } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | );
110 | };
111 |
112 | export default Example;
113 |
--------------------------------------------------------------------------------
/docs/examples/columns/reverse.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Column, Columns } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | );
19 | };
20 |
21 | export default Example;
22 |
--------------------------------------------------------------------------------
/docs/examples/float-box/float-box.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box, FloatBox } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | );
27 | };
28 |
29 | export default Example;
30 |
--------------------------------------------------------------------------------
/docs/examples/grid/grid.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Grid, Row, Rows } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 | <>
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | >
23 | );
24 | };
25 |
26 | export default Example;
27 |
--------------------------------------------------------------------------------
/docs/examples/inline/align-x.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Inline } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | );
20 | };
21 |
22 | export default Example;
23 |
--------------------------------------------------------------------------------
/docs/examples/inline/align-y.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Inline } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default Example;
24 |
--------------------------------------------------------------------------------
/docs/examples/inline/space.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Inline, Stack } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 | };
35 |
36 | export default Example;
37 |
--------------------------------------------------------------------------------
/docs/examples/inset/inset.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Inset, Stack } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | );
37 | };
38 |
39 | export default Example;
40 |
--------------------------------------------------------------------------------
/docs/examples/rows/align-x.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Row, Rows } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default Example;
24 |
--------------------------------------------------------------------------------
/docs/examples/rows/align-y.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Row, Rows } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default Example;
24 |
--------------------------------------------------------------------------------
/docs/examples/rows/flex.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Row, Rows } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 | };
19 |
20 | export default Example;
21 |
--------------------------------------------------------------------------------
/docs/examples/rows/rows.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Row, Rows } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default Example;
24 |
--------------------------------------------------------------------------------
/docs/examples/stack/align.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Stack } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 | );
16 | };
17 |
18 | export default Example;
19 |
--------------------------------------------------------------------------------
/docs/examples/stack/divider.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Stack } from '@grapp/stacks';
4 |
5 | import { Divider } from '../../components/Divider';
6 | import { Placeholder } from '../../components/Placeholder';
7 |
8 | const Example = () => {
9 | return (
10 | }>
11 |
12 |
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | export default Example;
20 |
--------------------------------------------------------------------------------
/docs/examples/stack/horizontal.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Stack } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 | };
19 |
20 | export default Example;
21 |
--------------------------------------------------------------------------------
/docs/examples/stack/space.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Stack } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | );
19 | };
20 |
21 | export default Example;
22 |
--------------------------------------------------------------------------------
/docs/examples/tiles/columns.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Tiles } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default Example;
24 |
--------------------------------------------------------------------------------
/docs/examples/tiles/fill.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Tiles } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 | );
16 | };
17 |
18 | export default Example;
19 |
--------------------------------------------------------------------------------
/docs/examples/tiles/space.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Stack, Tiles } from '@grapp/stacks';
4 |
5 | import { Placeholder } from '../../components/Placeholder';
6 |
7 | const Example = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | );
24 | };
25 |
26 | export default Example;
27 |
--------------------------------------------------------------------------------
/docs/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/docs/next.config.js:
--------------------------------------------------------------------------------
1 | const nextra = require('nextra');
2 | const path = require('path');
3 |
4 | const { getNextraOptions, getWithNextraOptions } = require('@grapp/nextra-theme/config/nextra');
5 |
6 | const withNextra = nextra(getNextraOptions());
7 |
8 | module.exports = withNextra(
9 | getWithNextraOptions({
10 | transpilePackages: ['react-native-unistyles', '@grapp/stacks'],
11 | webpack: config => {
12 | Object.assign(config.resolve.alias, {
13 | react: path.resolve(__dirname, '..', 'node_modules', 'react'),
14 | 'react-native$': path.resolve(__dirname, '..', 'node_modules', 'react-native'),
15 | });
16 | },
17 | }),
18 | );
19 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@grapp/stacks-docs",
3 | "module": "index.ts",
4 | "license": "MIT",
5 | "private": true,
6 | "scripts": {
7 | "dev": "next",
8 | "build": "next build",
9 | "start": "next start"
10 | },
11 | "devDependencies": {
12 | "@grapp/eslint-config": "^0.1.1",
13 | "@grapp/prettier-config": "^0.1.1",
14 | "@grapp/ts-config": "^0.1.3",
15 | "@types/react": "^18.2.65",
16 | "@types/react-dom": "^18.2.21",
17 | "babel-plugin-module-resolver": "^5.0.0",
18 | "bun-types": "latest",
19 | "eslint": "^8.57.0",
20 | "prettier": "^3.2.5",
21 | "raw-loader": "^4.0.2"
22 | },
23 | "dependencies": {
24 | "@grapp/nextra-theme": "0.4.9",
25 | "@grapp/stacks": "3.1.0",
26 | "react-element-to-jsx-string": "^15.0.0",
27 | "react-native-web": "^0.19.10",
28 | "react-slick": "^0.30.2",
29 | "slick-carousel": "^1.8.1"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/docs/pages/_app.mdx:
--------------------------------------------------------------------------------
1 | import '../styles.css';
2 | import '../unistyles'
3 |
4 | import * as React from 'react';
5 |
6 | export default function MyApp({ Component, pageProps }) {
7 | return ;
8 | }
9 |
--------------------------------------------------------------------------------
/docs/pages/_meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "index": {
3 | "type": "page",
4 | "title": "Stacks",
5 | "display": "hidden",
6 | "theme": {
7 | "layout": "raw"
8 | }
9 | },
10 | "docs": {
11 | "type": "page",
12 | "title": "Documentation"
13 | },
14 | "about": {
15 | "type": "page",
16 | "title": "About Grapp.Dev",
17 | "theme": {
18 | "typesetting": "article"
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/docs/pages/about.mdx:
--------------------------------------------------------------------------------
1 | import { About } from '@grapp/nextra-theme'
2 |
3 |
4 |
--------------------------------------------------------------------------------
/docs/pages/docs/_meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "index": "Introduction",
3 | "getting-started": "",
4 | "usage": "",
5 | "migration-guide": "",
6 | "-- reference": {
7 | "type": "separator",
8 | "title": "Reference"
9 | },
10 | "components": "",
11 | "hooks": "",
12 | "types": "",
13 | "-- more": {
14 | "type": "separator",
15 | "title": "More"
16 | },
17 | "sponsor_link": {
18 | "title": "Sponsor ↗",
19 | "href": "https://github.com/sponsors/mobily",
20 | "newWindow": true
21 | },
22 | "github_link": {
23 | "title": "GitHub ↗",
24 | "href": "https://github.com/grapp-dev/stacks",
25 | "newWindow": true
26 | },
27 | "discord_link": {
28 | "title": "Discord ↗",
29 | "href": "https://discord.gg/DhS6neVJBK",
30 | "newWindow": true
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/docs/pages/docs/components.mdx:
--------------------------------------------------------------------------------
1 | import { Cards, Card } from 'nextra/components';
2 | import { Components } from '../../components/Components'
3 |
4 | ## Components
5 |
6 | #### Layout
7 |
8 |
9 |
10 | #### Other
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/docs/pages/docs/components/_meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "box": "",
3 | "bleed": "",
4 | "columns": "",
5 | "float-box": "FloatBox"
6 | }
7 |
--------------------------------------------------------------------------------
/docs/pages/docs/components/bleed.mdx:
--------------------------------------------------------------------------------
1 | import { Property } from '@grapp/nextra-theme'
2 | import { Callout } from "nextra/components";
3 |
4 | import { Example, getExamples } from '../../../components/Example'
5 |
6 | export const getStaticProps = () => {
7 | return getExamples(['bleed/bleed']);
8 | }
9 |
10 | ## Bleed
11 |
12 | The `Bleed` component is specifically designed to create a container with negative margins so that its content appears to extend beyond the container's boundaries. This is particularly useful when you want to visually break out of a parent container without having to refactor the entire component tree.
13 |
14 |
15 |
16 | This component works in contrast to the [`Inset`](/docs/components/inset) component, which creates a padded container. While `Inset` creates a container that keeps its content within its boundaries, this component creates a container that allows its content to "bleed" into the surrounding layout.
17 |
18 | ### Props
19 |
20 | The `Bleed` component is created using the [`Box`](/docs/components/box) component. It extends all the props* supported by `Box`, as well as [React Native View](https://reactnative.dev/docs/view#props), and the props mentioned below.
21 |
22 |
23 | Unavailable `Box` props: `margin`, `marginX`, `marginY`, `marginTop`, `marginRight`, `marginBottom`, `marginLeft`, `marginStart`, `marginEnd`
24 |
25 |
26 | #### space
27 |
28 | "]}>
29 | Applies a negative margin to each side.
30 |
31 |
32 | #### horizontal
33 |
34 | "]}>
35 | Applies a negative margin horizontally.
36 |
37 |
38 | #### vertical
39 |
40 | "]}>
41 | Applies a negative margin vertically.
42 |
43 |
44 | #### top
45 |
46 | "]}>
47 | Applies a negative margin to the top.
48 |
49 |
50 | #### right
51 |
52 | "]}>
53 | Applies a negative margin to the right.
54 |
55 |
56 | #### bottom
57 |
58 | "]}>
59 | Applies a negative margin to the bottom.
60 |
61 |
62 | #### left
63 |
64 | "]}>
65 | Applies a negative margin to the left.
66 |
67 |
68 | #### start
69 |
70 | "]}>
71 | Applies a negative margin to the left for the `ltr` direction, otherwise to the right.
72 |
73 |
74 | #### end
75 |
76 | "]}>
77 | Applies a negative margin to the right for the `ltr` direction, otherwise to the left.
78 |
79 |
--------------------------------------------------------------------------------
/docs/pages/docs/components/columns.mdx:
--------------------------------------------------------------------------------
1 | import { Property } from '@grapp/nextra-theme'
2 | import { Callout } from "nextra/components";
3 |
4 | import { Example, getExamples } from '../../../components/Example'
5 |
6 | export const getStaticProps = () => {
7 | return getExamples(['columns/flex', 'columns/reverse', 'columns/align-x', 'columns/align-y']);
8 | }
9 |
10 | ## Columns
11 |
12 | `Columns` is a layout component that helps you organize your content horizontally into columns with consistent spacing between them. By default, all columns have the same width, but you can modify the width of each column based on your preference. You can even assign fractional flex values up to 1/5 to a column for more precise layout control.
13 |
14 |
15 |
16 | If you want a column to be as small as possible, you can set its flex value to `content`. This ensures the column takes up the least amount of space necessary to display its content.
17 |
18 | The `alignY` prop allows you to vertically align columns with varying content heights.
19 |
20 |
21 |
22 | You can use the `alignX` prop to horizontally align the columns when the total width of all columns is less than the width of the parent container.
23 |
24 |
25 |
26 | The `reverse` prop allows you to reverse the order of the columns if you need to display them in a different order.
27 |
28 |
29 |
30 | If you want to stack the columns vertically on smaller screens, you can use the `collapseBelow` prop.
31 |
32 | ### Custom Column component
33 |
34 | To include a custom component within `Columns`, you should utilize the `Column.from` method. This method enables you to create a new `Column` instance from a custom component and use it in your `Columns` layout. Following this approach, you can ensure your custom component is correctly integrated and behaves as expected.
35 |
36 | ```tsx
37 | const Content = Column.from(props => {
38 | return ;
39 | });
40 | ```
41 |
42 | ### Props
43 |
44 | The `Columns` component is created using the [`Box`](/docs/components/box) component. It extends all the props* supported by `Box`, as well as [React Native View](https://reactnative.dev/docs/view#props), and the props mentioned below.
45 |
46 |
47 | Unavailable `Box` props: `gap`, `rowGap`, `columnGap`
48 |
49 |
50 | #### defaultFlex
51 |
52 | "]} defaultValue="fluid">
53 | Assigns a default flex value to each column.
54 |
55 |
56 | #### space
57 |
58 | ']}>
59 | Sets the space between children.
60 |
61 |
62 | #### alignX
63 |
64 | "]}>
65 | Aligns children horizontally.
66 |
67 |
68 | #### alignY
69 |
70 | "]}>
71 | Aligns children vertically.
72 |
73 |
74 | #### collapseBelow
75 |
76 |
77 | Stacks the columns vertically when the current breakpoint is smaller than the provided breakpoint.
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/pages/docs/components/float-box.mdx:
--------------------------------------------------------------------------------
1 |
2 | import { Property } from '@grapp/nextra-theme'
3 |
4 | import { Example, getExamples } from '../../../components/Example'
5 |
6 | export const getStaticProps = () => {
7 | return getExamples(['float-box/float-box']);
8 | }
9 |
10 | ## FloatBox
11 |
12 | `FloatBox` is a component that can position its child elements over the parent component using absolute positioning. It's similar to the [`Box`](/docs/components/box) component but has the added benefit of positioning elements precisely within the parent container.
13 |
14 | With `FloatBox`, you can easily control the position of each child element by specifying exact coordinates, such as `top`, `bottom`, `left`, and `right`.
15 |
16 |
17 |
18 | ### Props
19 |
20 | The `FloatBox` component is created using the [`Box`](/docs/components/box) component. It extends all the props supported by `Box`, as well as [React Native View](https://reactnative.dev/docs/view#props), and the props mentioned below.
21 |
22 | #### offset
23 |
24 | "]}>
25 | Specifies the value of each offset.
26 |
27 |
28 | #### top
29 |
30 | "]}>
31 | Sets the top offset.
32 |
33 |
34 | #### right
35 |
36 | "]}>
37 | Sets the right offset.
38 |
39 |
40 | #### bottom
41 |
42 | "]}>
43 | Sets the bottom offset.
44 |
45 |
46 | #### left
47 |
48 | "]}>
49 | Sets the left offset.
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/docs/pages/docs/components/grid.mdx:
--------------------------------------------------------------------------------
1 | import { Property } from '@grapp/nextra-theme'
2 | import { Callout } from "nextra/components";
3 |
4 | import { Example, getExamples } from '../../../components/Example'
5 |
6 | export const getStaticProps = () => {
7 | return getExamples(['grid/grid']);
8 | }
9 |
10 | ## Grid
11 |
12 | The `Grid` component renders a design system grid in your React Native application. It's worth noting that you should only utilize this component once and at the highest level of your component tree. This will ensure that the grid is applied consistently throughout your application.
13 |
14 |
15 |
16 | ### Props
17 |
18 | #### gutter
19 |
20 | "]} defaultValue="2">
21 | Set the space between columns.
22 |
23 |
24 | #### margin
25 |
26 | "]} defaultValue="2">
27 | Specifies the amount of space on both the left and right sides.
28 |
29 |
30 | #### columns
31 |
32 | "]} defaultValue="8">
33 | Sets the number of columns.
34 |
35 |
36 | #### opacity
37 |
38 | "]} defaultValue="0.2">
39 | Adjusts the transparency level for each column.
40 |
41 |
42 | #### color
43 |
44 | "]} defaultValue="red">
45 | Sets the color of each column.
46 |
47 |
48 |
--------------------------------------------------------------------------------
/docs/pages/docs/components/hidden.mdx:
--------------------------------------------------------------------------------
1 | import { Property } from '@grapp/nextra-theme'
2 | import { Callout } from "nextra/components"
3 |
4 | ## Hidden
5 |
6 | The `Hidden` component allows you to hide content responsively. This can be achieved by using the `above` and `below` props, which let you specify a breakpoint name.
7 |
8 | ---
9 |
10 |
11 | To use `Hidden` you must add the Babel plugin to your config.
12 |
13 |
14 | ```js
15 | module.exports = {
16 | plugins: [
17 | ...
18 | '@grapp/stacks/plugin',
19 | ],
20 | };
21 | ```
22 |
23 | ---
24 |
25 | ### Props
26 |
27 | #### above
28 |
29 |
30 | Hides the content if the current breakpoint is larger than the provided breakpoint.
31 |
32 |
33 | #### below
34 |
35 |
36 | Hides the content if the current breakpoint is smaller than the provided breakpoint.
37 |
38 |
--------------------------------------------------------------------------------
/docs/pages/docs/components/inline.mdx:
--------------------------------------------------------------------------------
1 | import { Property } from '@grapp/nextra-theme'
2 | import { Callout } from "nextra/components";
3 |
4 | import { Example, getExamples } from '../../../components/Example'
5 |
6 | export const getStaticProps = () => {
7 | return getExamples(['inline/space', 'inline/align-y', 'inline/align-x']);
8 | }
9 |
10 | ## Inline
11 |
12 | The `Inline` component arranges its children horizontally, allowing you to display multiple items side by side. If the children exceed the available space, the component wraps them to the next line, with equal spacing between children.
13 |
14 |
15 |
16 | The `alignX` prop can be used to align children horizontally.
17 |
18 |
19 |
20 | The `alignY` prop enables you to vertically align children of varying heights vertically, ensuring they are all at the same level.
21 |
22 |
23 |
24 | The `collapseBelow` prop allows you to collapse all items into a single vertical stack, ensuring that your content remains accessible, even on smaller screens.
25 |
26 | ### Props
27 |
28 | The `Inline` component is created using the [`Box`](/docs/components/box) component. It extends all the props* supported by `Box`, as well as [React Native View](https://reactnative.dev/docs/view#props), and the props mentioned below.
29 |
30 |
31 | Unavailable `Box` props:: `gap`, `rowGap`, `columnGap`, `direction`, `wrap`
32 |
33 |
34 | #### space
35 |
36 | ']}>
37 | Sets the space between children.
38 |
39 |
40 | #### spaceX 🚀
41 |
42 | ']}>
43 | Sets the horizontal space between children.
44 |
45 |
46 | #### spaceY
47 |
48 | ']}>
49 | Sets the vertical space between children.
50 |
51 |
52 | #### alignX
53 |
54 | "]}>
55 | Aligns children horizontally.
56 |
57 |
58 | #### alignY
59 |
60 | "]}>
61 | Aligns children vertically.
62 |
63 |
64 | #### collapseBelow
65 |
66 |
67 | Stacks children vertically when the current breakpoint is smaller than the provided breakpoint.
68 |
69 |
70 |
--------------------------------------------------------------------------------
/docs/pages/docs/components/inset.mdx:
--------------------------------------------------------------------------------
1 | import { Property } from '@grapp/nextra-theme'
2 | import { Callout } from "nextra/components";
3 |
4 | import { Example, getExamples } from '../../../components/Example'
5 |
6 | export const getStaticProps = () => {
7 | return getExamples(['inset/inset']);
8 | }
9 |
10 | ## Inset
11 |
12 | The `Inset` component is a component that creates a container with additional space around its content. This extra space, also known as padding, can be customized via props.
13 |
14 |
15 |
16 | ### Props
17 |
18 | The `Inset` component is created using the [`Box`](/docs/components/box) component. It extends all the props* supported by `Box`, as well as [React Native View](https://reactnative.dev/docs/view#props), and the props mentioned below.
19 |
20 |
21 | Unavailable `Box` props: `padding`, `paddingX`, `paddingY`, `paddingTop`, `paddingRight`, `paddingBottom`, `paddingLeft`, `paddingStart`, `paddingEnd`
22 |
23 |
24 | #### space
25 |
26 | "]}>
27 | Add padding to all sides.
28 |
29 |
30 | #### horizontal
31 |
32 | "]}>
33 | Adds padding horizontally.
34 |
35 |
36 | #### vertical
37 |
38 | "]}>
39 | Adds padding vertically.
40 |
41 |
42 | #### top
43 |
44 | "]}>
45 | Adds padding to the top.
46 |
47 |
48 | #### right
49 |
50 | "]}>
51 | Adds padding to the right.
52 |
53 |
54 | #### bottom
55 |
56 | "]}>
57 | Adds padding to the bottom.
58 |
59 |
60 | #### left
61 |
62 | "]}>
63 | Adds padding to the left.
64 |
65 |
66 | #### start
67 |
68 | "]}>
69 | Adds padding to the left for the `ltr` direction, otherwise to the right.
70 |
71 |
72 | #### end
73 |
74 | "]}>
75 | Adds padding to the right for the `ltr` direction, otherwise to the left.
76 |
77 |
--------------------------------------------------------------------------------
/docs/pages/docs/components/rows.mdx:
--------------------------------------------------------------------------------
1 | import { Property } from '@grapp/nextra-theme'
2 | import { Callout } from "nextra/components";
3 |
4 | import { Example, getExamples } from '../../../components/Example'
5 |
6 | export const getStaticProps = () => {
7 | return getExamples(['rows/rows', 'rows/align-x', 'rows/align-y', 'rows/flex']);
8 | }
9 |
10 | ## Rows
11 |
12 | The `Rows` component is designed to organize its children in rows with consistent vertical spacing. It is particularly useful in building the generic `Screen` component.
13 |
14 |
15 |
16 | If you have rows with varying heights, you can use the `alignY` prop to align them vertically.
17 |
18 |
19 |
20 | The `alignX` prop can also help horizontally align the rows when their total width is less than that of the parent container.
21 |
22 |
23 |
24 | To set the flex value of a row, you can place `Row` directly inside `Rows` and set a flex property. To define a default flex for all rows, use the `defaultFlex` property on `Rows`.
25 |
26 |
27 |
28 | ### Custom Row component
29 |
30 | To include a custom component within `Rows`, you should utilize the `Row.from` method. This method enables you to create a new `Row` instance from a custom component and use it in your `Rows` layout. Following this approach, you can ensure your custom component is correctly integrated and behaves as expected.
31 |
32 | ```tsx
33 | const Content = Row.from(props => {
34 | return ;
35 | });
36 | ```
37 |
38 | ### Props
39 |
40 | The `Rows` component is created using the [`Box`](/docs/components/box) component. It extends all the props* supported by `Box`, as well as [React Native View](https://reactnative.dev/docs/view#props), and the props mentioned below.
41 |
42 |
43 | Unavailable `Box` props: `direction`, `gap`, `rowGap`, `columnGap`
44 |
45 |
46 | #### defaultFlex
47 |
48 | "]} defaultValue="fluid">
49 | Assigns a default flex value to each column.
50 |
51 |
52 | #### space
53 |
54 | ']}>
55 | Sets the space between children.
56 |
57 |
58 | #### alignX
59 |
60 | "]}>
61 | Aligns children horizontally.
62 |
63 |
64 | #### alignY
65 |
66 | "]}>
67 | Aligns children vertically.
68 |
69 |
70 |
--------------------------------------------------------------------------------
/docs/pages/docs/components/stack.mdx:
--------------------------------------------------------------------------------
1 | import { Property } from '@grapp/nextra-theme'
2 | import { Callout } from "nextra/components";
3 |
4 | import { Example, getExamples } from '../../../components/Example'
5 |
6 | export const getStaticProps = () => {
7 | return getExamples(['stack/space', 'stack/align', 'stack/horizontal', 'stack/divider']);
8 | }
9 |
10 | ## Stack
11 |
12 | `Stack` is a layout component that helps in positioning child elements vertically with equal spacing between them.
13 |
14 |
15 |
16 | The component also supports horizontal alignment, which allows you to align children left, right, or center within the stack.
17 |
18 |
19 |
20 | `Stack` allows you to arrange children in a horizontal layout by using the `horizontal` property.
21 |
22 |
23 |
24 | Additionally, you can insert a custom divider between all stack children by setting the `divider` prop on `Stack`. This is useful when you want to separate child elements with a visible line or space.
25 |
26 |
27 |
28 | ### Props
29 |
30 | The `Stack` component is created using the [`Box`](/docs/components/box) component. It extends all the props* supported by `Box`, as well as [React Native View](https://reactnative.dev/docs/view#props), and the props mentioned below.
31 |
32 |
33 | Unavailable `Box` props: `direction`, `alignX`, `alignY`, `rowGap`, `columnGap`
34 |
35 |
36 | #### space
37 |
38 | ']}>
39 | Set the space between children.
40 |
41 |
42 | #### horizontal
43 |
44 | "]}>
45 | Arranges children horizontally.
46 |
47 |
48 | #### align
49 |
50 | "]}>
51 | Aligns children horizontally or vertically, depending on the value of the `horizontal` prop.
52 |
53 |
54 | #### divider
55 |
56 |
57 | Inserts a custom divider between each child.
58 |
59 |
60 |
--------------------------------------------------------------------------------
/docs/pages/docs/components/tiles.mdx:
--------------------------------------------------------------------------------
1 | import { Property } from '@grapp/nextra-theme'
2 | import { Callout } from "nextra/components";
3 |
4 | import { Example, getExamples } from '../../../components/Example'
5 |
6 | export const getStaticProps = () => {
7 | return getExamples(['tiles/space', 'tiles/columns', 'tiles/fill']);
8 | }
9 |
10 | ## Tiles
11 |
12 | The `Tiles` component displays a grid of elements with equal spacing between them. You can use the `space` and `columns` props to customize the spacing and number of columns per screen size.
13 |
14 | The `space` prop adjusts the spacing between the tiles.
15 |
16 |
17 |
18 | The `columns` prop sets the number of columns to display.
19 |
20 |
21 |
22 | The `fill` prop allows you to determine whether the empty content needs to be filled if the last row has fewer columns than defined in the `columns` prop. Depending on your requirements, you can set the value of the `fill` prop to `true` or `false`.
23 |
24 |
25 |
26 | ### Props
27 |
28 | The `Tiles` component is created using the [`Box`](/docs/components/box) component. It extends all the props* supported by `Box`, as well as [React Native View](https://reactnative.dev/docs/view#props), and the props mentioned below.
29 |
30 |
31 | Unavailable `Box` props: `gap`, `rowGap`, `columnGap`, `direction`, `alignX`
32 |
33 |
34 | #### columns
35 |
36 | ']} defaultValue="1">
37 | Define the number of columns.
38 |
39 |
40 | #### space
41 |
42 | ']}>
43 | Sets the space between children.
44 |
45 |
46 | #### spaceX 🚀
47 |
48 | ']}>
49 | Sets the horizontal space between children.
50 |
51 |
52 | #### spaceY
53 |
54 | ']}>
55 | Sets the vertical space between children.
56 |
57 |
58 | #### fill
59 |
60 | "]} defaultValue="false">
61 | Fills the last row if it has fewer columns than defined in the `columns` prop.
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/docs/pages/docs/getting-started.mdx:
--------------------------------------------------------------------------------
1 | import { Tabs } from 'nextra/components'
2 |
3 | ## Getting Started
4 |
5 | #### Installation
6 |
7 | To install `Stacks`, you should use your preferred package manager.
8 |
9 |
10 |
11 | ```shell
12 | bun add @grapp/stacks
13 | ```
14 |
15 |
16 | ```shell
17 | pnpm add @grapp/stacks
18 | ```
19 |
20 |
21 | ```shell
22 | yarn add @grapp/stacks
23 | ```
24 |
25 |
26 | ```shell
27 | npm install @grapp/stacks --save
28 | ```
29 |
30 |
31 |
32 |
33 | #### Unistyles
34 |
35 | Once you have installed the library, check out the Unistyles setup guide at this [link](https://www.unistyl.es/start/setup/). This guide provides a step-by-step process for configuring Unistyles in your app. You can skip this step if you have installed and configured Unistyles properly.
36 |
37 | #### Stacks
38 |
39 | Adding `Stacks` support to the existing `Unistyles` configuration is a simple process. To do this, you need to add the following properties to your theme:
40 |
41 | ```tsx
42 | const theme = {
43 | ...
44 | stacks: {
45 | spacing: 4,
46 | debug: false,
47 | },
48 | };
49 | ```
50 |
51 | If you are using TypeScript, it is necessary to create types for breakpoints after overriding `Unistyles` types.
52 |
53 | ```tsx {6-8}
54 | declare module 'react-native-unistyles' {
55 | export interface UnistylesBreakpoints extends AppBreakpoints {}
56 | export interface UnistylesThemes extends AppThemes {}
57 | }
58 |
59 | declare module '@grapp/stacks' {
60 | export interface StacksBreakpoints extends Breakpoints {}
61 | }
62 | ```
63 |
64 | #### Explore
65 |
66 | You can now explore the various components and become familiar with their properties by visiting the [`Components`](/docs/components) page.
67 |
--------------------------------------------------------------------------------
/docs/pages/docs/hooks.mdx:
--------------------------------------------------------------------------------
1 | ## Hooks
2 |
3 | #### useResponsiveProp
4 |
5 | This hook returns a function that can be used to resolve responsive props in components. It accepts the responsive props format as an argument.
6 |
7 | ```ts
8 | function useResponsiveProp(value: ResponsiveProp): T;
9 | ```
10 |
11 | ```tsx
12 | type Props = {
13 | size: ResponsiveProp;
14 | };
15 |
16 | const Avatar = (props: Props) => {
17 | const resolveResponsiveProp = useResponsiveProp();
18 | const size = resolveResponsiveProp(props.size);
19 |
20 | return (
21 |
22 | );
23 | };
24 |
25 |
26 | ```
27 |
28 | #### useSpacingHelpers
29 |
30 | This hook returns two helper functions - `multiply` and `divide`. These functions allow you to adjust the spacing between elements in your components by multiplying or dividing a value by the spacing value set in the Unistyles theme object.
31 |
32 | ```ts
33 | type SpacingHelper = {
34 | (value: number): number;
35 | (value: number | undefined): number | undefined;
36 | };
37 |
38 | type SpacingHelpers = {
39 | multiply: SpacingHelper;
40 | divide: SpacingHelper;
41 | };
42 |
43 | function useSpacingHelpers(): SpacingHelpers;
44 | ```
45 |
46 | ```tsx
47 | type Props = React.PropsWitchChildren<{
48 | value: number;
49 | }>;
50 |
51 | const Padding = (props: Props) => {
52 | const { value, children } = props
53 | const { multiply } = useSpacingHelpers()
54 |
55 | return (
56 |
57 | {children}
58 |
59 | );
60 | };
61 |
62 |
63 | Hello!
64 |
65 | ```
66 |
67 | #### useBreakpointComparators
68 |
69 | This hook returns two functions - `isAbove` and `isBelow`. These functions can compare the current breakpoint with the provided breakpoint, which can be helpful when adjusting component styling based on screen size.
70 |
71 | ```ts
72 | type BreakpointComparator = (breakpoint?: Breakpoint) => boolean;
73 |
74 | type BreakpointComparators = {
75 | isBelow: BreakpointComparator;
76 | isAbove: BreakpointComparator;
77 | };
78 |
79 | function useBreakpointComparators(): BreakpointComparators;
80 | ```
81 |
82 | ```tsx
83 | type Props = React.PropsWitchChildren<{
84 | below: Breakpoint;
85 | }>;
86 |
87 | const Collapse = (props: Props) => {
88 | const { below, children } = props;
89 | const { isBelow } = useBreakpointComparators();
90 |
91 | const isCollapsed = breakpoint.isBelow(below);
92 | const direction = isCollapsed ? 'column' : 'row';
93 |
94 | return (
95 |
96 | {children}
97 |
98 | );
99 | };
100 |
101 |
102 | {elements}
103 |
104 | ```
105 |
106 | #### useDebugStyle
107 |
108 | This hook generates a style object with a random `backgroundColor` value. It can be useful for quickly identifying specific components in your application when the debug mode is enabled.
109 |
110 | ```ts
111 | function useDebugStyle(): ViewStyle | undefined;
112 | ```
113 |
114 | ```tsx
115 | const Debug = (props: React.PropsWitchChildren) => {
116 | const { children } = props;
117 | const debugStyle = useDebugStyle();
118 |
119 | return (
120 |
121 | {children}
122 |
123 | );
124 | };
125 |
126 |
127 | Debug
128 |
129 | ```
130 |
131 |
--------------------------------------------------------------------------------
/docs/pages/docs/index.mdx:
--------------------------------------------------------------------------------
1 | import { Cards, Card } from 'nextra/components';
2 | import { Components } from '../../components/Components'
3 |
4 | ## Stacks
5 |
6 | Stacks is a set of various components for building layouts in React Native.
7 |
8 | The `Stacks` library follows a design principle that states components should not provide surrounding white space. Instead, layout components solely own the spacing between elements. This approach ensures that the system is highly composable while maintaining predictable white space.
9 |
10 | All components can be nested infinitely within each other, creating various standard layouts in React Native. To work efficiently with `Stacks`, understanding the following components is necessary.
11 |
12 | ---
13 |
14 |
15 |
16 | ---
17 |
18 | The main objective of the `Stacks` library is to make building and maintaining layouts quick and easy. Whenever possible, component APIs should resemble how a designer would describe them.
19 |
20 | ## Unistyles
21 |
22 | To streamline the development process of `Stacks`, I have decided to add [Unistyles](https://github.com/jpudysz/react-native-unistyles/tree/main) as a dependency. Unistyles is a style library for React Native that has several benefits. First, it has a simple setup process, making it easy to use. Second, the library is actively developing, and the core maintainer ([@jacekp](https://twitter.com/jpudysz)) continuously improves and expands its capabilities. Any issues or bugs are quickly addressed and fixed.
23 |
24 | Another significant advantage of using Unistyles is that it provides everything you need to work with styles in React Native. It supports media queries, which enables the creation of responsive designs that adapt to different screen sizes. It also supports defined breakpoints, allowing you to set specific styles for various screen sizes. It also supports variants, which makes it easy to create different versions of a component based on certain conditions.
25 |
26 | Unistyles also provides excellent performance, which is crucial for any mobile app. The library is optimized for speed. Moreover, it's compatible with React Native Web, so you can write code once and use it on mobile and web platforms.
27 |
28 | Unistyles is an excellent choice for simplifying the styling process in React Native projects. Its simple setup, comprehensive features, and excellent performance make it a reliable and efficient style library.
29 |
--------------------------------------------------------------------------------
/docs/pages/docs/migration-guide.mdx:
--------------------------------------------------------------------------------
1 |
2 | import { Callout } from "nextra/components";
3 |
4 | ## Migration Guide
5 |
6 | The migration process should be hassle-free for users upgrading from Stacks v2 to v3. However, you should be aware of some notable changes.
7 |
8 |
9 | `Stacks` is now published in the `@grapp` scope. So, you need to fix the imports first. Don't worry, it's a quick fix!
10 | ~`@mobily/stacks`~ → `@grapp/stacks`
11 |
12 |
13 | ### General
14 |
15 | The `Stacks` library has been completely rewritten in TypeScript, so it no longer supports ReScript.
16 | The `flex gap` values now define spaces between components (excluding `Columns`, see [this](https://github.com/Doist/reactist/pull/739#issuecomment-1373825792)).
17 |
18 | ### Components
19 |
20 | #### Provider
21 |
22 | `Stacks` now uses Unistyles, which means that you can remove `StacksProvider` from the React component tree and provide configuration values to the `Unistyles` theme object, as described [here](/docs/getting-started).
23 |
24 | #### Box
25 |
26 | Several new props have been added to the `Box` component, including `width`, `height`, `gap`, `rowGap`, `columnGap`, `backgroundColor`, `borderRadius`, `borderTopLeftRadius`, `borderTopRightRadius`, `borderBottomLeftRadius`, `borderBottomRightRadius`, `borderColor`, `borderWidth`, `borderTopWidth`, `borderRightWidth`, `borderBottomWidth`, `borderLeftWidth`, and `debuggable`.
27 |
28 | #### Columns
29 |
30 | The `defaultWidth` prop is now `defaultFlex`, and the `width` prop has been changed to `flex`.
31 | Use `Column.from` to create a custom `Column` component.
32 | The `markAsColumn` function has been removed.
33 |
34 | #### FillView
35 |
36 | `FillView` has been renamed to `FloatBox`.
37 | The `unset` helper has been removed, and the positioning has been fixed if you don't provide all offset values.
38 |
39 | #### Hidden
40 |
41 | Experimental support for hiding elements by transforming the React component tree with the provided `Babel` plugin has been added.
42 |
43 | #### Inline
44 |
45 | The `spaceX` and `spaceY` props have been added.
46 |
47 | #### Rows
48 |
49 | The `defaultHeight` prop is now `defaultFlex`, and the `height` prop has been changed to `flex`.
50 | Use `Row.from` to create a custom `Row` component.
51 | The `markAsRow` function has been removed.
52 |
53 | #### Tiles
54 |
55 | The `spaceX` and `spaceY` props have been added, and `empty` has been renamed to `fill`.
56 |
57 | ### Hooks
58 |
59 | - `useStacks` has been removed, as `StacksProvider` is no longer needed.
60 | - `useCurrentBreakpoint` has been removed. To get the current breakpoint name, you can use `useStyles` from Unistyles.
61 | - `useSpacing` has been removed. To achieve the same result, you can use `useSpacingHelpers` and `multiply`.
62 | - `useWindowDimensions` has been removed. You can now get the screen dimensions with `UnistylesRuntime.screen`.
63 |
64 | _That's all folks!_
65 |
--------------------------------------------------------------------------------
/docs/pages/docs/types.mdx:
--------------------------------------------------------------------------------
1 | ## Types
2 |
3 | ```ts
4 | type Breakpoint = keyof UnistylesBreakpoints;
5 | type AxisX = 'left' | 'center' | 'right';
6 | type AxisY = 'top' | 'center' | 'bottom';
7 | type Stretch = 'stretch';
8 | type Space = 'between' | 'around' | 'evenly';
9 | type Direction = 'row' | 'row-reverse' | 'column' | 'column-reverse';
10 | type Wrap = 'wrap' | 'no-wrap' | 'wrap-reverse';
11 |
12 | type Flex =
13 | | 'content'
14 | | 'fluid'
15 | | '1/2'
16 | | '1/3'
17 | | '2/3'
18 | | '1/4'
19 | | '3/4'
20 | | '1/5'
21 | | '2/5'
22 | | '3/5'
23 | | '4/5';
24 |
25 | type ResponsiveProp = T | readonly T[];
26 | ```
27 |
--------------------------------------------------------------------------------
/docs/pages/docs/usage.mdx:
--------------------------------------------------------------------------------
1 | ## Usage
2 |
3 | #### Spacings
4 |
5 | To use `Stacks`, you must first define a default spacing value in the Unistyles theme object. This is explained in detail in the [`Getting Started`](/docs/getting-started) page.
6 |
7 | The spacing value is measured in logical pixels, the same unit you are already familiar with for `margin` or `padding`. `Stacks` automatically multiplies the default spacing value by the space passed to the components.
8 |
9 | ```tsx
10 | const theme = {
11 | stacks: {
12 | spacing: 4,
13 | },
14 | };
15 |
16 | … // 4 * 2 = 8 logical pixels
17 | … // 4 * 3 = 12 logical pixels
18 | ```
19 |
20 | #### Responsive Props
21 |
22 | `Stacks` also supports the responsive props format. You can use responsive props to customize the spacing, number of columns, or alignments per screen size.
23 |
24 | Let's say you have a configuration for breakpoints that defines the different screen sizes. This configuration could look like this:
25 |
26 | ```tsx
27 | const breakpoints = {
28 | mobile: 0,
29 | tablet: 768,
30 | desktop: 998,
31 | } as const;
32 | ```
33 |
34 | In Stacks, the responsive prop can be used to set values for different screen sizes. This prop can take either a primitive value or a responsive prop format.
35 |
36 | When you pass a primitive value, it applies to all the breakpoints in your configuration.
37 |
38 | ```tsx
39 | …
40 | ```
41 |
42 | On the other hand, a responsive prop is an array of values where you can specify different values to apply to different breakpoints. The first value in the array is applied to the `mobile` breakpoint, the second to the `tablet` breakpoint, and the third to the `desktop` breakpoint.
43 |
44 | For example:
45 |
46 | ```tsx
47 | …
48 | …
49 | …
50 | ```
51 |
52 | - if you pass `[1]`, the same value `1` will be used across all breakpoints.
53 | - if you pass `[1, 4]`, the value `1` will apply to the `mobile` breakpoint, and `4` will apply to the `tablet` and `desktop` breakpoints.
54 | - if you pass `[1, 4, 8]`, the value `1` will apply to the `mobile` breakpoint, `4` to the `tablet` breakpoint, and `8` to the `desktop` breakpoint.
55 |
56 |
--------------------------------------------------------------------------------
/docs/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = require('@grapp/nextra-theme/config/postcss')
2 |
--------------------------------------------------------------------------------
/docs/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/docs/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/docs/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/docs/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/docs/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/docs/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/docs/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/docs/public/favicon-16x16.png
--------------------------------------------------------------------------------
/docs/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/docs/public/favicon-32x32.png
--------------------------------------------------------------------------------
/docs/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/docs/public/favicon.ico
--------------------------------------------------------------------------------
/docs/public/og-slogan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/docs/public/og-slogan.png
--------------------------------------------------------------------------------
/docs/public/og-website.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/docs/public/og-website.png
--------------------------------------------------------------------------------
/docs/public/stacks-preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/docs/public/stacks-preview.png
--------------------------------------------------------------------------------
/docs/styles.css:
--------------------------------------------------------------------------------
1 | @import "@grapp/nextra-theme/lib/global.css";
2 | @import "@grapp/nextra-theme/lib/index.css";
3 |
4 | @import "slick-carousel/slick/slick.css";
5 |
6 | @tailwind base;
7 | @tailwind components;
8 | @tailwind utilities;
9 |
10 | .nextra-code-block pre {
11 | font-size: 16px;
12 | --tw-bg-opacity: 0.03;
13 | background-color: rgba(0, 0, 0, var(--tw-bg-opacity));
14 | }
15 |
16 | :root {
17 | --grapp-hero-text-column-span: span 4 / span 4;
18 | --grapp-features-list-column-span: span 2 / span 2;
19 | --grapp-features-grid-cols: repeat(2, minmax(0, 1fr));
20 | --grapp-features-container-grid-cols: repeat(4, minmax(0, 1fr));
21 | --grapp-features-preview-column-span: span 2 / span 2;
22 | }
23 |
24 | html {
25 | &.dark {
26 | --shiki-token-constant: #1BCABE;
27 | --shiki-token-string-expression: #faf39c;
28 | --grapp-property-type-color: #ff89af;
29 | --grapp-property-default-value-color: var(--shiki-token-string-expression);
30 | --grapp-hero-button-background-color: #232731;
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/docs/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = require('@grapp/nextra-theme/config/tailwind')
2 |
--------------------------------------------------------------------------------
/docs/theme.config.jsx:
--------------------------------------------------------------------------------
1 | import { Footer, Logo } from '@grapp/nextra-theme';
2 | import { getDefaultConfig } from '@grapp/nextra-theme/config/next';
3 |
4 | import StacksLogo from './components/svg/stacks-logo.svg';
5 |
6 | export default getDefaultConfig({
7 | title: 'Stacks',
8 | description:
9 | 'A set of components for building layouts in React Native. Powered by React Native Unistyles.',
10 | github: 'https://github.com/grapp-dev/stacks',
11 | discord: 'https://discord.gg/DhS6neVJBK',
12 | docs: 'https://stacks.grapp.dev',
13 | logo: () => {
14 | return ;
15 | },
16 | footer: () => {
17 | return (
18 |
58 | );
59 | },
60 | hue: 176,
61 | saturation: 76,
62 | });
63 |
--------------------------------------------------------------------------------
/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@grapp/nextra-theme/tsconfig.json",
3 | "include": [
4 | "next-env.d.ts",
5 | "./docs/**/*.ts",
6 | "./docs/**/*.tsx",
7 | "./examples/**/*.tsx",
8 | "./unistyles.ts"
9 | ],
10 | "compilerOptions": {
11 | "incremental": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/docs/unistyles.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-empty-interface */
2 | import { UnistylesRegistry } from 'react-native-unistyles';
3 |
4 | const breakpoints = {
5 | mobile: 0,
6 | tablet: 768,
7 | desktop: 998,
8 | } as const;
9 |
10 | const theme = {
11 | stacks: {
12 | spacing: 4,
13 | debug: false,
14 | },
15 | };
16 |
17 | type Breakpoints = typeof breakpoints;
18 |
19 | type Themes = {
20 | readonly light: typeof theme;
21 | };
22 |
23 | declare module 'react-native-unistyles' {
24 | export interface UnistylesBreakpoints extends Breakpoints {}
25 | export interface UnistylesThemes extends Themes {}
26 | }
27 |
28 | declare module '@grapp/stacks' {
29 | export interface StacksBreakpoints extends Breakpoints {}
30 | }
31 |
32 | UnistylesRegistry.addBreakpoints(breakpoints).addThemes({
33 | light: theme,
34 | });
35 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
2 |
3 | # dependencies
4 | node_modules/
5 |
6 | # Expo
7 | .expo/
8 | dist/
9 | web-build/
10 |
11 | # Native
12 | *.orig.*
13 | *.jks
14 | *.p8
15 | *.p12
16 | *.key
17 | *.mobileprovision
18 |
19 | # Metro
20 | .metro-health-check*
21 |
22 | # debug
23 | npm-debug.*
24 | yarn-debug.*
25 | yarn-error.*
26 |
27 | # macOS
28 | .DS_Store
29 | *.pem
30 |
31 | # local env files
32 | .env*.local
33 |
34 | # typescript
35 | *.tsbuildinfo
36 |
37 | app/playground/
38 |
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Android/IntelliJ
6 | #
7 | build/
8 | .idea
9 | .gradle
10 | local.properties
11 | *.iml
12 | *.hprof
13 |
14 | # Bundle artifacts
15 | *.jsbundle
16 |
--------------------------------------------------------------------------------
/example/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/debug.keystore
--------------------------------------------------------------------------------
/example/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # react-native-reanimated
11 | -keep class com.swmansion.reanimated.** { *; }
12 | -keep class com.facebook.react.turbomodule.** { *; }
13 |
14 | # Add any project specific keep options here:
15 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
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 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/anonymous/stacks/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.anonymous.stacks
2 |
3 | import android.os.Build
4 | import android.os.Bundle
5 |
6 | import com.facebook.react.ReactActivity
7 | import com.facebook.react.ReactActivityDelegate
8 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
9 | import com.facebook.react.defaults.DefaultReactActivityDelegate
10 |
11 | import expo.modules.ReactActivityDelegateWrapper
12 |
13 | class MainActivity : ReactActivity() {
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | // Set the theme to AppTheme BEFORE onCreate to support
16 | // coloring the background, status bar, and navigation bar.
17 | // This is required for expo-splash-screen.
18 | setTheme(R.style.AppTheme);
19 | super.onCreate(null)
20 | }
21 |
22 | /**
23 | * Returns the name of the main component registered from JavaScript. This is used to schedule
24 | * rendering of the component.
25 | */
26 | override fun getMainComponentName(): String = "main"
27 |
28 | /**
29 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
30 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
31 | */
32 | override fun createReactActivityDelegate(): ReactActivityDelegate {
33 | return ReactActivityDelegateWrapper(
34 | this,
35 | BuildConfig.IS_NEW_ARCHITECTURE_ENABLED,
36 | object : DefaultReactActivityDelegate(
37 | this,
38 | mainComponentName,
39 | fabricEnabled
40 | ){})
41 | }
42 |
43 | /**
44 | * Align the back button behavior with Android S
45 | * where moving root activities to background instead of finishing activities.
46 | * @see onBackPressed
47 | */
48 | override fun invokeDefaultOnBackPressed() {
49 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
50 | if (!moveTaskToBack(false)) {
51 | // For non-root activities, use the default implementation to finish them.
52 | super.invokeDefaultOnBackPressed()
53 | }
54 | return
55 | }
56 |
57 | // Use the default back button implementation on Android S
58 | // because it's doing more than [Activity.moveTaskToBack] in fact.
59 | super.invokeDefaultOnBackPressed()
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/anonymous/stacks/MainApplication.kt:
--------------------------------------------------------------------------------
1 | package com.anonymous.stacks
2 |
3 | import android.app.Application
4 | import android.content.res.Configuration
5 | import androidx.annotation.NonNull
6 |
7 | import com.facebook.react.PackageList
8 | import com.facebook.react.ReactApplication
9 | import com.facebook.react.ReactNativeHost
10 | import com.facebook.react.ReactPackage
11 | import com.facebook.react.ReactHost
12 | import com.facebook.react.config.ReactFeatureFlags
13 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
14 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
15 | import com.facebook.react.defaults.DefaultReactNativeHost
16 | import com.facebook.react.flipper.ReactNativeFlipper
17 | import com.facebook.soloader.SoLoader
18 |
19 | import expo.modules.ApplicationLifecycleDispatcher
20 | import expo.modules.ReactNativeHostWrapper
21 |
22 | class MainApplication : Application(), ReactApplication {
23 |
24 | override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper(
25 | this,
26 | object : DefaultReactNativeHost(this) {
27 | override fun getPackages(): List {
28 | // Packages that cannot be autolinked yet can be added manually here, for example:
29 | // packages.add(new MyReactNativePackage());
30 | return PackageList(this).packages
31 | }
32 |
33 | override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry"
34 |
35 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
36 |
37 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
38 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
39 | }
40 | )
41 |
42 | override val reactHost: ReactHost
43 | get() = getDefaultReactHost(this.applicationContext, reactNativeHost)
44 |
45 | override fun onCreate() {
46 | super.onCreate()
47 | SoLoader.init(this, false)
48 | if (!BuildConfig.REACT_NATIVE_UNSTABLE_USE_RUNTIME_SCHEDULER_ALWAYS) {
49 | ReactFeatureFlags.unstable_useRuntimeSchedulerAlways = false
50 | }
51 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
52 | // If you opted-in for the New Architecture, we load the native entry point for this app.
53 | load()
54 | }
55 | if (BuildConfig.DEBUG) {
56 | ReactNativeFlipper.initializeFlipper(this, reactNativeHost.reactInstanceManager)
57 | }
58 | ApplicationLifecycleDispatcher.onApplicationCreate(this)
59 | }
60 |
61 | override fun onConfigurationChanged(newConfig: Configuration) {
62 | super.onConfigurationChanged(newConfig)
63 | ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig)
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-hdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/drawable-hdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-mdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/drawable-mdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-xhdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/drawable-xhdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-xxhdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/drawable-xxhdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-xxxhdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/drawable-xxxhdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/rn_edit_text_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/splashscreen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 | #ffffff
3 | #ffffff
4 | #023c69
5 | #ffffff
6 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | stacks
3 | contain
4 | false
5 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
14 |
17 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | buildToolsVersion = findProperty('android.buildToolsVersion') ?: '34.0.0'
6 | minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '23')
7 | compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '34')
8 | targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '34')
9 | kotlinVersion = findProperty('android.kotlinVersion') ?: '1.8.10'
10 |
11 | ndkVersion = "25.1.8937393"
12 | }
13 | repositories {
14 | google()
15 | mavenCentral()
16 | }
17 | dependencies {
18 | classpath('com.android.tools.build:gradle')
19 | classpath('com.facebook.react:react-native-gradle-plugin')
20 | }
21 | }
22 |
23 | apply plugin: "com.facebook.react.rootproject"
24 |
25 | allprojects {
26 | repositories {
27 | maven {
28 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
29 | url(new File(['node', '--print', "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), '../android'))
30 | }
31 | maven {
32 | // Android JSC is installed from npm
33 | url(new File(['node', '--print', "require.resolve('jsc-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), '../dist'))
34 | }
35 |
36 | google()
37 | mavenCentral()
38 | maven { url 'https://www.jitpack.io' }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 |
25 | # Automatically convert third-party libraries to use AndroidX
26 | android.enableJetifier=true
27 |
28 | # Use this property to specify which architecture you want to build.
29 | # You can also override it from the CLI using
30 | # ./gradlew -PreactNativeArchitectures=x86_64
31 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
32 |
33 | # Use this property to enable support to the new architecture.
34 | # This will allow you to use TurboModules and the Fabric render in
35 | # your application. You should enable this flag either if you want
36 | # to write custom TurboModules/Fabric components OR use libraries that
37 | # are providing them.
38 | newArchEnabled=false
39 |
40 | # Use this property to enable or disable the Hermes JS engine.
41 | # If set to false, you will be using JSC instead.
42 | hermesEnabled=true
43 |
44 | # Enable GIF support in React Native images (~200 B increase)
45 | expo.gif.enabled=true
46 | # Enable webp support in React Native images (~85 KB increase)
47 | expo.webp.enabled=true
48 | # Enable animated webp support (~3.4 MB increase)
49 | # Disabled by default because iOS doesn't support animated webp
50 | expo.webp.animated=false
51 |
52 | # Enable network inspector
53 | EX_DEV_CLIENT_NETWORK_INSPECTOR=true
54 |
55 | # Use legacy packaging to compress native libraries in the resulting APK.
56 | expo.useLegacyPackaging=false
57 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if %ERRORLEVEL% equ 0 goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if %ERRORLEVEL% equ 0 goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | set EXIT_CODE=%ERRORLEVEL%
84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
86 | exit /b %EXIT_CODE%
87 |
88 | :mainEnd
89 | if "%OS%"=="Windows_NT" endlocal
90 |
91 | :omega
92 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'stacks'
2 |
3 | dependencyResolutionManagement {
4 | versionCatalogs {
5 | reactAndroidLibs {
6 | from(files(new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../gradle/libs.versions.toml")))
7 | }
8 | }
9 | }
10 |
11 | apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle");
12 | useExpoModules()
13 |
14 | apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
15 | applyNativeModulesSettingsGradle(settings)
16 |
17 | include ':app'
18 | includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile())
19 |
--------------------------------------------------------------------------------
/example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "stacks",
4 | "slug": "stacks",
5 | "version": "1.0.0",
6 | "icon": "./assets/icon.png",
7 | "userInterfaceStyle": "light",
8 | "splash": {
9 | "image": "./assets/splash.png",
10 | "resizeMode": "contain",
11 | "backgroundColor": "#ffffff"
12 | },
13 | "assetBundlePatterns": [
14 | "**/*"
15 | ],
16 | "ios": {
17 | "supportsTablet": true,
18 | "bundleIdentifier": "com.anonymous.stacks"
19 | },
20 | "android": {
21 | "adaptiveIcon": {
22 | "foregroundImage": "./assets/adaptive-icon.png",
23 | "backgroundColor": "#ffffff"
24 | },
25 | "package": "com.anonymous.stacks"
26 | },
27 | "web": {
28 | "favicon": "./assets/favicon.png"
29 | },
30 | "plugins": [
31 | "expo-router"
32 | ],
33 | "scheme": "stacks"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/example/app/_layout.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-default-export */
2 | import { Stack } from 'expo-router';
3 |
4 | import './styles/unistyles';
5 |
6 | const Layout = () => {
7 | return ;
8 | };
9 |
10 | export default Layout;
11 |
--------------------------------------------------------------------------------
/example/app/bleed/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-native/no-raw-text */
2 |
3 | /* eslint-disable import/no-default-export */
4 | import * as React from 'react';
5 |
6 | import { Bleed, Stack } from '@grapp/stacks';
7 |
8 | import { Placeholder } from '../components/Placeholder';
9 | import { Screen } from '../components/Screen';
10 |
11 | const Page = () => {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | );
43 | };
44 |
45 | export default Page;
46 |
--------------------------------------------------------------------------------
/example/app/components/Divider.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { StyleSheet } from 'react-native';
3 |
4 | import { Box } from '@grapp/stacks';
5 |
6 | type Props = {
7 | readonly color?: string;
8 | };
9 |
10 | export const Divider = (props: Props) => {
11 | const { color = '#aaa' } = props;
12 |
13 | return ;
14 | };
15 |
--------------------------------------------------------------------------------
/example/app/components/Placeholder.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Platform, Text } from 'react-native';
3 | import { createStyleSheet, useStyles } from 'react-native-unistyles';
4 |
5 | import { AxisX, AxisY, Box, Flex, FloatBox, Space } from '@grapp/stacks';
6 |
7 | type Props = React.PropsWithChildren<{
8 | readonly height?: number;
9 | readonly width?: number;
10 | readonly flex?: Flex;
11 | readonly alignX?: AxisX | Space;
12 | readonly alignY?: AxisY | Space;
13 | readonly label?: string;
14 | readonly backgroundColor?: string;
15 | }>;
16 |
17 | export const Placeholder = (props: Props) => {
18 | const {
19 | width,
20 | height = 40,
21 | flex,
22 | children,
23 | alignX = 'center',
24 | alignY = 'center',
25 | label,
26 | backgroundColor = '#1BCABE',
27 | } = props;
28 | const { styles } = useStyles(stylesheet);
29 |
30 | const common = {
31 | backgroundColor,
32 | borderRadius: 2,
33 | style: styles.root,
34 | alignY: alignY,
35 | alignX: alignX,
36 | };
37 |
38 | const labelComponent = (
39 |
40 | {label}
41 |
42 | );
43 |
44 | if (flex) {
45 | return (
46 |
47 | {children}
48 | {labelComponent}
49 |
50 | );
51 | }
52 |
53 | return (
54 |
55 | {children}
56 | {labelComponent}
57 |
58 | );
59 | };
60 |
61 | const stylesheet = createStyleSheet({
62 | root: {
63 | borderColor: '#1D525640',
64 | borderWidth: 1,
65 | },
66 | label: {
67 | fontFamily:
68 | Platform.OS === 'android' ? 'monospace' : Platform.OS === 'ios' ? 'Menlo' : 'sans-serif',
69 | fontSize: 9,
70 | },
71 | });
72 |
--------------------------------------------------------------------------------
/example/app/components/Screen.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-native/no-color-literals */
2 | import * as React from 'react';
3 | import { Button, ScrollView as RNScrollView, ScrollViewProps } from 'react-native';
4 | import { createStyleSheet, UnistylesRuntime, useStyles } from 'react-native-unistyles';
5 |
6 | import {
7 | Box,
8 | FloatBox,
9 | ResponsiveProp,
10 | Row,
11 | RowProps,
12 | Rows,
13 | RowsProps,
14 | useResponsiveProp,
15 | useSpacingHelpers,
16 | } from '@grapp/stacks';
17 |
18 | type ContextProps = {
19 | readonly paddingX: number;
20 | };
21 |
22 | type Props = React.PropsWithChildren>;
23 |
24 | type ScreenProps = Omit & {
25 | readonly topInset?: ResponsiveProp;
26 | readonly bottomInset?: ResponsiveProp;
27 | };
28 |
29 | const Context = React.createContext({
30 | paddingX: 4,
31 | });
32 |
33 | const useScreen = () => {
34 | return React.useContext(Context);
35 | };
36 |
37 | export const Screen = (props: ScreenProps) => {
38 | const {
39 | children,
40 | paddingX = 4,
41 | backgroundColor = '#fff',
42 | topInset,
43 | bottomInset,
44 | ...rest
45 | } = props;
46 |
47 | const { divide } = useSpacingHelpers();
48 |
49 | const resolveResponsiveProp = useResponsiveProp();
50 | const paddingTop = resolveResponsiveProp(topInset) ?? divide(UnistylesRuntime.insets.top);
51 | const paddingBottom =
52 | resolveResponsiveProp(bottomInset) ?? divide(UnistylesRuntime.insets.bottom);
53 |
54 | const handleDebugMode = React.useCallback(() => {
55 | UnistylesRuntime.updateTheme('light', theme => {
56 | console.log(theme);
57 | return {
58 | ...theme,
59 | stacks: {
60 | ...theme.stacks,
61 | debug: !theme.stacks.debug,
62 | },
63 | };
64 | });
65 | }, []);
66 |
67 | return (
68 |
69 |
75 | {children}
76 |
77 |
78 |
79 |
80 |
81 | );
82 | };
83 |
84 | const Content = Row.from(props => {
85 | const { paddingX } = useScreen();
86 | return ;
87 | });
88 |
89 | const ScrollView = Row.from(props => {
90 | const { children, contentContainerStyle, flex = 'fluid', horizontal } = props;
91 | const { paddingX } = useScreen();
92 | const { multiply } = useSpacingHelpers();
93 | const { styles } = useStyles(stylesheet);
94 |
95 | return (
96 |
97 |
105 | {children}
106 |
107 |
108 | );
109 | });
110 |
111 | const stylesheet = createStyleSheet({
112 | scrollContainer: {
113 | flexGrow: 1,
114 | },
115 | });
116 |
117 | Screen.Content = Content;
118 | Screen.ScrollView = ScrollView;
119 |
--------------------------------------------------------------------------------
/example/app/float-box/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-native/no-raw-text */
2 | import * as React from 'react';
3 |
4 | /* eslint-disable import/no-default-export */
5 | import { FloatBox } from '@grapp/stacks';
6 |
7 | import { Placeholder } from '../components/Placeholder';
8 | import { Screen } from '../components/Screen';
9 |
10 | const Page = () => {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | );
38 | };
39 |
40 | export default Page;
41 |
--------------------------------------------------------------------------------
/example/app/grid/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-native/no-raw-text */
2 | import * as React from 'react';
3 |
4 | /* eslint-disable import/no-default-export */
5 | import { Grid } from '@grapp/stacks';
6 |
7 | const Page = () => {
8 | return ;
9 | };
10 |
11 | export default Page;
12 |
--------------------------------------------------------------------------------
/example/app/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-native/no-raw-text */
2 |
3 | /* eslint-disable import/no-default-export */
4 | import { createStyleSheet, useStyles } from 'react-native-unistyles';
5 |
6 | import { Stack } from '@grapp/stacks';
7 |
8 | import { Link } from 'expo-router';
9 |
10 | import { Screen } from './components/Screen';
11 |
12 | const Page = () => {
13 | const { styles } = useStyles(stylesheet);
14 | return (
15 |
16 |
17 |
18 |
19 | Bleed
20 |
21 |
22 | Columns
23 |
24 |
25 | FloatBox
26 |
27 |
28 | Inline
29 |
30 |
31 | Inset
32 |
33 |
34 | Grid
35 |
36 |
37 | Rows
38 |
39 |
40 | Stack
41 |
42 |
43 | Tiles
44 |
45 |
46 | Playground
47 |
48 |
49 |
50 |
51 | );
52 | };
53 |
54 | const stylesheet = createStyleSheet({
55 | link: {
56 | color: '#0B0F1A',
57 | },
58 | });
59 |
60 | export default Page;
61 |
--------------------------------------------------------------------------------
/example/app/inset/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-native/no-raw-text */
2 | import * as React from 'react';
3 |
4 | /* eslint-disable import/no-default-export */
5 | import { Inset, Stack } from '@grapp/stacks';
6 |
7 | import { Placeholder } from '../components/Placeholder';
8 | import { Screen } from '../components/Screen';
9 |
10 | const Page = () => {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | );
42 | };
43 |
44 | export default Page;
45 |
--------------------------------------------------------------------------------
/example/app/rows/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-native/no-raw-text */
2 | import * as React from 'react';
3 |
4 | /* eslint-disable import/no-default-export */
5 | import { Row, Rows } from '@grapp/stacks';
6 |
7 | import { Placeholder } from '../components/Placeholder';
8 |
9 | const Page = () => {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | );
27 | };
28 |
29 | export default Page;
30 |
--------------------------------------------------------------------------------
/example/app/stack/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-native/no-raw-text */
2 | import * as React from 'react';
3 |
4 | /* eslint-disable import/no-default-export */
5 | import { Stack } from '@grapp/stacks';
6 |
7 | import { Divider } from '../components/Divider';
8 | import { Placeholder } from '../components/Placeholder';
9 | import { Screen } from '../components/Screen';
10 |
11 | const Page = () => {
12 | return (
13 |
14 |
15 | }>
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | );
55 | };
56 |
57 | export default Page;
58 |
--------------------------------------------------------------------------------
/example/app/styles/unistyles.ts:
--------------------------------------------------------------------------------
1 | import { UnistylesRegistry } from 'react-native-unistyles';
2 |
3 | const breakpoints = {
4 | mobile: 0,
5 | tablet: 768,
6 | desktop: 998,
7 | } as const;
8 |
9 | const theme = {
10 | stacks: {
11 | spacing: 4,
12 | debug: false,
13 | },
14 | };
15 |
16 | type Breakpoints = typeof breakpoints;
17 |
18 | type Themes = {
19 | readonly light: typeof theme;
20 | };
21 |
22 | declare module 'react-native-unistyles' {
23 | export interface UnistylesBreakpoints extends Breakpoints {}
24 | export interface UnistylesThemes extends Themes {}
25 | }
26 |
27 | declare module '@grapp/stacks' {
28 | export interface StacksBreakpoints extends Breakpoints {}
29 | }
30 |
31 | UnistylesRegistry.addBreakpoints(breakpoints)
32 | .addThemes({
33 | light: theme,
34 | })
35 | .addConfig({
36 | adaptiveThemes: true,
37 | });
38 |
--------------------------------------------------------------------------------
/example/app/tiles/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-native/no-raw-text */
2 | import * as React from 'react';
3 |
4 | /* eslint-disable import/no-default-export */
5 | import { Tiles } from '@grapp/stacks';
6 |
7 | import { Placeholder } from '../components/Placeholder';
8 | import { Screen } from '../components/Screen';
9 |
10 | const Page = () => {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | );
61 | };
62 |
63 | export default Page;
64 |
--------------------------------------------------------------------------------
/example/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/example/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/assets/favicon.png
--------------------------------------------------------------------------------
/example/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/assets/icon.png
--------------------------------------------------------------------------------
/example/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/assets/splash.png
--------------------------------------------------------------------------------
/example/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = api => {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | plugins: [require.resolve('../plugin')],
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/example/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/bun.lockb
--------------------------------------------------------------------------------
/example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 | .xcode.env.local
25 |
26 | # Bundle artifacts
27 | *.jsbundle
28 |
29 | # CocoaPods
30 | /Pods/
31 |
--------------------------------------------------------------------------------
/example/ios/.xcode.env:
--------------------------------------------------------------------------------
1 | # This `.xcode.env` file is versioned and is used to source the environment
2 | # used when running script phases inside Xcode.
3 | # To customize your local environment, you can create an `.xcode.env.local`
4 | # file that is not versioned.
5 |
6 | # NODE_BINARY variable contains the PATH to the node executable.
7 | #
8 | # Customize the NODE_BINARY variable here.
9 | # For example, to use nvm with brew, add the following line
10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use
11 | export NODE_BINARY=$(command -v node)
12 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
2 | require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
3 |
4 | require 'json'
5 | podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
6 |
7 | ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0'
8 | ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
9 |
10 | platform :ios, podfile_properties['ios.deploymentTarget'] || '13.4'
11 | install! 'cocoapods',
12 | :deterministic_uuids => false
13 |
14 | prepare_react_native_project!
15 |
16 | # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
17 | # because `react-native-flipper` depends on (FlipperKit,...), which will be excluded. To fix this,
18 | # you can also exclude `react-native-flipper` in `react-native.config.js`
19 | #
20 | # ```js
21 | # module.exports = {
22 | # dependencies: {
23 | # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
24 | # }
25 | # }
26 | # ```
27 | flipper_config = FlipperConfiguration.disabled
28 | if ENV['NO_FLIPPER'] == '1' then
29 | # Explicitly disabled through environment variables
30 | flipper_config = FlipperConfiguration.disabled
31 | elsif podfile_properties.key?('ios.flipper') then
32 | # Configure Flipper in Podfile.properties.json
33 | if podfile_properties['ios.flipper'] == 'true' then
34 | flipper_config = FlipperConfiguration.enabled(["Debug", "Release"])
35 | elsif podfile_properties['ios.flipper'] != 'false' then
36 | flipper_config = FlipperConfiguration.enabled(["Debug", "Release"], { 'Flipper' => podfile_properties['ios.flipper'] })
37 | end
38 | end
39 |
40 | target 'stacks' do
41 | use_expo_modules!
42 | config = use_native_modules!
43 |
44 | use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
45 | use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
46 |
47 | use_react_native!(
48 | :path => config[:reactNativePath],
49 | :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
50 | # An absolute path to your application root.
51 | :app_path => "#{Pod::Config.instance.installation_root}/..",
52 | # Note that if you have use_frameworks! enabled, Flipper will not work if enabled
53 | :flipper_configuration => flipper_config
54 | )
55 |
56 | post_install do |installer|
57 | react_native_post_install(
58 | installer,
59 | config[:reactNativePath],
60 | :mac_catalyst_enabled => false
61 | )
62 |
63 | # This is necessary for Xcode 14, because it signs resource bundles by default
64 | # when building for devices.
65 | installer.target_installation_results.pod_target_installation_results
66 | .each do |pod_name, target_installation_result|
67 | target_installation_result.resource_bundle_targets.each do |resource_bundle_target|
68 | resource_bundle_target.build_configurations.each do |config|
69 | config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
70 | end
71 | end
72 | end
73 | end
74 |
75 | post_integrate do |installer|
76 | begin
77 | expo_patch_react_imports!(installer)
78 | rescue => e
79 | Pod::UI.warn e
80 | end
81 | end
82 | end
83 |
--------------------------------------------------------------------------------
/example/ios/Podfile.properties.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo.jsEngine": "hermes",
3 | "EX_DEV_CLIENT_NETWORK_INSPECTOR": "true"
4 | }
5 |
--------------------------------------------------------------------------------
/example/ios/stacks.xcodeproj/xcshareddata/xcschemes/stacks.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/example/ios/stacks.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/stacks/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 |
5 | @interface AppDelegate : EXAppDelegateWrapper
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/example/ios/stacks/AppDelegate.mm:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 | #import
5 |
6 | @implementation AppDelegate
7 |
8 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
9 | {
10 | self.moduleName = @"main";
11 |
12 | // You can add your custom initial props in the dictionary below.
13 | // They will be passed down to the ViewController used by React Native.
14 | self.initialProps = @{};
15 |
16 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
17 | }
18 |
19 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
20 | {
21 | return [self getBundleURL];
22 | }
23 |
24 | - (NSURL *)getBundleURL
25 | {
26 | #if DEBUG
27 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"];
28 | #else
29 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
30 | #endif
31 | }
32 |
33 | // Linking API
34 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options {
35 | return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
36 | }
37 |
38 | // Universal Links
39 | - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler {
40 | BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
41 | return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result;
42 | }
43 |
44 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
45 | - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
46 | {
47 | return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
48 | }
49 |
50 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
51 | - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
52 | {
53 | return [super application:application didFailToRegisterForRemoteNotificationsWithError:error];
54 | }
55 |
56 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
57 | - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
58 | {
59 | return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
60 | }
61 |
62 | @end
63 |
--------------------------------------------------------------------------------
/example/ios/stacks/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/ios/stacks/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/stacks/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "filename": "App-Icon-1024x1024@1x.png",
5 | "idiom": "universal",
6 | "platform": "ios",
7 | "size": "1024x1024"
8 | }
9 | ],
10 | "info": {
11 | "version": 1,
12 | "author": "expo"
13 | }
14 | }
--------------------------------------------------------------------------------
/example/ios/stacks/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "expo"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/example/ios/stacks/Images.xcassets/SplashScreen.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "idiom": "universal",
5 | "filename": "image.png",
6 | "scale": "1x"
7 | },
8 | {
9 | "idiom": "universal",
10 | "scale": "2x"
11 | },
12 | {
13 | "idiom": "universal",
14 | "scale": "3x"
15 | }
16 | ],
17 | "info": {
18 | "version": 1,
19 | "author": "expo"
20 | }
21 | }
--------------------------------------------------------------------------------
/example/ios/stacks/Images.xcassets/SplashScreen.imageset/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/ios/stacks/Images.xcassets/SplashScreen.imageset/image.png
--------------------------------------------------------------------------------
/example/ios/stacks/Images.xcassets/SplashScreenBackground.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "idiom": "universal",
5 | "filename": "image.png",
6 | "scale": "1x"
7 | },
8 | {
9 | "idiom": "universal",
10 | "scale": "2x"
11 | },
12 | {
13 | "idiom": "universal",
14 | "scale": "3x"
15 | }
16 | ],
17 | "info": {
18 | "version": 1,
19 | "author": "expo"
20 | }
21 | }
--------------------------------------------------------------------------------
/example/ios/stacks/Images.xcassets/SplashScreenBackground.imageset/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grapp-dev/stacks/34fa5f71c5cc597a0fef279d6219cc277efecf9e/example/ios/stacks/Images.xcassets/SplashScreenBackground.imageset/image.png
--------------------------------------------------------------------------------
/example/ios/stacks/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CADisableMinimumFrameDurationOnPhone
6 |
7 | CFBundleDevelopmentRegion
8 | $(DEVELOPMENT_LANGUAGE)
9 | CFBundleDisplayName
10 | stacks
11 | CFBundleExecutable
12 | $(EXECUTABLE_NAME)
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | $(PRODUCT_NAME)
19 | CFBundlePackageType
20 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
21 | CFBundleShortVersionString
22 | 1.0.0
23 | CFBundleSignature
24 | ????
25 | CFBundleURLTypes
26 |
27 |
28 | CFBundleURLSchemes
29 |
30 | com.anonymous.stacks
31 |
32 |
33 |
34 | CFBundleVersion
35 | 1
36 | LSRequiresIPhoneOS
37 |
38 | NSAppTransportSecurity
39 |
40 | NSAllowsArbitraryLoads
41 |
42 | NSAllowsLocalNetworking
43 |
44 |
45 | UILaunchStoryboardName
46 | SplashScreen
47 | UIRequiredDeviceCapabilities
48 |
49 | armv7
50 |
51 | UIRequiresFullScreen
52 |
53 | UIStatusBarStyle
54 | UIStatusBarStyleDefault
55 | UISupportedInterfaceOrientations
56 |
57 | UIInterfaceOrientationPortrait
58 | UIInterfaceOrientationPortraitUpsideDown
59 |
60 | UISupportedInterfaceOrientations~ipad
61 |
62 | UIInterfaceOrientationPortrait
63 | UIInterfaceOrientationPortraitUpsideDown
64 | UIInterfaceOrientationLandscapeLeft
65 | UIInterfaceOrientationLandscapeRight
66 |
67 | UIUserInterfaceStyle
68 | Light
69 | UIViewControllerBasedStatusBarAppearance
70 |
71 |
72 |
--------------------------------------------------------------------------------
/example/ios/stacks/Supporting/Expo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | EXUpdatesCheckOnLaunch
6 | ALWAYS
7 | EXUpdatesEnabled
8 |
9 | EXUpdatesLaunchWaitMs
10 | 0
11 | EXUpdatesSDKVersion
12 | 50.0.0
13 |
14 |
--------------------------------------------------------------------------------
/example/ios/stacks/main.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char * argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/stacks/noop-file.swift:
--------------------------------------------------------------------------------
1 | //
2 | // @generated
3 | // A blank Swift file must be created for native modules with Swift files to work correctly.
4 | //
5 |
--------------------------------------------------------------------------------
/example/ios/stacks/stacks-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
--------------------------------------------------------------------------------
/example/ios/stacks/stacks.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | aps-environment
6 | development
7 |
8 |
--------------------------------------------------------------------------------
/example/metro.config.js:
--------------------------------------------------------------------------------
1 | const { getDefaultConfig } = require('expo/metro-config');
2 | const path = require('path');
3 |
4 | const config = getDefaultConfig(__dirname);
5 |
6 | const modules = (...paths) => {
7 | return path.resolve(__dirname, 'node_modules', ...paths);
8 | };
9 |
10 | config.watchFolders = config.watchFolders.concat([path.resolve(__dirname, '..', 'src')]);
11 | config.resolver.extraNodeModules = {
12 | '@babel/runtime': modules('@babel', 'runtime'),
13 | react: modules('react'),
14 | 'react-native': modules('react-native'),
15 | 'react-native-unistyles': modules('react-native-unistyles'),
16 | 'react-native-web': modules('react-native-web'),
17 | };
18 |
19 | module.exports = config;
20 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stacks",
3 | "version": "1.0.0",
4 | "main": "expo-router/entry",
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo run:android",
8 | "ios": "expo run:ios",
9 | "web": "expo start --web"
10 | },
11 | "dependencies": {
12 | "@expo/metro-runtime": "~3.1.3",
13 | "@grapp/stacks": "link:@grapp/stacks",
14 | "expo": "~50.0.14",
15 | "expo-constants": "~15.4.5",
16 | "expo-image": "^1.10.6",
17 | "expo-linking": "~6.2.2",
18 | "expo-router": "~3.4.8",
19 | "expo-status-bar": "~1.11.1",
20 | "react": "18.2.0",
21 | "react-dom": "18.2.0",
22 | "react-native": "0.73.6",
23 | "react-native-safe-area-context": "4.8.2",
24 | "react-native-screens": "~3.29.0",
25 | "react-native-unistyles": "^2.5.5",
26 | "react-native-web": "~0.19.6"
27 | },
28 | "devDependencies": {
29 | "@babel/core": "^7.20.0"
30 | },
31 | "private": true
32 | }
33 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@grapp/ts-config/react-native.json",
3 | "compilerOptions": {
4 | "paths": {
5 | "@grapp/stacks": ["../src"]
6 | }
7 | },
8 | "include": [
9 | "./app/**/*.{ts,tsx}"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@grapp/stacks",
3 | "description": "A set of components for building layouts in React Native. Powered by React Native Unistyles.",
4 | "version": "3.1.0",
5 | "license": "MIT",
6 | "author": "Marcin Dziewulski ",
7 | "react-native": "src/index.ts",
8 | "source": "src/index.ts",
9 | "main": "lib/index.js",
10 | "module": "lib/index.mjs",
11 | "types": "lib/index.d.mts",
12 | "files": [
13 | "src",
14 | "lib",
15 | "plugin",
16 | "*.md",
17 | "package.json"
18 | ],
19 | "scripts": {
20 | "build": "rm -rf ./lib && tsup",
21 | "types:check": "tsc --noEmit"
22 | },
23 | "publishConfig": {
24 | "access": "public"
25 | },
26 | "keywords": [
27 | "react",
28 | "react-native",
29 | "react-native-web",
30 | "typescript",
31 | "stacks",
32 | "layout",
33 | "ui"
34 | ],
35 | "homepage": "https://stacks.grapp.dev",
36 | "bugs": "https://github.com/grapp-dev/stacks/issues",
37 | "repository": {
38 | "type": "git",
39 | "url": "https://github.com/grapp-dev/stacks.git"
40 | },
41 | "peerDependencies": {
42 | "react": ">=18.0.0",
43 | "react-native": ">=0.71.0",
44 | "react-native-unistyles": ">=2.0"
45 | },
46 | "devDependencies": {
47 | "@grapp/eslint-config": "^0.1.1",
48 | "@grapp/prettier-config": "^0.1.1",
49 | "@grapp/ts-config": "^0.1.3",
50 | "@types/node": "^18.11.10",
51 | "@types/react": "~18.2.45",
52 | "react": "^18.2.0",
53 | "react-native": "0.73.6",
54 | "react-native-unistyles": "^2.5.5",
55 | "tsup": "^8.0.2",
56 | "typescript": "^5.4.4"
57 | },
58 | "cacheDirectories": ["node_modules"]
59 | }
60 |
--------------------------------------------------------------------------------
/src/components/Bleed.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box } from './Box';
4 |
5 | import type { ResponsiveProp } from '../types';
6 | import { negate } from '../utils';
7 |
8 | type BoxProps = Omit<
9 | React.ComponentProps,
10 | | 'margin'
11 | | 'marginX'
12 | | 'marginY'
13 | | 'marginTop'
14 | | 'marginRight'
15 | | 'marginBottom'
16 | | 'marginLeft'
17 | | 'marginStart'
18 | | 'marginEnd'
19 | >;
20 |
21 | export type BleedProps = BoxProps & {
22 | readonly space?: ResponsiveProp;
23 | readonly horizontal?: ResponsiveProp;
24 | readonly vertical?: ResponsiveProp;
25 | readonly top?: ResponsiveProp;
26 | readonly right?: ResponsiveProp;
27 | readonly bottom?: ResponsiveProp;
28 | readonly left?: ResponsiveProp;
29 | readonly start?: ResponsiveProp;
30 | readonly end?: ResponsiveProp;
31 | };
32 |
33 | export const Bleed = (props: BleedProps) => {
34 | const { children, space, horizontal, vertical, start, end, top, right, bottom, left, ...rest } =
35 | props;
36 |
37 | return (
38 |
50 | {children}
51 |
52 | );
53 | };
54 |
55 | Bleed.displayName = 'Bleed';
56 |
--------------------------------------------------------------------------------
/src/components/Column.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { BoxProps } from './Box';
4 |
5 | export const Column = (_props: BoxProps): JSX.Element => {
6 | throw new Error('[Stacks] Column must be a direct child of Columns.');
7 | };
8 |
9 | const markAsColumn = (node: React.FC) => {
10 | // @ts-expect-error: this_is_fine.png
11 | // eslint-disable-next-line functional/immutable-data
12 | node.__isColumn__ = true;
13 | };
14 |
15 | const from = >(Component: T) => {
16 | // @ts-expect-error: this_is_fine.png
17 | Component.__isColumnForwarded__ = true;
18 | return Component;
19 | };
20 |
21 | Column.from = from;
22 | Column.displayName = 'Column';
23 | markAsColumn(Column);
24 |
--------------------------------------------------------------------------------
/src/components/FloatBox.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { DimensionValue, StyleSheet } from 'react-native';
3 |
4 | import { Box } from './Box';
5 |
6 | import { useResponsiveProp } from '../hooks';
7 | import type { ResponsiveProp } from '../types';
8 |
9 | type BoxProps = React.ComponentProps;
10 |
11 | export type FloatBoxProps = Omit & {
12 | readonly top?: ResponsiveProp;
13 | readonly right?: ResponsiveProp;
14 | readonly bottom?: ResponsiveProp;
15 | readonly left?: ResponsiveProp;
16 | readonly offset?: ResponsiveProp;
17 | };
18 |
19 | export const FloatBox = (props: FloatBoxProps) => {
20 | const { children, top, right, bottom, left, offset, style, ...rest } = props;
21 |
22 | const resolveResponsiveProp = useResponsiveProp();
23 |
24 | const all = resolveResponsiveProp(offset);
25 | const edges = {
26 | top,
27 | right,
28 | bottom,
29 | left,
30 | };
31 |
32 | const fillObject = Object.fromEntries(
33 | Object.entries(edges).map(([key, value]) => {
34 | return [key, key in props ? resolveResponsiveProp(value) : all ?? null];
35 | }),
36 | );
37 |
38 | return (
39 |
40 | {children}
41 |
42 | );
43 | };
44 |
45 | FloatBox.displayName = 'FloatBox';
46 |
--------------------------------------------------------------------------------
/src/components/Grid.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Platform, Text } from 'react-native';
3 | import { createStyleSheet, UnistylesRuntime, useStyles } from 'react-native-unistyles';
4 |
5 | import { Box } from './Box';
6 | import { FloatBox } from './FloatBox';
7 |
8 | import { useResponsiveProp, useSpacingHelpers } from '../hooks';
9 | import type { ResponsiveProp } from '../types';
10 | import { makeWithIndex } from '../utils';
11 |
12 | export type GridProps = {
13 | readonly gutter?: ResponsiveProp;
14 | readonly margin?: ResponsiveProp;
15 | readonly columns?: ResponsiveProp;
16 | readonly opacity?: ResponsiveProp;
17 | readonly color?: ResponsiveProp;
18 | };
19 |
20 | type Options = {
21 | readonly width: number;
22 | readonly columns: number;
23 | readonly gutter: number;
24 | readonly margin: number;
25 | };
26 |
27 | const calculateColumnWidth = (options: Options) => {
28 | const { columns } = options;
29 | const gutterCount = columns - 1;
30 | return (options.width - options.margin * 2 - gutterCount * options.gutter) / columns;
31 | };
32 |
33 | const calculateGridWidth = (options: Options) => {
34 | const { columns } = options;
35 | const gutterCount = columns - 1;
36 | return options.width * columns + gutterCount * options.gutter + options.margin * 2;
37 | };
38 |
39 | export const Grid = (props: GridProps) => {
40 | const { gutter = 2, margin = 2, opacity = 0.2, columns = 8, color = 'red' } = props;
41 |
42 | const { multiply } = useSpacingHelpers();
43 | const { styles } = useStyles(stylesheet);
44 | const resolveResponsiveProp = useResponsiveProp();
45 |
46 | const numberOfColumns = resolveResponsiveProp(columns);
47 | const defaultOpacity = resolveResponsiveProp(opacity);
48 | const backgroundColor = resolveResponsiveProp(color);
49 |
50 | const options: Options = {
51 | width: UnistylesRuntime.screen.width,
52 | columns: numberOfColumns,
53 | margin: multiply(resolveResponsiveProp(margin)),
54 | gutter: multiply(resolveResponsiveProp(gutter)),
55 | };
56 |
57 | const columnWidth = calculateColumnWidth(options);
58 | const gridWidth = calculateGridWidth({
59 | ...options,
60 | width: columnWidth,
61 | });
62 | const columnStyle = { opacity: defaultOpacity, backgroundColor };
63 |
64 | return (
65 | <>
66 |
74 | {makeWithIndex(numberOfColumns, index => {
75 | return ;
76 | })}
77 |
78 | {gridWidth !== UnistylesRuntime.screen.width ? (
79 |
80 | {`Calculated grid width (${gridWidth}) doesn't equal to the window width (${UnistylesRuntime.screen.width}). Please, adjust \`Grid\` options.`}
83 |
84 | ) : null}
85 | >
86 | );
87 | };
88 |
89 | Grid.displayName = 'Grid';
90 |
91 | const stylesheet = createStyleSheet({
92 | root: {
93 | pointerEvents: 'none',
94 | },
95 | text: {
96 | color: 'white',
97 | fontFamily:
98 | Platform.OS === 'android' ? 'monospace' : Platform.OS === 'ios' ? 'Menlo' : 'sans-serif',
99 | },
100 | });
101 |
--------------------------------------------------------------------------------
/src/components/Hidden.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import type { Breakpoint } from '../types';
4 |
5 | export type HiddenProps = React.PropsWithChildren<{
6 | readonly above?: Breakpoint;
7 | readonly below?: Breakpoint;
8 | }>;
9 |
10 | export const Hidden = (_props: HiddenProps): JSX.Element => {
11 | throw new Error(
12 | '[Stacks] Logical expression for the `Hidden` component could not be handled. Open a new issue and provide reproduction code.',
13 | );
14 | };
15 |
--------------------------------------------------------------------------------
/src/components/Inline.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box } from './Box';
4 |
5 | import { useBreakpointComparators } from '../hooks';
6 | import type { AxisX, AxisY, Breakpoint, ResponsiveProp, Space } from '../types';
7 |
8 | type BoxProps = Omit<
9 | React.ComponentProps,
10 | 'gap' | 'rowGap' | 'columnGap' | 'alignX' | 'alignY' | 'direction' | 'wrap'
11 | >;
12 |
13 | export type InlineProps = BoxProps & {
14 | readonly space?: ResponsiveProp;
15 | readonly spaceX?: ResponsiveProp;
16 | readonly spaceY?: ResponsiveProp;
17 | readonly alignX?: ResponsiveProp;
18 | readonly alignY?: ResponsiveProp;
19 | readonly collapseBelow?: Breakpoint;
20 | };
21 |
22 | export const Inline = (props: InlineProps) => {
23 | const { space, children, spaceX, spaceY, alignX, alignY, collapseBelow, ...rest } = props;
24 | const breakpoint = useBreakpointComparators();
25 |
26 | const isCollapsed = breakpoint.isBelow(collapseBelow);
27 | const direction = isCollapsed ? 'column' : 'row';
28 | const wrap = isCollapsed ? 'no-wrap' : 'wrap';
29 |
30 | return (
31 |
41 | {children}
42 |
43 | );
44 | };
45 |
46 | Inline.displayName = 'Inline';
47 |
--------------------------------------------------------------------------------
/src/components/Inset.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box } from './Box';
4 |
5 | import type { ResponsiveProp } from '../types';
6 |
7 | type BoxProps = Omit<
8 | React.ComponentProps,
9 | | 'padding'
10 | | 'paddingX'
11 | | 'paddingY'
12 | | 'paddingTop'
13 | | 'paddingRight'
14 | | 'paddingBottom'
15 | | 'paddingLeft'
16 | | 'paddingStart'
17 | | 'paddingEnd'
18 | >;
19 |
20 | export type InsetProps = BoxProps & {
21 | readonly space?: ResponsiveProp;
22 | readonly horizontal?: ResponsiveProp;
23 | readonly vertical?: ResponsiveProp;
24 | readonly top?: ResponsiveProp;
25 | readonly right?: ResponsiveProp;
26 | readonly bottom?: ResponsiveProp;
27 | readonly left?: ResponsiveProp;
28 | readonly start?: ResponsiveProp;
29 | readonly end?: ResponsiveProp;
30 | };
31 |
32 | export const Inset = (props: InsetProps) => {
33 | const { children, space, horizontal, vertical, start, end, top, right, bottom, left, ...rest } =
34 | props;
35 |
36 | return (
37 |
49 | {children}
50 |
51 | );
52 | };
53 |
54 | Inset.displayName = 'Inset';
55 |
--------------------------------------------------------------------------------
/src/components/Row.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { BoxProps } from './Box';
4 |
5 | export const Row = (_props: BoxProps): JSX.Element => {
6 | throw new Error('[Stacks] Row must be a direct child of Rows.');
7 | };
8 |
9 | const markAsRow = (node: React.FC) => {
10 | // @ts-expect-error: this_is_fine.png
11 | node.__isRow__ = true;
12 | };
13 |
14 | const from = >(Component: T) => {
15 | // @ts-expect-error: this_is_fine.png
16 | Component.__isRowForwarded__ = true;
17 | return Component;
18 | };
19 |
20 | Row.from = from;
21 | Row.displayName = 'Row';
22 | markAsRow(Row);
23 |
--------------------------------------------------------------------------------
/src/components/Rows.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box } from './Box';
4 | import { Row } from './Row';
5 |
6 | import type { AxisX, AxisY, Flex, ResponsiveProp, Space } from '../types';
7 | import { flattenChildren } from '../utils';
8 |
9 | type BoxProps = Omit<
10 | React.ComponentProps,
11 | 'direction' | 'gap' | 'rowGap' | 'columnGap' | 'alignX' | 'alignY'
12 | >;
13 |
14 | export type RowProps = React.ComponentProps;
15 |
16 | export type RowsProps = BoxProps & {
17 | readonly space?: ResponsiveProp;
18 | readonly defaultFlex?: ResponsiveProp;
19 | readonly alignX?: ResponsiveProp;
20 | readonly alignY?: ResponsiveProp;
21 | };
22 |
23 | const getRowProps = (node: React.ReactNode): RowProps | null => {
24 | return node !== undefined &&
25 | node !== null &&
26 | typeof node === 'object' &&
27 | 'type' in node &&
28 | // @ts-expect-error: this is ok
29 | node.type.__isRow__
30 | ? (node.props as RowProps)
31 | : null;
32 | };
33 |
34 | const isRowForwarded = (node: React.ReactNode): node is React.ReactElement => {
35 | return (
36 | node !== undefined &&
37 | node !== null &&
38 | typeof node === 'object' &&
39 | 'type' in node &&
40 | // @ts-expect-error: this is ok
41 | node.type.__isRowForwarded__ &&
42 | React.isValidElement(node)
43 | );
44 | };
45 |
46 | export const Rows = (props: RowsProps) => {
47 | const { children, space, defaultFlex = 'fluid', ...rest } = props;
48 |
49 | return (
50 |
51 | {React.Children.map(flattenChildren(children), child => {
52 | if (isRowForwarded(child)) {
53 | return React.cloneElement(
54 | child,
55 | { ...child.props, flex: child.props.flex ?? defaultFlex },
56 | child.props.children,
57 | );
58 | }
59 |
60 | const props = getRowProps(child);
61 |
62 | if (props) {
63 | const { children, flex, ...rest } = props;
64 |
65 | return (
66 |
67 | {children}
68 |
69 | );
70 | }
71 |
72 | return {child};
73 | })}
74 |
75 | );
76 | };
77 |
78 | Rows.displayName = 'Rows';
79 |
--------------------------------------------------------------------------------
/src/components/Stack.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box } from './Box';
4 |
5 | import { useResponsiveProp } from '../hooks';
6 | import type { AxisX, AxisY, ResponsiveProp } from '../types';
7 | import { flattenChildren, intersperse } from '../utils';
8 |
9 | type BoxProps = Omit<
10 | React.ComponentProps,
11 | 'direction' | 'alignX' | 'alignY' | 'rowGap' | 'columnGap'
12 | >;
13 |
14 | export type StackProps = BoxProps & {
15 | readonly space?: ResponsiveProp;
16 | readonly horizontal?: ResponsiveProp;
17 | readonly align?: ResponsiveProp;
18 | readonly divider?: React.ReactElement;
19 | };
20 |
21 | export const Stack = (props: StackProps) => {
22 | const { children, flex = 'content', space, horizontal, align, divider, ...rest } = props;
23 |
24 | const resolveResponsiveProp = useResponsiveProp();
25 | const isHorizontal = resolveResponsiveProp(horizontal);
26 |
27 | const direction = isHorizontal ? 'row' : 'column';
28 | const alignY = isHorizontal ? align : undefined;
29 | const alignX = isHorizontal ? undefined : align;
30 |
31 | return (
32 |
33 | {React.isValidElement(divider)
34 | ? flattenChildren(intersperse(React.Children.toArray(children), divider))
35 | : children}
36 |
37 | );
38 | };
39 |
40 | Stack.displayName = 'Stack';
41 |
--------------------------------------------------------------------------------
/src/components/Tiles.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Box } from './Box';
4 |
5 | import { useResponsiveProp } from '../hooks';
6 | import type { AxisY, ResponsiveProp, Space } from '../types';
7 | import { makeWithIndex, splitEvery } from '../utils';
8 |
9 | type BoxProps = Omit<
10 | React.ComponentProps,
11 | 'gap' | 'rowGap' | 'columnGap' | 'direction' | 'alignX' | 'alignY'
12 | >;
13 |
14 | export type TilesProps = BoxProps & {
15 | readonly columns?: ResponsiveProp;
16 | readonly space?: ResponsiveProp;
17 | readonly spaceX?: ResponsiveProp;
18 | readonly spaceY?: ResponsiveProp;
19 | readonly fill?: ResponsiveProp;
20 | readonly alignY?: ResponsiveProp;
21 | };
22 |
23 | export const Tiles = (props: TilesProps) => {
24 | const { children, columns = 1, space, spaceX, spaceY, fill = false, reverse, ...rest } = props;
25 |
26 | const resolveResponsiveProp = useResponsiveProp();
27 |
28 | const numberOfColumns = Math.max(resolveResponsiveProp(columns), 1);
29 | const shouldFill = resolveResponsiveProp(fill);
30 | const rows = splitEvery(React.Children.toArray(children), numberOfColumns);
31 |
32 | return (
33 |
34 | {rows.map((columns, rowIndex) => {
35 | const elements = makeWithIndex(numberOfColumns, index => {
36 | return columns[index] ?? null;
37 | });
38 |
39 | return (
40 |
41 | {elements.map((element, columnIndex) => {
42 | return React.isValidElement(element) || !shouldFill ? (
43 |
44 | {element}
45 |
46 | ) : null;
47 | })}
48 |
49 | );
50 | })}
51 |
52 | );
53 | };
54 |
55 | Tiles.displayName = 'Tiles';
56 |
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Bleed';
2 | export * from './Box';
3 | export * from './Column';
4 | export * from './Columns';
5 | export * from './FloatBox';
6 | export * from './Grid';
7 | export * from './Hidden';
8 | export * from './Inline';
9 | export * from './Inset';
10 | export * from './Rows';
11 | export * from './Row';
12 | export * from './Stack';
13 | export * from './Tiles';
14 |
--------------------------------------------------------------------------------
/src/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useSpacingHelpers';
2 | export * from './useDebugStyle';
3 | export * from './useBreakpointComparators';
4 | export * from './useResponsiveProp';
5 |
--------------------------------------------------------------------------------
/src/hooks/useBreakpointComparators.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { useStyles } from 'react-native-unistyles';
3 |
4 | import { Breakpoint } from '../types';
5 | import { isBreakpointAbove, isBreakpointBelow } from '../utils';
6 |
7 | export const useBreakpointComparators = () => {
8 | const { breakpoint: currentBreakpoint } = useStyles();
9 |
10 | const isBelow = React.useCallback(
11 | (breakpoint?: Breakpoint) => {
12 | return isBreakpointBelow(currentBreakpoint, breakpoint);
13 | },
14 | [currentBreakpoint],
15 | );
16 |
17 | const isAbove = React.useCallback(
18 | (breakpoint?: Breakpoint) => {
19 | return isBreakpointAbove(currentBreakpoint, breakpoint);
20 | },
21 | [currentBreakpoint],
22 | );
23 |
24 | return { isBelow, isAbove };
25 | };
26 |
--------------------------------------------------------------------------------
/src/hooks/useDebugStyle.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { ViewStyle } from 'react-native';
3 | import { useStyles } from 'react-native-unistyles';
4 |
5 | import { randomColor } from '../utils';
6 |
7 | export const useDebugStyle = () => {
8 | const { theme } = useStyles();
9 | const backgroundColor = React.useRef(randomColor()).current;
10 | // @ts-expect-error: this_is_fine.png
11 | const debug = theme?.stacks?.debug;
12 | const style = React.useRef(debug ? { backgroundColor } : undefined);
13 |
14 | React.useEffect(() => {
15 | style.current = debug
16 | ? {
17 | backgroundColor,
18 | }
19 | : undefined;
20 | }, [debug]);
21 |
22 | return style.current;
23 | };
24 |
--------------------------------------------------------------------------------
/src/hooks/useResponsiveProp.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { useStyles } from 'react-native-unistyles';
3 |
4 | import { ResponsiveProp } from '../types';
5 | import { resolveResponsiveProp } from '../utils';
6 |
7 | export const useResponsiveProp = () => {
8 | const { breakpoint } = useStyles();
9 |
10 | return React.useCallback(
11 | (value: ResponsiveProp) => {
12 | return resolveResponsiveProp(value, breakpoint);
13 | },
14 | [breakpoint],
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/src/hooks/useSpacingHelpers.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { useStyles } from 'react-native-unistyles';
3 |
4 | import { useResponsiveProp } from './useResponsiveProp';
5 |
6 | type SpacingHelper = {
7 | (value: number): number;
8 | (value: number | undefined): number | undefined;
9 | };
10 |
11 | export const useSpacingHelpers = () => {
12 | const { theme } = useStyles();
13 |
14 | const resolveResponsiveProp = useResponsiveProp();
15 | // @ts-expect-error: this_is_fine.png
16 | const spacing = resolveResponsiveProp(theme?.stacks?.spacing ?? 4);
17 |
18 | const multiply = React.useCallback(
19 | value => {
20 | if (typeof value !== 'undefined') {
21 | return value * spacing;
22 | }
23 |
24 | return undefined;
25 | },
26 | [spacing],
27 | ) as SpacingHelper;
28 |
29 | const divide = React.useCallback(
30 | value => {
31 | if (typeof value !== 'undefined') {
32 | return value / spacing;
33 | }
34 |
35 | return undefined;
36 | },
37 | [spacing],
38 | ) as SpacingHelper;
39 |
40 | return {
41 | multiply,
42 | divide,
43 | };
44 | };
45 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './components';
2 | export * from './hooks';
3 | export * from './types';
4 |
--------------------------------------------------------------------------------
/src/polymorphic.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/ban-types */
2 |
3 | /*
4 | Adapted from Radix's react-polymorphic package
5 | https://github.com/radix-ui/primitives/tree/main/packages/react/polymorphic
6 |
7 | ---
8 |
9 | MIT License
10 |
11 | Copyright (c) 2020 Modulz
12 |
13 | Permission is hereby granted, free of charge, to any person obtaining a copy
14 | of this software and associated documentation files (the "Software"), to deal
15 | in the Software without restriction, including without limitation the rights
16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 | copies of the Software, and to permit persons to whom the Software is
18 | furnished to do so, subject to the following conditions:
19 |
20 | The above copyright notice and this permission notice shall be included in all
21 | copies or substantial portions of the Software.
22 |
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 | SOFTWARE.
30 | */
31 | import * as React from 'react';
32 |
33 | /* -------------------------------------------------------------------------------------------------
34 | * Utility types
35 | * -----------------------------------------------------------------------------------------------*/
36 | type Merge = Omit & P2;
37 |
38 | /**
39 | * Infers the OwnProps if E is a ForwardRefExoticComponentWithAs
40 | */
41 | type OwnProps = E extends ForwardRefComponent ? P : {};
42 |
43 | /**
44 | * Infers the JSX.IntrinsicElement if E is a ForwardRefExoticComponentWithAs
45 | */
46 | type IntrinsicElement = E extends ForwardRefComponent ? I : never;
47 |
48 | type ForwardRefExoticComponent = React.ForwardRefExoticComponent<
49 | Merge<
50 | E extends React.ElementType ? React.ComponentPropsWithRef : never,
51 | OwnProps & { readonly as?: E }
52 | >
53 | >;
54 |
55 | /* -------------------------------------------------------------------------------------------------
56 | * ForwardRefComponent
57 | * -----------------------------------------------------------------------------------------------*/
58 |
59 | interface ForwardRefComponent<
60 | DefaultComponentType,
61 | OwnProps = {},
62 | /**
63 | * Extends original type to ensure built in React types play nice
64 | * with polymorphic components still e.g. `React.ElementRef` etc.
65 | */
66 | > extends ForwardRefExoticComponent {
67 | /**
68 | * When `as` prop is passed, use this overload.
69 | * Merges original own props (without DOM props) and the inferred props
70 | * from `as` element with the own props taking precendence.
71 | *
72 | * We explicitly avoid `React.ElementType` and manually narrow the prop types
73 | * so that events are typed when using JSX.IntrinsicElements.
74 | */
75 | (
76 | props: As extends React.ComponentType
77 | ? Merge
78 | : never,
79 | ): React.ReactElement | null;
80 | }
81 |
82 | export type { ForwardRefComponent, OwnProps, IntrinsicElement, Merge };
83 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-empty-interface */
2 | import { UnistylesBreakpoints } from 'react-native-unistyles';
3 |
4 | export interface StacksBreakpoints {}
5 |
6 | export type Breakpoint = keyof StacksBreakpoints | keyof UnistylesBreakpoints;
7 | export type AxisX = 'left' | 'center' | 'right';
8 | export type AxisY = 'top' | 'center' | 'bottom';
9 | export type Stretch = 'stretch';
10 | export type Space = 'between' | 'around' | 'evenly';
11 | export type Direction = 'row' | 'row-reverse' | 'column' | 'column-reverse';
12 | export type Wrap = 'wrap' | 'no-wrap' | 'wrap-reverse';
13 |
14 | export type Flex =
15 | | 'content'
16 | | 'fluid'
17 | | '1/2'
18 | | '1/3'
19 | | '2/3'
20 | | '1/4'
21 | | '3/4'
22 | | '1/5'
23 | | '2/5'
24 | | '3/5'
25 | | '4/5';
26 |
27 | export type ResponsiveProp = T | readonly T[];
28 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@grapp/ts-config/react-native.json",
3 | "include": ["./src/**/*"]
4 | }
5 |
--------------------------------------------------------------------------------
/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup';
2 |
3 | export default defineConfig({
4 | entry: ['src/index.ts'],
5 | splitting: false,
6 | sourcemap: false,
7 | outDir: './lib',
8 | format: ['esm', 'cjs'],
9 | clean: true,
10 | dts: true,
11 | external: ['react', 'react-native', 'react-native-unistyles'],
12 | });
13 |
--------------------------------------------------------------------------------