Showing preview only (545K chars total). Download the full file or copy to clipboard to get everything.
Repository: airbnb/react-sketchapp
Branch: master
Commit: b238e69c6f1e
Files: 320
Total size: 477.5 KB
Directory structure:
gitextract_dx66tsc9/
├── .bookignore
├── .editorconfig
├── .github/
│ ├── CODE_OF_CONDUCT.md
│ ├── CONTRIBUTING.md
│ └── ISSUE_TEMPLATE.md
├── .gitignore
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── __tests__/
│ ├── jest/
│ │ ├── components/
│ │ │ ├── Artboard.tsx
│ │ │ ├── Document.tsx
│ │ │ ├── Image.tsx
│ │ │ ├── Page.tsx
│ │ │ ├── RedBox.tsx
│ │ │ ├── Svg.tsx
│ │ │ ├── Text.tsx
│ │ │ ├── View.tsx
│ │ │ ├── __snapshots__/
│ │ │ │ ├── Artboard.tsx.snap
│ │ │ │ ├── Document.tsx.snap
│ │ │ │ ├── Image.tsx.snap
│ │ │ │ ├── Page.tsx.snap
│ │ │ │ ├── RedBox.tsx.snap
│ │ │ │ ├── Svg.tsx.snap
│ │ │ │ ├── Text.tsx.snap
│ │ │ │ └── View.tsx.snap
│ │ │ └── nodeImpl/
│ │ │ ├── Svg.tsx
│ │ │ └── __snapshots__/
│ │ │ └── Svg.tsx.snap
│ │ ├── index.ts
│ │ ├── jsonUtils/
│ │ │ ├── computeTextTree.ts
│ │ │ ├── computeYogaNode.ts
│ │ │ ├── computeYogaTree.ts
│ │ │ ├── layerGroup.ts
│ │ │ ├── models.ts
│ │ │ ├── shapeLayers.ts
│ │ │ └── style.ts
│ │ ├── reactTreeToFlexTree.ts
│ │ ├── sharedStyles/
│ │ │ └── TextStyles.ts
│ │ └── utils/
│ │ ├── isDefined.ts
│ │ ├── sortObjectKeys.ts
│ │ └── zIndex.ts
│ └── skpm/
│ ├── basic.test.js
│ ├── render-context.test.js
│ └── render-in-wrapped-object.test.js
├── book.json
├── docs/
│ ├── API.md
│ ├── FAQ.md
│ ├── README.md
│ ├── examples.md
│ └── guides/
│ ├── README.md
│ ├── data-fetching.md
│ ├── getting-started.md
│ ├── rendering.md
│ ├── styling.md
│ ├── universal-rendering.md
│ └── using-skpm.md
├── examples/
│ ├── .eslintrc
│ ├── .gitignore
│ ├── basic-setup/
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── manifest.json
│ │ │ └── my-command.js
│ │ └── webpack.skpm.config.js
│ ├── basic-setup-typescript/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── manifest.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── my-command.tsx
│ │ │ └── types/
│ │ │ └── sketch.d.ts
│ │ ├── tsconfig.json
│ │ └── webpack.skpm.config.js
│ ├── basic-svg/
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── manifest.json
│ │ │ └── my-command.js
│ │ └── webpack.skpm.config.js
│ ├── colors/
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── main.js
│ │ │ └── manifest.json
│ │ └── webpack.skpm.config.js
│ ├── emotion/
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── manifest.json
│ │ │ └── my-command.js
│ │ └── webpack.skpm.config.js
│ ├── form-validation/
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ ├── Button.js
│ │ │ │ ├── Register.js
│ │ │ │ ├── Space.js
│ │ │ │ ├── StrengthMeter.js
│ │ │ │ └── TextBox/
│ │ │ │ ├── index.js
│ │ │ │ ├── index.sketch.js
│ │ │ │ └── style.js
│ │ │ ├── data.js
│ │ │ ├── designSystem.js
│ │ │ ├── main.js
│ │ │ ├── manifest.json
│ │ │ └── web.js
│ │ └── webpack.skpm.config.js
│ ├── foursquare-maps/
│ │ ├── .eslintrc
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.js
│ │ │ ├── getVenues.js
│ │ │ ├── main.js
│ │ │ ├── manifest.json
│ │ │ └── web.js
│ │ └── webpack.skpm.config.js
│ ├── glamorous/
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── manifest.json
│ │ │ └── my-command.js
│ │ └── webpack.skpm.config.js
│ ├── profile-cards/
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ ├── Profile.js
│ │ │ │ └── Space.js
│ │ │ ├── designSystem.js
│ │ │ ├── main.js
│ │ │ └── manifest.json
│ │ └── webpack.skpm.config.js
│ ├── profile-cards-graphql/
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ ├── Profile.js
│ │ │ │ └── Space.js
│ │ │ ├── designSystem.js
│ │ │ ├── main.js
│ │ │ └── manifest.json
│ │ └── webpack.skpm.config.js
│ ├── profile-cards-primitives/
│ │ ├── README.md
│ │ ├── nwb.config.js
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ ├── Profile.js
│ │ │ │ └── Space.js
│ │ │ ├── data.js
│ │ │ ├── designSystem.js
│ │ │ ├── main.js
│ │ │ ├── manifest.json
│ │ │ └── web.js
│ │ └── webpack.skpm.config.js
│ ├── profile-cards-react-with-styles/
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ └── Profile.js
│ │ │ ├── main.js
│ │ │ ├── manifest.json
│ │ │ ├── theme.js
│ │ │ ├── types.js
│ │ │ └── withStyles.js
│ │ └── webpack.skpm.config.js
│ ├── react-router-prototyping/
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.js
│ │ │ ├── components/
│ │ │ │ ├── AppBar.js
│ │ │ │ └── NavBar.js
│ │ │ ├── main.js
│ │ │ ├── manifest.json
│ │ │ └── routes/
│ │ │ ├── about.js
│ │ │ ├── home.js
│ │ │ ├── post.js
│ │ │ └── profile.js
│ │ └── webpack.skpm.config.js
│ ├── styled-components/
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── manifest.json
│ │ │ └── my-command.js
│ │ └── webpack.skpm.config.js
│ ├── styleguide/
│ │ ├── .flowconfig
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ ├── AccessibilityBadge.js
│ │ │ │ ├── Badge.js
│ │ │ │ ├── Label.js
│ │ │ │ ├── Palette.js
│ │ │ │ ├── Section.js
│ │ │ │ ├── Swatch.js
│ │ │ │ └── TypeSpecimen.js
│ │ │ ├── designSystem.js
│ │ │ ├── main.js
│ │ │ ├── manifest.json
│ │ │ └── processColor.js
│ │ └── webpack.skpm.config.js
│ ├── symbols/
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── manifest.json
│ │ │ └── my-command.js
│ │ └── webpack.skpm.config.js
│ └── timeline-airtable/
│ ├── .eslintrc
│ ├── README.md
│ ├── package.json
│ ├── src/
│ │ ├── main.js
│ │ └── manifest.json
│ └── webpack.skpm.config.js
├── jest.config.js
├── package.json
├── prettier.config.js
├── src/
│ ├── Platform.ts
│ ├── buildTree.ts
│ ├── components/
│ │ ├── Artboard.tsx
│ │ ├── Document.tsx
│ │ ├── Image.tsx
│ │ ├── ImageStylePropTypes.ts
│ │ ├── Page.tsx
│ │ ├── PageStylePropTypes.ts
│ │ ├── RedBox.tsx
│ │ ├── ResizeModePropTypes.ts
│ │ ├── ResizingConstraintPropTypes.ts
│ │ ├── ShadowsPropTypes.ts
│ │ ├── Svg/
│ │ │ ├── Circle.tsx
│ │ │ ├── ClipPath.tsx
│ │ │ ├── Defs.tsx
│ │ │ ├── Ellipse.tsx
│ │ │ ├── G.tsx
│ │ │ ├── Image.tsx
│ │ │ ├── Line.tsx
│ │ │ ├── LinearGradient.tsx
│ │ │ ├── Path.tsx
│ │ │ ├── Pattern.tsx
│ │ │ ├── Polygon.tsx
│ │ │ ├── Polyline.tsx
│ │ │ ├── RadialGradient.tsx
│ │ │ ├── Rect.tsx
│ │ │ ├── Stop.tsx
│ │ │ ├── Svg.tsx
│ │ │ ├── Symbol.tsx
│ │ │ ├── TSpan.tsx
│ │ │ ├── Text.tsx
│ │ │ ├── TextPath.tsx
│ │ │ ├── Use.tsx
│ │ │ ├── index.tsx
│ │ │ └── props.ts
│ │ ├── Text.tsx
│ │ ├── TextStylePropTypes.ts
│ │ ├── View.tsx
│ │ ├── ViewStylePropTypes.ts
│ │ └── index.ts
│ ├── context.tsx
│ ├── entrypoint.sketch.ts
│ ├── entrypoint.ts
│ ├── flexToSketchJSON.ts
│ ├── index.ts
│ ├── jsonUtils/
│ │ ├── borders.ts
│ │ ├── computeTextTree.ts
│ │ ├── computeYogaNode.ts
│ │ ├── computeYogaTree.ts
│ │ ├── hotspotLayer.ts
│ │ ├── layerGroup.ts
│ │ ├── makeSvgLayer/
│ │ │ ├── graphics/
│ │ │ │ ├── curvePoint.ts
│ │ │ │ ├── path.ts
│ │ │ │ ├── point.ts
│ │ │ │ ├── rect.ts
│ │ │ │ └── types.ts
│ │ │ ├── index.sketch.ts
│ │ │ └── index.ts
│ │ ├── models.ts
│ │ ├── resizeConstraint.ts
│ │ ├── shapeLayers.ts
│ │ ├── sketchJson/
│ │ │ ├── fromSJSON.ts
│ │ │ └── toSJSON.ts
│ │ ├── style.ts
│ │ └── textLayers.ts
│ ├── platformBridges/
│ │ ├── macos.ts
│ │ └── sketch/
│ │ ├── createStringMeasurer.ts
│ │ ├── findFontName.ts
│ │ ├── index.ts
│ │ └── makeImageDataFromUrl.ts
│ ├── render.tsx
│ ├── renderToJSON.ts
│ ├── renderers/
│ │ ├── ArtboardRenderer.ts
│ │ ├── ImageRenderer.ts
│ │ ├── SketchRenderer.ts
│ │ ├── SvgRenderer.ts
│ │ ├── SymbolInstanceRenderer.ts
│ │ ├── SymbolMasterRenderer.ts
│ │ ├── TextRenderer.ts
│ │ ├── ViewRenderer.ts
│ │ └── index.ts
│ ├── resets.ts
│ ├── sharedStyles/
│ │ └── TextStyles.ts
│ ├── stylesheet/
│ │ ├── expandStyle.ts
│ │ ├── index.ts
│ │ └── types.ts
│ ├── symbol.tsx
│ ├── types/
│ │ ├── globals.d.ts
│ │ ├── index.ts
│ │ ├── intrinsic.d.ts
│ │ ├── js-sha1.d.ts
│ │ ├── murmur2js.d.ts
│ │ ├── node-sketch-bridge.d.ts
│ │ └── normalize-css-color.d.ts
│ └── utils/
│ ├── Context.ts
│ ├── constants.ts
│ ├── createStringMeasurer.ts
│ ├── getDocument.ts
│ ├── getImageDataFromURL.ts
│ ├── getSketchVersion.ts
│ ├── hasAnyDefined.ts
│ ├── hashStyle.ts
│ ├── isDefined.ts
│ ├── isNativeDocument.ts
│ ├── isNativePage.ts
│ ├── isNativeSymbolsPage.ts
│ ├── pick.ts
│ ├── processTransform/
│ │ ├── index.ts
│ │ ├── matrix2D.ts
│ │ ├── parseTransformOriginProp.ts
│ │ └── parseTransformProp.ts
│ ├── same.ts
│ ├── sharedTextStyles/
│ │ ├── index.sketch.ts
│ │ └── index.ts
│ ├── sortObjectKeys.ts
│ └── zIndex.ts
├── template/
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ └── src/
│ ├── manifest.json
│ └── my-command.js
├── tsconfig.json
└── tsconfig.module.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .bookignore
================================================
.github/
__tests__/
examples/
lib/
scratch/
src/
================================================
FILE: .editorconfig
================================================
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
end_of_line = lf
insert_final_newline = true
================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at jon.gold@airbnb.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing
Contributions are welcome and are greatly appreciated! Every little bit helps, and credit will always be given. By contributing, you agree to abide by the [code of conduct](https://github.com/airbnb/react-sketchapp/blob/master/.github/CODE_OF_CONDUCT.md).
## Reporting Issues and Asking Questions
**For support or usage questions like “how do I do X with react-sketchapp” and “my code doesn't work”, please search and ask on [StackOverflow with a react-sketchapp tag](http://stackoverflow.com/questions/tagged/react-sketchapp?sort=votes&pageSize=50) first.**
We ask you to do this because StackOverflow does a much better job at keeping popular questions visible. Unfortunately good answers get lost and outdated on GitHub.
Some questions take a long time to get an answer. **If your question gets closed or you don't get a reply on StackOverflow for longer than a few days,** we encourage you to post an issue linking to your question. We will close your issue but this will give people watching the repo an opportunity to see your question and reply to it on StackOverflow if they know the answer.
Please be considerate when doing this as this is not the primary purpose of the issue tracker.
### Help Us Help You
On both websites, it is a good idea to structure your code and question in a way that is easy to read to entice people to answer it. For example, we encourage you to use syntax highlighting, indentation, and split text in paragraphs.
Please keep in mind that people spend their free time trying to help you. You can make it easier for them if you provide versions of the relevant libraries and a runnable small project reproducing your issue. You can put your code on [JSBin](http://jsbin.com) or, for bigger projects, on GitHub. Make sure all the necessary dependencies are declared in `package.json` so anyone can run `npm install && npm start` and reproduce your issue.
## Development
Visit the [issue tracker](https://github.com/airbnb/react-sketchapp/issues) to find a list of open issues that need attention.
Fork, then clone the repo
```bash
git clone https://github.com/your-username/react-sketchapp.git
```
### Setting up your environment
### Testing, style & Linting
To run tests
```bash
npm run test
```
To run tests continuously
```bash
npm run test:watch
```
This style of the codebase is enforced by [Prettier](https://prettier.io/).
It is recommended that you install a Prettier plugin for your editor of choice when working on this codebase.
### Docs
We always appreciate improvements to the documentation!
#### Installing Gitbook
To install the latest version of `gitbook` and prepare to build the documentation, run the following:
```
npm run docs:prepare
```
#### Building the Docs
To build the documentation, run the following:
```
npm run docs:build
```
To watch and rebuild documentation when changes occur, run the following:
```
npm run docs:watch
```
The docs will be served at http://localhost:4000.
#### Publishing the Docs
To publish the documentation, run the following:
```
npm run docs:publish
```
### Sending a Pull Request
For non-trivial changes, please open an issue with a proposal for a new feature or refactoring before starting work — we don't want you to waste your time on a pull request that won't be accepted.
On the other hand, sometimes the best way to start a discussion _is_ to send a pull request. Use your judgement!
In general, the contribution workflow looks like this:
- Open a new issue in the [Issue tracker](https://github.com/airbnb/react-sketchapp/issues).
- Fork the repo.
- Create a new feature branch based off the `master` branch.
- Make sure all tests pass.
- Submit a pull request, referencing any issues it addresses.
Please try to keep your pull request focused in scope and avoid including unrelated commits.
After you have submitted your pull request, we'll try to get back to you as soon as possible. We may suggest some changes or improvements.
Thank you for contributing!
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
👋 Hello! Thanks for contributing. Please use the template that matches your intention
_I am..._
| -------------------------------------------------------------------------------------------------
| Requesting a new feature
| -------------------------------------------------------------------------------------------------
**Proposal/Feature-request:**
| -------------------------------------------------------------------------------------------------
| Reporting a bug or issue
| -------------------------------------------------------------------------------------------------
**Expected behavior:**
**Observed behavior:**
**How to reproduce:**
**Sketch version:**
**Please attach screenshots, a zip file of your project, and/or a link to your github project**
================================================
FILE: .gitignore
================================================
# gitignore
coverage/
.DS_Store
*.log*
node_modules
lib
react-example.sketchplugin
_book
.vscode
# only apps should have lockfiles
yarn.lock
package-lock.json
npm-shrinkwrap.json
================================================
FILE: .npmignore
================================================
.DS_Store
*.log*
node_modules
_book
examples
docs
.vscode
yarn.lock
__tests__
.github
template
book.json
prettier.config.js
.editorconfig
.bookignore
src
jest.config.js
tsconfig.json
tsconfig.module.json
.travis.yml
================================================
FILE: .travis.yml
================================================
os: osx
language: node_js
cache:
directories:
- node_modules
# - $HOME/Library/Caches/Homebrew
notifications:
email: false
node_js:
- 'lts/*'
# before_install:
# - brew update
# - brew cask install sketch # install Sketch
# - mkdir -p "~/Library/Application Support/com.bohemiancoding.sketch3/Plugins" # create plugins folder
# - echo $SKETCH_LICENSE > "~/Library/Application Support/com.bohemiancoding.sketch3/.deployment" # add the Sketch license
before_script:
- npm prune
script:
- npm run test:ci
# - npm run test:e2e -- --app=/Applications/Sketch.app
# after_script:
# - rm "~/Library/App Support/com.bohemiancoding.sketch3/.deployment" # remove the Sketch license
branches:
except:
- /^v\d+\.\d+\.\d+$/
================================================
FILE: CHANGELOG.md
================================================
# Change Log
This project adheres to [Semantic Versioning](http://semver.org/). Every release, along with the migration instructions, is documented on the Github [Releases](https://github.com/airbnb/react-sketchapp/releases) page.
## Version 3.2.6
- Fix the SVG component export
## Version 3.2.5
- Fix Skpm taking the wrong entry point when requiring react-sketchapp
## Version 3.2.4
- Fix the generated ES package
## Version 3.2.3
- Fix getting the font name (#510)
## Version 3.2.2
- Fix getting the default bridge on NodeJS
## Version 3.2.1
- `Platform.version` now reflects the Sketch version
- Fix a bug for a broken version of `@sketch-hq/sketch-file-format-ts`
## Version 3.2.0
- Add a new `useWindowDimensions` hook for Artboard viewport (#501)
## Version 3.1.3
- Add proptypes for Text
- Allow `fontWeigth` to be a number
## Version 3.1.2
- Handle passing a Sketch document more properly
## Version 3.1.1
- Fix for Sketch 64
## Version 3.1.0
- Fix acceptable text children (#474)
- Fix parsing of SVG arc shorthand parameters (#467)
- Change default font resolution, always falling back to the system font when the `fontFamily` is missing or not specified
## Version 3.0.5
- Fix missing dependency (#462)
## Version 3.0.4
- Fix rendering images (#458)
## Version 3.0.3
- Fix typo in Symbol (Thanks @antoni!)
- Fix messed up `js-sha` import (#456)
## Version 3.0.2
- Fix rotation direction (#433)
- Fix Svg renders when the shape doesn't fit the viewbox (#288)
- Add missing strokeAlignment prop (#276)
## Version 3.0.1
- Allow passing a style object when making a symbol
- Expose `getSymbolMasterByName`
## Version 3.0.0
- Export Svg components in the Svg/index.js file (Thanks @saschazar21!)
- Fix setting the overflow
- The symbol masters will try to maintain their overrides IDs so as not to reset instances that have overrides
- Improve error messages when trying to render a broken override
- Do not crash if there is no source for an Image, we will just show an placeholder for the image
- Handle specifying document in injectSymbols (#388)
- Add support for paragraph spacing (#382 - Thanks @lessthanzero!)
- `Image` and `Text` now support multiple shadows just like `View`
- Add support for `TextShadow`
- Add support for `transform`
- Add support for running `react-sketchapp` on NodeJS using `renderToJSON()`
- Port to TypeScript and publish TypeScript definitions
- `TextStyles.get(name)` now returns text styles that are part of the document (even if they haven't been defined with `react-sketchapp`) (#407)
- `getSymbolComponentByName` now returns Symbols that are part of the document (even if they haven't been defined with `react-sketchapp`) (#177)
- Switch the order of the `TextStyles.create` arguments to `TextStyles.create(styles, options)`
## Version 3.0.0-beta.9
- Fix setting the overflow
- The symbol masters will try to maintain their overrides IDs so as not to reset instances that have overrides
- Improve error messages when trying to render a broken override
- Export Svg components in the Svg/index.js file (Thanks @saschazar21!)
## Version 3.0.0-beta.8
- Flatten styles in exported Svg component (Thanks @dabbott!)
## Version 3.0.0-beta.7
- Add Node.js SVG renderer (Thanks @dabbott!)
## Version 3.0.0-beta.6
- Do not crash if there is no source for an Image, we will just show an placeholder for the image
## Version 3.0.0-beta.3 to 3.0.0-beta.5
- Fix setting overrides (#409)
- Fix images on NodeJS
- Fix Border-radius clipping incorrectly calculated (#279)
## Version 3.0.0-beta.1
- Fix ShapeGroup on nodejs (#387)
- Handle specifying document in injectSymbols (#388)
- Fix support for paragraph spacing on sketch >= 49 (#390)
## Version 3.0.0-beta.0
- Add support for paragraph spacing (#382 - Thanks @lessthanzero!)
- `Image` and `Text` now support multiple shadows just like `View`
- add support for `TextShadow`
- Experimental support for `transform`
- Experimental support for running `react-sketchapp` on NodeJS
## Version 2.1.0
- Ensure `makeSymbol` does not change currentPage (#353 - Thanks @jaridmargolin!)
- Fix Text decoration underline style (#370 - Thanks @thecalvinchan!)
- Add possibility to add multiple shadows and shadow spread (#277 - Thanks @ludwigfrank and @thierryc!)
- Support rendering into wrapped object (hence support the new Sketch API) (#379)
## Version 2.0.0
- Now throws if the "Symbols" page is explicitly passed in as the `container` on the `render` method. Previously if you explicitly passed in the "Symbols" pages as a container, it would create a new page and render onto that. (#297 - Thanks @jaridmargolin!)
- Now throws an error if you attempt to render a Document component into a node intended to be a child of `Document`. (#297 - Thanks @jaridmargolin!)
- Adds support for rendering a `Page` component into a container passed through the `render` method. This allows for rendering multiple `Artboard`s onto an existing page. (#297 - Thanks @jaridmargolin!)
- More predictable rendering of `RedBox`. (#297 - Thanks @jaridmargolin!)
- Fix Symbols overrides for Sketch >= 46 (#198 - Thanks @ianhook!)
- Fix text overrides when the name of the Text layer is not explicitly defined (#292 - Thanks @jaridmargolin!)
- update `yoga-node` to 1.9 (#314)
- Add support for Sketch 50 (#290)
- Fix shared text style matching (#290)
- Remove n^2 rendering problem with large symbol sets (#235 - Thanks @ianhook!)
- `Page` without a name explicitly set will be auto-incremented ("Page 1", "Page 2", etc.) just like how Sketch is doing by default (#296 - Thanks @jaridmargolin!)
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 Airbnb
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<div align="center">
<img alt="react-sketchapp" src="https://cldup.com/MxSVEkc_gb.png" style="max-height:163px; width:100; height: auto; max-width:100%" />
</div>
<div align="center">
<strong>render React components to Sketch; tailor-made for design systems</strong>
</div>
## Quick-start 🏃
First, make sure you have installed [Sketch](http://sketch.com) version 50+, & a recent [npm](https://nodejs.org/en/download/).
Open a new Sketch file, then in a terminal:
```bash
git clone https://github.com/airbnb/react-sketchapp.git
cd react-sketchapp/examples/basic-setup && npm install
npm run render
```
Next, [check out some more examples](https://github.com/airbnb/react-sketchapp/tree/master/examples)!

[](https://www.npmjs.com/package/react-sketchapp)  [](https://travis-ci.org/airbnb/react-sketchapp)
## Why?!
Managing the assets of design systems in Sketch is complex, error-prone and time consuming. Sketch is scriptable, but the API often changes. React provides the perfect wrapper to build reusable documents in a way already familiar to JavaScript developers.
## What does the code look like?
```js
import * as React from 'react';
import { render, Text, Artboard } from 'react-sketchapp';
const App = props => (
<Artboard>
<Text style={{ fontFamily: 'Comic Sans MS', color: 'hotPink' }}>{props.message}</Text>
</Artboard>
);
export default context => {
render(<App message="Hello world!" />, context.document.currentPage());
};
```
## What can I do with it?
- **Manage design systems—** `react-sketchapp` was built for [Airbnb’s design system](http://airbnb.design/building-a-visual-language/); this is the easiest way to manage Sketch assets in a large design system
- **Use real components for designs—** Implement your designs in code as React components and render them into Sketch
- **Design with real data—** Designing with data is important but challenging; `react-sketchapp` makes it simple to fetch and incorporate real data into your Sketch files
- **Build new tools on top of Sketch—** the easiest way to use Sketch as a canvas for custom design tooling
Found a novel use? We'd love to hear about it!
[Read more about why we built it](http://airbnb.design/painting-with-code/)
## Documentation
- [Examples](http://airbnb.io/react-sketchapp/docs/examples.html)
- [API Reference](http://airbnb.io/react-sketchapp/docs/API.html)
- [Styling](http://airbnb.io/react-sketchapp/docs/guides/styling.html)
- [Universal Rendering](http://airbnb.io/react-sketchapp/docs/guides/universal-rendering.html)
- [Data Fetching](http://airbnb.io/react-sketchapp/docs/guides/data-fetching.html)
- [FAQ](http://airbnb.io/react-sketchapp/docs/FAQ.html)
- [Contributing](https://github.com/airbnb/react-sketchapp/blob/master/.github/CONTRIBUTING.md)
================================================
FILE: __tests__/jest/components/Artboard.tsx
================================================
import * as React from 'react';
import * as renderer from 'react-test-renderer';
import { Artboard } from '../../../src/components/Artboard';
import { StyleSheet } from '../../../src/stylesheet';
describe('<Artboard />', () => {
it('renders children', () => {
const tree = renderer.create(<Artboard>foo</Artboard>).toJSON();
expect(tree).toMatchSnapshot();
});
it.todo('flattens its stylesheet');
describe('name', () => {
it('passes its name', () => {
const tree = renderer.create(<Artboard name="bar" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('defaults to Artboard', () => {
const tree = renderer.create(<Artboard />).toJSON();
expect(tree).toMatchSnapshot();
});
});
describe('style', () => {
const styles = StyleSheet.create({
view: {
flex: 1,
},
});
it('accepts a plain object', () => {
const tree = renderer.create(<Artboard style={{ flex: 1 }} />).toJSON();
expect(tree).toMatchSnapshot();
});
it('accepts a StyleSheet ordinal', () => {
const tree = renderer.create(<Artboard style={styles.view} />).toJSON();
expect(tree).toMatchSnapshot();
});
it('accepts an array of plain objects and/or StyleSheet ordinals', () => {
const tree = renderer.create(<Artboard style={[{ flexGrow: 1 }, styles.view]} />).toJSON();
expect(tree).toMatchSnapshot();
});
it('accepts artboard viewport preset', () => {
const tree = renderer
.create(
<Artboard
style={{ flex: 1, width: 360, height: 1280 }}
viewport={{
name: 'Mobile',
width: 360,
height: 640,
}}
/>,
)
.toJSON();
expect(tree).toMatchSnapshot();
});
});
});
================================================
FILE: __tests__/jest/components/Document.tsx
================================================
import * as React from 'react';
import * as renderer from 'react-test-renderer';
import { Document } from '../../../src/components/Document';
describe('<Document />', () => {
it('renders children', () => {
const tree = renderer.create(<Document>foo</Document>).toJSON();
expect(tree).toMatchSnapshot();
});
});
================================================
FILE: __tests__/jest/components/Image.tsx
================================================
import * as React from 'react';
import * as renderer from 'react-test-renderer';
import { Image } from '../../../src/components/Image';
import { StyleSheet } from '../../../src/stylesheet';
describe('<Image />', () => {
it('renders children', () => {
const tree = renderer.create(<Image source="foo">foo</Image>).toJSON();
expect(tree).toMatchSnapshot();
});
it.todo('flattens its stylesheet');
describe('name', () => {
it('passes its name', () => {
const tree = renderer.create(<Image source="foo" name="bar" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('defaults to Image', () => {
const tree = renderer.create(<Image source="foo" />).toJSON();
expect(tree).toMatchSnapshot();
});
});
describe('resizeMode', () => {
it('translates contain', () => {
const tree = renderer.create(<Image source="foo" resizeMode="contain" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('translates cover', () => {
const tree = renderer.create(<Image source="foo" resizeMode="cover" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('translates stretch', () => {
const tree = renderer.create(<Image source="foo" resizeMode="stretch" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('translates center', () => {
const tree = renderer.create(<Image source="foo" resizeMode="center" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('translates repeat', () => {
const tree = renderer.create(<Image source="foo" resizeMode="repeat" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('translates none', () => {
const tree = renderer.create(<Image source="foo" resizeMode="none" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('falls back to cover', () => {
const tree = renderer.create(<Image source="foo" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('prefers prop to style', () => {
const tree = renderer
.create(<Image source="foo" resizeMode="cover" style={{ resizeMode: 'contain' }} />)
.toJSON();
expect(tree).toMatchSnapshot();
});
it('falls back to a resizeMode from style', () => {
const tree = renderer
.create(<Image source="foo" style={{ resizeMode: 'contain' }} />)
.toJSON();
expect(tree).toMatchSnapshot();
});
});
describe('source', () => {
it('prefers source over defaultSource', () => {
const tree = renderer.create(<Image source="foo" defaultSource="bar" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('falls back to defaultSource if available', () => {
const tree = renderer.create(<Image defaultSource="foo" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('sets height from source', () => {
const tree = renderer.create(<Image source={{ uri: 'foo', height: 500 }} />).toJSON();
expect(tree).toMatchSnapshot();
});
it('sets width from source', () => {
const tree = renderer.create(<Image source={{ uri: 'foo', width: 500 }} />).toJSON();
expect(tree).toMatchSnapshot();
});
it('favors style over source for height', () => {
const tree = renderer
.create(<Image source={{ uri: 'foo', height: 500 }} style={{ height: 400, width: 300 }} />)
.toJSON();
expect(tree).toMatchSnapshot();
});
it('favors style over source for width', () => {
const tree = renderer
.create(<Image source={{ uri: 'foo', width: 500 }} style={{ height: 400, width: 300 }} />)
.toJSON();
expect(tree).toMatchSnapshot();
});
});
describe('style', () => {
const styles = StyleSheet.create({
view: {
flex: 1,
},
});
it('accepts a plain object', () => {
const tree = renderer.create(<Image style={{ flex: 1 }} />).toJSON();
expect(tree).toMatchSnapshot();
});
it('accepts a StyleSheet ordinal', () => {
const tree = renderer.create(<Image style={styles.view} />).toJSON();
expect(tree).toMatchSnapshot();
});
it('accepts an array of plain objects and/or StyleSheet ordinals', () => {
const tree = renderer.create(<Image style={[{ flexGrow: 1 }, styles.view]} />).toJSON();
expect(tree).toMatchSnapshot();
});
});
});
================================================
FILE: __tests__/jest/components/Page.tsx
================================================
import * as React from 'react';
import * as renderer from 'react-test-renderer';
import { Page } from '../../../src/components/Page';
describe('<Page />', () => {
it('renders children', () => {
const tree = renderer.create(<Page>foo</Page>).toJSON();
expect(tree).toMatchSnapshot();
});
describe('name', () => {
it('passes its name', () => {
const tree = renderer.create(<Page name="bar" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('passes its name and avoids Symbol page conflict', () => {
const tree = renderer.create(<Page name="Symbols" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('passes otherProps', () => {
// @ts-ignore
const tree = renderer.create(<Page propName="something" style={{}} />).toJSON();
expect(tree).toMatchSnapshot();
});
});
});
================================================
FILE: __tests__/jest/components/RedBox.tsx
================================================
import * as React from 'react';
import * as renderer from 'react-test-renderer';
import { RedBox } from '../../../src/components/RedBox';
describe('<RedBox />', () => {
it('renders simple errors', () => {
const mockedError = new Error('THIS IS AN ERROR');
// override stack trace so that it's constant accross node versions
mockedError.stack = `Error: awdawd
at repl:1:13
at Script.runInThisContext (vm.js:65:33)
at REPLServer.defaultEval (repl.js:248:29)
at bound (domain.js:375:14)
at REPLServer.runBound [as eval] (domain.js:388:12)
at REPLServer.onLine (repl.js:501:10)
at REPLServer.emit (events.js:185:15)
at REPLServer.emit (domain.js:421:20)
at REPLServer.Interface._onLine (readline.js:285:10)
at REPLServer.Interface._line (readline.js:638:8)`;
const tree = renderer.create(<RedBox error={mockedError} />).toJSON();
expect(tree).toMatchSnapshot();
});
it('renders string errors', () => {
const tree = renderer.create(<RedBox error="String only error" />).toJSON();
expect(tree).toMatchSnapshot();
});
});
================================================
FILE: __tests__/jest/components/Svg.tsx
================================================
import * as React from 'react';
import * as renderer from 'react-test-renderer';
import Svg, { G, Path } from '../../../src/components/Svg';
describe('<Svg />', () => {
it('passes its children', () => {
const tree = renderer
.create(
<Svg xmlns="http://www.w3.org/2000/svg" width="494" height="447" viewBox="0 0 494 447">
<Svg.G fill="none" fillRule="evenodd">
<Svg.Path fill="#FFAE00" d="M247 447L0 160 107 15 247 0l140 15 107 145" />
<Svg.Path fill="#EC6C00" d="M247 447L0 160h494" />
<Svg.Path fill="#FFAE00" d="M247 447L100 160h294" />
<Svg.Path fill="#FFEFB4" d="M247 0L100 160h294" />
<Svg.Path fill="#FFAE00" d="M107 15L52 88 0 160h101M387 15l55 73 52 72H393" />
<Svg.Path fill="#FED305" d="M107 15l-7 145L247 0m140 15l7 145L247 0" />
</Svg.G>
</Svg>,
)
.toJSON();
expect(tree).toMatchSnapshot();
});
it('also works when child is directly imported', () => {
const tree = renderer.create(
<Svg xmlns="http://www.w3.org/2000/svg" width="494" height="447" viewBox="0 0 494 447">
<G fill="none" fillRule="evenodd">
<Path fill="#FFAE00" d="M247 447L0 160 107 15 247 0l140 15 107 145" />
<Path fill="#EC6C00" d="M247 447L0 160h494" />
<Path fill="#FFAE00" d="M247 447L100 160h294" />
<Path fill="#FFEFB4" d="M247 0L100 160h294" />
<Path fill="#FFAE00" d="M107 15L52 88 0 160h101M387 15l55 73 52 72H393" />
<Path fill="#FED305" d="M107 15l-7 145L247 0m140 15l7 145L247 0" />
</G>
</Svg>,
);
expect(tree.toJSON()).toMatchSnapshot();
});
});
================================================
FILE: __tests__/jest/components/Text.tsx
================================================
import * as React from 'react';
import * as renderer from 'react-test-renderer';
import { Text } from '../../../src/components/Text';
import { StyleSheet } from '../../../src/stylesheet';
describe('<Text />', () => {
it('passes its children', () => {
const tree = renderer.create(<Text>foo</Text>).toJSON();
expect(tree).toMatchSnapshot();
});
describe('name', () => {
it('passes its name', () => {
const tree = renderer.create(<Text name="foo" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('defaults to Text', () => {
const tree = renderer.create(<Text />).toJSON();
expect(tree).toMatchSnapshot();
});
});
describe('style', () => {
const styles = StyleSheet.create({
view: {
flex: 1,
},
});
it('accepts a plain object', () => {
const tree = renderer.create(<Text style={{ flex: 1 }} />).toJSON();
expect(tree).toMatchSnapshot();
});
it('accepts a StyleSheet ordinal', () => {
const tree = renderer.create(<Text style={styles.view} />).toJSON();
expect(tree).toMatchSnapshot();
});
it('accepts an array of plain objects and/or StyleSheet ordinals', () => {
const tree = renderer.create(<Text style={[{ flexGrow: 1 }, styles.view]} />).toJSON();
expect(tree).toMatchSnapshot();
});
});
});
================================================
FILE: __tests__/jest/components/View.tsx
================================================
import * as React from 'react';
import * as renderer from 'react-test-renderer';
import { View } from '../../../src/components/View';
import { StyleSheet } from '../../../src/stylesheet';
describe('<View />', () => {
it('passes its children', () => {
const tree = renderer.create(<View>foo</View>).toJSON();
expect(tree).toMatchSnapshot();
});
describe('name', () => {
it('passes its name', () => {
const tree = renderer.create(<View name="foo" />).toJSON();
expect(tree).toMatchSnapshot();
});
it('defaults to View', () => {
const tree = renderer.create(<View />).toJSON();
expect(tree).toMatchSnapshot();
});
});
describe('style', () => {
const styles = StyleSheet.create({
view: {
flex: 1,
},
});
it('accepts a plain object', () => {
const tree = renderer.create(<View style={{ flex: 1 }} />).toJSON();
expect(tree).toMatchSnapshot();
});
it('accepts a StyleSheet ordinal', () => {
const tree = renderer.create(<View style={styles.view} />).toJSON();
expect(tree).toMatchSnapshot();
});
it('accepts an array of plain objects and/or StyleSheet ordinals', () => {
const tree = renderer.create(<View style={[{ flexGrow: 1 }, styles.view]} />).toJSON();
expect(tree).toMatchSnapshot();
});
});
});
================================================
FILE: __tests__/jest/components/__snapshots__/Artboard.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Artboard /> name defaults to Artboard 1`] = `
<sketch_artboard
name="Artboard"
/>
`;
exports[`<Artboard /> name passes its name 1`] = `
<sketch_artboard
name="bar"
/>
`;
exports[`<Artboard /> renders children 1`] = `
<sketch_artboard
name="Artboard"
>
foo
</sketch_artboard>
`;
exports[`<Artboard /> style accepts a StyleSheet ordinal 1`] = `
<sketch_artboard
name="Artboard"
style={
Object {
"flex": 1,
}
}
/>
`;
exports[`<Artboard /> style accepts a plain object 1`] = `
<sketch_artboard
name="Artboard"
style={
Object {
"flex": 1,
}
}
/>
`;
exports[`<Artboard /> style accepts an array of plain objects and/or StyleSheet ordinals 1`] = `
<sketch_artboard
name="Artboard"
style={
Object {
"flex": 1,
"flexGrow": 1,
}
}
/>
`;
exports[`<Artboard /> style accepts artboard viewport preset 1`] = `
<sketch_artboard
name="Artboard"
style={
Object {
"flex": 1,
"height": 1280,
"width": 360,
}
}
viewport={
Object {
"height": 640,
"name": "Mobile",
"width": 360,
}
}
/>
`;
================================================
FILE: __tests__/jest/components/__snapshots__/Document.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Document /> renders children 1`] = `
<sketch_document>
foo
</sketch_document>
`;
================================================
FILE: __tests__/jest/components/__snapshots__/Image.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Image /> name defaults to Image 1`] = `
<sketch_image
name="Image"
resizeMode={1}
source="foo"
style={Object {}}
/>
`;
exports[`<Image /> name passes its name 1`] = `
<sketch_image
name="bar"
resizeMode={1}
source="foo"
style={Object {}}
/>
`;
exports[`<Image /> renders children 1`] = `
<sketch_image
name="Image"
resizeMode={1}
source="foo"
style={Object {}}
>
foo
</sketch_image>
`;
exports[`<Image /> resizeMode falls back to a resizeMode from style 1`] = `
<sketch_image
name="Image"
resizeMode={3}
source="foo"
style={
Object {
"resizeMode": "contain",
}
}
/>
`;
exports[`<Image /> resizeMode falls back to cover 1`] = `
<sketch_image
name="Image"
resizeMode={1}
source="foo"
style={Object {}}
/>
`;
exports[`<Image /> resizeMode prefers prop to style 1`] = `
<sketch_image
name="Image"
resizeMode={1}
source="foo"
style={
Object {
"resizeMode": "contain",
}
}
/>
`;
exports[`<Image /> resizeMode translates center 1`] = `
<sketch_image
name="Image"
resizeMode={1}
source="foo"
style={Object {}}
/>
`;
exports[`<Image /> resizeMode translates contain 1`] = `
<sketch_image
name="Image"
resizeMode={3}
source="foo"
style={Object {}}
/>
`;
exports[`<Image /> resizeMode translates cover 1`] = `
<sketch_image
name="Image"
resizeMode={1}
source="foo"
style={Object {}}
/>
`;
exports[`<Image /> resizeMode translates none 1`] = `
<sketch_image
name="Image"
resizeMode={1}
source="foo"
style={Object {}}
/>
`;
exports[`<Image /> resizeMode translates repeat 1`] = `
<sketch_image
name="Image"
resizeMode={0}
source="foo"
style={Object {}}
/>
`;
exports[`<Image /> resizeMode translates stretch 1`] = `
<sketch_image
name="Image"
resizeMode={2}
source="foo"
style={Object {}}
/>
`;
exports[`<Image /> source falls back to defaultSource if available 1`] = `
<sketch_image
name="Image"
resizeMode={1}
source="foo"
style={Object {}}
/>
`;
exports[`<Image /> source favors style over source for height 1`] = `
<sketch_image
name="Image"
resizeMode={1}
source={
Object {
"height": 500,
"uri": "foo",
}
}
style={
Object {
"height": 400,
"width": 300,
}
}
/>
`;
exports[`<Image /> source favors style over source for width 1`] = `
<sketch_image
name="Image"
resizeMode={1}
source={
Object {
"uri": "foo",
"width": 500,
}
}
style={
Object {
"height": 400,
"width": 300,
}
}
/>
`;
exports[`<Image /> source prefers source over defaultSource 1`] = `
<sketch_image
name="Image"
resizeMode={1}
source="foo"
style={Object {}}
/>
`;
exports[`<Image /> source sets height from source 1`] = `
<sketch_image
name="Image"
resizeMode={1}
source={
Object {
"height": 500,
"uri": "foo",
}
}
style={
Object {
"height": 500,
"width": undefined,
}
}
/>
`;
exports[`<Image /> source sets width from source 1`] = `
<sketch_image
name="Image"
resizeMode={1}
source={
Object {
"uri": "foo",
"width": 500,
}
}
style={
Object {
"height": undefined,
"width": 500,
}
}
/>
`;
exports[`<Image /> style accepts a StyleSheet ordinal 1`] = `
<sketch_image
name="Image"
resizeMode={1}
style={
Object {
"flex": 1,
}
}
/>
`;
exports[`<Image /> style accepts a plain object 1`] = `
<sketch_image
name="Image"
resizeMode={1}
style={
Object {
"flex": 1,
}
}
/>
`;
exports[`<Image /> style accepts an array of plain objects and/or StyleSheet ordinals 1`] = `
<sketch_image
name="Image"
resizeMode={1}
style={
Object {
"flex": 1,
"flexGrow": 1,
}
}
/>
`;
================================================
FILE: __tests__/jest/components/__snapshots__/Page.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Page /> name passes its name 1`] = `
<sketch_page
name="bar"
/>
`;
exports[`<Page /> name passes its name and avoids Symbol page conflict 1`] = `
<sketch_page
name="Symbols (renamed to avoid conflict)"
/>
`;
exports[`<Page /> name passes otherProps 1`] = `
<sketch_page
propName="something"
style={Object {}}
/>
`;
exports[`<Page /> renders children 1`] = `
<sketch_page>
foo
</sketch_page>
`;
================================================
FILE: __tests__/jest/components/__snapshots__/RedBox.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<RedBox /> renders simple errors 1`] = `
<sketch_view
name="RedBox"
style={
Object {
"backgroundColor": "rgb(204, 0, 0)",
"paddingBottom": 10,
"paddingLeft": 10,
"paddingRight": 10,
"paddingTop": 10,
"width": 480,
}
}
>
<sketch_text
name="Message"
style={
Object {
"color": "white",
"fontSize": 16,
"fontWeight": "bold",
"lineHeight": 19.2,
}
}
>
Error: THIS IS AN ERROR
</sketch_text>
<sketch_view
name="Frames"
style={
Object {
"color": "white",
"fontFamily": "Monaco",
"marginTop": 20,
}
}
>
<sketch_text
style={
Object {
"color": "white",
"fontFamily": "Monaco",
"marginTop": 20,
}
}
/>
<sketch_text
style={
Object {
"color": "white",
"fontFamily": "Monaco",
"marginTop": 20,
}
}
>
Script.runInThisContext
</sketch_text>
<sketch_text
style={
Object {
"color": "white",
"fontFamily": "Monaco",
"marginTop": 20,
}
}
>
REPLServer.defaultEval
</sketch_text>
<sketch_text
style={
Object {
"color": "white",
"fontFamily": "Monaco",
"marginTop": 20,
}
}
>
bound
</sketch_text>
<sketch_text
style={
Object {
"color": "white",
"fontFamily": "Monaco",
"marginTop": 20,
}
}
>
REPLServer.runBound [as eval]
</sketch_text>
<sketch_text
style={
Object {
"color": "white",
"fontFamily": "Monaco",
"marginTop": 20,
}
}
>
REPLServer.onLine
</sketch_text>
<sketch_text
style={
Object {
"color": "white",
"fontFamily": "Monaco",
"marginTop": 20,
}
}
>
REPLServer.emit
</sketch_text>
<sketch_text
style={
Object {
"color": "white",
"fontFamily": "Monaco",
"marginTop": 20,
}
}
>
REPLServer.emit
</sketch_text>
<sketch_text
style={
Object {
"color": "white",
"fontFamily": "Monaco",
"marginTop": 20,
}
}
>
REPLServer.Interface._onLine
</sketch_text>
<sketch_text
style={
Object {
"color": "white",
"fontFamily": "Monaco",
"marginTop": 20,
}
}
>
REPLServer.Interface._line
</sketch_text>
</sketch_view>
</sketch_view>
`;
exports[`<RedBox /> renders string errors 1`] = `
<sketch_view
name="RedBox"
style={
Object {
"backgroundColor": "rgb(204, 0, 0)",
"paddingBottom": 10,
"paddingLeft": 10,
"paddingRight": 10,
"paddingTop": 10,
"width": 480,
}
}
>
<sketch_text
name="Message"
style={
Object {
"color": "white",
"fontSize": 16,
"fontWeight": "bold",
"lineHeight": 19.2,
}
}
>
Error: String only error
</sketch_text>
</sketch_view>
`;
================================================
FILE: __tests__/jest/components/__snapshots__/Svg.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Svg /> also works when child is directly imported 1`] = `
<sketch_svg
height="447"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 494 447"
width="494"
xmlns="http://www.w3.org/2000/svg"
>
<svg_g
fill="none"
fillRule="evenodd"
>
<svg_path
d="M247 447L0 160 107 15 247 0l140 15 107 145"
fill="#FFAE00"
/>
<svg_path
d="M247 447L0 160h494"
fill="#EC6C00"
/>
<svg_path
d="M247 447L100 160h294"
fill="#FFAE00"
/>
<svg_path
d="M247 0L100 160h294"
fill="#FFEFB4"
/>
<svg_path
d="M107 15L52 88 0 160h101M387 15l55 73 52 72H393"
fill="#FFAE00"
/>
<svg_path
d="M107 15l-7 145L247 0m140 15l7 145L247 0"
fill="#FED305"
/>
</svg_g>
</sketch_svg>
`;
exports[`<Svg /> passes its children 1`] = `
<sketch_svg
height="447"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 494 447"
width="494"
xmlns="http://www.w3.org/2000/svg"
>
<svg_g
fill="none"
fillRule="evenodd"
>
<svg_path
d="M247 447L0 160 107 15 247 0l140 15 107 145"
fill="#FFAE00"
/>
<svg_path
d="M247 447L0 160h494"
fill="#EC6C00"
/>
<svg_path
d="M247 447L100 160h294"
fill="#FFAE00"
/>
<svg_path
d="M247 0L100 160h294"
fill="#FFEFB4"
/>
<svg_path
d="M107 15L52 88 0 160h101M387 15l55 73 52 72H393"
fill="#FFAE00"
/>
<svg_path
d="M107 15l-7 145L247 0m140 15l7 145L247 0"
fill="#FED305"
/>
</svg_g>
</sketch_svg>
`;
================================================
FILE: __tests__/jest/components/__snapshots__/Text.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Text /> name defaults to Text 1`] = `<sketch_text />`;
exports[`<Text /> name passes its name 1`] = `
<sketch_text
name="foo"
/>
`;
exports[`<Text /> passes its children 1`] = `
<sketch_text>
foo
</sketch_text>
`;
exports[`<Text /> style accepts a StyleSheet ordinal 1`] = `
<sketch_text
style={
Object {
"flex": 1,
}
}
/>
`;
exports[`<Text /> style accepts a plain object 1`] = `
<sketch_text
style={
Object {
"flex": 1,
}
}
/>
`;
exports[`<Text /> style accepts an array of plain objects and/or StyleSheet ordinals 1`] = `
<sketch_text
style={
Object {
"flex": 1,
"flexGrow": 1,
}
}
/>
`;
================================================
FILE: __tests__/jest/components/__snapshots__/View.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<View /> name defaults to View 1`] = `
<sketch_view
name="View"
/>
`;
exports[`<View /> name passes its name 1`] = `
<sketch_view
name="foo"
/>
`;
exports[`<View /> passes its children 1`] = `
<sketch_view
name="View"
>
foo
</sketch_view>
`;
exports[`<View /> style accepts a StyleSheet ordinal 1`] = `
<sketch_view
name="View"
style={
Object {
"flex": 1,
}
}
/>
`;
exports[`<View /> style accepts a plain object 1`] = `
<sketch_view
name="View"
style={
Object {
"flex": 1,
}
}
/>
`;
exports[`<View /> style accepts an array of plain objects and/or StyleSheet ordinals 1`] = `
<sketch_view
name="View"
style={
Object {
"flex": 1,
"flexGrow": 1,
}
}
/>
`;
================================================
FILE: __tests__/jest/components/nodeImpl/Svg.tsx
================================================
import * as React from 'react';
import * as ReactSketch from '../../../../src';
import Svg from '../../../../src/components/Svg';
jest.mock('../../../../src/jsonUtils/models', () => ({
...require.requireActual('../../../../src/jsonUtils/models'),
generateID: jest.fn((seed) => (seed ? `${seed}mockID` : 'mockID')),
}));
describe('node <Svg />', () => {
it('generates the json for an svg', () => {
class SVGElement extends React.Component {
render() {
return (
<Svg xmlns="http://www.w3.org/2000/svg" width="494" height="447" viewBox="0 0 494 447">
<Svg.Path fill="#FFAE00" d="M247 447L0 160 107 15 247 0l140 15 107 145" />
<Svg.Path fill="#EC6C00" d="M247 447L0 160h494" />
<Svg.Path fill="#FFAE00" d="M247 447L100 160h294" />
<Svg.Path fill="#FFEFB4" d="M247 0L100 160h294" />
<Svg.Path fill="#FFAE00" d="M107 15L52 88 0 160h101M387 15l55 73 52 72H393" />
<Svg.Path fill="#FED305" d="M107 15l-7 145L247 0m140 15l7 145L247 0" />
</Svg>
);
}
}
expect(ReactSketch.renderToJSON(<SVGElement />)).toMatchSnapshot();
});
});
================================================
FILE: __tests__/jest/components/nodeImpl/__snapshots__/Svg.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`node <Svg /> generates the json for an svg 1`] = `
Object {
"_class": "group",
"booleanOperation": -1,
"do_objectID": "mockID",
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 447,
"width": 494,
"x": 0,
"y": 0,
},
"hasClickThrough": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 2,
"layers": Array [
Object {
"_class": "group",
"booleanOperation": -1,
"do_objectID": "mockID",
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 447,
"width": 494,
"x": 0,
"y": 0,
},
"hasClickThrough": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 2,
"layers": Array [
Object {
"_class": "shapeGroup",
"booleanOperation": -1,
"clippingMaskMode": 0,
"do_objectID": "mockID",
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 447,
"width": 494,
"x": 0,
"y": 0,
},
"hasClickThrough": false,
"hasClippingMask": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 0,
"layers": Array [
Object {
"_class": "shapePath",
"booleanOperation": -1,
"do_objectID": "mockID",
"edited": false,
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 447,
"width": 494,
"x": 0,
"y": 0,
},
"isClosed": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 0,
"name": "Path",
"nameIsFixed": false,
"pointRadiusBehaviour": 1,
"points": Array [
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.5, 1}",
"curveMode": 1,
"curveTo": "{0.5, 1}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.5, 1}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0, 0.3579418344519016}",
"curveMode": 1,
"curveTo": "{0, 0.3579418344519016}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0, 0.3579418344519016}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.2165991902834008, 0.03355704697986577}",
"curveMode": 1,
"curveTo": "{0.2165991902834008, 0.03355704697986577}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.2165991902834008, 0.03355704697986577}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.5, 0}",
"curveMode": 1,
"curveTo": "{0.5, 0}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.5, 0}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.7834008097165992, 0.03355704697986577}",
"curveMode": 1,
"curveTo": "{0.7834008097165992, 0.03355704697986577}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.7834008097165992, 0.03355704697986577}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{1, 0.3579418344519016}",
"curveMode": 1,
"curveTo": "{1, 0.3579418344519016}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{1, 0.3579418344519016}",
},
],
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
},
],
"name": "ShapeGroup",
"nameIsFixed": false,
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
"style": Object {
"_class": "style",
"borderOptions": Object {
"_class": "borderOptions",
"dashPattern": Array [],
"isEnabled": false,
"lineCapStyle": 0,
"lineJoinStyle": 0,
},
"colorControls": Object {
"_class": "colorControls",
"brightness": 1,
"contrast": 1,
"hue": 1,
"isEnabled": false,
"saturation": 1,
},
"endMarkerType": 0,
"fills": Array [
Object {
"_class": "fill",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 0,
"green": 0.6823529411764706,
"red": 1,
},
"contextSettings": Object {
"_class": "graphicsContextSettings",
"blendMode": 0,
"opacity": 1,
},
"fillType": 0,
"gradient": Object {
"_class": "gradient",
"elipseLength": 0,
"from": "{0.5, 0}",
"gradientType": 0,
"stops": Array [
Object {
"_class": "gradientStop",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 1,
"green": 1,
"red": 1,
},
"position": 0,
},
Object {
"_class": "gradientStop",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 0,
"green": 0,
"red": 0,
},
"position": 1,
},
],
"to": "{0.5, 1}",
},
"isEnabled": true,
"noiseIndex": 0,
"noiseIntensity": 0,
"patternFillType": 1,
"patternTileScale": 1,
},
],
"innerShadows": Array [],
"miterLimit": 10,
"shadows": Array [],
"startMarkerType": 0,
"windingRule": 1,
},
"windingRule": 1,
},
Object {
"_class": "shapeGroup",
"booleanOperation": -1,
"clippingMaskMode": 0,
"do_objectID": "mockID",
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 287,
"width": 494,
"x": 0,
"y": 160,
},
"hasClickThrough": false,
"hasClippingMask": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 0,
"layers": Array [
Object {
"_class": "shapePath",
"booleanOperation": -1,
"do_objectID": "mockID",
"edited": false,
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 287,
"width": 494,
"x": 0,
"y": 0,
},
"isClosed": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 0,
"name": "Path",
"nameIsFixed": false,
"pointRadiusBehaviour": 1,
"points": Array [
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.5, 1}",
"curveMode": 1,
"curveTo": "{0.5, 1}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.5, 1}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0, 0}",
"curveMode": 1,
"curveTo": "{0, 0}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0, 0}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{1, 0}",
"curveMode": 1,
"curveTo": "{1, 0}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{1, 0}",
},
],
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
},
],
"name": "ShapeGroup",
"nameIsFixed": false,
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
"style": Object {
"_class": "style",
"borderOptions": Object {
"_class": "borderOptions",
"dashPattern": Array [],
"isEnabled": false,
"lineCapStyle": 0,
"lineJoinStyle": 0,
},
"colorControls": Object {
"_class": "colorControls",
"brightness": 1,
"contrast": 1,
"hue": 1,
"isEnabled": false,
"saturation": 1,
},
"endMarkerType": 0,
"fills": Array [
Object {
"_class": "fill",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 0,
"green": 0.4235294117647059,
"red": 0.9254901960784314,
},
"contextSettings": Object {
"_class": "graphicsContextSettings",
"blendMode": 0,
"opacity": 1,
},
"fillType": 0,
"gradient": Object {
"_class": "gradient",
"elipseLength": 0,
"from": "{0.5, 0}",
"gradientType": 0,
"stops": Array [
Object {
"_class": "gradientStop",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 1,
"green": 1,
"red": 1,
},
"position": 0,
},
Object {
"_class": "gradientStop",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 0,
"green": 0,
"red": 0,
},
"position": 1,
},
],
"to": "{0.5, 1}",
},
"isEnabled": true,
"noiseIndex": 0,
"noiseIntensity": 0,
"patternFillType": 1,
"patternTileScale": 1,
},
],
"innerShadows": Array [],
"miterLimit": 10,
"shadows": Array [],
"startMarkerType": 0,
"windingRule": 1,
},
"windingRule": 1,
},
Object {
"_class": "shapeGroup",
"booleanOperation": -1,
"clippingMaskMode": 0,
"do_objectID": "mockID",
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 287,
"width": 294,
"x": 100,
"y": 160,
},
"hasClickThrough": false,
"hasClippingMask": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 0,
"layers": Array [
Object {
"_class": "shapePath",
"booleanOperation": -1,
"do_objectID": "mockID",
"edited": false,
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 287,
"width": 294,
"x": 0,
"y": 0,
},
"isClosed": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 0,
"name": "Path",
"nameIsFixed": false,
"pointRadiusBehaviour": 1,
"points": Array [
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.5, 1}",
"curveMode": 1,
"curveTo": "{0.5, 1}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.5, 1}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0, 0}",
"curveMode": 1,
"curveTo": "{0, 0}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0, 0}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{1, 0}",
"curveMode": 1,
"curveTo": "{1, 0}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{1, 0}",
},
],
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
},
],
"name": "ShapeGroup",
"nameIsFixed": false,
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
"style": Object {
"_class": "style",
"borderOptions": Object {
"_class": "borderOptions",
"dashPattern": Array [],
"isEnabled": false,
"lineCapStyle": 0,
"lineJoinStyle": 0,
},
"colorControls": Object {
"_class": "colorControls",
"brightness": 1,
"contrast": 1,
"hue": 1,
"isEnabled": false,
"saturation": 1,
},
"endMarkerType": 0,
"fills": Array [
Object {
"_class": "fill",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 0,
"green": 0.6823529411764706,
"red": 1,
},
"contextSettings": Object {
"_class": "graphicsContextSettings",
"blendMode": 0,
"opacity": 1,
},
"fillType": 0,
"gradient": Object {
"_class": "gradient",
"elipseLength": 0,
"from": "{0.5, 0}",
"gradientType": 0,
"stops": Array [
Object {
"_class": "gradientStop",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 1,
"green": 1,
"red": 1,
},
"position": 0,
},
Object {
"_class": "gradientStop",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 0,
"green": 0,
"red": 0,
},
"position": 1,
},
],
"to": "{0.5, 1}",
},
"isEnabled": true,
"noiseIndex": 0,
"noiseIntensity": 0,
"patternFillType": 1,
"patternTileScale": 1,
},
],
"innerShadows": Array [],
"miterLimit": 10,
"shadows": Array [],
"startMarkerType": 0,
"windingRule": 1,
},
"windingRule": 1,
},
Object {
"_class": "shapeGroup",
"booleanOperation": -1,
"clippingMaskMode": 0,
"do_objectID": "mockID",
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 160,
"width": 294,
"x": 100,
"y": 0,
},
"hasClickThrough": false,
"hasClippingMask": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 0,
"layers": Array [
Object {
"_class": "shapePath",
"booleanOperation": -1,
"do_objectID": "mockID",
"edited": false,
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 160,
"width": 294,
"x": 0,
"y": 0,
},
"isClosed": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 0,
"name": "Path",
"nameIsFixed": false,
"pointRadiusBehaviour": 1,
"points": Array [
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.5, 0}",
"curveMode": 1,
"curveTo": "{0.5, 0}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.5, 0}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0, 1}",
"curveMode": 1,
"curveTo": "{0, 1}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0, 1}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{1, 1}",
"curveMode": 1,
"curveTo": "{1, 1}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{1, 1}",
},
],
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
},
],
"name": "ShapeGroup",
"nameIsFixed": false,
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
"style": Object {
"_class": "style",
"borderOptions": Object {
"_class": "borderOptions",
"dashPattern": Array [],
"isEnabled": false,
"lineCapStyle": 0,
"lineJoinStyle": 0,
},
"colorControls": Object {
"_class": "colorControls",
"brightness": 1,
"contrast": 1,
"hue": 1,
"isEnabled": false,
"saturation": 1,
},
"endMarkerType": 0,
"fills": Array [
Object {
"_class": "fill",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 0.7058823529411765,
"green": 0.9372549019607843,
"red": 1,
},
"contextSettings": Object {
"_class": "graphicsContextSettings",
"blendMode": 0,
"opacity": 1,
},
"fillType": 0,
"gradient": Object {
"_class": "gradient",
"elipseLength": 0,
"from": "{0.5, 0}",
"gradientType": 0,
"stops": Array [
Object {
"_class": "gradientStop",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 1,
"green": 1,
"red": 1,
},
"position": 0,
},
Object {
"_class": "gradientStop",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 0,
"green": 0,
"red": 0,
},
"position": 1,
},
],
"to": "{0.5, 1}",
},
"isEnabled": true,
"noiseIndex": 0,
"noiseIntensity": 0,
"patternFillType": 1,
"patternTileScale": 1,
},
],
"innerShadows": Array [],
"miterLimit": 10,
"shadows": Array [],
"startMarkerType": 0,
"windingRule": 1,
},
"windingRule": 1,
},
Object {
"_class": "shapeGroup",
"booleanOperation": -1,
"clippingMaskMode": 0,
"do_objectID": "mockID",
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 145,
"width": 494,
"x": 0,
"y": 15,
},
"hasClickThrough": false,
"hasClippingMask": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 0,
"layers": Array [
Object {
"_class": "shapePath",
"booleanOperation": -1,
"do_objectID": "mockID",
"edited": false,
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 145,
"width": 494,
"x": 0,
"y": 0,
},
"isClosed": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 0,
"name": "Path",
"nameIsFixed": false,
"pointRadiusBehaviour": 1,
"points": Array [
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.2165991902834008, 0}",
"curveMode": 1,
"curveTo": "{0.2165991902834008, 0}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.2165991902834008, 0}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.10526315789473684, 0.503448275862069}",
"curveMode": 1,
"curveTo": "{0.10526315789473684, 0.503448275862069}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.10526315789473684, 0.503448275862069}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0, 1}",
"curveMode": 1,
"curveTo": "{0, 1}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0, 1}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.20445344129554655, 1}",
"curveMode": 1,
"curveTo": "{0.20445344129554655, 1}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.20445344129554655, 1}",
},
],
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
},
Object {
"_class": "shapePath",
"booleanOperation": -1,
"do_objectID": "mockID",
"edited": false,
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 145,
"width": 494,
"x": 0,
"y": 0,
},
"isClosed": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 0,
"name": "Path",
"nameIsFixed": false,
"pointRadiusBehaviour": 1,
"points": Array [
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.7834008097165992, 0}",
"curveMode": 1,
"curveTo": "{0.7834008097165992, 0}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.7834008097165992, 0}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.8947368421052632, 0.503448275862069}",
"curveMode": 1,
"curveTo": "{0.8947368421052632, 0.503448275862069}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.8947368421052632, 0.503448275862069}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{1, 1}",
"curveMode": 1,
"curveTo": "{1, 1}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{1, 1}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.7955465587044535, 1}",
"curveMode": 1,
"curveTo": "{0.7955465587044535, 1}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.7955465587044535, 1}",
},
],
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
},
],
"name": "ShapeGroup",
"nameIsFixed": false,
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
"style": Object {
"_class": "style",
"borderOptions": Object {
"_class": "borderOptions",
"dashPattern": Array [],
"isEnabled": false,
"lineCapStyle": 0,
"lineJoinStyle": 0,
},
"colorControls": Object {
"_class": "colorControls",
"brightness": 1,
"contrast": 1,
"hue": 1,
"isEnabled": false,
"saturation": 1,
},
"endMarkerType": 0,
"fills": Array [
Object {
"_class": "fill",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 0,
"green": 0.6823529411764706,
"red": 1,
},
"contextSettings": Object {
"_class": "graphicsContextSettings",
"blendMode": 0,
"opacity": 1,
},
"fillType": 0,
"gradient": Object {
"_class": "gradient",
"elipseLength": 0,
"from": "{0.5, 0}",
"gradientType": 0,
"stops": Array [
Object {
"_class": "gradientStop",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 1,
"green": 1,
"red": 1,
},
"position": 0,
},
Object {
"_class": "gradientStop",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 0,
"green": 0,
"red": 0,
},
"position": 1,
},
],
"to": "{0.5, 1}",
},
"isEnabled": true,
"noiseIndex": 0,
"noiseIntensity": 0,
"patternFillType": 1,
"patternTileScale": 1,
},
],
"innerShadows": Array [],
"miterLimit": 10,
"shadows": Array [],
"startMarkerType": 0,
"windingRule": 1,
},
"windingRule": 1,
},
Object {
"_class": "shapeGroup",
"booleanOperation": -1,
"clippingMaskMode": 0,
"do_objectID": "mockID",
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 160,
"width": 294,
"x": 100,
"y": 0,
},
"hasClickThrough": false,
"hasClippingMask": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 0,
"layers": Array [
Object {
"_class": "shapePath",
"booleanOperation": -1,
"do_objectID": "mockID",
"edited": false,
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 160,
"width": 294,
"x": 0,
"y": 0,
},
"isClosed": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 0,
"name": "Path",
"nameIsFixed": false,
"pointRadiusBehaviour": 1,
"points": Array [
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.023809523809523808, 0.09375}",
"curveMode": 1,
"curveTo": "{0.023809523809523808, 0.09375}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.023809523809523808, 0.09375}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0, 1}",
"curveMode": 1,
"curveTo": "{0, 1}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0, 1}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.5, 0}",
"curveMode": 1,
"curveTo": "{0.5, 0}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.5, 0}",
},
],
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
},
Object {
"_class": "shapePath",
"booleanOperation": -1,
"do_objectID": "mockID",
"edited": false,
"exportOptions": Object {
"_class": "exportOptions",
"exportFormats": Array [],
"includedLayerIds": Array [],
"layerOptions": 0,
"shouldTrim": false,
},
"frame": Object {
"_class": "rect",
"constrainProportions": false,
"height": 160,
"width": 294,
"x": 0,
"y": 0,
},
"isClosed": false,
"isFixedToViewport": false,
"isFlippedHorizontal": false,
"isFlippedVertical": false,
"isLocked": false,
"isVisible": true,
"layerListExpandedType": 0,
"name": "Path",
"nameIsFixed": false,
"pointRadiusBehaviour": 1,
"points": Array [
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.9761904761904762, 0.09375}",
"curveMode": 1,
"curveTo": "{0.9761904761904762, 0.09375}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.9761904761904762, 0.09375}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{1, 1}",
"curveMode": 1,
"curveTo": "{1, 1}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{1, 1}",
},
Object {
"_class": "curvePoint",
"cornerRadius": 0,
"curveFrom": "{0.5, 0}",
"curveMode": 1,
"curveTo": "{0.5, 0}",
"hasCurveFrom": false,
"hasCurveTo": false,
"point": "{0.5, 0}",
},
],
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
},
],
"name": "ShapeGroup",
"nameIsFixed": false,
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
"style": Object {
"_class": "style",
"borderOptions": Object {
"_class": "borderOptions",
"dashPattern": Array [],
"isEnabled": false,
"lineCapStyle": 0,
"lineJoinStyle": 0,
},
"colorControls": Object {
"_class": "colorControls",
"brightness": 1,
"contrast": 1,
"hue": 1,
"isEnabled": false,
"saturation": 1,
},
"endMarkerType": 0,
"fills": Array [
Object {
"_class": "fill",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 0.0196078431372549,
"green": 0.8274509803921568,
"red": 0.996078431372549,
},
"contextSettings": Object {
"_class": "graphicsContextSettings",
"blendMode": 0,
"opacity": 1,
},
"fillType": 0,
"gradient": Object {
"_class": "gradient",
"elipseLength": 0,
"from": "{0.5, 0}",
"gradientType": 0,
"stops": Array [
Object {
"_class": "gradientStop",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 1,
"green": 1,
"red": 1,
},
"position": 0,
},
Object {
"_class": "gradientStop",
"color": Object {
"_class": "color",
"alpha": 1,
"blue": 0,
"green": 0,
"red": 0,
},
"position": 1,
},
],
"to": "{0.5, 1}",
},
"isEnabled": true,
"noiseIndex": 0,
"noiseIntensity": 0,
"patternFillType": 1,
"patternTileScale": 1,
},
],
"innerShadows": Array [],
"miterLimit": 10,
"shadows": Array [],
"startMarkerType": 0,
"windingRule": 1,
},
"windingRule": 1,
},
],
"name": "Shape",
"nameIsFixed": false,
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
"style": Object {
"_class": "style",
"borderOptions": Object {
"_class": "borderOptions",
"dashPattern": Array [],
"isEnabled": false,
"lineCapStyle": 0,
"lineJoinStyle": 0,
},
"colorControls": Object {
"_class": "colorControls",
"brightness": 1,
"contrast": 1,
"hue": 1,
"isEnabled": false,
"saturation": 1,
},
"contextSettings": Object {
"_class": "graphicsContextSettings",
"blendMode": 0,
"opacity": 1,
},
"endMarkerType": 0,
"fills": Array [],
"innerShadows": Array [],
"miterLimit": 10,
"shadows": Array [],
"startMarkerType": 0,
"windingRule": 1,
},
},
],
"name": "Svg",
"nameIsFixed": false,
"resizingConstraint": 63,
"resizingType": 0,
"rotation": 0,
"shouldBreakMaskChain": false,
"style": Object {
"_class": "style",
"borderOptions": Object {
"_class": "borderOptions",
"dashPattern": Array [],
"isEnabled": false,
"lineCapStyle": 0,
"lineJoinStyle": 0,
},
"colorControls": Object {
"_class": "colorControls",
"brightness": 1,
"contrast": 1,
"hue": 1,
"isEnabled": false,
"saturation": 1,
},
"contextSettings": Object {
"_class": "graphicsContextSettings",
"blendMode": 0,
"opacity": 1,
},
"endMarkerType": 0,
"fills": Array [],
"innerShadows": Array [],
"miterLimit": 10,
"shadows": Array [],
"startMarkerType": 0,
"windingRule": 1,
},
}
`;
================================================
FILE: __tests__/jest/index.ts
================================================
import * as ReactSketch from '../../src';
describe('public API', () => {
it('exports render', () => {
expect(ReactSketch.render).toBeDefined();
});
it('exports renderToJSON', () => {
expect(ReactSketch.renderToJSON).toBeDefined();
});
it('exports StyleSheet', () => {
expect(ReactSketch.StyleSheet).toBeDefined();
});
it('exports Document', () => {
expect(ReactSketch.Document).toBeDefined();
});
it('exports Page', () => {
expect(ReactSketch.Page).toBeDefined();
});
it('exports Artboard', () => {
expect(ReactSketch.Artboard).toBeDefined();
});
it('exports Image', () => {
expect(ReactSketch.Image).toBeDefined();
});
it('exports RedBox', () => {
expect(ReactSketch.RedBox).toBeDefined();
});
it('exports Text', () => {
expect(ReactSketch.Text).toBeDefined();
});
it('exports TextStyles', () => {
expect(ReactSketch.TextStyles).toBeDefined();
});
it('exports View', () => {
expect(ReactSketch.View).toBeDefined();
});
it('exports Platform', () => {
expect(ReactSketch.Platform).toBeDefined();
});
});
================================================
FILE: __tests__/jest/jsonUtils/computeTextTree.ts
================================================
import { computeTextTree } from '../../../src/jsonUtils/computeTextTree';
import { Context } from '../../../src/utils/Context';
// Example Text component tree
const treeStub = {
type: 'text',
props: {
name: 'Swatch Hex',
style: {
color: '#636464',
zIndex: 1,
},
},
children: [
'#F3F4F4',
' ',
{
type: 'text',
props: {
name: 'Text',
style: {
color: 'blue',
},
},
children: ['Hello World'],
},
],
};
// Correct Output
const treeFixture = [
{ content: '#F3F4F4', textStyles: {} },
{ content: ' ', textStyles: {} },
{ content: 'Hello World', textStyles: { color: 'blue' } },
];
describe('Compute Text Tree', () => {
it('correctly handle Text nodes', () => {
const tree = computeTextTree(treeStub, new Context());
expect(tree).toEqual(treeFixture);
});
});
================================================
FILE: __tests__/jest/jsonUtils/computeYogaNode.ts
================================================
import yoga from 'yoga-layout-prebuilt';
import { computeYogaNode } from '../../../src/jsonUtils/computeYogaNode';
import { Context } from '../../../src/utils/Context';
import bridge from '../../../src/platformBridges/macos';
const widthAndHeightStylesStub = {
width: 10,
height: 10,
};
const widthAndHeightStylesStubFixture = {
left: 0,
right: 0,
top: 0,
bottom: 0,
width: 10,
height: 10,
};
const createTreeNode = (style: { [key: string]: number | string }) => ({
type: 'foo',
props: {
style,
},
children: [],
});
const createYogaNodes = (
styles: Array<{ [key: string]: number | string }>,
containerWidth?: number,
containerHeight?: number,
) => {
const yogaNodes = [];
styles.forEach((style) => {
const treeNode = createTreeNode(style);
const ctx = new Context();
const { node } = computeYogaNode(bridge)(treeNode, ctx);
node.calculateLayout(
containerWidth || undefined,
containerHeight || undefined,
yoga.DIRECTION_LTR,
);
yogaNodes.push(node.getComputedLayout());
});
return yogaNodes;
};
describe('Compute Yoga Node', () => {
it('correctly handles width: 0, auto, number', () => {
const stylesToTest = [{ width: 100 }, { width: 0 }, { width: 'auto' }];
const [numberNode, noneNode, autoNode] = createYogaNodes(stylesToTest);
expect(numberNode.width).toEqual(100);
expect(noneNode.width).toEqual(0);
expect(autoNode.width).toEqual(0);
});
it('correctly handles height: 0, auto, number', () => {
const stylesToTest = [{ height: 100 }, { height: 0 }, { height: 'auto' }];
const [numberNode, noneNode, autoNode] = createYogaNodes(stylesToTest);
expect(numberNode.height).toEqual(100);
expect(noneNode.height).toEqual(0);
expect(autoNode.height).toEqual(0);
});
it('correctly handles min-height: 0 & number', () => {
const stylesToTest = [{ minHeight: 100 }, { minHeight: 0 }];
const [numberNode, noneNode] = createYogaNodes(stylesToTest);
expect(numberNode.height).toEqual(100);
expect(noneNode.height).toEqual(0);
});
it('correctly handles max-height: 0 & number', () => {
const stylesToTest = [{ height: '100%', maxHeight: 100 }, { maxHeight: 0 }];
const [numberNode, noneNode] = createYogaNodes(stylesToTest, 500, 500);
expect(numberNode.height).toEqual(100);
expect(noneNode.height).toEqual(0);
});
it('correctly handles min-width: 0 & number', () => {
const stylesToTest = [{ minWidth: 100 }, { minWidth: 0 }];
const [numberNode, noneNode] = createYogaNodes(stylesToTest);
expect(numberNode.width).toEqual(100);
expect(noneNode.width).toEqual(0);
});
it('correctly handles max-width: 0 & number', () => {
const stylesToTest = [{ width: '100%', maxWidth: 100 }, { maxWidth: 0 }];
const [numberNode, noneNode] = createYogaNodes(stylesToTest, 500, 500);
expect(numberNode.width).toEqual(100);
expect(noneNode.width).toEqual(0);
});
it('correctly handles margin', () => {
const stylesToTest = [{ margin: 100 }, { margin: 0 }, { margin: 'auto' }];
const [numberNode, noneNode, autoNode] = createYogaNodes(stylesToTest);
expect(numberNode).toEqual({
left: 100,
right: 100,
top: 100,
bottom: 100,
width: 0,
height: 0,
});
expect(noneNode).toEqual({
left: 0,
right: 0,
top: 0,
bottom: 0,
width: 0,
height: 0,
});
expect(autoNode).toEqual({
left: 0,
right: 0,
top: 0,
bottom: 0,
width: 0,
height: 0,
});
});
it('correctly handles padding', () => {
const stylesToTest = [{ padding: 100 }, { padding: 0 }];
const [numberNode, noneNode] = createYogaNodes(stylesToTest);
expect(numberNode).toEqual({
left: 0,
right: 0,
top: 0,
bottom: 0,
width: 200,
height: 200,
});
expect(noneNode).toEqual({
left: 0,
right: 0,
top: 0,
bottom: 0,
width: 0,
height: 0,
});
});
it('correctly handles border', () => {
const stylesToTest = [{ borderWidth: 10 }, { borderWidth: 0 }];
const [numberNode, noneNode] = createYogaNodes(stylesToTest);
expect(numberNode).toEqual({
left: 0,
right: 0,
top: 0,
bottom: 0,
width: 20,
height: 20,
});
expect(noneNode).toEqual({
left: 0,
right: 0,
top: 0,
bottom: 0,
width: 0,
height: 0,
});
});
it('correctly handles flex: 0, number', () => {
const stylesToTest = [{ flex: 1 }, { flex: 0 }];
const [numberNode, noneNode] = createYogaNodes(stylesToTest);
expect(numberNode.width).toEqual(0);
expect(noneNode.width).toEqual(0);
});
it('correctly handles flexGrow: 0, number', () => {
const stylesToTest = [{ flexGrow: 1 }, { flexGrow: 0 }];
const [numberNode, noneNode] = createYogaNodes(stylesToTest);
expect(numberNode.width).toEqual(0);
expect(noneNode.width).toEqual(0);
});
it('correctly handles flexShrink: 0, number', () => {
const stylesToTest = [{ flexShrink: 1 }, { flexShrink: 0 }];
const [numberNode, noneNode] = createYogaNodes(stylesToTest);
expect(numberNode.width).toEqual(0);
expect(noneNode.width).toEqual(0);
});
it('correctly handles flexBasis: 0, number', () => {
const stylesToTest = [{ flexBasis: 1 }, { flexBasis: 0 }];
const [numberNode, noneNode] = createYogaNodes(stylesToTest);
expect(numberNode.width).toEqual(0);
expect(noneNode.width).toEqual(0);
});
it('correctly handles position: relative & absolute', () => {
const stylesToTest = [
{ position: 'relative', left: 10 },
{ position: 'absolute', top: 10 },
];
const [relativeNode, absoluteNode] = createYogaNodes(stylesToTest);
expect(relativeNode).toEqual({
left: 10,
right: 10,
top: 0,
bottom: 0,
width: 0,
height: 0,
});
expect(absoluteNode).toEqual({
left: 0,
right: 0,
top: 10,
bottom: 10,
width: 0,
height: 0,
});
});
it('correctly handles display: flex & none', () => {
const stylesToTest = [
{ display: 'flex', ...widthAndHeightStylesStub },
{ display: 'none', width: 10, height: 10 },
];
const [relativeNode, absoluteNode] = createYogaNodes(stylesToTest);
expect(relativeNode).toEqual(widthAndHeightStylesStubFixture);
expect(absoluteNode).toEqual(widthAndHeightStylesStubFixture);
});
it('correctly handles overflow: visible, scroll, hidden', () => {
const stylesToTest = [
{ overflow: 'visible', ...widthAndHeightStylesStub },
{ overflow: 'scroll', ...widthAndHeightStylesStub },
{ overflow: 'hidden', ...widthAndHeightStylesStub },
];
const [visibleNode, scrollNode, hiddenNode] = createYogaNodes(stylesToTest);
expect(visibleNode).toEqual(widthAndHeightStylesStubFixture);
expect(scrollNode).toEqual(widthAndHeightStylesStubFixture);
expect(hiddenNode).toEqual(widthAndHeightStylesStubFixture);
});
it('correctly handles flexDirection', () => {
const stylesToTest = [
{ flexDirection: 'row', ...widthAndHeightStylesStub },
{ flexDirection: 'column', ...widthAndHeightStylesStub },
{ flexDirection: 'row-reverse', ...widthAndHeightStylesStub },
{ flexDirection: 'column-reverse', ...widthAndHeightStylesStub },
];
const [rowNode, colNode, rowReverseNode, colReverseNode] = createYogaNodes(stylesToTest);
expect(rowNode).toEqual(widthAndHeightStylesStubFixture);
expect(colNode).toEqual(widthAndHeightStylesStubFixture);
expect(rowReverseNode).toEqual(widthAndHeightStylesStubFixture);
expect(colReverseNode).toEqual(widthAndHeightStylesStubFixture);
});
it('correctly handles justifyContent', () => {
const stylesToTest = [
{ justifyContent: 'flex-start', ...widthAndHeightStylesStub },
{ justifyContent: 'flex-end', ...widthAndHeightStylesStub },
{ justifyContent: 'center', ...widthAndHeightStylesStub },
{ justifyContent: 'space-between', ...widthAndHeightStylesStub },
{ justifyContent: 'space-around', ...widthAndHeightStylesStub },
];
const [startNode, endNode, centerNode, spaceBetweenNode, spaceAroundNode] = createYogaNodes(
stylesToTest,
);
expect(startNode).toEqual(widthAndHeightStylesStubFixture);
expect(endNode).toEqual(widthAndHeightStylesStubFixture);
expect(centerNode).toEqual(widthAndHeightStylesStubFixture);
expect(spaceBetweenNode).toEqual(widthAndHeightStylesStubFixture);
expect(spaceAroundNode).toEqual(widthAndHeightStylesStubFixture);
});
it('correctly handles alignContent', () => {
const stylesToTest = [
{ alignContent: 'flex-start', ...widthAndHeightStylesStub },
{ alignContent: 'flex-end', ...widthAndHeightStylesStub },
{ alignContent: 'center', ...widthAndHeightStylesStub },
{ alignContent: 'stretch', ...widthAndHeightStylesStub },
{ alignContent: 'baseline', ...widthAndHeightStylesStub },
{ alignContent: 'space-between', ...widthAndHeightStylesStub },
{ alignContent: 'space-around', ...widthAndHeightStylesStub },
{ alignContent: 'auto', ...widthAndHeightStylesStub },
];
const [
startNode,
endNode,
centerNode,
stretchNode,
baselineNode,
spaceBetweenNode,
spaceAroundNode,
autoNode,
] = createYogaNodes(stylesToTest);
expect(startNode).toEqual(widthAndHeightStylesStubFixture);
expect(endNode).toEqual(widthAndHeightStylesStubFixture);
expect(centerNode).toEqual(widthAndHeightStylesStubFixture);
expect(stretchNode).toEqual(widthAndHeightStylesStubFixture);
expect(baselineNode).toEqual(widthAndHeightStylesStubFixture);
expect(spaceBetweenNode).toEqual(widthAndHeightStylesStubFixture);
expect(spaceAroundNode).toEqual(widthAndHeightStylesStubFixture);
expect(autoNode).toEqual(widthAndHeightStylesStubFixture);
});
it('correctly handles alignItems', () => {
const stylesToTest = [
{ alignItems: 'flex-start', ...widthAndHeightStylesStub },
{ alignItems: 'flex-end', ...widthAndHeightStylesStub },
{ alignItems: 'center', ...widthAndHeightStylesStub },
{ alignItems: 'stretch', ...widthAndHeightStylesStub },
{ alignItems: 'baseline', ...widthAndHeightStylesStub },
];
const [startNode, endNode, centerNode, stretchNode, baselineNode] = createYogaNodes(
stylesToTest,
);
expect(startNode).toEqual(widthAndHeightStylesStubFixture);
expect(endNode).toEqual(widthAndHeightStylesStubFixture);
expect(centerNode).toEqual(widthAndHeightStylesStubFixture);
expect(stretchNode).toEqual(widthAndHeightStylesStubFixture);
expect(baselineNode).toEqual(widthAndHeightStylesStubFixture);
});
it('correctly handles alignSelf', () => {
const stylesToTest = [
{ alignSelf: 'flex-start', ...widthAndHeightStylesStub },
{ alignSelf: 'flex-end', ...widthAndHeightStylesStub },
{ alignSelf: 'center', ...widthAndHeightStylesStub },
{ alignSelf: 'stretch', ...widthAndHeightStylesStub },
{ alignSelf: 'baseline', ...widthAndHeightStylesStub },
];
const [startNode, endNode, centerNode, stretchNode, baselineNode] = createYogaNodes(
stylesToTest,
);
expect(startNode).toEqual(widthAndHeightStylesStubFixture);
expect(endNode).toEqual(widthAndHeightStylesStubFixture);
expect(centerNode).toEqual(widthAndHeightStylesStubFixture);
expect(stretchNode).toEqual(widthAndHeightStylesStubFixture);
expect(baselineNode).toEqual(widthAndHeightStylesStubFixture);
});
it('correctly handles flexWrap', () => {
const stylesToTest = [
{ flexWrap: 'no-wrap', ...widthAndHeightStylesStub },
{ flexWrap: 'wrap', ...widthAndHeightStylesStub },
{ flexWrap: 'wrap-reverse', ...widthAndHeightStylesStub },
];
const [noWrapNode, wrapNode, wrapReverseNode] = createYogaNodes(stylesToTest);
expect(noWrapNode).toEqual(widthAndHeightStylesStubFixture);
expect(wrapNode).toEqual(widthAndHeightStylesStubFixture);
expect(wrapReverseNode).toEqual(widthAndHeightStylesStubFixture);
});
});
================================================
FILE: __tests__/jest/jsonUtils/computeYogaTree.ts
================================================
import yoga from 'yoga-layout-prebuilt';
import { computeYogaTree } from '../../../src/jsonUtils/computeYogaTree';
import { Context } from '../../../src/utils/Context';
import bridge from '../../../src/platformBridges/macos';
const treeRootStub = {
type: 'artboard',
props: {
style: {
flexDirection: 'row',
flexWrap: 'wrap',
width: 416,
},
name: 'Swatches',
},
children: [
{
type: 'view',
props: {
name: 'Swatch Haus',
style: {
backgroundColor: '#F3F4F4',
height: 96,
marginTop: 4,
marginRight: 4,
marginBottom: 4,
marginLeft: 4,
paddingTop: 8,
paddingRight: 8,
paddingBottom: 8,
paddingLeft: 8,
width: 96,
},
},
children: [],
},
],
};
computeYogaTree(bridge)(treeRootStub, new Context());
describe('Compute Yoga Tree', () => {
it('correctly create yoga nodes into layout tree', () => {
const yogaTree = computeYogaTree(bridge)(treeRootStub, new Context());
yogaTree.calculateLayout(undefined, undefined, yoga.DIRECTION_LTR);
expect(yogaTree.getComputedLayout()).toEqual({
bottom: 0,
height: 104,
left: 0,
right: 0,
top: 0,
width: 416,
});
expect(yogaTree.getChild(0).getComputedLayout()).toEqual({
bottom: 4,
height: 96,
left: 4,
right: 4,
top: 4,
width: 96,
});
});
});
================================================
FILE: __tests__/jest/jsonUtils/layerGroup.ts
================================================
import { layerGroup } from '../../../src/jsonUtils/layerGroup';
describe('layer group', () => {
it('is correctly constructed', () => {
const group = layerGroup(100, 200, 300, 400, 0.5);
expect(group).toHaveProperty('frame.x', 100);
expect(group).toHaveProperty('frame.y', 200);
expect(group).toHaveProperty('frame.width', 300);
expect(group).toHaveProperty('frame.height', 400);
expect(group).toHaveProperty('style.contextSettings.opacity', 0.5);
});
});
================================================
FILE: __tests__/jest/jsonUtils/models.ts
================================================
import {
generateID,
makeColorFromCSS,
makeColorFill,
makeRect,
makeSymbolInstance,
makeSymbolMaster,
} from '../../../src/jsonUtils/models';
describe('generateID', () => {
it('is unique', () => {
expect(generateID()).not.toBe(generateID());
});
it('seed generates different ID', () => {
expect(generateID('test')).not.toBe(generateID('test'));
});
it('hardcoded seed generates same ID', () => {
expect(generateID('test', true)).toBe(generateID('test', true));
});
});
const BLACK = {
_class: 'color',
red: 0,
green: 0,
blue: 0,
alpha: 1,
};
const WHITE = {
_class: 'color',
red: 1,
green: 1,
blue: 1,
alpha: 1,
};
const GOLD = {
_class: 'color',
red: 0.8745098039215686,
green: 0.7294117647058823,
blue: 0.4117647058823529,
alpha: 1,
};
const PURPLE = {
_class: 'color',
red: 0.4,
green: 0.2,
blue: 0.6,
alpha: 1,
};
describe('makeColorFromCSS', () => {
it('works with hex colors', () => {
expect(makeColorFromCSS('#000')).toEqual(BLACK);
expect(makeColorFromCSS('#000000')).toEqual(BLACK);
expect(makeColorFromCSS('#FFF')).toEqual(WHITE);
expect(makeColorFromCSS('#FFFFFF')).toEqual(WHITE);
expect(makeColorFromCSS('#DFBA69')).toEqual(GOLD);
});
it('works with named colors', () => {
expect(makeColorFromCSS('black')).toEqual(BLACK);
expect(makeColorFromCSS('white')).toEqual(WHITE);
expect(makeColorFromCSS('rebeccapurple')).toEqual(PURPLE);
});
it('is case-insensitive', () => {
expect(makeColorFromCSS('BLACK')).toEqual(BLACK);
expect(makeColorFromCSS('wHIte')).toEqual(WHITE);
expect(makeColorFromCSS('rebeccaPurple')).toEqual(PURPLE);
});
it('works with rgb colors', () => {
expect(makeColorFromCSS('rgb(0, 0, 0)')).toEqual(BLACK);
expect(makeColorFromCSS('rgb(255, 255, 255)')).toEqual(WHITE);
expect(makeColorFromCSS('rgb(102, 51, 153)')).toEqual(PURPLE);
});
it('works with rgba colors', () => {
expect(makeColorFromCSS('rgba(0, 0, 0, 1)')).toEqual(BLACK);
expect(makeColorFromCSS('rgba(255, 255, 255, 1)')).toEqual(WHITE);
expect(makeColorFromCSS('rgba(102, 51, 153, 1)')).toEqual(PURPLE);
});
it('multiplies rgba components with an alpha', () => {
expect(makeColorFromCSS('rgba(0, 0, 0, 0.5)').alpha).toBeCloseTo(0.5);
expect(makeColorFromCSS('rgba(0, 0, 0, 1)', 0.5).alpha).toBeCloseTo(0.5);
expect(makeColorFromCSS('rgba(0, 0, 0, 0.5)', 0.5).alpha).toBeCloseTo(0.25);
});
it('works with hsl colors', () => {
expect(makeColorFromCSS('hsl(0, 0%, 0%)')).toEqual(BLACK);
expect(makeColorFromCSS('hsl(0, 0%, 100%)')).toEqual(WHITE);
});
it('works with hsla colors', () => {
expect(makeColorFromCSS('hsla(0, 0%, 0%, 1)')).toEqual(BLACK);
expect(makeColorFromCSS('hsla(0, 0%, 100%, 1)')).toEqual(WHITE);
});
});
describe('makeColorFill', () => {
it('sets the correct color', () => {
expect(makeColorFill('#000')).toHaveProperty('color', BLACK);
expect(makeColorFill('#fff')).toHaveProperty('color', WHITE);
expect(makeColorFill('rebeccapurple')).toHaveProperty('color', PURPLE);
expect(makeColorFill('#DFBA69')).toHaveProperty('color', GOLD);
});
});
describe('makeRect', () => {
it('is correctly constructed', () => {
const group = makeRect(100, 200, 300, 400);
expect(group).toHaveProperty('x', 100);
expect(group).toHaveProperty('y', 200);
expect(group).toHaveProperty('width', 300);
expect(group).toHaveProperty('height', 400);
});
});
describe('makeSymbolInstance', () => {
it('is correctly constructed', () => {
const instance = makeSymbolInstance(
makeRect(0, 0, 100, 100),
'this is the symbol id',
'this is the name',
);
expect(instance).toHaveProperty('symbolID', 'this is the symbol id');
expect(instance).toHaveProperty('name', 'this is the name');
});
});
describe('makeSymbolMaster', () => {
it('is correctly constructed', () => {
const master = makeSymbolMaster(
makeRect(0, 0, 100, 100),
'this is the symbol id',
'this is the name',
);
expect(master).toHaveProperty('symbolID', 'this is the symbol id');
expect(master).toHaveProperty('name', 'this is the name');
});
});
================================================
FILE: __tests__/jest/jsonUtils/shapeLayers.ts
================================================
import {
makeRectPath,
makeShapePath,
makeRectShapeLayer,
makeShapeGroup,
} from '../../../src/jsonUtils/shapeLayers';
describe('makeRectPath', () => {
it('is correctly constructed', () => {
const path = makeRectPath([10, 20, 30, 40]);
expect(path.points[0]).toHaveProperty('cornerRadius', 10);
expect(path.points[1]).toHaveProperty('cornerRadius', 20);
expect(path.points[2]).toHaveProperty('cornerRadius', 30);
expect(path.points[3]).toHaveProperty('cornerRadius', 40);
});
});
describe('makeShapePath', () => {
it('is correctly constructed', () => {
const frame = { foo: 'bar' };
const path = { baz: 'qux' };
// @ts-ignore
const shapePath = makeShapePath(frame, path);
expect(shapePath).toHaveProperty('frame', frame);
expect(shapePath).toHaveProperty('baz', 'qux');
});
});
describe('makeRectShapeLayer', () => {
it('is correctly constructed', () => {
const shapeLayer = makeRectShapeLayer(100, 200, 300, 400, [10, 20, 30, 40]);
expect(shapeLayer).toHaveProperty('frame.x', 100);
expect(shapeLayer).toHaveProperty('frame.y', 200);
expect(shapeLayer).toHaveProperty('frame.width', 300);
expect(shapeLayer).toHaveProperty('frame.height', 400);
expect(shapeLayer).toHaveProperty('fixedRadius', 10);
});
});
describe('makeShapeGroup', () => {
it('is correctly constructed', () => {
const frame = { foo: 'bar' };
const layers = [{ baz: 'qux' }];
const fills = ['foo', 'bar'];
// @ts-ignore
const shapeGroup = makeShapeGroup(frame, layers, undefined, undefined, fills);
expect(shapeGroup).toHaveProperty('frame', frame);
expect(shapeGroup).toHaveProperty('layers', layers);
expect(shapeGroup).toHaveProperty('style.fills', fills);
});
});
================================================
FILE: __tests__/jest/jsonUtils/style.ts
================================================
import { makeBorderOptions, makeShadow } from '../../../src/jsonUtils/style';
describe('makeBorderOptions', () => {
it('makes solid borders', () => {
expect(makeBorderOptions('solid', 1)).toHaveProperty('dashPattern', []);
});
it('makes dotted borders', () => {
expect(makeBorderOptions('dotted', 1)).toHaveProperty('dashPattern', [1, 1]);
expect(makeBorderOptions('dotted', 5)).toHaveProperty('dashPattern', [5, 5]);
});
it('makes dashed borders', () => {
expect(makeBorderOptions('dashed', 1)).toHaveProperty('dashPattern', [3, 3]);
expect(makeBorderOptions('dashed', 5)).toHaveProperty('dashPattern', [15, 15]);
});
});
describe('makeShadow', () => {
it('has sensible defaults', () => {
const result = makeShadow({});
expect(result).toHaveProperty('color.alpha', 1);
expect(result).toHaveProperty('blurRadius', 1);
expect(result).toHaveProperty('offsetX', 0);
expect(result).toHaveProperty('offsetY', 0);
});
it('passes through props', () => {
const result = makeShadow({
shadowOpacity: 0.5,
shadowColor: 'red',
shadowRadius: 10,
shadowOffset: {
width: 5,
height: 7,
},
});
expect(result).toHaveProperty('color.alpha', 0.5);
expect(result).toHaveProperty('blurRadius', 10);
expect(result).toHaveProperty('offsetX', 5);
expect(result).toHaveProperty('offsetY', 7);
});
it('combines rgba alpha & shadowOpacity', () => {
const result = makeShadow({
shadowOpacity: 0.5,
shadowColor: 'rgba(0,0,0,0.5)',
shadowRadius: 10,
shadowOffset: {
width: 5,
height: 7,
},
});
expect(result.color.alpha).toBeCloseTo(0.25);
});
});
================================================
FILE: __tests__/jest/reactTreeToFlexTree.ts
================================================
import yoga from 'yoga-layout-prebuilt';
import { computeYogaTree } from '../../src/jsonUtils/computeYogaTree';
import { Context } from '../../src/utils/Context';
import { reactTreeToFlexTree } from '../../src/buildTree';
import bridge from '../../src/platformBridges/macos';
const treeRootStub = {
type: 'artboard',
props: {
style: {
flexDirection: 'row',
flexWrap: 'wrap',
width: 416,
},
name: 'Swatches',
},
children: [
{
type: 'view',
props: {
name: 'Layer 1',
style: {
height: 100,
position: 'absolute',
width: 100,
zIndex: 1,
},
},
children: [],
},
{
type: 'view',
props: {
name: 'Layer 3',
style: {
height: 300,
position: 'absolute',
width: 300,
zIndex: 3,
},
},
children: [],
},
{
type: 'view',
props: {
name: 'Layer 2',
style: {
height: 200,
position: 'absolute',
width: 200,
zIndex: 2,
},
},
children: [],
},
],
};
describe('Compute Flex Tree', () => {
it('correctly creates flex tree', () => {
const yogaNode = computeYogaTree(bridge)(treeRootStub, new Context());
yogaNode.calculateLayout(undefined, undefined, yoga.DIRECTION_LTR);
const tree = reactTreeToFlexTree(treeRootStub, yogaNode, new Context());
expect(tree.children).toEqual([
{
type: 'view',
style: {
height: 100,
position: 'absolute',
width: 100,
zIndex: 1,
},
textStyle: {},
layout: {
left: 0,
right: 0,
top: 0,
bottom: 0,
width: 100,
height: 100,
},
props: {
name: 'Layer 1',
style: {
height: 100,
position: 'absolute',
width: 100,
zIndex: 1,
},
textNodes: [],
},
children: [],
},
{
type: 'view',
style: {
height: 200,
position: 'absolute',
width: 200,
zIndex: 2,
},
textStyle: {},
layout: {
left: 0,
right: 0,
top: 0,
bottom: 0,
width: 200,
height: 200,
},
props: {
name: 'Layer 2',
style: {
height: 200,
position: 'absolute',
width: 200,
zIndex: 2,
},
textNodes: [],
},
children: [],
},
{
type: 'view',
style: {
height: 300,
position: 'absolute',
width: 300,
zIndex: 3,
},
textStyle: {},
layout: {
left: 0,
right: 0,
top: 0,
bottom: 0,
width: 300,
height: 300,
},
props: {
name: 'Layer 3',
style: {
height: 300,
position: 'absolute',
width: 300,
zIndex: 3,
},
textNodes: [],
},
children: [],
},
]);
});
});
================================================
FILE: __tests__/jest/sharedStyles/TextStyles.ts
================================================
import bridge from '../../../src/platformBridges/macos';
let TextStyles;
let doc;
let sharedTextStyles;
beforeEach(() => {
jest.resetModules();
jest.mock('../../../src/utils/getSketchVersion', () => ({
getSketchVersion: jest.fn(() => 51),
}));
TextStyles = require('../../../src/sharedStyles/TextStyles').TextStyles;
sharedTextStyles = require('../../../src/utils/sharedTextStyles');
jest.mock('../../../src/utils/sharedTextStyles');
TextStyles = TextStyles(() => bridge);
sharedTextStyles = sharedTextStyles.sharedTextStyles;
sharedTextStyles.setDocument = jest.fn((doc) => {
if (!doc) {
throw new Error('Please provide a sketch document reference');
}
});
sharedTextStyles.addStyle = jest.fn(() => 'styleId');
sharedTextStyles.setStyles = jest.fn(() => sharedTextStyles);
doc = jest.fn();
});
describe('create', () => {
describe('without a context', () => {
it('it errors', () => {
const styles = {};
expect(() => TextStyles.create({}, styles)).toThrowError(
/Please provide a sketch document reference/,
);
});
});
describe('with a context', () => {
it('clears clearExistingStyles when true', () => {
TextStyles.create(
{},
{
clearExistingStyles: true,
document: doc,
},
);
expect(sharedTextStyles.setStyles).toHaveBeenCalled();
});
it('doesn’t clearExistingStyles when false', () => {
TextStyles.create(
{},
{
clearExistingStyles: false,
document: doc,
},
);
expect(sharedTextStyles.setStyles).not.toHaveBeenCalled();
});
it('stores one style', () => {
const styles = {
foo: {
fontSize: 'bar',
},
};
const res = TextStyles.create(styles, { document: doc });
expect(Object.keys(res).length).toBe(1);
});
it('stores unique styles seperately', () => {
const styles = {
foo: {
fontSize: 'bar',
},
bar: {
fontSize: 'baz',
},
};
const res = TextStyles.create(styles, { document: doc });
expect(Object.keys(res).length).toBe(2);
expect(sharedTextStyles.addStyle).toHaveBeenCalledTimes(2);
});
it('merges duplicate styles', () => {
const styles = {
foo: {
fontSize: 'foo',
},
bar: {
fontSize: 'foo',
},
};
const res = TextStyles.create(styles, { document: doc });
expect(Object.keys(res).length).toBe(1);
expect(sharedTextStyles.addStyle).toHaveBeenCalledTimes(2);
});
it('only stores text attributes', () => {
const whitelist = [
'color',
'fontFamily',
'fontSize',
'fontStyle',
'fontWeight',
'textShadowOffset',
'textShadowRadius',
'textShadowColor',
'textTransform',
'letterSpacing',
'lineHeight',
'textAlign',
'writingDirection',
];
const blacklist = ['foo', 'bar', 'baz'];
const input = [...whitelist, ...blacklist].reduce(
(acc, key) => ({
...acc,
[key]: '',
}),
{},
);
const res = TextStyles.create({ foo: input }, { document: doc });
const firstStoredStyle = res[Object.keys(res)[0]].cssStyle;
whitelist.forEach((key) => {
expect(firstStoredStyle).toHaveProperty(key, '');
});
blacklist.forEach((key) => {
expect(firstStoredStyle).not.toHaveProperty(key);
});
});
});
});
describe('resolve', () => {
beforeEach(() => {
TextStyles.create({}, { document: doc });
});
it('retrieves a matching style', () => {
const key = 'foo';
const styles = {
[key]: { fontSize: 'bar' },
};
TextStyles.create(styles, { document: doc });
expect(TextStyles.resolve(styles[key])).toBeDefined();
expect(sharedTextStyles.addStyle).toHaveBeenCalledTimes(1);
});
it('returns null with no matching style', () => {
const key = 'foo';
const styles = {
[key]: {
fontSize: 'bar',
},
};
const style2 = {
fontSize: 'qux',
};
TextStyles.create(styles, { document: doc });
expect(TextStyles.resolve(style2)).not.toBeDefined();
expect(sharedTextStyles.addStyle).toHaveBeenCalledTimes(1);
});
});
describe('get', () => {
it('finds a matching registered style by name', () => {
const styles = {
foo: {
fontSize: 'bar',
},
bar: {
fontSize: 'baz',
},
};
TextStyles.create(styles, { document: doc });
expect(TextStyles.get('foo')).toEqual(styles.foo);
expect(TextStyles.get('baz')).toEqual(undefined);
});
it('returns undefined when not found', () => {
const styles = {
foo: {
fontSize: 'bar',
},
};
TextStyles.create(styles, { document: doc });
expect(TextStyles.get('baz')).toEqual(undefined);
});
});
describe('clear', () => {
it('clears previously registered styles', () => {
const styles = {
foo: {
fontSize: 'bar',
},
bar: {
fontSize: 'baz',
},
};
TextStyles.create(styles, { document: doc });
TextStyles.clear();
expect(TextStyles.styles()).toEqual({});
});
});
================================================
FILE: __tests__/jest/utils/isDefined.ts
================================================
import { isDefined } from '../../../src/utils/isDefined';
describe('isNullOrUndefined', () => {
it('correctly identify null', () => {
const shouldBeNull = isDefined(null);
expect(shouldBeNull).toEqual(false);
});
it('correctly identify undefined', () => {
const shouldBeUndefined = isDefined(undefined);
expect(shouldBeUndefined).toEqual(false);
});
it('correctly identify zero (0)', () => {
const shouldBeZero = isDefined(0);
expect(shouldBeZero).toEqual(true);
});
});
================================================
FILE: __tests__/jest/utils/sortObjectKeys.ts
================================================
import { sortObjectKeys } from '../../../src/utils/sortObjectKeys';
test('simple example', () => {
const a = {
foo: true,
bar: true,
qux: true,
baz: true,
};
const b = {
bar: true,
baz: true,
foo: true,
qux: true,
};
expect(sortObjectKeys(a)).toEqual(b);
});
================================================
FILE: __tests__/jest/utils/zIndex.ts
================================================
import { zIndex } from '../../../src/utils/zIndex';
const noZIndexNode = {
props: {
style: {
zIndex: 0,
},
},
};
const zIndexNode = {
props: {
style: {
zIndex: 1,
},
},
};
const fixureThatShouldBeSortedDifferently = [noZIndexNode, zIndexNode];
const fixureThatShouldNotBeSortedDifferently = [noZIndexNode, noZIndexNode];
describe('zIndex', () => {
it('correctly resort zIndex', () => {
// @ts-ignore
const shouldBeResorted = zIndex(fixureThatShouldBeSortedDifferently);
// @ts-ignore
expect(shouldBeResorted[0].props.style.zIndex).toEqual(0);
});
it('correctly add original index to returned objects ', () => {
// @ts-ignore
const shouldBeResorted = zIndex(fixureThatShouldBeSortedDifferently);
// @ts-ignore
expect(shouldBeResorted[0].oIndex).toEqual(0);
});
it('correctly resort zIndexes that are all the same', () => {
// @ts-ignore
const shouldNotBeResorted = zIndex(fixureThatShouldNotBeSortedDifferently);
// @ts-ignore
expect(shouldNotBeResorted[0].props.style.zIndex).toEqual(0);
});
});
================================================
FILE: __tests__/skpm/basic.test.js
================================================
import * as React from 'react';
import * as sketch from 'sketch';
import { render, View, Artboard, Text } from '../../lib';
// depending on where those tests run, we don't get the things,
// eg. the context might be empty or there is no selected document
// This make sure we always get something
function getDoc(context, document) {
return context.document || (sketch.getSelectedDocument() || document).sketchObject;
}
const colorList = {
Haus: '#F3F4F4',
Night: '#333',
Sur: '#96DBE4',
'Sur Dark': '#24828F',
Peach: '#EFADA0',
'Peach Dark': '#E37059',
Pear: '#93DAAB',
'Pear Dark': '#2E854B',
};
test('should render a Page with a rectangle', (context, document) => {
const nativePage = getDoc(context, document).currentPage();
const Swatch = ({ name, hex }) => (
<View
name={`Swatch ${name}`}
style={{
height: 96,
width: 96,
margin: 4,
backgroundColor: hex,
padding: 8,
}}
>
<Text name="Swatch Name" style={{ color: '#000', fontWeight: 'bold' }}>
{name}
</Text>
<Text name="Swatch Hex" style={{ color: '#000' }}>
{hex}
</Text>
</View>
);
render(
<Artboard
name="Swatches"
style={{
flexDirection: 'row',
flexWrap: 'wrap',
width: (96 + 8) * 4,
}}
>
{Object.keys(colorList).map(color => (
<Swatch name={color} hex={colorList[color]} key={color} />
))}
</Artboard>,
nativePage,
);
const page = sketch.Page.fromNative(nativePage);
expect(page.layers[0].name).toBe('Swatches');
});
================================================
FILE: __tests__/skpm/render-context.test.js
================================================
import * as React from 'react';
import * as sketch from 'sketch';
import { render, View, Text } from '../../lib';
// depending on where those tests run, we don't get the things,
// eg. the context might be empty or there is no selected document
// This make sure we always get something
function getDoc(document) {
return sketch.getSelectedDocument() || document;
}
test('should render a Page with context events', (context, document) => {
const { selectedPage } = getDoc(document);
const Swatch = ({ hex }) => {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
setCount(10);
}, [count]);
return (
<View
name={`Count is ${count}`}
style={{
height: 96,
width: 96,
margin: 4,
backgroundColor: hex,
padding: 8,
}}
>
<Text name="Swatch Name" style={{ color: '#000', fontWeight: 'bold' }}>
Count is {count}
</Text>
</View>
);
};
render(<Swatch hex="#F3F4F4" />, selectedPage);
expect(selectedPage.layers[0].name).toBe('Count is 10');
});
================================================
FILE: __tests__/skpm/render-in-wrapped-object.test.js
================================================
import * as React from 'react';
import * as sketch from 'sketch';
import { render, View, Artboard, Text } from '../../lib';
// depending on where those tests run, we don't get the things,
// eg. the context might be empty or there is no selected document
// This make sure we always get something
function getDoc(document) {
return sketch.getSelectedDocument() || document;
}
const colorList = {
Haus: '#F3F4F4',
Night: '#333',
Sur: '#96DBE4',
'Sur Dark': '#24828F',
Peach: '#EFADA0',
'Peach Dark': '#E37059',
Pear: '#93DAAB',
'Pear Dark': '#2E854B',
};
test('should render a Page with a rectangle', (context, document) => {
const { selectedPage } = getDoc(document);
const Swatch = ({ name, hex }) => (
<View
name={`Swatch ${name}`}
style={{
height: 96,
width: 96,
margin: 4,
backgroundColor: hex,
padding: 8,
}}
>
<Text name="Swatch Name" style={{ color: '#000', fontWeight: 'bold' }}>
{name}
</Text>
<Text name="Swatch Hex" style={{ color: '#000' }}>
{hex}
</Text>
</View>
);
render(
<Artboard
name="Swatches"
style={{
flexDirection: 'row',
flexWrap: 'wrap',
width: (96 + 8) * 4,
}}
>
{Object.keys(colorList).map(color => (
<Swatch name={color} hex={colorList[color]} key={color} />
))}
</Artboard>,
selectedPage,
);
expect(selectedPage.layers[0].name).toBe('Swatches');
});
================================================
FILE: book.json
================================================
{
"gitbook": ">= 3.2.1",
"title": "react-sketchapp",
"plugins": [
"edit-link",
"prism",
"-highlight",
"github",
"-search",
"codeblock-disable-glossary",
"anchorjs"
],
"pluginsConfig": {
"edit-link": {
"base": "https://github.com/airbnb/react-sketchapp/tree/master",
"label": "Edit This Page"
},
"github": {
"url": "https://github.com/airbnb/react-sketchapp/"
}
}
}
================================================
FILE: docs/API.md
================================================
# API Reference
- [`render`](#renderelement-container)
- [`renderToJSON`](#rendertojsonelement)
- [Components](#components)
- [`<Document>`](#document)
- [`<Page>`](#page)
- [`<Artboard>`](#artboard)
- [`<Image>`](#image)
- [`<RedBox>`](#redbox)
- [`<Svg>`](#svg)
- [`<Text>`](#text)
- [`<View>`](#view)
- [`Hooks`](#hooks)
- [`useWindowDimensions`](#usewindowdimensions)
- [`Platform`](#platform)
- [`OS`](#os)
- [`Version`](#version)
- [`select`](#selectobj)
- [`StyleSheet`](#stylesheet)
- [`hairlineWidth`](#hairlinewidth)
- [`absoluteFill`](#absolutefill)
- [`create`](#createstyles)
- [`flatten`](#flattenstyles)
- [`resolve`](#resolvestyle)
- [`TextStyles`](#textstyles)
- [`create`](#createstyleoptionsstyles)
- [`resolve`](#resolvestyle)
- [`Symbols`](#symbols)
- [`makeSymbol`](#makesymbolnode-props-document)
### `render(element, container)`
Returns the top-level rendered Sketch object or an array of Sketch objects if you use `<Page>` components.
#### Parameters
##### `element` (required)
Top-level React component that defines your Sketch document.
Example:
```js
<Document>
<Page name="Mobile">
<Artboard name="iPhone">
<View>
<Text>Hello World</Text>
</View>
</Artboard>
</Page>
</Document>
```
##### `container` (optional)
The element to render into - will be replaced. Should either be a Sketch [Document](https://developer.sketchapp.com/reference/api/#document), Sketch [Group](https://developer.sketchapp.com/reference/api/#group) or Sketch [Page](https://developer.sketchapp.com/reference/api/#page) Object.
Example: `sketch.getSelectedDocument().selectedPage`.
#### Returns
The top-most rendered native Sketch layer.
#### Example
```js
import sketch from 'sketch';
import { View, Text, render } from 'react-sketchapp';
const Document = props => (
<View>
<Text>Hello world!</Text>
</View>
);
export default () => {
render(<Document />, sketch.getSelectedDocument().selectedPage);
};
```
### `renderToJSON(element)`
Returns a Sketch JSON object for further consumption - doesn't add to the page.
#### Parameters
##### `element` (required)
Top-level React component that defines your Sketch document.
#### Returns
The top-most Sketch layer as JSON.
## Components
### `<Document>`
Wrapper for Sketch's Documents. Must be used at the root of your application and is required if you would like to have multiple pages.
#### Props
| Prop | Type | Default | Note |
| ---------- | ------ | ------- | ---------------------------------------- |
| `children` | `Node` | | Can only be [`<Page>`](#page) components |
#### Example
```js
<Document>
<Page>
<Text>Hello world!</Text>
</Page>
<Page>
<Text>Hello second world!!</Text>
</Page>
</Document>
```
### `<Page>`
Wrapper for Sketch's Pages. Requires a [`<Document>`](#document) component as a parent if you would like to use multiple of these components.
#### Props
| Prop | Type | Default | Note |
| ---------- | -------- | ------- | ------------------------------------------------ |
| `name` | `String` | | The name to be displayed in the Sketch Page List |
| `children` | `Node` | | |
#### Example
```js
<Page name="My Page">
<Text>Hello world!</Text>
</Page>
```
### `<Artboard>`
Wrapper for Sketch's Artboards. Requires a [`<Page>`](#page) component as a parent if you would like to use multiple of these components.
#### Props
| Prop | Type | Default | Note |
| --- | --- | --- | --- |
| `name` | `String` | | The name to be displayed in the Sketch Layer List |
| `children` | `Node` | | |
| `style` | [`Style`](/docs/styling.md) | | |
| `viewport` | `Viewport` | | Object: { name: string, width: number, height: number, scale?: number, fontScale?: number } |
| `isHome` | `Boolean` | | Is prototype home screen if true |
The `scale` and `fontScale` attributes in the `viewport` prop are not used by Sketch, but can be used together with the [`useWindowDimensions`](#usewindowdimensions) hook for conditional styling/rendering.
#### Examples
Hello world with width of 480px.
```js
<Artboard
name="My Artboard"
style={{
width: 480,
}}
>
<Text>Hello world!</Text>
</Artboard>
```
Mobile screen artboard with viewport preset (supports scrolling in prototypes).
```js
<Artboard
name="Home/Mobile"
style={{
width: 360,
height: 1280,
}}
viewport={{
name: 'Mobile',
width: 360,
height: 640,
}}
>
<Text>Hello world!</Text>
</Artboard>
```
### `<Image>`
#### Props
| Prop | Type | Default | Note |
| ------------ | --------------------------- | --------- | ---- |
| `children` | `Node` | | |
| `source` | `ImageSource` | | |
| `style` | [`Style`](/docs/styling.md) | | |
| `resizeMode` | `ResizeMode` | `contain` | |
```js
type ImageSource = string | { src: string };
type ResizeMode = 'contain' | 'cover' | 'stretch' | 'center' | 'repeat' | 'none';
```
#### Example
```js
<Image
source="http://placekitten.com/400"
resizeMode="contain"
style={{
height: 400,
width: 400,
}}
/>
```
### `<RedBox>`
A red box / 'red screen of death' error handler. Thanks to [commissure/redbox-react](https://github.com/commissure/redbox-react).
#### Props
| Prop | Type | Default | Note |
| --- | --- | --- | --- |
| `error` | [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) | **required** | A JavaScript Error object |
#### Example
```js
import sketch from 'sketch';
import { RedBox, render } from 'react-sketchapp';
export default () => {
const { selectedPage } = sketch.getSelectedDocument();
try {
render(<BrokenComponent />, selectedPage);
} catch (err) {
render(<RedBox error={err} />, selectedPage);
}
};
```
### `<Svg>`
SVG Interface to Sketch
The API is based on [`react-native-svg`](https://github.com/react-native-community/react-native-svg). See more information on [its README](https://github.com/react-native-community/react-native-svg#Usage).
#### Example
```js
import sketch from 'sketch';
import { Svg, render } from 'react-sketchapp';
export default () => {
render(
<Svg xmlns="http://www.w3.org/2000/svg" width="494" height="447" viewBox="0 0 494 447">
<Svg.G fill="none" fillRule="evenodd">
<Svg.Path fill="#FFAE00" d="M247 447L0 160 107 15 247 0l140 15 107 145" />
<Svg.Path fill="#EC6C00" d="M247 447L0 160h494" />
<Svg.Path fill="#FFAE00" d="M247 447L100 160h294" />
<Svg.Path fill="#FFEFB4" d="M247 0L100 160h294" />
<Svg.Path fill="#FFAE00" d="M107 15L52 88 0 160h101M387 15l55 73 52 72H393" />
<Svg.Path fill="#FED305" d="M107 15l-7 145L247 0m140 15l7 145L247 0" />
</Svg.G>
</Svg>,
sketch.getSelectedDocument().selectedPage,
);
};
```
#### Direct imports
Additionally, to have a somewhat more compliant mode to the `react-native-svg` API, the SVG components might as well be imported directly:
```js
import sketch from 'sketch';
import { render } from 'react-sketchapp';
import Svg, { G, Path } from 'react-sketchapp/lib/components/Svg';
export default () => {
render(
<Svg xmlns="http://www.w3.org/2000/svg" width="494" height="447" viewBox="0 0 494 447">
<G fill="none" fillRule="evenodd">
<Path fill="#FFAE00" d="M247 447L0 160 107 15 247 0l140 15 107 145" />
<Path fill="#EC6C00" d="M247 447L0 160h494" />
<Path fill="#FFAE00" d="M247 447L100 160h294" />
<Path fill="#FFEFB4" d="M247 0L100 160h294" />
<Path fill="#FFAE00" d="M107 15L52 88 0 160h101M387 15l55 73 52 72H393" />
<Path fill="#FED305" d="M107 15l-7 145L247 0m140 15l7 145L247 0" />
</G>
</Svg>,
sketch.getSelectedDocument().selectedPage,
);
};
```
### `<Text>`
Text primitives
#### Props
| Prop | Type | Default | Note |
| --- | --- | --- | --- |
| `name` | `String` | | The name to be displayed in the Sketch Layer List |
| `children` | `String` | | |
| `style` | [`Style`](/docs/styling.md) | | |
#### Example
```js
<Text
name="Sketch Layer name"
style={{
fontSize: 24,
fontFamily: 'Helvetica',
fontWeight: 'bold',
color: '#01ffae',
}}
>
Hello World!
</Text>
```
### `<View>`
View primitives
#### Props
| Prop | Type | Default | Note |
| --- | --- | --- | --- |
| `name` | `String` | | The name to be displayed in the Sketch Layer List |
| `children` | `Node` | | |
| `style` | [`Style`](/docs/styling.md) | | |
| `flow` | `Flow` | | Object: { target: string, targetId: string, animationType: string } |
#### Examples
##### Example with children
```js
<View
name="Sketch Layer name"
style={{
flexDirection: 'row',
width: 480,
backgroundColor: '#01ffae',
}}
>
<Text>Hello World!</Text>
<Text>Hello World!</Text>
<Text>Hello World!</Text>
</View>
```
##### Example using `flow` prop for prototyping destination
```js
<Document>
<Artboard name="Home">
<View
name="Menu Button"
style={{
height: 100,
backgroundColor: '#01ffae',
}}
flow={{
target: 'menu', // From <Artboard name" or can be "back"
// targetId: uuid (can be used to reference existing artboards/uuids)
// animationType: string (constants can be used from require('sketch') API, or hardcoded)
}}
>
<Text>Open menu!</Text>
</View>
</Artboard>
<Artboard name="Menu">
<View name="Go back" flow={{ target: 'back' }} /* "back" used instead of <Artboard> id */>
<Text>Go back!</Text>
</View>
</Artboard>
</Document>
```
## Hooks
### `useWindowDimensions()`
Returns the window dimensions of the parent `<Artboard>`. Returns `{ width: number, height: number, fontScale: number, scale: number }`.
#### Example
```js
import { Page, Artboard, View, Text, useWindowDimensions } from 'react-sketchapp';
const HomePage = () => {
const { height, width } = useWindowDimensions();
return (
<View style={{ flex: 1 }}>
<View style={{ height, width }}>
<Text>Hello World</Text>
</View>
{(width >= 768) && (
<View style={{ height, width, backgroundColor: 'blue' }}>
<Text style={{ color: 'white' }}>
You can only see this text on tablet/desktop
</Text>
</View>
)}
</View>
);
};
const devices = [{
name: 'Mobile',
width: 360,
height: 640,
}, {
name: 'Tablet',
width: 768
height: 1024,
}, {
name: 'Desktop',
width: 1024
height: 1280,
}];
render(
<Page style={{ flexDirection: 'row' }}>
{devices.map(viewport => (
<Artboard viewport={viewport} style={{ marginRight: 80 }}>
<HomePage />
</Artboard>
))}
</Page>,
);
```
## Platform
### `OS`
`sketch`
### `Version`
`1`
### `select(obj)`
#### Parameters
##### `obj`
## StyleSheet
Compared to single-use `style` objects, `StyleSheets` enable creation of re-usable, optimized style references.
### `hairlineWidth`
The platform's global 'hairline width'.
### `absoluteFill`
A constant 'absolute fill' style.
### `create(styles)`
Create an optimized `StyleSheet` reference from a style object.
#### Parameters
##### `styles`
#### Example
```js
const styles = StyleSheet.create({
foo: {
fontSize: 24,
color: 'red',
},
bar: {
fontSize: 36,
color: 'blue',
},
});
// { foo: 1, bar: 2 }
<View>
<Text style={styles.foo} />
<Text style={styles.bar} />
</View>;
```
### `flatten(styles)`
Flatten an array of style objects into one aggregated object, **or** look up the definition for a registered stylesheet.
#### Parameters
##### `styles`
#### Example
```js
const styles = StyleSheet.create({
foo: {
fontSize: 24,
color: 'red',
},
bar: {
backgroundColor: 'blue',
lineHeight: 36,
},
});
StyleSheet.flatten([styles.foo, styles.bar]);
// { fontSize: 24, color: 'red', backgroundColor: 'blue', lineHeight: 36 }
// alternatively:
StyleSheet.flatten(styles.foo);
// { fontSize: 24, color: 'red' }
```
### `resolve(style)`
Resolve one style.
#### Parameters
##### `style`
#### Example
```js
const styles = StyleSheet.create({
foo: {
fontSize: 24,
color: 'red',
},
});
StyleSheet.resolve(styles.foo);
// { fontSize: 24, color: 'red' }
```
## TextStyles
An interface to Sketch's shared text styles. Create styles with or without rendering them to the document canvas.
### `create(styles, options)`
The primary interface to TextStyles. **Call this before rendering**.
#### Parameters
##### `styles` **(required)**
An object of JavaScript styles. The keys will be used as Sketch's Text Style names.
##### `options: { document, clearExistingStyles }`
###### `document`
The Sketch Document currently being rendered into.
###### `clearExistingStyles`
Clear any styles already registered in the document.
#### Example
```js
import sketch from 'sketch';
import { TextStyles, View, Text, render } from 'react-sketchapp';
export default () => {
const typeStyles = {
Headline: {
fontSize: 36,
fontFamily: 'Apercu',
lineHeight: 38,
},
Body: {
fontSize: 16,
fontFamily: 'Helvetica',
lineHeight: 22,
},
};
TextStyles.create(
{
context: context,
clearExistingStyles: true,
},
typeStyles,
);
const Document = () => (
<View>
<Text style={typeStyles.Headline}>Headline text</Text>
<Text style={typeStyles.Body}>Body text</Text>
</View>
);
render(<Document />, sketch.getSelectedDocument().selectedPage);
};
```
### `resolve(style)`
Find a stored native Sketch style object for a given JavaScript style object. You probably don't need to use this.
#### Parameters
##### `style`
A JavaScript style
### `styles`
Find all of the registered styles. You probably don't need to use this.
### `get(name)`
Find a text style by _name_.
#### Parameters
##### `name`
The style name
#### Example
```js
import sketch from 'sketch';
import { TextStyles, View, Text, render } from 'react-sketchapp';
export default () => {
const typeStyles = {
Headline: {
fontSize: 36,
fontFamily: 'Apercu',
lineHeight: 38,
},
Body: {
fontSize: 16,
fontFamily: 'Helvetica',
lineHeight: 22,
},
};
TextStyles.create(
{
context: context,
clearExistingStyles: true,
},
typeStyles,
);
const Document = () => (
<View>
<Text style={TextStyles.get('Headline')}>Headline text</Text>
<Text style={TextStyles.get('Body')}>Body text</Text>
</View>
);
render(<Document />, sketch.getSelectedDocument().selectedPage);
};
```
### `clear`
Reset the registered styles.
## Symbols
An interface to Sketch's symbols. Create symbols and optionally inject them into the symbols page.
### `makeSymbol(node, props, document)`
Creates a new symbol and injects it into the `Symbols` page. The name of the symbol can be optionally provided and will default to the display name of the component.
Returns a react component which is an can be used to render instances of the symbol.
#### Parameters
| Parameter | Type | Default | Note |
| --- | --- | --- | --- |
| `node` | `Node` | | The node object that will be rendered as a symbol |
| `props` | `Object` | The node name | Optional name for the symbol, string can include backslashes to organize these symbols with Sketch. For example `squares/blue` |
| `props.name` | `String` | The node name | Optional name for the symbol, string can include backslashes to organize these symbols with Sketch. For example `squares/blue` |
| `props.style` | [`Style`](/docs/styling.md) | | |
| `document` | `Object` | The current document | The Sketch document to make the symbol in |
### `getSymbolComponentByName(name)`
Returns a react component which can be used to render the symbol instance that is associated with that name.
### `getSymbolMasterByName(name)`
Returns the JSON representation of the symbol master that is associated with that name.
### Symbol example
```js
import sketch from 'sketch';
import { View, makeSymbol, Artboard, render } from 'react-sketchapp';
const BlueSquare = () => (
<View name="Blue Square" style={{ width: 100, height: 100, backgroundColor: 'blue' }} />
);
const BlueSquareSymbol = makeSymbol(BlueSquare);
const Document = () => (
<Artboard>
<BlueSquareSymbol />
</Artboard>
);
export default () => {
render(<Document />, sketch.getSelectedDocument().selectedPage);
};
```
### Text override example
Text overrides use the name parameter to target a specific Text primitive. When no name is given the value within the Text primitive can be used to override the value.
```js
import sketch from 'sketch';
import { View, Text, makeSymbol, Artboard, render } from 'react-sketchapp';
const BlueSquare = () => (
<View name="Blue Square" style={{ width: 100, height: 100, backgroundColor: 'blue' }}>
<Text>Blue Square Text</Text>
</View>
);
const BlueSquareSymbol = makeSymbol(BlueSquare, 'squares/blue');
const Document = () => (
<Artboard>
<BlueSquareSymbol
overrides={{
'Blue Square Text': 'Override Text',
}}
/>
</Artboard>
);
export default () => {
render(<Document />, sketch.getSelectedDocument().selectedPage);
};
```
### Image override example
Image overrides use the name parameter to target a specific Image primitive.
```js
import sketch from 'sketch';
import { View, Image, Artboard, makeSymbol, render } from 'react-sketchapp';
const BlueSquare = () => (
<View name="Blue Square" style={{ width: 100, height: 100, backgroundColor: 'blue' }}>
<Image name="Blue Square Image" source="https://hello.world/image.jpg" />
</View>
);
const BlueSquareSymbol = makeSymbol(BlueSquare, 'squares/blue');
const Document = () => (
<Artboard>
<BlueSquareSymbol
overrides={{
'Blue Square Image': 'https://hello.world/different.jpg',
}}
/>
</Artboard>
);
export default () => {
render(<Document />, sketch.getSelectedDocument().selectedPage);
};
```
#### Nested symbol + override example
```js
import sketch from 'sketch';
import { View, Text, makeSymbol, Image, Artboard, render } from 'react-sketchapp';
const RedSquare = () => (
<View name="Red Square" style={{ width: 100, height: 100, backgroundColor: 'red' }}>
<Text name="Red Square Text">Red Square</Text>
</View>
);
const RedSquareSymbol = makeSymbol(RedSquare, 'squares/red');
const BlueSquare = () => (
<View name="Blue Square" style={{ width: 100, height: 100, backgroundColor: 'blue' }}>
<Text name="Blue Square Text">Blue Square</Text>
</View>
);
const BlueSquareSymbol = makeSymbol(BlueSquare, 'squares/blue');
const Photo = () => (
<Image
name="Photo"
source="https://pbs.twimg.com/profile_images/895665264464764930/7Mb3QtEB_400x400.jpg"
style={{ width: 100, height: 100 }}
/>
);
const PhotoSymbol = makeSymbol(Photo);
const Nested = () => (
<View name="Nested" style={{ display: 'flex', flexDirection: 'column', width: 75, height: 150 }}>
<PhotoSymbol name="Photo Instance" style={{ width: 75, height: 75 }} />
<RedSquareSymbol name="Red Square Instance" style={{ width: 75, height: 75 }} />
</View>
);
const NestedSymbol = makeSymbol(Nested);
const Document = () => (
<Artboard style={{ display: 'flex' }}>
<NestedSymbol
name="Nested Symbol"
style={{ width: 75, height: 150 }}
overrides={{
'Red Square Instance': BlueSquareSymbol,
'Blue Square Text': 'Text override',
Photo: 'https://pbs.twimg.com/profile_images/833785170285178881/loBb32g3.jpg',
}}
/>
</Artboard>
);
export default () => {
render(<Document />, sketch.getSelectedDocument().selectedPage);
};
```
================================================
FILE: docs/FAQ.md
================================================
# Frequently Asked Questions
#### Why?!??!
`react-sketchapp` evolved out of our need to generate **high-quality, consistent Sketch assets** for our design system at Airbnb. Wrapping Sketch’s imperative API is a pragmatic solution for a great developer experience and predictable rendering.
#### How do I `console.log`?
You have multiple options to view the logs:
- Using the [sketch-dev-tools](https://github.com/skpm/sketch-dev-tools)
- `Console.app -> ~/Library/Logs -> com.bohemiancoding.sketch -> Plugin Output.log`
- in the terminal
```bash
skpm log -f
```
- in the terminal
```bash
tail -F ~/Library/Logs/com.bohemiancoding.sketch3/Plugin\ Output.log
```
Occasionally this file disappears — in that case, run this and then try `tail`ing again.
```bash
touch ~/Library/Logs/com.bohemiancoding.sketch3/Plugin\ Output.log
```
For more information, check out the [Sketch developer documentation](https://developer.sketch.com/plugins/debugging).
#### I'm running a project as a plugin & Sketch isn't showing my changes
Sketch has a [developer mode](https://developer.sketch.com/plugins/debugging#reload-scripts) which refreshes plugins before running. If you're using `skpm` this should be set up automatically, but just in case try running
```bash
defaults write com.bohemiancoding.sketch3.plist AlwaysReloadScript -bool YES
```
#### `<View>` & `<Text>`? Where are the shapes? Talk to me about your API decisions!
Early versions of `react-sketchapp` mirrored Sketch's layers — `<Rect>`, `<Oval>`, `<Star>` etc. This was adequate for rendering simplistic designs such as grids of color palettes, but our focus is on production design systems.
At some point, we had to translate from our component codebase's primitives to Sketch's shapes. We tried translating trees of React Native elements into `<Rect>`s etc, but it felt clumsy. Not every Sketch property has an analog in react-native, but **most react-native properties are translatable to Sketch**.
By aligning with react-native's API we:
- think in the same primitives as we actually use in production
- use the same layout algorithm in design & code
- [render real components](http://airbnb.io/react-sketchapp/docs/guides/universal-rendering.html) into Sketch with `react-primitives` (a platform independent set of primitives)
Where it makes sense we're open to creating Sketch-specific components —there's no analog for `<Artboard>` on web or mobile—but the goal of `react-sketchapp` is to bring design & engineering closer together.
#### So I can't draw arbitrary shapes?
You can use the [SVG API](/docs/API.md#svg) to draw arbitrary shapes.
#### Any plans to support Sketch's constraints for layout?
Not currently. FlexBox is the closest we have to a predictable, cross-platform layout specification — by using it, we can use the same styles on every platform we build for.
We currently use [`yoga`](https://github.com/facebook/yoga).
#### Is there two-way binding? Can I generate React components from Sketch? 🔁
Nope.
Isomorphisms are compelling but our focus is on tools that we can use day-to-day to improve the productivity of designers and engineers working on large-scale production applications.
Getting production-ready semantics out of Sketch is more difficult than generating production-ready Sketch templates from React components 💀
Our solution is to keep our [our design system](http://airbnb.design/building-a-visual-language/)’s source of truth in code, and use `react-sketchapp` to compose & consume it.
To _edit_ our design system, we are free to leverage any technology that can create React components, or be compiled to JSX, such as:
- [React-centric IDEs](https://www.decosoftware.com/)
- in-house design tools that are tailored to our workflow (whilst being backed by data, version control & semantic versioning) 🔜 👀
- writing React components in text editors with our fingers
#### Does this tie your workflow to Sketch? What about other design tools?
Treating Sketch primarily as a _rendering target_ for cross-platform components pushes you to store components & style in code — you're then free to build translation layers for any other design tool that exposes an API.
Given equivalent API support it would be possible to simultaneously render to `react-sketchapp`, `react-figma`, `react-xd` & `react-quark`.
Rather than tying us into one design tools, reasoning about design in cross-platform primitives _frees us_ to use the tooling we want.
#### Can I use [TypeScript](https://www.typescriptlang.org/)?
Of course!
TypeScript definitions are published with the npm package.
================================================
FILE: docs/README.md
================================================
## Table of Contents
- [Introduction](/README.md)
- [Guides](/docs/guides/README.md)
- [Getting Started](/docs/guides/getting-started.md)
- [Using `skpm` as a build system](/docs/guides/using-skpm.md)
- [Rendering](/docs/guides/rendering.md)
- [Data Fetching](/docs/guides/data-fetching.md)
- [Universal Rendering](/docs/guides/universal-rendering.md)
- [Styling](/docs/guides/styling.md)
- [API Reference](/docs/API.md)
- [render](/docs/API.md#renderelement-container)
- [renderToJSON](/docs/API.md#rendertojsonelement)
- [Document](/docs/API.md#document)
- [Page](/docs/API.md#page)
- [Artboard](/docs/API.md#artboard)
- [Image](/docs/API.md#image)
- [RedBox](/docs/API.md#redbox)
- [Svg](/docs/API.md#svg)
- [Text](/docs/API.md#text)
- [View](/docs/API.md#view)
- [Platform](/docs/API.md#platform)
- [StyleSheet](/docs/API.md#stylesheet)
- [TextStyles](/docs/API.md#textstyles)
- [Symbols](/docs/API.md#symbols)
- [Examples](/docs/examples.md)
- [Change Log](/CHANGELOG.md)
- [FAQ](/docs/FAQ.md)
- [Contributing](https://github.com/airbnb/react-sketchapp/blob/master/.github/CONTRIBUTING.md)
================================================
FILE: docs/examples.md
================================================
# Examples
`react-sketchapp` is bundled with lots of examples!
### [Basic setup](https://github.com/airbnb/react-sketchapp/tree/master/examples/basic-setup)

### [Style guide](https://github.com/airbnb/react-sketchapp/tree/master/examples/styleguide)

### [Profile Cards](https://github.com/airbnb/react-sketchapp/tree/master/examples/profile-cards)

### [Profile Cards on Web + Sketch w/ `react-primitives`](https://github.com/airbnb/react-sketchapp/tree/master/examples/profile-cards-primitives)

### [Profile Cards w/ `react-with-styles`](https://github.com/airbnb/react-sketchapp/tree/master/examples/profile-cards-react-with-styles)

### [Profile Cards w/ GraphQL](https://github.com/airbnb/react-sketchapp/tree/master/examples/profile-cards-graphql)

### [Venue Search on Web + Sketch w/ `react-primitives`, Foursquare & Google Maps](https://github.com/airbnb/react-sketchapp/tree/master/examples/foursquare-maps)

### [Generative Colors w/ Chroma-JS](https://github.com/airbnb/react-sketchapp/tree/master/examples/colors)

### [Timeline w/ AirTable](https://github.com/airbnb/react-sketchapp/tree/master/examples/timeline-airtable)

### [Basic setup w/ Typescript](https://github.com/airbnb/react-sketchapp/tree/master/examples/basic-setup-typescript)
 
================================================
FILE: docs/guides/README.md
================================================
# Guides
How to use `react-sketchapp` for fun and profit.
- [Getting Started](getting-started.md)
- [Using `skpm` as a build system](using-skpm.md)
- [Rendering](rendering.md)
- [Data Fetching](data-fetching.md)
- [Universal Rendering](universal-rendering.md)
- [Styling](styling.md)
================================================
FILE: docs/guides/data-fetching.md
================================================
# Data Fetching
Pull real data from an API with `fetch` or GraphQL.
## Fetch
[Full example](https://github.com/airbnb/react-sketchapp/tree/master/examples/foursquare-maps)
`skpm` automatically provides the [Sketch `fetch` polyfill](https://github.com/skpm/sketch-polyfill-fetch) — just use `fetch` as usual.
```js
import fetch from 'sketch-module-fetch-polyfill';
import { render } from 'react-sketchapp';
import MyApp from './MyApp';
export default context => {
fetch('https://reqres.in/api/users')
.then(res => res.json())
.then(data => {
render(<MyApp users={data.users} />, context.document.currentPage());
});
};
```
## GraphQL
[Full example](https://github.com/airbnb/react-sketchapp/tree/master/examples/profile-cards-graphql)
[`gql-sketch`](https://github.com/jongold/gql-sketch) provides a convenient interface for interacting with GraphQL APIs.
```bash
npm install gql-sketch --save
```
```js
import Client from 'gql-sketch';
import { render } from 'react-sketchapp';
import MyApp from './MyApp';
export default context => {
Client('http://example.com/my-graphql-endpoint')
.query(
`
{
allFilms {
films {
title,
actor,
catchphrase
}
}
}
`,
)
.then(({ allFilms }) => {
render(<MyApp films={allFilms} />, context.document.currentPage());
});
};
```
================================================
FILE: docs/guides/getting-started.md
================================================
# Getting Started
You can create a `react-sketchapp` project with `skpm`, by cloning a ready-made [example](../examples.md), or by manually setting up the `package.json` and `manifest.json` scripts (advanced usage).
## Environment Setup
You will need npm, Node and Sketch.
- Terminal (if you’re new to the command line, this [guide](https://medium.com/32pixels/the-designers-guide-to-the-osx-command-prompt-71b0016cac31) may help)
- You need to make sure `git` is installed – type `git --version` in your Terminal to check if it's installed, if it isn’t, you should be prompted to install via “command line developer tools”.
- Code editor e.g. [VSCode](https://code.visualstudio.com/), [Atom](https://atom.io/)
- Node.js & `npm` – [install with Homebrew](https://nodejs.org/en/download/package-manager/#macos) (or install with [Node Version Manager](https://nodejs.org/en/download/package-manager/#nvm))
- [Sketch](https://www.sketch.com/)
- requires macOS
## Creating a Project With Skpm
**Replace** `my-app` with your desired project name:
### Installation
```bash
npm install --global skpm
skpm create my-app --template=airbnb/react-sketchapp # template is a GitHub repo
cd my-app
```
### Setup
You can now open `my-app` in your code editor of choice. You will see a `src` folder with a `manifest.json` file and Sketch entrypoint (e.g. `my-command.js`). If you wish to rename `my-command.js`, you can do so and update the file name in `script` in `manifest.json`
Example modifications (assuming we want to rename the entrypoint file to `main.js` and don't want to have sub-commands):
`src/manifest.json`
```diff
"commands": [
{
- "name": "my-command",
+ "name": "My App Name: Sketch Components"
- "identifier": "my-command-identifier",
+ "identifier": "main",
- "script": "./my-command.js"
+ "script": "./main.js"
}
],
"menu": {
- "title": "my-app",
- "items": [
- "my-command-identifier"
- ]
+ "isRoot": true,
+ "items": [
+ "main"
+ ]
+ }
}
```
### Rendering to Sketch
To render your app to Sketch, open the Sketch application, create a new blank document, then go to your Terminal and run:
```bash
# Make sure you've already done `cd my-app`
npm run render
```
You can pass the target Sketch container layer (i.e. document, group or page object) to the `render` function in your Sketch plugin entrypoint file, using the Sketch API: `render(<App />, sketch.getSelectedDocument()`.
For more info on rendering to Sketch, see the [rendering](./rendering.md) page.
================================================
FILE: docs/guides/rendering.md
================================================
# Rendering Guide
You can use the Sketch API to select Sketch containers such as documents, pages or groups, to pass through to the `render` function.
### Rendering to Multiple Pages or New Documents
`src/my-command.js` (or whatever file your Sketch plugin entrypoint is).
```js
import React from 'react';
import { render, Document, Page } from 'react-sketchapp';
// <Document> wrapper is required if you want to use multiple pages
const App = () => (
<Document>
<Page name="Page 1">
<Artboard>
<Text>Hello World!</Text>
</Artboard>
</Page>
<Page name="Page 2">
<Artboard>
<Text>Hello World, again!</Text>
</Artboard>
</Page>
</Document>
);
export default () => {
const documents = sketch.getDocuments();
const document =
sketch.getSelectedDocument() || new sketch.Document(); // get the current document // or create a new document
};
```
## Rendering to Selected Document
This will render to the last active document. If there is no document open, document will be undefined and you will get an error, so you can add `|| new sketch.Document()` as a fallback to handle this.
```js
import sketch from 'sketch';
import { render } from 'react-sketchapp';
// const App = () => ... or import App from './App';
export default () => {
const document = sketch.getSelectedDocument();
render(<App />, document);
};
```
## Rendering to Document by Name
We can select a document by name, by looping through `sketch.getDocuments()` and checking `doc.path` inside the loop.
```js
import path from 'path';
import sketch from 'sketch';
import { render } from 'react-sketchapp';
// const App = () => ... or import App from './App';
const getDocumentByName = name => {
return (sketch.getDocuments() || []).find(doc => {
return doc.path && path.basename(doc.path, '.sketch') === name;
});
};
export default () => {
const document = getDocumentByName('My App Design') || new sketch.Document(); // Fallback to new document if document not found
render(<App />, document);
};
```
================================================
FILE: docs/guides/styling.md
================================================
# Styling
Components use CSS styles + FlexBox layout.
## Layout Styles
| property | type | supported? |
| --- | --- | --- |
| `width` | `number` | `percentage` | ✅ |
| `height` | `number` | `percentage` | ✅ |
| `top` | `number` | `percentage` | ✅ |
| `left` | `number` | `percentage` | ✅ |
| `right` | `number` | `percentage` | ✅ |
| `bottom` | `number` | `percentage` | ✅ |
| `minWidth` | `number` | `percentage` | ✅ |
| `maxWidth` | `number` | `percentage` | ✅ |
| `minHeight` | `number` | `percentage` | ✅ |
| `maxHeight` | `number` | `percentage` | ✅ |
| `margin` | `number` | `percentage` | ✅ |
| `marginVertical` | `number` | `percentage` | ✅ |
| `marginHorizontal` | `number` | `percentage` | ✅ |
| `marginTop` | `number` | `percentage` | ✅ |
| `marginBottom` | `number` | `percentage` | ✅ |
| `marginLeft` | `number` | `percentage` | ✅ |
| `marginRight` | `number` | `percentage` | ✅ |
| `padding` | `number` | `percentage` | ✅ |
| `paddingVertical` | `number` | `percentage` | ✅ |
| `paddingHorizontal` | `number` | `percentage` | ✅ |
| `paddingTop` | `number` | `percentage` | ✅ |
| `paddingBottom` | `number` | `percentage` | ✅ |
| `paddingLeft` | `number` | `percentage` | ✅ |
| `paddingRight` | `number` | `percentage` | ✅ |
| `borderWidth` | `number` | `percentage` | ✅ |
| `borderTopWidth` | `number` | `percentage` | ✅ |
| `borderRightWidth` | `number` | `percentage` | ✅ |
| `borderBottomWidth` | `number` | `percentage` | ✅ |
| `borderLeftWidth` | `number` | `percentage` | ✅ |
| `position` | `absolute` | `relative` | ✅ |
| `flexDirection` | `row` | `row-reverse` | `column` | `column-reverse` | ✅ |
| `flexWrap` | `wrap` | `nowrap` | ✅ |
| `justifyContent` | `flex-start` | `flex-end` | `center` | `space-between` | `space-around` | ✅ |
| `alignItems` | `flex-start` | `flex-end` | `center` | `stretch` | ✅ |
| `alignSelf` | `auto` | `flex-start` | `flex-end` | `center` | `stretch` | ✅ |
| `overflow` | `visible` | `hidden` | `scroll` | ✅ |
| `flex` | `number` | ✅ |
| `flexGrow` | `number` | ✅ |
| `flexShrink` | `number` | ✅ |
| `flexBasis` | `number` | ✅ |
| `aspectRatio` | `number` | ⛔️ |
| `zIndex` | `number` | ✅ |
| `backfaceVisibility` | `visible` | `hidden` | ⛔️ |
| `backgroundColor` | `Color` | ✅ |
| `borderColor` | `Color` | ✅ |
| `borderTopColor` | `Color` | ✅ |
| `borderRightColor` | `Color` | ✅ |
| `borderBottomColor` | `Color` | ✅ |
| `borderLeftColor` | `Color` | ✅ |
| `borderRadius` | `number` | `percentage` | ✅ |
| `borderTopLeftRadius` | `number` | `percentage` | ✅ |
| `borderTopRightRadius` | `number` | `percentage` | ✅ |
| `borderBottomLeftRadius` | `number` | `percentage` | ✅ |
| `borderBottomRightRadius` | `number` | `percentage` | ✅ |
| `borderStyle` | `solid` | `dotted` | `dashed` | ✅ |
| `borderWidth` | `number` | `percentage` | ✅ |
| `borderTopWidth` | `number` | `percentage` | ✅ |
| `borderRightWidth` | `number` | `percentage` | ✅ |
| `borderBottomWidth` | `number` | `percentage` | ✅ |
| `borderLeftWidth` | `number` | `percentage` | ✅ |
| `opacity` | `number` | ✅ |
## Shadow Styles
| property | type | supported? |
| --- | --- | --- |
| `shadowColor` | `Color` | ✅ |
| `shadowOffset` | `{ width: number, height: number }` | ✅ |
| `shadowOpacity` | `number` | ✅ |
| `shadowRadius` | `number` | `percentage` | ✅ |
## Type Styles
| property | type | supported? |
| --- | --- | --- |
| `color` | `Color` | ✅ |
| `fontFamily` | `string` | ✅ |
| `fontSize` | `number` | ✅ |
| `fontStyle` | `normal` | `italic` | ✅ |
| `fontWeight` | `string` | `number` | ✅ |
| `textDecorationLine` | `none` | `underline` | `double` | `line-through` | ✅ |
| `textShadowOffset` | `{ width: number, height: number }` | ✅ |
| `textShadowRadius` | `number` | ✅ |
| `textShadowColor` | `Color` | ✅ |
| `textTransform` | `none` | `uppercase` | `lowercase` | ✅ |
| `letterSpacing` | `number` | ✅ |
| `lineHeight` | `number` | ✅ |
| `textAlign` | `auto` | `left` | `right` | `center` | `justify` | ✅ |
| `writingDirection` | `auto` | `ltr` | `rtl` | ⛔️ |
| `opacity` | `number` | ✅ |
| `percentage` | `points` | `percentages` | ✅ |
## Styles Specific To `react-sketchapp`
Some properties are Sketch specific and won't work cross-platform but give you a better control over your components.
| property | type | supported? |
| --- | --- | --- |
| `shadowSpread` | `number` | ✅ |
| `shadowInner` | `boolean` | ✅ |
## Examples
Styles can be passed to components as plain objects, or via [`StyleSheet`](/docs/API.md).
```js
import { View, StyleSheet } from 'react-sketchapp';
// inline props
<View
style={{
backgroundColor: 'hotPink',
width: 300,
}}
/>
// plain JS object
const style = {
backgroundColor: 'hotPink',
width: 300,
}
<View style={style} />
// StyleSheet
const styles = StyleSheet.create({
foo: {
backgroundColor: 'hotPink',
width: 300,
}
})
<View style={styles.foo} />
<View style={[styles.foo, styles.bar]} />
```
You can use variables in your styles just like a standard React application:
```javascript
const colors = {
Haus: '#F3F4F4',
Night: '#333',
Sur: '#96DBE4',
Peach: '#EFADA0',
Pear: '#93DAAB',
};
<View>
{Object.keys(colors).map(name => (
<View
key={name}
style={{
flex: 1,
backgroundColor: colors[name],
}}
/>
))}
</View>;
```
================================================
FILE: docs/guides/universal-rendering.md
================================================
# Universal Rendering
The `react-sketchapp` components have been architected to provide the same metaphors, layout system & interfaces as `react-native`, so there is less switching cost between platforms. However, it is also possible to render the _same code_ across multiple platforms. We call this _Universal Rendering_.
The [`react-primitives`](https://github.com/lelandrichardson/react-primitives) project provides consistent primitive interfaces across platforms, and is the simplest way to achieve Universal Rendering.
## Setup
React Primitives works out-of-the-box with `react-dom` & `react-native`, and `react-sketchapp` (when using `skpm`).
Install `react-primitives` and its peer dependencies
```bash
npm install --save react-primitives react react-dom react-native react-sketchapp
```
## Creating your components
Import base primitives from `react-primitives` rather than `react-sketchapp` / `react-native` — e.g.
```diff
/**
* components/Row.js
* Define your component using platform-independent primitives
*/
import React from 'react';
- import { View, Text, StyleSheet } from 'react-sketchapp';
+ import { View, Text, StyleSheet } from 'react-primitives';
const Row = props =>
<View>
<Text>{ props.title }</Text>
<Text>{ props.subtitle }</Text>
</View>
export default Row;
```
## Importing existing components
If you have a large existing React Native component library, you might enjoy using a `codemod` to automatically convert `react-native` imports to `react-primitives` — [a proof-of-concept `codemod` is provided on ASTExplorer](https://astexplorer.net/#/gist/68d1b3ae3ec7b0a088452a7d38643dc4/latest).
## Rendering
Each platform will require an entry point with its respective `render` / registration call - e.g:
```js
/**
* dom-entry.js
* Standard ReactDOM setup for the browser
*/
import React from 'react';
import { render } from 'react-dom';
import Row from './components/Row';
render(<Row title="Foo" subtitle="Bar" />, document.getElementById('root'));
```
```js
/**
* native-entry.js
* Standard ReactNative setup
*/
import React from 'react';
import { AppRegistry } from 'react-native';
import Row from './components/Row';
AppRegistry.registerComponent('Row', () => Row);
```
```js
/**
* sketch-entry.js
* same setup as other examples
*/
import React from 'react';
import { render } from 'react-sketchapp';
import Row from './components/Row';
export default context => {
render(<Row title="Foo" subtitle="Bar" />, context.document.currentPage());
};
```
React Primitives only provides components that make sense on every platform, so Sketch-specific concepts like `TextStyles` and `<Artboard />` should be imported from the main `react-sketchapp` package. You can mix-and-match them as necessary - e.g.
```js
/**
* sketch-entry.js
* same setup as other examples
*/
import React from 'react';
import { Artboard, render } from 'react-sketchapp';
import Row from './components/Row'; // built with react-primitives
export default context => {
render(
<Artboard>
<Row title="Foo" subtitle="Bar" />
</Artboard>,
context.document.currentPage(),
);
};
```
================================================
FILE: docs/guides/using-skpm.md
================================================
# Using `skpm` as a build system
Sketch allows arbitrary plugins written in [CocoaScript](http://developer.sketchapp.com/guides/cocoascript) to run. [`skpm`](https://github.com/skpm/skpm) is a utility to create, build and manage Sketch plugins. It takes care of transforming your JavaScript into CocoaScript and makes sure the context it is running in is as close as possible to what you are used to when writing JavaScript.
## Installation
> Important: Node.JS > V6.x is a minimum requirement.
```bash
npm install -g skpm
```
## Usage
### Creating a new plugin
```bash
skpm create my-plugin --template=airbnb/react-sketchapp
```
> A note on templates
>
> The purpose of skpm templates are to provide opinionated development tooling setups so that users can get started with actual plugin code as fast as possible.
>
> - [`airbnb/react-sketchapp`](https://github.com/airbnb/react-sketchapp) is a simple template to get started with `react-sketchapp`
>
> 💁 Tip: Any Github repo with a 'template' folder can be used as a custom template: `skpm create <project-name> --template=<username>/<repository>`
### Build the plugin
Once the installation is done, you can run some commands inside the project folder:
```bash
npm run build
```
To watch for changes:
```bash
npm run watch
```
Additionally, if you wish to run the plugin every time it is built:
```bash
npm run render
```
### View the plugin's log
To view the output of your `console.log`, you have a few different options:
- Using the [sketch-dev-tools](https://github.com/skpm/sketch-dev-tools)
- Open Console.app and look for the sketch logs
- Look at the `~/Library/Logs/com.bohemiancoding.sketch3/Plugin Output.log` file
Skpm provides a convenient way to do the latter:
```bash
skpm log
-f, -F The `-f` option causes tail to not stop when end of file is
reached, but rather to wait for additional data to be appended
to the input. [boolean] [default: "false"]
--number, -n Shows `number` lines of the logs. [number]
```
## Custom Configuration
### Babel
To customize Babel, you have two options:
- You may create a [`.babelrc`](https://babeljs.io/docs/usage/babelrc) file in your project's root directory. Any settings you define here will overwrite matching config-keys within skpm preset. For example, if you pass a "presets" object, it will replace & reset all Babel presets that skpm defaults to.
- If you'd like to modify or add to the existing Babel config, you must use a `webpack.skpm.config.js` file. Visit the [`webpack`](#webpack) section for more info.
### `webpack`
To customize `webpack` create `webpack.skpm.config.js` file which exports function that will change `webpack`'s config.
```js
/**
* Function that mutates original webpack config.
* Supports asynchronous changes when promise is returned.
*
* @param {object} config - original webpack config.
* @param {boolean} isPluginCommand - wether the config is for a plugin command or a resource
**/
module.exports = function(config, isPluginCommand) {
/** you can change config here **/
};
```
================================================
FILE: examples/.eslintrc
================================================
{
"rules": {
"import/no-unresolved": 0,
"import/extensions": 0
}
}
================================================
FILE: examples/.gitignore
================================================
**/*.sketchplugin
================================================
FILE: examples/basic-setup/README.md
================================================
# Basic setup
## How to use
Download the example or [clone the repo](http://github.com/airbnb/react-sketchapp):
```bash
curl https://codeload.github.com/airbnb/react-sketchapp/tar.gz/master | tar -xz --strip=2 react-sketchapp-master/examples/basic-setup
cd basic-setup
```
Install the dependencies
```bash
npm install
```
Then, open Sketch and navigate to `Plugins → react-sketchapp: Basic skpm Example`
Run with live reloading in Sketch, need a new sketch doc open
```bash
npm run render
```
## The idea behind the example
[`skpm`](https://github.com/skpm/skpm) is the easiest way to build `react-sketchapp` projects - this is a minimal example of it in use.

================================================
FILE: examples/basic-setup/package.json
================================================
{
"name": "basic-setup",
"version": "1.0.0",
"description": "",
"skpm": {
"main": "basic-setup.sketchplugin",
"manifest": "src/manifest.json"
},
"scripts": {
"build": "skpm-build",
"watch": "skpm-build --watch",
"render": "skpm-build --watch --run",
"render:once": "skpm-build --run",
"postinstall": "npm run build && skpm-link"
},
"author": "Jon Gold <jon.gold@airbnb.com>",
"license": "MIT",
"devDependencies": {
"@skpm/builder": "^0.7.5"
},
"dependencies": {
"chroma-js": "^1.2.2",
"prop-types": "^15.5.8",
"react": "^16.3.2",
"react-sketchapp": "^3.0.0",
"react-test-renderer": "^16.3.2"
}
}
================================================
FILE: examples/basic-setup/src/manifest.json
================================================
{
"compatibleVersion": 3,
"bundleVersion": 1,
"commands": [
{
"name": "react-sketchapp: Basic Setup",
"identifier": "main",
"script": "./my-command.js"
}
],
"menu": {
"isRoot": true,
"items": [
"main"
]
}
}
================================================
FILE: examples/basic-setup/src/my-command.js
================================================
import * as React from 'react';
import * as PropTypes from 'prop-types';
import { render, Artboard, Text, View } from 'react-sketchapp';
import chroma from 'chroma-js';
// take a hex and give us a nice text color to put over it
const textColor = (hex) => {
const vsWhite = chroma.contrast(hex, 'white');
if (vsWhite > 4) {
return '#FFF';
}
return chroma(hex).darken(3).hex();
};
const Swatch = ({ name, hex }) => (
<View
name={`Swatch ${name}`}
style={{
height: 96,
width: 96,
margin: 4,
backgroundColor: hex,
padding: 8,
}}
>
<Text
name="Swatch Name"
style={{ color: textColor(hex), fontWeight: 'bold', fontFamily: 'Helvetica' }}
>
{name}
</Text>
<Text name="Swatch Hex" style={{ color: textColor(hex) }}>
{hex}
</Text>
</View>
);
const Color = {
hex: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
};
Swatch.propTypes = Color;
const Document = ({ colors }) => (
<Artboard
name="Swatches"
style={{
flexDirection: 'row',
flexWrap: 'wrap',
width: (96 + 8) * 4,
}}
>
{Object.keys(colors).map((color) => (
<Swatch name={color} hex={colors[color]} key={color} />
))}
</Artboard>
);
Document.propTypes = {
colors: PropTypes.objectOf(PropTypes.string).isRequired,
};
export default () => {
const colorList = {
Haus: '#F3F4F4',
Night: '#333',
Sur: '#96DBE4',
'Sur Dark': '#24828F',
Peach: '#EFADA0',
'Peach Dark': '#E37059',
Pear: '#93DAAB',
'Pear Dark': '#2E854B',
};
render(<Document colors={colorList} />, context.document.currentPage());
};
================================================
FILE: examples/basic-setup/webpack.skpm.config.js
================================================
const path = require('path');
module.exports = (config) => {
if (process.env.LOCAL_DEV) {
config.resolve = {
...config.resolve,
alias: {
...config.resolve.alias,
'react-sketchapp': path.resolve(__dirname, '../../'),
},
};
}
};
================================================
FILE: examples/basic-setup-typescript/.gitignore
================================================
node_modules
.ts-compiled
================================================
FILE: examples/basic-setup-typescript/README.md
================================================
# Basic setup with Typescript
This example was adapted from the [basic-setup example](../basic-setup).
> **NOTE:** you may also use the typings _without_ using typescript if you editor supports it. [See here](../../docs/guides/community-provided-tooling.md).
## How to use
Download the example or [clone the repo](http://github.com/airbnb/react-sketchapp):
```bash
curl https://codeload.github.com/airbnb/react-sketchapp/tar.gz/master | tar -xz --strip=2 react-sketchapp-master/examples/basic-setup-typescript
cd basic-setup-typescript
```
Install the dependencies
```bash
npm install
```
Run with live reloading in Sketch, need a new sketch doc open. This will put both skpm and the Typescript compiler in watch mode:
```bash
npm run render
```
To clean the `.ts-compiled` directory, you can run:
```bash
npm run typescript:clean
```
## How the typescript works
This example compiles the typescript into javascript that can be used by `skpm`. The compiled typescript files get output into the `.ts-compiled` directory. The `manifest.json` of `skpm` then simply points to the compiled javascript. To get live re-loading working, use the typescript compiler in watch mode. Whenever you save a typescript file, the typescript compiler will output javascript to the `.ts-compiled` directory. Once `skpm` notices the javascript file in `.ts-compiled` changes, it will re-build and re-render.
Here is a reference `tsconfig.json`:
```json
{
"compilerOptions": {
"target": "es2015",
"module": "es2015",
"jsx": "react-native",
"allowJs": true,
"strict": true,
"outDir": "./.ts-compiled",
"rootDir": "./src",
"allowSyntheticDefaultImports": true,
"moduleResolution": "node"
},
"include": [
"./src/**/*"
]
}
```
================================================
FILE: examples/basic-setup-typescript/manifest.json
================================================
{
"compatibleVersion": 3,
"bundleVersion": 1,
"commands": [
{
"name": "react-sketchapp: Basic Setup with Typescript",
"identifier": "main",
"script": "./.ts-compiled/my-command.js"
}
],
"menu": {
"isRoot": true,
"items": [
"main"
]
}
}
================================================
FILE: examples/basic-setup-typescript/package.json
================================================
{
"name": "basic-setup-typescript",
"version": "1.0.0",
"description": "",
"skpm": {
"main": "basic-setup.sketchplugin",
"manifest": "./manifest.json"
},
"scripts": {
"build": "npm run typescript:once && skpm-build",
"watch": "skpm-build --watch & npm run typescript",
"render": "skpm-build --watch --run & npm run typescript",
"render:once": "npm run typescript:once && skpm-build --run",
"postinstall": "npm run build && skpm-link",
"typescript": "tsc --watch",
"typescript:once": "tsc",
"typescript:clean": "rm -rf ./.ts-compiled"
},
"author": "Jon Gold <jon.gold@airbnb.com>",
"license": "MIT",
"devDependencies": {
"@skpm/builder": "^0.4.0",
"@types/chroma-js": "^1.3.3",
"typescript": "^3.7.2"
},
"dependencies": {
"chroma-js": "^1.2.2",
"prop-types": "^15.5.8",
"react": "^16.3.2",
"react-sketchapp": "^3.0.0",
"react-test-renderer": "^16.3.2"
}
}
================================================
FILE: examples/basic-setup-typescript/src/my-command.tsx
================================================
import * as React from 'react';
import sketch from 'sketch';
import { render, Artboard, Text, View } from 'react-sketchapp';
import chroma from 'chroma-js';
// take a hex and give us a nice text color to put over it
const textColor = (hex: string) => {
const vsWhite = chroma.contrast(hex, 'white');
if (vsWhite > 4) {
return '#FFF';
}
return chroma(hex).darken(3).hex();
};
interface SwatchProps {
name: string;
hex: string;
}
const Swatch = ({ name, hex }: SwatchProps) => (
<View
name={`Swatch ${name}`}
style={{
height: 96,
width: 96,
margin: 4,
backgroundColor: hex,
padding: 8,
}}
>
<Text name="Swatch Name" style={{ color: textColor(hex), fontWeight: 'bold' }}>
{name}
</Text>
<Text name="Swatch Hex" style={{ color: textColor(hex) }}>
{hex}
</Text>
</View>
);
interface DocumentProps {
colors: { [key: string]: string };
}
const Document = ({ colors }: DocumentProps) => (
<Artboard
name="Swatches"
style={{
flexDirection: 'row',
flexWrap: 'wrap',
width: (96 + 8) * 4,
}}
>
{Object.keys(colors).map((color) => (
<Swatch name={color} hex={colors[color]} key={color} />
))}
</Artboard>
);
export default () => {
const colorList = {
Haus: '#F3F4F4',
Night: '#333',
Sur: '#96DBE4',
'Sur Dark': '#24828F',
Peach: '#EFADA0',
'Peach Dark': '#E37059',
Pear: '#93DAAB',
'Pear Dark': '#2E854B',
'TypeScript Blue': '#007ACC',
};
render(<Document colors={colorList} />, sketch.getSelectedDocument().selectedPage);
};
================================================
FILE: examples/basic-setup-typescript/src/types/sketch.d.ts
================================================
declare module 'sketch';
================================================
FILE: examples/basic-setup-typescript/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es2015",
"module": "es2015",
"jsx": "react-native",
"allowJs": true,
"strict": true,
"outDir": "./.ts-compiled",
"rootDir": "./src",
"allowSyntheticDefaultImports": true,
"moduleResolution": "node"
},
"include": [
"./src/**/*"
]
}
================================================
FILE: examples/basic-setup-typescript/webpack.skpm.config.js
================================================
const path = require('path');
module.exports = (config) => {
if (process.env.LOCAL_DEV) {
config.resolve = {
...config.resolve,
alias: {
...config.resolve.alias,
'react-sketchapp': path.resolve(__dirname, '../../'),
},
};
}
};
================================================
FILE: examples/basic-svg/README.md
================================================
# Basic SVG example
## How to use
Download the example or [clone the repo](http://github.com/airbnb/react-sketchapp):
```bash
curl https://codeload.github.com/airbnb/react-sketchapp/tar.gz/master | tar -xz --strip=2 react-sketchapp-master/examples/basic-svg
cd basic-svg
```
Install the dependencies
```bash
npm install
```
Run with live reloading in Sketch, need a new sketch doc open
```bash
npm run render
```
Or, to install as a Sketch plugin:
```bash
npm run build
npm run link-plugin
```
Then, open Sketch and navigate to `Plugins → react-sketchapp: Basic SVG Example`
## The idea behind the example
[`skpm`](https://github.com/sketch-pm/skpm) is the easiest way to build `react-sketchapp` projects - this is a minimal example of it in use.

================================================
FILE: examples/basic-svg/package.json
================================================
{
"name": "basic-svg",
"version": "1.0.0",
"description": "",
"skpm": {
"main": "basic-svg.sketchplugin",
"manifest": "src/manifest.json"
},
"scripts": {
"build": "skpm-build",
"watch": "skpm-build --watch",
"render": "skpm-build --watch --run",
"render:once": "skpm-build --run",
"postinstall": "npm run build && skpm-link"
},
"author": "Jon Gold <jon.gold@airbnb.com>",
"license": "MIT",
"devDependencies": {
"@skpm/builder": "^0.7.5"
},
"dependencies": {
"prop-types": "^15.5.8",
"react": "^16.3.2",
"react-sketchapp": "^3.0.0",
"react-test-renderer": "^16.3.2"
}
}
================================================
FILE: examples/basic-svg/src/manifest.json
================================================
{
"compatibleVersion": 3,
"bundleVersion": 1,
"commands": [
{
"name": "react-sketchapp: Basic SVG",
"identifier": "main",
"script": "./my-command.js"
}
],
"menu": {
"isRoot": true,
"items": [
"main"
]
}
}
================================================
FILE: examples/basic-svg/src/my-command.js
================================================
import * as React from 'react';
import { render, Artboard, Svg } from 'react-sketchapp';
const Document = () => (
<Artboard
name="Sketch Logo"
style={{
width: 494,
height: 447,
}}
>
<Svg xmlns="http://www.w3.org/2000/svg" width="494" height="447" viewBox="0 0 494 447">
<Svg.G fill="none" fillRule="evenodd">
<Svg.Path fill="#FFAE00" d="M247 447L0 160 107 15 247 0l140 15 107 145" />
<Svg.Path fill="#EC6C00" d="M247 447L0 160h494" />
<Svg.Path fill="#FFAE00" d="M247 447L100 160h294" />
<Svg.Path fill="#FFEFB4" d="M247 0L100 160h294" />
<Svg.Path fill="#FFAE00" d="M107 15L52 88 0 160h101M387 15l55 73 52 72H393" />
<Svg.Path fill="#FED305" d="M107 15l-7 145L247 0m140 15l7 145L247 0" />
</Svg.G>
</Svg>
</Artboard>
);
export default () => {
render(<Document />, context.document.currentPage());
};
============================================
gitextract_dx66tsc9/ ├── .bookignore ├── .editorconfig ├── .github/ │ ├── CODE_OF_CONDUCT.md │ ├── CONTRIBUTING.md │ └── ISSUE_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __tests__/ │ ├── jest/ │ │ ├── components/ │ │ │ ├── Artboard.tsx │ │ │ ├── Document.tsx │ │ │ ├── Image.tsx │ │ │ ├── Page.tsx │ │ │ ├── RedBox.tsx │ │ │ ├── Svg.tsx │ │ │ ├── Text.tsx │ │ │ ├── View.tsx │ │ │ ├── __snapshots__/ │ │ │ │ ├── Artboard.tsx.snap │ │ │ │ ├── Document.tsx.snap │ │ │ │ ├── Image.tsx.snap │ │ │ │ ├── Page.tsx.snap │ │ │ │ ├── RedBox.tsx.snap │ │ │ │ ├── Svg.tsx.snap │ │ │ │ ├── Text.tsx.snap │ │ │ │ └── View.tsx.snap │ │ │ └── nodeImpl/ │ │ │ ├── Svg.tsx │ │ │ └── __snapshots__/ │ │ │ └── Svg.tsx.snap │ │ ├── index.ts │ │ ├── jsonUtils/ │ │ │ ├── computeTextTree.ts │ │ │ ├── computeYogaNode.ts │ │ │ ├── computeYogaTree.ts │ │ │ ├── layerGroup.ts │ │ │ ├── models.ts │ │ │ ├── shapeLayers.ts │ │ │ └── style.ts │ │ ├── reactTreeToFlexTree.ts │ │ ├── sharedStyles/ │ │ │ └── TextStyles.ts │ │ └── utils/ │ │ ├── isDefined.ts │ │ ├── sortObjectKeys.ts │ │ └── zIndex.ts │ └── skpm/ │ ├── basic.test.js │ ├── render-context.test.js │ └── render-in-wrapped-object.test.js ├── book.json ├── docs/ │ ├── API.md │ ├── FAQ.md │ ├── README.md │ ├── examples.md │ └── guides/ │ ├── README.md │ ├── data-fetching.md │ ├── getting-started.md │ ├── rendering.md │ ├── styling.md │ ├── universal-rendering.md │ └── using-skpm.md ├── examples/ │ ├── .eslintrc │ ├── .gitignore │ ├── basic-setup/ │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── manifest.json │ │ │ └── my-command.js │ │ └── webpack.skpm.config.js │ ├── basic-setup-typescript/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── manifest.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── my-command.tsx │ │ │ └── types/ │ │ │ └── sketch.d.ts │ │ ├── tsconfig.json │ │ └── webpack.skpm.config.js │ ├── basic-svg/ │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── manifest.json │ │ │ └── my-command.js │ │ └── webpack.skpm.config.js │ ├── colors/ │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── main.js │ │ │ └── manifest.json │ │ └── webpack.skpm.config.js │ ├── emotion/ │ │ ├── package.json │ │ ├── src/ │ │ │ ├── manifest.json │ │ │ └── my-command.js │ │ └── webpack.skpm.config.js │ ├── form-validation/ │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── Button.js │ │ │ │ ├── Register.js │ │ │ │ ├── Space.js │ │ │ │ ├── StrengthMeter.js │ │ │ │ └── TextBox/ │ │ │ │ ├── index.js │ │ │ │ ├── index.sketch.js │ │ │ │ └── style.js │ │ │ ├── data.js │ │ │ ├── designSystem.js │ │ │ ├── main.js │ │ │ ├── manifest.json │ │ │ └── web.js │ │ └── webpack.skpm.config.js │ ├── foursquare-maps/ │ │ ├── .eslintrc │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── getVenues.js │ │ │ ├── main.js │ │ │ ├── manifest.json │ │ │ └── web.js │ │ └── webpack.skpm.config.js │ ├── glamorous/ │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── manifest.json │ │ │ └── my-command.js │ │ └── webpack.skpm.config.js │ ├── profile-cards/ │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── Profile.js │ │ │ │ └── Space.js │ │ │ ├── designSystem.js │ │ │ ├── main.js │ │ │ └── manifest.json │ │ └── webpack.skpm.config.js │ ├── profile-cards-graphql/ │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── Profile.js │ │ │ │ └── Space.js │ │ │ ├── designSystem.js │ │ │ ├── main.js │ │ │ └── manifest.json │ │ └── webpack.skpm.config.js │ ├── profile-cards-primitives/ │ │ ├── README.md │ │ ├── nwb.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── Profile.js │ │ │ │ └── Space.js │ │ │ ├── data.js │ │ │ ├── designSystem.js │ │ │ ├── main.js │ │ │ ├── manifest.json │ │ │ └── web.js │ │ └── webpack.skpm.config.js │ ├── profile-cards-react-with-styles/ │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ └── Profile.js │ │ │ ├── main.js │ │ │ ├── manifest.json │ │ │ ├── theme.js │ │ │ ├── types.js │ │ │ └── withStyles.js │ │ └── webpack.skpm.config.js │ ├── react-router-prototyping/ │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── components/ │ │ │ │ ├── AppBar.js │ │ │ │ └── NavBar.js │ │ │ ├── main.js │ │ │ ├── manifest.json │ │ │ └── routes/ │ │ │ ├── about.js │ │ │ ├── home.js │ │ │ ├── post.js │ │ │ └── profile.js │ │ └── webpack.skpm.config.js │ ├── styled-components/ │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── manifest.json │ │ │ └── my-command.js │ │ └── webpack.skpm.config.js │ ├── styleguide/ │ │ ├── .flowconfig │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── AccessibilityBadge.js │ │ │ │ ├── Badge.js │ │ │ │ ├── Label.js │ │ │ │ ├── Palette.js │ │ │ │ ├── Section.js │ │ │ │ ├── Swatch.js │ │ │ │ └── TypeSpecimen.js │ │ │ ├── designSystem.js │ │ │ ├── main.js │ │ │ ├── manifest.json │ │ │ └── processColor.js │ │ └── webpack.skpm.config.js │ ├── symbols/ │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── manifest.json │ │ │ └── my-command.js │ │ └── webpack.skpm.config.js │ └── timeline-airtable/ │ ├── .eslintrc │ ├── README.md │ ├── package.json │ ├── src/ │ │ ├── main.js │ │ └── manifest.json │ └── webpack.skpm.config.js ├── jest.config.js ├── package.json ├── prettier.config.js ├── src/ │ ├── Platform.ts │ ├── buildTree.ts │ ├── components/ │ │ ├── Artboard.tsx │ │ ├── Document.tsx │ │ ├── Image.tsx │ │ ├── ImageStylePropTypes.ts │ │ ├── Page.tsx │ │ ├── PageStylePropTypes.ts │ │ ├── RedBox.tsx │ │ ├── ResizeModePropTypes.ts │ │ ├── ResizingConstraintPropTypes.ts │ │ ├── ShadowsPropTypes.ts │ │ ├── Svg/ │ │ │ ├── Circle.tsx │ │ │ ├── ClipPath.tsx │ │ │ ├── Defs.tsx │ │ │ ├── Ellipse.tsx │ │ │ ├── G.tsx │ │ │ ├── Image.tsx │ │ │ ├── Line.tsx │ │ │ ├── LinearGradient.tsx │ │ │ ├── Path.tsx │ │ │ ├── Pattern.tsx │ │ │ ├── Polygon.tsx │ │ │ ├── Polyline.tsx │ │ │ ├── RadialGradient.tsx │ │ │ ├── Rect.tsx │ │ │ ├── Stop.tsx │ │ │ ├── Svg.tsx │ │ │ ├── Symbol.tsx │ │ │ ├── TSpan.tsx │ │ │ ├── Text.tsx │ │ │ ├── TextPath.tsx │ │ │ ├── Use.tsx │ │ │ ├── index.tsx │ │ │ └── props.ts │ │ ├── Text.tsx │ │ ├── TextStylePropTypes.ts │ │ ├── View.tsx │ │ ├── ViewStylePropTypes.ts │ │ └── index.ts │ ├── context.tsx │ ├── entrypoint.sketch.ts │ ├── entrypoint.ts │ ├── flexToSketchJSON.ts │ ├── index.ts │ ├── jsonUtils/ │ │ ├── borders.ts │ │ ├── computeTextTree.ts │ │ ├── computeYogaNode.ts │ │ ├── computeYogaTree.ts │ │ ├── hotspotLayer.ts │ │ ├── layerGroup.ts │ │ ├── makeSvgLayer/ │ │ │ ├── graphics/ │ │ │ │ ├── curvePoint.ts │ │ │ │ ├── path.ts │ │ │ │ ├── point.ts │ │ │ │ ├── rect.ts │ │ │ │ └── types.ts │ │ │ ├── index.sketch.ts │ │ │ └── index.ts │ │ ├── models.ts │ │ ├── resizeConstraint.ts │ │ ├── shapeLayers.ts │ │ ├── sketchJson/ │ │ │ ├── fromSJSON.ts │ │ │ └── toSJSON.ts │ │ ├── style.ts │ │ └── textLayers.ts │ ├── platformBridges/ │ │ ├── macos.ts │ │ └── sketch/ │ │ ├── createStringMeasurer.ts │ │ ├── findFontName.ts │ │ ├── index.ts │ │ └── makeImageDataFromUrl.ts │ ├── render.tsx │ ├── renderToJSON.ts │ ├── renderers/ │ │ ├── ArtboardRenderer.ts │ │ ├── ImageRenderer.ts │ │ ├── SketchRenderer.ts │ │ ├── SvgRenderer.ts │ │ ├── SymbolInstanceRenderer.ts │ │ ├── SymbolMasterRenderer.ts │ │ ├── TextRenderer.ts │ │ ├── ViewRenderer.ts │ │ └── index.ts │ ├── resets.ts │ ├── sharedStyles/ │ │ └── TextStyles.ts │ ├── stylesheet/ │ │ ├── expandStyle.ts │ │ ├── index.ts │ │ └── types.ts │ ├── symbol.tsx │ ├── types/ │ │ ├── globals.d.ts │ │ ├── index.ts │ │ ├── intrinsic.d.ts │ │ ├── js-sha1.d.ts │ │ ├── murmur2js.d.ts │ │ ├── node-sketch-bridge.d.ts │ │ └── normalize-css-color.d.ts │ └── utils/ │ ├── Context.ts │ ├── constants.ts │ ├── createStringMeasurer.ts │ ├── getDocument.ts │ ├── getImageDataFromURL.ts │ ├── getSketchVersion.ts │ ├── hasAnyDefined.ts │ ├── hashStyle.ts │ ├── isDefined.ts │ ├── isNativeDocument.ts │ ├── isNativePage.ts │ ├── isNativeSymbolsPage.ts │ ├── pick.ts │ ├── processTransform/ │ │ ├── index.ts │ │ ├── matrix2D.ts │ │ ├── parseTransformOriginProp.ts │ │ └── parseTransformProp.ts │ ├── same.ts │ ├── sharedTextStyles/ │ │ ├── index.sketch.ts │ │ └── index.ts │ ├── sortObjectKeys.ts │ └── zIndex.ts ├── template/ │ ├── .gitignore │ ├── README.md │ ├── package.json │ └── src/ │ ├── manifest.json │ └── my-command.js ├── tsconfig.json └── tsconfig.module.json
SYMBOL INDEX (290 symbols across 88 files)
FILE: __tests__/jest/components/nodeImpl/Svg.tsx
class SVGElement (line 13) | class SVGElement extends React.Component {
method render (line 14) | render() {
FILE: __tests__/jest/jsonUtils/models.ts
constant BLACK (line 22) | const BLACK = {
constant WHITE (line 29) | const WHITE = {
constant GOLD (line 36) | const GOLD = {
constant PURPLE (line 43) | const PURPLE = {
FILE: __tests__/skpm/basic.test.js
function getDoc (line 8) | function getDoc(context, document) {
FILE: __tests__/skpm/render-context.test.js
function getDoc (line 8) | function getDoc(document) {
FILE: __tests__/skpm/render-in-wrapped-object.test.js
function getDoc (line 8) | function getDoc(document) {
FILE: examples/basic-setup-typescript/src/my-command.tsx
type SwatchProps (line 15) | interface SwatchProps {
type DocumentProps (line 40) | interface DocumentProps {
FILE: examples/emotion/src/my-command.js
class App (line 24) | class App extends React.Component {
method render (line 25) | render() {
FILE: examples/form-validation/src/components/TextBox/index.js
class TextBox (line 4) | class TextBox extends Component {
method constructor (line 5) | constructor(props) {
method handleChange (line 15) | handleChange(event) {
method render (line 19) | render() {
FILE: examples/glamorous/src/my-command.js
class App (line 22) | class App extends React.Component {
method render (line 23) | render() {
FILE: examples/profile-cards-graphql/src/main.js
constant GRAPHQL_ENDPOINT (line 12) | const GRAPHQL_ENDPOINT = 'https://api.graph.cool/simple/v1/cj09zm1k4jcpc...
constant QUERY (line 20) | const QUERY = gql`
FILE: examples/profile-cards-react-with-styles/src/withStyles.js
method create (line 8) | create(styleHash) {
method resolve (line 12) | resolve(styles) {
FILE: examples/styleguide/src/components/Palette.js
constant SWATCH_WIDTH (line 5) | const SWATCH_WIDTH = 100;
FILE: examples/styleguide/src/components/Swatch.js
constant SWATCH_WIDTH (line 6) | const SWATCH_WIDTH = 100;
FILE: examples/timeline-airtable/src/main.js
constant API_ENDPOINT_URL (line 5) | const API_ENDPOINT_URL =
FILE: src/components/Artboard.tsx
type Props (line 24) | type Props = PropTypes.InferProps<typeof ArtboardPropTypes>;
class Artboard (line 26) | class Artboard extends React.Component<Props> {
method render (line 33) | render() {
FILE: src/components/Document.tsx
type Props (line 8) | type Props = PropTypes.InferProps<typeof DocumentPropTypes>;
class Document (line 10) | class Document extends React.Component<Props> {
method render (line 15) | render() {
FILE: src/components/Image.tsx
type Props (line 45) | type Props = PropTypes.InferProps<typeof ImagePropTypes>;
class Image (line 47) | class Image extends React.Component<Props> {
method render (line 54) | render() {
FILE: src/components/Page.tsx
type Props (line 13) | type Props = PropTypes.InferProps<typeof PagePropTypes>;
class Page (line 15) | class Page extends React.Component<Props> {
method render (line 18) | render() {
FILE: src/components/RedBox.tsx
type StackFrame (line 7) | type StackFrame = {
type Props (line 49) | type Props = PropTypes.InferProps<typeof ErrorBoxPropTypes>;
class RedBox (line 51) | class RedBox extends React.Component<Props> {
method renderFrames (line 59) | renderFrames(frames: Array<StackFrame>) {
method render (line 67) | render() {
FILE: src/components/Svg/Circle.tsx
type Props (line 12) | type Props = PropTypes.InferProps<typeof propTypes>;
class Circle (line 14) | class Circle extends React.Component<Props> {
method render (line 23) | render() {
FILE: src/components/Svg/ClipPath.tsx
type Props (line 9) | type Props = PropTypes.InferProps<typeof propTypes>;
class ClipPath (line 11) | class ClipPath extends React.Component<Props> {
method render (line 14) | render() {
FILE: src/components/Svg/Defs.tsx
type Props (line 8) | type Props = PropTypes.InferProps<typeof propTypes>;
class Defs (line 10) | class Defs extends React.Component<Props> {
method render (line 13) | render() {
FILE: src/components/Svg/Ellipse.tsx
type Props (line 13) | type Props = PropTypes.InferProps<typeof propTypes>;
class Ellipse (line 15) | class Ellipse extends React.Component<Props> {
method render (line 25) | render() {
FILE: src/components/Svg/G.tsx
type Props (line 10) | type Props = PropTypes.InferProps<typeof propTypes>;
class G (line 12) | class G extends React.Component<Props> {
method render (line 15) | render() {
FILE: src/components/Svg/Image.tsx
type Props (line 16) | type Props = PropTypes.InferProps<typeof propTypes>;
class SVGImage (line 18) | class SVGImage extends React.Component<Props> {
method render (line 29) | render() {
FILE: src/components/Svg/Line.tsx
type Props (line 13) | type Props = PropTypes.InferProps<typeof propTypes>;
class Line (line 15) | class Line extends React.Component<Props> {
method render (line 25) | render() {
FILE: src/components/Svg/LinearGradient.tsx
type Props (line 15) | type Props = PropTypes.InferProps<typeof propTypes>;
class LinearGradient (line 17) | class LinearGradient extends React.Component<Props> {
method render (line 27) | render() {
FILE: src/components/Svg/Path.tsx
type Props (line 10) | type Props = PropTypes.InferProps<typeof propTypes>;
class Path (line 12) | class Path extends React.Component<Props> {
method render (line 15) | render() {
FILE: src/components/Svg/Pattern.tsx
type Props (line 16) | type Props = PropTypes.InferProps<typeof propTypes>;
class Pattern (line 18) | class Pattern extends React.Component<Props> {
method render (line 21) | render() {
FILE: src/components/Svg/Polygon.tsx
type Props (line 10) | type Props = PropTypes.InferProps<typeof propTypes>;
class Polygon (line 12) | class Polygon extends React.Component<Props> {
method render (line 21) | render() {
FILE: src/components/Svg/Polyline.tsx
type Props (line 10) | type Props = PropTypes.InferProps<typeof propTypes>;
class Polyline (line 12) | class Polyline extends React.Component<Props> {
method render (line 19) | render() {
FILE: src/components/Svg/RadialGradient.tsx
type Props (line 18) | type Props = PropTypes.InferProps<typeof propTypes>;
class RadialGradient (line 20) | class RadialGradient extends React.Component<Props> {
method render (line 31) | render() {
FILE: src/components/Svg/Rect.tsx
type Props (line 15) | type Props = PropTypes.InferProps<typeof propTypes>;
class Rect (line 17) | class Rect extends React.Component<Props> {
method render (line 29) | render() {
FILE: src/components/Svg/Stop.tsx
type Props (line 11) | type Props = PropTypes.InferProps<typeof propTypes>;
class Stop (line 13) | class Stop extends React.Component<Props> {
method render (line 21) | render() {
FILE: src/components/Svg/Svg.tsx
type Props (line 38) | type Props = PropTypes.InferProps<typeof propTypes>;
class Svg (line 40) | class Svg extends React.Component<Props> {
method render (line 89) | render() {
FILE: src/components/Svg/Symbol.tsx
type Props (line 11) | type Props = PropTypes.InferProps<typeof propTypes>;
class Symbol (line 13) | class Symbol extends React.Component<Props> {
method render (line 16) | render() {
FILE: src/components/Svg/TSpan.tsx
type Props (line 5) | type Props = PropTypes.InferProps<typeof textProps>;
class TSpan (line 7) | class TSpan extends React.Component<Props> {
method getChildContext (line 14) | getChildContext() {
method getContextTypes (line 20) | getContextTypes() {
method render (line 26) | render() {
FILE: src/components/Svg/Text.tsx
type Props (line 5) | type Props = PropTypes.InferProps<typeof textProps>;
class Text (line 7) | class Text extends React.Component<Props> {
method getChildContext (line 14) | getChildContext() {
method getContextTypes (line 20) | getContextTypes() {
method render (line 26) | render() {
FILE: src/components/Svg/TextPath.tsx
type Props (line 5) | type Props = PropTypes.InferProps<typeof textPathProps>;
class TextPath (line 9) | class TextPath extends React.Component<Props> {
method render (line 12) | render() {
FILE: src/components/Svg/Use.tsx
type Props (line 12) | type Props = PropTypes.InferProps<typeof propTypes>;
class Use (line 16) | class Use extends React.Component<Props> {
method render (line 19) | render() {
FILE: src/components/Text.tsx
type Props (line 13) | type Props = PropTypes.InferProps<typeof TextPropTypes>;
class Text (line 21) | class Text extends React.Component<Props> {
method render (line 24) | render() {
FILE: src/components/View.tsx
type Props (line 29) | type Props = PropTypes.InferProps<typeof ViewPropTypes>;
class View (line 31) | class View extends React.Component<Props> {
method render (line 38) | render() {
FILE: src/context.tsx
type ArtboardProps (line 35) | type ArtboardProps = PropTypes.InferProps<typeof ArtboardPropTypes> & {
FILE: src/entrypoint.sketch.ts
function render (line 15) | function render(
function renderToJSON (line 23) | function renderToJSON(
function makeSymbol (line 30) | function makeSymbol(
FILE: src/entrypoint.ts
function getDefaultPlatformBridge (line 16) | function getDefaultPlatformBridge() {
function render (line 20) | function render(
function renderToJSON (line 28) | function renderToJSON(
function makeSymbol (line 35) | function makeSymbol(
FILE: src/flexToSketchJSON.ts
function missingRendererError (line 6) | function missingRendererError(type: string, annotations?: string) {
FILE: src/jsonUtils/borders.ts
constant DEFAULT_BORDER_COLOR (line 8) | const DEFAULT_BORDER_COLOR = 'transparent';
constant DEFAULT_BORDER_STYLE (line 9) | const DEFAULT_BORDER_STYLE = 'solid';
FILE: src/jsonUtils/makeSvgLayer/graphics/curvePoint.ts
function describePoint (line 4) | function describePoint(point: Point): string {
function makeCurvePoint (line 9) | function makeCurvePoint(
FILE: src/jsonUtils/makeSvgLayer/graphics/path.ts
type Path (line 6) | type Path = Pick<FileFormat.ShapePath, 'isClosed' | 'points'>;
function makePath (line 8) | function makePath(curvePoints: FileFormat.CurvePoint[], isClosed: boolea...
function makePathsFromCommands (line 20) | function makePathsFromCommands(
function makeLineCapStyle (line 102) | function makeLineCapStyle(strokeLineCap: 'butt' | 'round' | 'square'): 0...
FILE: src/jsonUtils/makeSvgLayer/graphics/point.ts
function normalizePointInRect (line 4) | function normalizePointInRect(point: Point, rect: FileFormat.Rect): Point {
FILE: src/jsonUtils/makeSvgLayer/graphics/rect.ts
function makeBoundingRectFromPoints (line 5) | function makeBoundingRectFromPoints(points: Point[]): FileFormat.Rect {
function makeBoundingRectFromCommands (line 14) | function makeBoundingRectFromCommands(commands: any): FileFormat.Rect {
function unionRects (line 38) | function unionRects(...rects: FileFormat.Rect[]): FileFormat.Rect {
function scaleRect (line 55) | function scaleRect(rect: FileFormat.Rect, scale: number) {
function resize (line 61) | function resize(
FILE: src/jsonUtils/makeSvgLayer/graphics/types.ts
type Point (line 1) | type Point = { x: number; y: number };
type Size (line 2) | type Size = { width: number; height: number };
FILE: src/jsonUtils/makeSvgLayer/index.sketch.ts
function makeSvgLayer (line 6) | function makeSvgLayer(_layout: LayoutInfo, name: string, svg: string): F...
FILE: src/jsonUtils/makeSvgLayer/index.ts
function makeLayerFromPathElement (line 11) | function makeLayerFromPathElement(pathElement: any, _parentFrame: FileFo...
function makeLayerGroup (line 55) | function makeLayerGroup(
function makeSvgLayer (line 81) | function makeSvgLayer(layout: LayoutInfo, name: string, svg: string) {
FILE: src/jsonUtils/models.ts
function e7 (line 12) | function e7(seed?: string) {
function generateIdNumber (line 31) | function generateIdNumber() {
function generateID (line 47) | function generateID(seed?: string, hardcoded?: boolean): string {
FILE: src/jsonUtils/resizeConstraint.ts
constant RESIZE_CONSTRAINTS (line 15) | const RESIZE_CONSTRAINTS: { [key: string]: number } = {
function makeResizeConstraint (line 67) | function makeResizeConstraint(resizingConstraint?: ResizeConstraints | n...
FILE: src/jsonUtils/shapeLayers.ts
type Radii (line 7) | type Radii = (number | null)[];
FILE: src/jsonUtils/sketchJson/fromSJSON.ts
constant SKETCH_HIGHEST_COMPATIBLE_VERSION (line 8) | const SKETCH_HIGHEST_COMPATIBLE_VERSION = '95';
function fromSJSON (line 13) | function fromSJSON(
FILE: src/jsonUtils/sketchJson/toSJSON.ts
function toSJSON (line 4) | function toSJSON(
FILE: src/jsonUtils/style.ts
constant DEFAULT_SHADOW_COLOR (line 7) | const DEFAULT_SHADOW_COLOR = '#000';
constant SHADOW_STYLES (line 9) | const SHADOW_STYLES = [
function parseStyle (line 188) | function parseStyle(json: FileFormat.Style): ViewStyle {
FILE: src/jsonUtils/textLayers.ts
constant TEXT_DECORATION_UNDERLINE (line 7) | const TEXT_DECORATION_UNDERLINE: { [key: string]: number } = {
constant TEXT_ALIGN (line 14) | const TEXT_ALIGN: { [key: string]: number } = {
constant TEXT_ALIGN_REVERSE (line 22) | const TEXT_ALIGN_REVERSE: { [key: number]: 'center' | 'auto' | 'left' | ...
constant TEXT_DECORATION_LINETHROUGH (line 29) | const TEXT_DECORATION_LINETHROUGH: { [key: string]: number } = {
constant TEXT_TRANSFORM (line 36) | const TEXT_TRANSFORM: { [key: string]: number } = {
constant FONT_STYLES (line 49) | const FONT_STYLES: { [key: string]: boolean } = {
FILE: src/platformBridges/sketch/createStringMeasurer.ts
constant FLOAT_MAX (line 12) | const FLOAT_MAX = 999999;
function makeParagraphStyle (line 14) | function makeParagraphStyle(textStyle: TextStyle) {
function createStringAttributes (line 35) | function createStringAttributes(textStyles: TextStyle): Object {
type NSAttributedString (line 64) | type NSAttributedString = any;
function createAttributedString (line 66) | function createAttributedString(textNode: TextNode): NSAttributedString {
function createStringMeasurer (line 74) | function createStringMeasurer(textNodes: TextNode[], width: number): Size {
FILE: src/platformBridges/sketch/findFontName.ts
constant FONT_WEIGHTS (line 9) | const FONT_WEIGHTS: { [key: string]: number } = {
type NSFont (line 33) | type NSFont = any;
function findFontName (line 193) | function findFontName(style: TextStyle): string {
FILE: src/platformBridges/sketch/makeImageDataFromUrl.ts
function makeImageDataFromUrl (line 1) | function makeImageDataFromUrl(url?: string) {
FILE: src/renderers/ArtboardRenderer.ts
class ArtboardRenderer (line 8) | class ArtboardRenderer extends SketchRenderer {
method renderGroupLayer (line 9) | renderGroupLayer({ layout, style, props }: TreeNode<Props>): FileForma...
FILE: src/renderers/ImageRenderer.ts
function extractURLFromSource (line 11) | function extractURLFromSource(source?: string | { uri?: string } | null)...
class ImageRenderer (line 18) | class ImageRenderer extends SketchRenderer {
method renderBackingLayers (line 19) | renderBackingLayers({
FILE: src/renderers/SketchRenderer.ts
constant DEFAULT_OPACITY (line 7) | const DEFAULT_OPACITY = 1.0;
class SketchRenderer (line 9) | class SketchRenderer {
method constructor (line 12) | constructor(bridge: PlatformBridge) {
method getDefaultGroupName (line 16) | getDefaultGroupName(_props: any) {
method renderGroupLayer (line 20) | renderGroupLayer({
method renderBackingLayers (line 52) | renderBackingLayers(
FILE: src/renderers/SvgRenderer.ts
function toSnakeCase (line 18) | function toSnakeCase(string: string) {
function makeSvgString (line 28) | function makeSvgString(el: string | TreeNode<Props>) {
class SvgRenderer (line 64) | class SvgRenderer extends ViewRenderer {
method getDefaultGroupName (line 65) | getDefaultGroupName(props: Props) {
method renderBackingLayers (line 69) | renderBackingLayers(node: TreeNode<Props>) {
FILE: src/renderers/SymbolInstanceRenderer.ts
type Override (line 13) | type Override = {
class SymbolInstanceRenderer (line 100) | class SymbolInstanceRenderer extends SketchRenderer {
method renderGroupLayer (line 101) | renderGroupLayer({
FILE: src/renderers/SymbolMasterRenderer.ts
class SymbolMasterRenderer (line 6) | class SymbolMasterRenderer extends SketchRenderer {
method renderGroupLayer (line 7) | renderGroupLayer({
FILE: src/renderers/TextRenderer.ts
class TextRenderer (line 8) | class TextRenderer extends SketchRenderer {
method getDefaultGroupName (line 9) | getDefaultGroupName(props: Props) {
method renderBackingLayers (line 13) | renderBackingLayers({ layout, style, textStyle, props }: TreeNode<Prop...
FILE: src/renderers/ViewRenderer.ts
constant VISIBLE_STYLES (line 10) | const VISIBLE_STYLES = [
constant OVERFLOW_STYLES (line 34) | const OVERFLOW_STYLES = ['overflow', 'overflowX', 'overflowY'];
class ViewRenderer (line 36) | class ViewRenderer extends SketchRenderer {
method getDefaultGroupName (line 37) | getDefaultGroupName(_props: Props) {
method renderBackingLayers (line 41) | renderBackingLayers({
FILE: src/sharedStyles/TextStyles.ts
type MurmurHash (line 17) | type MurmurHash = string;
type RegisteredStyle (line 19) | type RegisteredStyle = {
type StyleHash (line 26) | type StyleHash = { [key: string]: RegisteredStyle };
type Options (line 28) | type Options = {
method registerStyle (line 37) | registerStyle(
method create (line 58) | create(
method resolve (line 88) | resolve(style?: TextStyle): RegisteredStyle | undefined {
method get (line 98) | get(
method clear (line 112) | clear(): void {
method toJSON (line 117) | toJSON(): FileFormat.SharedStyle[] {
method styles (line 126) | styles() {
FILE: src/stylesheet/types.ts
type Transform (line 1) | type Transform = { [key: string]: number }[];
type Style (line 2) | type Style = {
type StyleId (line 9) | type StyleId = number;
type RawStyle (line 10) | type RawStyle = { [key: string]: any };
type RawStyles (line 11) | type RawStyles = { [key: string]: RawStyle };
type UserStyle (line 12) | type UserStyle = RawStyle | StyleId;
type UserStyles (line 13) | type UserStyles = Array<UserStyle> | UserStyle;
type Rules (line 14) | type Rules = { declarations: { [key: string]: RawStyle } };
type StyleSheetInstance (line 15) | type StyleSheetInstance = { [key: string]: StyleId };
FILE: src/symbol.tsx
function msListToArray (line 29) | function msListToArray<T>(pageList: T[]): T[] {
function exists (line 40) | function exists(x: FileFormat.SymbolMaster | undefined | null): x is Fil...
type SymbolInstanceProps (line 123) | type SymbolInstanceProps = PropTypes.InferProps<typeof SymbolInstancePro...
method render (line 135) | render() {
type SymbolMasterProps (line 154) | type SymbolMasterProps = PropTypes.InferProps<typeof SymbolMasterPropTyp...
function tryGettingSymbolMasterInDocumentByName (line 192) | function tryGettingSymbolMasterInDocumentByName(
function tryGettingSymbolMasterInDocumentById (line 212) | function tryGettingSymbolMasterInDocumentById(
FILE: src/types/index.ts
type SketchLayer (line 11) | type SketchLayer = any;
type WrappedSketchLayer (line 13) | type WrappedSketchLayer = {
type MSArray (line 17) | type MSArray<T> = {
type NSString (line 22) | type NSString = any;
type SketchPage (line 24) | type SketchPage = {
type SketchStyle (line 31) | type SketchStyle = any;
type SketchSharedStyleContainer (line 33) | type SketchSharedStyleContainer = {
type MSGradient (line 40) | type MSGradient = any;
type MSColor (line 41) | type MSColor = any;
type SketchAssetCollection (line 43) | type SketchAssetCollection = {
type SketchDocumentData (line 48) | type SketchDocumentData = {
type SketchDocument (line 63) | type SketchDocument = {
type WrappedSketchDocument (line 68) | type WrappedSketchDocument = {
type SketchContext (line 72) | type SketchContext = {
type Size (line 81) | type Size = { width: number; height: number };
type LayoutInfo (line 83) | type LayoutInfo = {
type MeasureMode (line 96) | type MeasureMode = 'undefined' | 'exactly' | 'at-most';
type Color (line 98) | type Color = PropTypes.InferType<typeof ColorProp>;
type BorderStyle (line 99) | type BorderStyle = PropTypes.InferType<typeof BorderProp>;
type Overflow (line 100) | type Overflow = PropTypes.InferType<typeof OverflowProp>;
type ViewStyle (line 101) | type ViewStyle = PropTypes.InferProps<typeof ViewStylePropTypes> & { col...
type TextStyle (line 102) | type TextStyle = PropTypes.InferProps<typeof TextStylePropTypes> & { col...
type TextNode (line 104) | type TextNode = { content: string; textStyles: TextStyle };
type TreeNode (line 106) | type TreeNode<Props = any> = {
type ResizeConstraints (line 115) | type ResizeConstraints = {
type PlatformBridge (line 124) | type PlatformBridge = {
FILE: src/types/intrinsic.d.ts
type IntrinsicElements (line 2) | interface IntrinsicElements {
FILE: src/utils/Context.ts
type Styles (line 1) | type Styles = Object;
class Context (line 3) | class Context {
method constructor (line 8) | constructor(styles: Styles = {}) {
method addInheritableStyles (line 13) | addInheritableStyles(styles: Styles) {
method forChildren (line 17) | forChildren() {
method getInheritedStyles (line 25) | getInheritedStyles() {
FILE: src/utils/constants.ts
constant INHERITABLE_FONT_STYLES (line 1) | const INHERITABLE_FONT_STYLES = [
constant VALID_TEXT_CHILDREN_TYPES (line 22) | const VALID_TEXT_CHILDREN_TYPES = ['text', 'sketch_text'];
FILE: src/utils/createStringMeasurer.ts
function isNaN (line 3) | function isNaN(num: number): boolean {
FILE: src/utils/getSketchVersion.ts
function getSketchVersion (line 1) | function getSketchVersion(): number | 'NodeJS' {
FILE: src/utils/pick.ts
function pick (line 1) | function pick<T, K extends keyof T>(obj: T, keys: readonly K[]): Pick<T,...
FILE: src/utils/processTransform/index.ts
function closeEnough (line 5) | function closeEnough(a: number, b: number) {
function isRotation (line 9) | function isRotation(a: number, b: number, c: number, d: number) {
function getRotation (line 15) | function getRotation(a: number, b: number) {
function processTransform (line 24) | function processTransform(
FILE: src/utils/processTransform/matrix2D.ts
class Matrix2D (line 18) | class Matrix2D {
method constructor (line 31) | constructor(a?: number, b?: number, c?: number, d?: number, tx?: numbe...
FILE: src/utils/processTransform/parseTransformOriginProp.ts
constant KEYWORDS (line 3) | const KEYWORDS: { [keyword: string]: [number, number, number] } = {
function isPercentage (line 11) | function isPercentage(token: string) {
function parseTransformOriginProp (line 15) | function parseTransformOriginProp(
FILE: src/utils/processTransform/parseTransformProp.ts
function appendTransform (line 163) | function appendTransform(transform: string) {
function transformToMatrix (line 175) | function transformToMatrix(transform: string, origin: [number, number, n...
function parseTransformProp (line 183) | function parseTransformProp(transform: string, origin: [number, number, ...
FILE: src/utils/sharedTextStyles/index.sketch.ts
class TextStyles (line 9) | class TextStyles {
method constructor (line 12) | constructor() {
method setDocument (line 16) | setDocument(doc?: SketchDocument) {
method setStyles (line 23) | setStyles(styles: Array<any>) {
method addStyle (line 31) | addStyle(name: string, style: FileFormat.Style): string {
method getStyle (line 64) | getStyle(name: string, document?: SketchDocument): TextStyle | undefin...
FILE: src/utils/sharedTextStyles/index.ts
class TextStyles (line 5) | class TextStyles {
method setDocument (line 6) | setDocument(_doc?: SketchDocument) {
method setStyles (line 10) | setStyles(_styles: Array<any>) {
method addStyle (line 14) | addStyle(name: string, _style: FileFormat.Style): string {
method getStyle (line 18) | getStyle(_name: string, _document?: SketchDocument): TextStyle | undef...
Condensed preview — 320 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (534K chars).
[
{
"path": ".bookignore",
"chars": 49,
"preview": ".github/\n__tests__/\nexamples/\nlib/\nscratch/\nsrc/\n"
},
{
"path": ".editorconfig",
"chars": 173,
"preview": "# http://editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\ncharset = utf-8\ntrim_trailing_whitespace"
},
{
"path": ".github/CODE_OF_CONDUCT.md",
"chars": 3223,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": ".github/CONTRIBUTING.md",
"chars": 4016,
"preview": "# Contributing\n\nContributions are welcome and are greatly appreciated! Every little bit helps, and credit will always be"
},
{
"path": ".github/ISSUE_TEMPLATE.md",
"chars": 775,
"preview": "👋 Hello! Thanks for contributing. Please use the template that matches your intention \n\n_I am..._\n\n| ------------------"
},
{
"path": ".gitignore",
"chars": 180,
"preview": "# gitignore\ncoverage/\n.DS_Store\n*.log*\nnode_modules\nlib\nreact-example.sketchplugin\n_book\n.vscode\n\n# only apps should hav"
},
{
"path": ".npmignore",
"chars": 216,
"preview": ".DS_Store\n*.log*\nnode_modules\n_book\nexamples\ndocs\n.vscode\nyarn.lock\n__tests__\n.github\ntemplate\nbook.json\nprettier.config"
},
{
"path": ".travis.yml",
"chars": 746,
"preview": "os: osx\nlanguage: node_js\ncache:\n directories:\n - node_modules\n# - $HOME/Library/Caches/Homebrew\nnotifications:\n"
},
{
"path": "CHANGELOG.md",
"chars": 5603,
"preview": "# Change Log\n\nThis project adheres to [Semantic Versioning](http://semver.org/). Every release, along with the migration"
},
{
"path": "LICENSE",
"chars": 1063,
"preview": "MIT License\n\nCopyright (c) 2018 Airbnb\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
},
{
"path": "README.md",
"chars": 3091,
"preview": "<div align=\"center\">\n <img alt=\"react-sketchapp\" src=\"https://cldup.com/MxSVEkc_gb.png\" style=\"max-height:163px; width:"
},
{
"path": "__tests__/jest/components/Artboard.tsx",
"chars": 1825,
"preview": "import * as React from 'react';\nimport * as renderer from 'react-test-renderer';\nimport { Artboard } from '../../../src/"
},
{
"path": "__tests__/jest/components/Document.tsx",
"chars": 325,
"preview": "import * as React from 'react';\nimport * as renderer from 'react-test-renderer';\nimport { Document } from '../../../src/"
},
{
"path": "__tests__/jest/components/Image.tsx",
"chars": 4394,
"preview": "import * as React from 'react';\nimport * as renderer from 'react-test-renderer';\nimport { Image } from '../../../src/com"
},
{
"path": "__tests__/jest/components/Page.tsx",
"chars": 861,
"preview": "import * as React from 'react';\nimport * as renderer from 'react-test-renderer';\nimport { Page } from '../../../src/comp"
},
{
"path": "__tests__/jest/components/RedBox.tsx",
"chars": 1075,
"preview": "import * as React from 'react';\nimport * as renderer from 'react-test-renderer';\nimport { RedBox } from '../../../src/co"
},
{
"path": "__tests__/jest/components/Svg.tsx",
"chars": 1686,
"preview": "import * as React from 'react';\nimport * as renderer from 'react-test-renderer';\nimport Svg, { G, Path } from '../../../"
},
{
"path": "__tests__/jest/components/Text.tsx",
"chars": 1358,
"preview": "import * as React from 'react';\nimport * as renderer from 'react-test-renderer';\nimport { Text } from '../../../src/comp"
},
{
"path": "__tests__/jest/components/View.tsx",
"chars": 1358,
"preview": "import * as React from 'react';\nimport * as renderer from 'react-test-renderer';\nimport { View } from '../../../src/comp"
},
{
"path": "__tests__/jest/components/__snapshots__/Artboard.tsx.snap",
"chars": 1164,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`<Artboard /> name defaults to Artboard 1`] = `\n<sketch_artboard\n n"
},
{
"path": "__tests__/jest/components/__snapshots__/Document.tsx.snap",
"chars": 137,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`<Document /> renders children 1`] = `\n<sketch_document>\n foo\n</ske"
},
{
"path": "__tests__/jest/components/__snapshots__/Image.tsx.snap",
"chars": 3810,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`<Image /> name defaults to Image 1`] = `\n<sketch_image\n name=\"Imag"
},
{
"path": "__tests__/jest/components/__snapshots__/Page.tsx.snap",
"chars": 461,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`<Page /> name passes its name 1`] = `\n<sketch_page\n name=\"bar\"\n/>\n"
},
{
"path": "__tests__/jest/components/__snapshots__/RedBox.tsx.snap",
"chars": 3307,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`<RedBox /> renders simple errors 1`] = `\n<sketch_view\n name=\"RedBo"
},
{
"path": "__tests__/jest/components/__snapshots__/Svg.tsx.snap",
"chars": 1604,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`<Svg /> also works when child is directly imported 1`] = `\n<sketch_"
},
{
"path": "__tests__/jest/components/__snapshots__/Text.tsx.snap",
"chars": 714,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`<Text /> name defaults to Text 1`] = `<sketch_text />`;\n\nexports[`<"
},
{
"path": "__tests__/jest/components/__snapshots__/View.tsx.snap",
"chars": 787,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`<View /> name defaults to View 1`] = `\n<sketch_view\n name=\"View\"\n/"
},
{
"path": "__tests__/jest/components/nodeImpl/Svg.tsx",
"chars": 1163,
"preview": "import * as React from 'react';\n\nimport * as ReactSketch from '../../../../src';\nimport Svg from '../../../../src/compon"
},
{
"path": "__tests__/jest/components/nodeImpl/__snapshots__/Svg.tsx.snap",
"chars": 46824,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`node <Svg /> generates the json for an svg 1`] = `\nObject {\n \"_cla"
},
{
"path": "__tests__/jest/index.ts",
"chars": 1100,
"preview": "import * as ReactSketch from '../../src';\n\ndescribe('public API', () => {\n it('exports render', () => {\n expect(Reac"
},
{
"path": "__tests__/jest/jsonUtils/computeTextTree.ts",
"chars": 881,
"preview": "import { computeTextTree } from '../../../src/jsonUtils/computeTextTree';\nimport { Context } from '../../../src/utils/Co"
},
{
"path": "__tests__/jest/jsonUtils/computeYogaNode.ts",
"chars": 12252,
"preview": "import yoga from 'yoga-layout-prebuilt';\nimport { computeYogaNode } from '../../../src/jsonUtils/computeYogaNode';\nimpor"
},
{
"path": "__tests__/jest/jsonUtils/computeYogaTree.ts",
"chars": 1482,
"preview": "import yoga from 'yoga-layout-prebuilt';\nimport { computeYogaTree } from '../../../src/jsonUtils/computeYogaTree';\nimpor"
},
{
"path": "__tests__/jest/jsonUtils/layerGroup.ts",
"chars": 485,
"preview": "import { layerGroup } from '../../../src/jsonUtils/layerGroup';\n\ndescribe('layer group', () => {\n it('is correctly cons"
},
{
"path": "__tests__/jest/jsonUtils/models.ts",
"chars": 4234,
"preview": "import {\n generateID,\n makeColorFromCSS,\n makeColorFill,\n makeRect,\n makeSymbolInstance,\n makeSymbolMaster,\n} from"
},
{
"path": "__tests__/jest/jsonUtils/shapeLayers.ts",
"chars": 1770,
"preview": "import {\n makeRectPath,\n makeShapePath,\n makeRectShapeLayer,\n makeShapeGroup,\n} from '../../../src/jsonUtils/shapeLa"
},
{
"path": "__tests__/jest/jsonUtils/style.ts",
"chars": 1720,
"preview": "import { makeBorderOptions, makeShadow } from '../../../src/jsonUtils/style';\n\ndescribe('makeBorderOptions', () => {\n i"
},
{
"path": "__tests__/jest/reactTreeToFlexTree.ts",
"chars": 3256,
"preview": "import yoga from 'yoga-layout-prebuilt';\nimport { computeYogaTree } from '../../src/jsonUtils/computeYogaTree';\nimport {"
},
{
"path": "__tests__/jest/sharedStyles/TextStyles.ts",
"chars": 5352,
"preview": "import bridge from '../../../src/platformBridges/macos';\n\nlet TextStyles;\nlet doc;\nlet sharedTextStyles;\n\nbeforeEach(() "
},
{
"path": "__tests__/jest/utils/isDefined.ts",
"chars": 513,
"preview": "import { isDefined } from '../../../src/utils/isDefined';\n\ndescribe('isNullOrUndefined', () => {\n it('correctly identif"
},
{
"path": "__tests__/jest/utils/sortObjectKeys.ts",
"chars": 304,
"preview": "import { sortObjectKeys } from '../../../src/utils/sortObjectKeys';\n\ntest('simple example', () => {\n const a = {\n fo"
},
{
"path": "__tests__/jest/utils/zIndex.ts",
"chars": 1100,
"preview": "import { zIndex } from '../../../src/utils/zIndex';\n\nconst noZIndexNode = {\n props: {\n style: {\n zIndex: 0,\n "
},
{
"path": "__tests__/skpm/basic.test.js",
"chars": 1603,
"preview": "import * as React from 'react';\nimport * as sketch from 'sketch';\nimport { render, View, Artboard, Text } from '../../li"
},
{
"path": "__tests__/skpm/render-context.test.js",
"chars": 1115,
"preview": "import * as React from 'react';\nimport * as sketch from 'sketch';\nimport { render, View, Text } from '../../lib';\n\n// de"
},
{
"path": "__tests__/skpm/render-in-wrapped-object.test.js",
"chars": 1501,
"preview": "import * as React from 'react';\nimport * as sketch from 'sketch';\nimport { render, View, Artboard, Text } from '../../li"
},
{
"path": "book.json",
"chars": 439,
"preview": "{\n \"gitbook\": \">= 3.2.1\",\n \"title\": \"react-sketchapp\",\n \"plugins\": [\n \"edit-link\",\n \"prism\",\n \"-highlight\",\n"
},
{
"path": "docs/API.md",
"chars": 20035,
"preview": "# API Reference\n\n- [`render`](#renderelement-container)\n- [`renderToJSON`](#rendertojsonelement)\n- [Components](#compone"
},
{
"path": "docs/FAQ.md",
"chars": 4621,
"preview": "# Frequently Asked Questions\n\n#### Why?!??!\n\n`react-sketchapp` evolved out of our need to generate **high-quality, consi"
},
{
"path": "docs/README.md",
"chars": 1136,
"preview": "## Table of Contents\n\n- [Introduction](/README.md)\n- [Guides](/docs/guides/README.md)\n - [Getting Started](/docs/guides"
},
{
"path": "docs/examples.md",
"chars": 2536,
"preview": "# Examples\n\n`react-sketchapp` is bundled with lots of examples!\n\n### [Basic setup](https://github.com/airbnb/react-sketc"
},
{
"path": "docs/guides/README.md",
"chars": 286,
"preview": "# Guides\n\nHow to use `react-sketchapp` for fun and profit.\n\n- [Getting Started](getting-started.md)\n- [Using `skpm` as a"
},
{
"path": "docs/guides/data-fetching.md",
"chars": 1387,
"preview": "# Data Fetching\n\nPull real data from an API with `fetch` or GraphQL.\n\n## Fetch\n\n[Full example](https://github.com/airbnb"
},
{
"path": "docs/guides/getting-started.md",
"chars": 2563,
"preview": "# Getting Started\n\nYou can create a `react-sketchapp` project with `skpm`, by cloning a ready-made [example](../examples"
},
{
"path": "docs/guides/rendering.md",
"chars": 2062,
"preview": "# Rendering Guide\n\nYou can use the Sketch API to select Sketch containers such as documents, pages or groups, to pass th"
},
{
"path": "docs/guides/styling.md",
"chars": 5664,
"preview": "# Styling\n\nComponents use CSS styles + FlexBox layout.\n\n## Layout Styles\n\n| property | type | supported? |\n| --- | --- |"
},
{
"path": "docs/guides/universal-rendering.md",
"chars": 3150,
"preview": "# Universal Rendering\n\nThe `react-sketchapp` components have been architected to provide the same metaphors, layout syst"
},
{
"path": "docs/guides/using-skpm.md",
"chars": 3144,
"preview": "# Using `skpm` as a build system\n\nSketch allows arbitrary plugins written in [CocoaScript](http://developer.sketchapp.co"
},
{
"path": "examples/.eslintrc",
"chars": 79,
"preview": "{\n \"rules\": {\n \"import/no-unresolved\": 0,\n \"import/extensions\": 0\n }\n}\n"
},
{
"path": "examples/.gitignore",
"chars": 18,
"preview": "**/*.sketchplugin\n"
},
{
"path": "examples/basic-setup/README.md",
"chars": 790,
"preview": "# Basic setup\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/react-sketchapp):\n\n```ba"
},
{
"path": "examples/basic-setup/package.json",
"chars": 674,
"preview": "{\n \"name\": \"basic-setup\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"skpm\": {\n \"main\": \"basic-setup.sketchplugin\""
},
{
"path": "examples/basic-setup/src/manifest.json",
"chars": 235,
"preview": "{\n\t\"compatibleVersion\": 3,\n\t\"bundleVersion\": 1,\n\t\"commands\": [\n\t\t{\n\t\t\t\"name\": \"react-sketchapp: Basic Setup\",\n\t\t\t\"identi"
},
{
"path": "examples/basic-setup/src/my-command.js",
"chars": 1661,
"preview": "import * as React from 'react';\nimport * as PropTypes from 'prop-types';\nimport { render, Artboard, Text, View } from 'r"
},
{
"path": "examples/basic-setup/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/basic-setup-typescript/.gitignore",
"chars": 26,
"preview": "node_modules\n.ts-compiled\n"
},
{
"path": "examples/basic-setup-typescript/README.md",
"chars": 1765,
"preview": "# Basic setup with Typescript\n\nThis example was adapted from the [basic-setup example](../basic-setup).\n\n> **NOTE:** you"
},
{
"path": "examples/basic-setup-typescript/manifest.json",
"chars": 263,
"preview": "{\n\t\"compatibleVersion\": 3,\n\t\"bundleVersion\": 1,\n\t\"commands\": [\n\t\t{\n\t\t\t\"name\": \"react-sketchapp: Basic Setup with Typescr"
},
{
"path": "examples/basic-setup-typescript/package.json",
"chars": 953,
"preview": "{\n \"name\": \"basic-setup-typescript\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"skpm\": {\n \"main\": \"basic-setup.sk"
},
{
"path": "examples/basic-setup-typescript/src/my-command.tsx",
"chars": 1605,
"preview": "import * as React from 'react';\nimport sketch from 'sketch';\nimport { render, Artboard, Text, View } from 'react-sketcha"
},
{
"path": "examples/basic-setup-typescript/src/types/sketch.d.ts",
"chars": 25,
"preview": "declare module 'sketch';\n"
},
{
"path": "examples/basic-setup-typescript/tsconfig.json",
"chars": 313,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2015\",\n \"module\": \"es2015\",\n \"jsx\": \"react-native\",\n \"allowJs\": true,"
},
{
"path": "examples/basic-setup-typescript/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/basic-svg/README.md",
"chars": 879,
"preview": "# Basic SVG example\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/react-sketchapp):\n"
},
{
"path": "examples/basic-svg/package.json",
"chars": 643,
"preview": "{\n \"name\": \"basic-svg\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"skpm\": {\n \"main\": \"basic-svg.sketchplugin\",\n "
},
{
"path": "examples/basic-svg/src/manifest.json",
"chars": 233,
"preview": "{\n\t\"compatibleVersion\": 3,\n\t\"bundleVersion\": 1,\n\t\"commands\": [\n\t\t{\n\t\t\t\"name\": \"react-sketchapp: Basic SVG\",\n\t\t\t\"identifi"
},
{
"path": "examples/basic-svg/src/my-command.js",
"chars": 903,
"preview": "import * as React from 'react';\nimport { render, Artboard, Svg } from 'react-sketchapp';\n\nconst Document = () => (\n <Ar"
},
{
"path": "examples/basic-svg/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/colors/README.md",
"chars": 735,
"preview": "# Generative Colors w/ chroma-js\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/react"
},
{
"path": "examples/colors/package.json",
"chars": 724,
"preview": "{\n \"name\": \"colors\",\n \"version\": \"1.0.0\",\n \"private\": true,\n \"skpm\": {\n \"main\": \"colors.sketchplugin\",\n \"manif"
},
{
"path": "examples/colors/src/main.js",
"chars": 1158,
"preview": "import * as React from 'react';\nimport * as PropTypes from 'prop-types';\nimport { render, StyleSheet, View } from 'react"
},
{
"path": "examples/colors/src/manifest.json",
"chars": 365,
"preview": "{\n \"name\": \"colors\",\n \"author\": \"Jon Gold\",\n \"version\": 0.1,\n \"compatibleVersion\": 1,\n \"bundleVersion\": 1,\n \"disab"
},
{
"path": "examples/colors/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/emotion/package.json",
"chars": 681,
"preview": "{\n \"name\": \"emotion-example\",\n \"version\": \"1.0.0\",\n \"skpm\": {\n \"main\": \"emotion.sketchplugin\",\n \"manifest\": \"sr"
},
{
"path": "examples/emotion/src/manifest.json",
"chars": 224,
"preview": "{\n\t\"compatibleVersion\": 3,\n\t\"bundleVersion\": 1,\n\t\"commands\": [\n\t\t{\n\t\t\t\"name\": \"react-sketchapp: emotion\",\n\t\t\t\"identifier"
},
{
"path": "examples/emotion/src/my-command.js",
"chars": 962,
"preview": "import React from 'react';\nimport emotion from 'emotion-primitives';\nimport { render } from 'react-sketchapp';\n\nconst Co"
},
{
"path": "examples/emotion/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/form-validation/README.md",
"chars": 741,
"preview": "# Form Validation\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/react-sketchapp):\n\n`"
},
{
"path": "examples/form-validation/package.json",
"chars": 858,
"preview": "{\n \"name\": \"form-validation\",\n \"version\": \"1.0.0\",\n \"private\": true,\n \"skpm\": {\n \"main\": \"form-validation.sketchp"
},
{
"path": "examples/form-validation/src/components/Button.js",
"chars": 595,
"preview": "import * as React from 'react';\nimport { Text, View } from 'react-primitives';\nimport { spacing, colors, fontFamily } fr"
},
{
"path": "examples/form-validation/src/components/Register.js",
"chars": 1136,
"preview": "import * as React from 'react';\nimport { View, Text, StyleSheet } from 'react-primitives';\nimport { spacing, colors, typ"
},
{
"path": "examples/form-validation/src/components/Space.js",
"chars": 251,
"preview": "import * as React from 'react';\nimport { View } from 'react-primitives';\n\nconst Space = ({ h, v, children }) => (\n <Vie"
},
{
"path": "examples/form-validation/src/components/StrengthMeter.js",
"chars": 1858,
"preview": "import * as React from 'react';\nimport { View, Text } from 'react-primitives';\nimport { colors, fontFamily, spacing, typ"
},
{
"path": "examples/form-validation/src/components/TextBox/index.js",
"chars": 932,
"preview": "import React, { Component } from 'react';\nimport styles from './style';\n\nclass TextBox extends Component {\n constructor"
},
{
"path": "examples/form-validation/src/components/TextBox/index.sketch.js",
"chars": 461,
"preview": "import * as React from 'react';\nimport { View, Text } from 'react-primitives';\nimport styles from './style';\n\ntype Props"
},
{
"path": "examples/form-validation/src/components/TextBox/style.js",
"chars": 558,
"preview": "import { colors, spacing, fontFamily, typeRamp } from '../../designSystem';\n\nexport default {\n formElement: {\n margi"
},
{
"path": "examples/form-validation/src/data.js",
"chars": 390,
"preview": "export default [\n {\n email: 'john.hornsby@example.com',\n password: '',\n },\n {\n email: 'john.hornsby@example."
},
{
"path": "examples/form-validation/src/designSystem.js",
"chars": 664,
"preview": "export const colors = {\n Purple: '#5700A2',\n Yellow: '#BB9A05',\n Orange: '#fd6134',\n Rose: '#ff4289',\n Green: '#005"
},
{
"path": "examples/form-validation/src/main.js",
"chars": 822,
"preview": "import * as React from 'react';\nimport { render, View } from 'react-sketchapp';\nimport { Text } from 'react-primitives';"
},
{
"path": "examples/form-validation/src/manifest.json",
"chars": 261,
"preview": "{\n \"compatibleVersion\": 1,\n \"bundleVersion\": 1,\n \"commands\": [\n {\n \"name\": \"react-sketchapp: Form Validation\""
},
{
"path": "examples/form-validation/src/web.js",
"chars": 638,
"preview": "import * as React from 'react';\nimport { typography, fontFamily } from './designSystem';\nimport Register from './compone"
},
{
"path": "examples/form-validation/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/foursquare-maps/.eslintrc",
"chars": 40,
"preview": "{\n \"env\": {\n \"browser\": true,\n }\n}\n"
},
{
"path": "examples/foursquare-maps/README.md",
"chars": 1072,
"preview": "# Foursquare + Google Maps\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/react-sketc"
},
{
"path": "examples/foursquare-maps/package.json",
"chars": 885,
"preview": "{\n \"name\": \"foursquare-maps\",\n \"version\": \"1.0.0\",\n \"private\": true,\n \"skpm\": {\n \"main\": \"foursquare-maps.sketchp"
},
{
"path": "examples/foursquare-maps/src/App.js",
"chars": 2043,
"preview": "import * as React from 'react';\nimport * as PropTypes from 'prop-types';\nimport Map from 'react-primitives-google-static"
},
{
"path": "examples/foursquare-maps/src/getVenues.js",
"chars": 651,
"preview": "import param from 'jquery-param';\n\nexport default () => {\n const query = 'burger';\n const latitude = '37.773972';\n co"
},
{
"path": "examples/foursquare-maps/src/main.js",
"chars": 395,
"preview": "import * as React from 'react';\nimport { Artboard, render } from 'react-sketchapp';\nimport App from './App';\nimport getV"
},
{
"path": "examples/foursquare-maps/src/manifest.json",
"chars": 270,
"preview": "{\n \"compatibleVersion\": 1,\n \"bundleVersion\": 1,\n \"commands\": [\n {\n \"name\": \"react-sketchapp: Foursquare + Goo"
},
{
"path": "examples/foursquare-maps/src/web.js",
"chars": 291,
"preview": "import * as React from 'react';\nimport { render } from 'react-dom';\nimport App from './App';\nimport getVenues from './ge"
},
{
"path": "examples/foursquare-maps/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/glamorous/README.md",
"chars": 482,
"preview": "# glamorous 💄\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/react-sketchapp):\n\n```ba"
},
{
"path": "examples/glamorous/package.json",
"chars": 694,
"preview": "{\n \"name\": \"glamorous-example\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"skpm\": {\n \"main\": \"glamorous.sketchplu"
},
{
"path": "examples/glamorous/src/manifest.json",
"chars": 233,
"preview": "{\n\t\"compatibleVersion\": 3,\n\t\"bundleVersion\": 1,\n\t\"commands\": [\n\t\t{\n\t\t\t\"name\": \"react-sketchapp: glamorous\",\n\t\t\t\"identifi"
},
{
"path": "examples/glamorous/src/my-command.js",
"chars": 790,
"preview": "import * as React from 'react';\nimport glamorous from 'glamorous-primitives';\nimport { render } from 'react-sketchapp';\n"
},
{
"path": "examples/glamorous/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/profile-cards/README.md",
"chars": 732,
"preview": "# Profile Cards\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/react-sketchapp):\n\n```"
},
{
"path": "examples/profile-cards/package.json",
"chars": 598,
"preview": "{\n \"name\": \"profile-cards\",\n \"private\": true,\n \"skpm\": {\n \"main\": \"profile-cards.sketchplugin\",\n \"manifest\": \"s"
},
{
"path": "examples/profile-cards/src/components/Profile.js",
"chars": 1199,
"preview": "import * as React from 'react';\nimport { Image, View, Text, StyleSheet } from 'react-sketchapp';\nimport { colors, fonts,"
},
{
"path": "examples/profile-cards/src/components/Space.js",
"chars": 250,
"preview": "import * as React from 'react';\nimport { View } from 'react-sketchapp';\n\nconst Space = ({ h, v, children }) => (\n <View"
},
{
"path": "examples/profile-cards/src/designSystem.js",
"chars": 1324,
"preview": "export const colors = {\n Haus: '#F3F4F4',\n Night: '#333',\n Sur: '#96DBE4',\n 'Sur a11y': '#24828F',\n Peach: '#EFADA0"
},
{
"path": "examples/profile-cards/src/main.js",
"chars": 1940,
"preview": "import * as React from 'react';\nimport { render, Text, View } from 'react-sketchapp';\nimport { fonts, spacing } from './"
},
{
"path": "examples/profile-cards/src/manifest.json",
"chars": 259,
"preview": "{\n \"compatibleVersion\": 1,\n \"bundleVersion\": 1,\n \"commands\": [\n {\n \"name\": \"react-sketchapp: Profile Cards\",\n"
},
{
"path": "examples/profile-cards/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/profile-cards-graphql/README.md",
"chars": 798,
"preview": "# Profile Cards w/ GraphQL\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/react-sketc"
},
{
"path": "examples/profile-cards-graphql/package.json",
"chars": 818,
"preview": "{\n \"name\": \"profile-cards-gql\",\n \"version\": \"1.0.0\",\n \"skpm\": {\n \"main\": \"profile-cards-gql.sketchplugin\",\n \"ma"
},
{
"path": "examples/profile-cards-graphql/src/components/Profile.js",
"chars": 1191,
"preview": "import * as React from 'react';\nimport { Image, View, Text, StyleSheet } from 'react-sketchapp';\nimport { colors, fonts,"
},
{
"path": "examples/profile-cards-graphql/src/components/Space.js",
"chars": 250,
"preview": "import * as React from 'react';\nimport { View } from 'react-sketchapp';\n\nconst Space = ({ h, v, children }) => (\n <View"
},
{
"path": "examples/profile-cards-graphql/src/designSystem.js",
"chars": 1324,
"preview": "export const colors = {\n Haus: '#F3F4F4',\n Night: '#333',\n Sur: '#96DBE4',\n 'Sur a11y': '#24828F',\n Peach: '#EFADA0"
},
{
"path": "examples/profile-cards-graphql/src/main.js",
"chars": 1719,
"preview": "import * as React from 'react';\nimport { render, Text, View } from 'react-sketchapp';\nimport { ApolloClient } from 'apol"
},
{
"path": "examples/profile-cards-graphql/src/manifest.json",
"chars": 270,
"preview": "{\n \"compatibleVersion\": 1,\n \"bundleVersion\": 1,\n \"commands\": [\n {\n \"name\": \"react-sketchapp: Profile Cards w/"
},
{
"path": "examples/profile-cards-graphql/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/profile-cards-primitives/README.md",
"chars": 961,
"preview": "# Profile Cards w/ React Primitives\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/re"
},
{
"path": "examples/profile-cards-primitives/nwb.config.js",
"chars": 141,
"preview": "module.exports = {\n webpack: {\n resolve: {\n // look for .web.js first\n extensions: ['.web.js', '.js', '.js"
},
{
"path": "examples/profile-cards-primitives/package.json",
"chars": 770,
"preview": "{\n \"name\": \"profile-cards-primitives\",\n \"private\": true,\n \"skpm\": {\n \"main\": \"profile-cards-primitives.sketchplugi"
},
{
"path": "examples/profile-cards-primitives/src/components/Profile.js",
"chars": 1202,
"preview": "import * as React from 'react';\nimport { Image, View, Text, StyleSheet } from 'react-primitives';\nimport { colors, fonts"
},
{
"path": "examples/profile-cards-primitives/src/components/Space.js",
"chars": 251,
"preview": "import * as React from 'react';\nimport { View } from 'react-primitives';\n\nconst Space = ({ h, v, children }) => (\n <Vie"
},
{
"path": "examples/profile-cards-primitives/src/data.js",
"chars": 1163,
"preview": "export default [\n {\n screen_name: 'mxstbr',\n name: 'Max Stoiber',\n description:\n '⚛️ Makes styled-compone"
},
{
"path": "examples/profile-cards-primitives/src/designSystem.js",
"chars": 1324,
"preview": "export const colors = {\n Haus: '#F3F4F4',\n Night: '#333',\n Sur: '#96DBE4',\n 'Sur a11y': '#24828F',\n Peach: '#EFADA0"
},
{
"path": "examples/profile-cards-primitives/src/main.js",
"chars": 798,
"preview": "import * as React from 'react';\nimport { render } from 'react-sketchapp';\nimport { Text, View } from 'react-primitives';"
},
{
"path": "examples/profile-cards-primitives/src/manifest.json",
"chars": 273,
"preview": "{\n \"compatibleVersion\": 1,\n \"bundleVersion\": 1,\n \"commands\": [\n {\n \"name\": \"react-sketchapp: Profile Cards w/"
},
{
"path": "examples/profile-cards-primitives/src/web.js",
"chars": 1083,
"preview": "import * as React from 'react';\nimport Profile from './components/Profile';\nimport Space from './components/Space';\nimpo"
},
{
"path": "examples/profile-cards-primitives/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/profile-cards-react-with-styles/README.md",
"chars": 866,
"preview": "# Profile Cards w/ react-with-styles\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/r"
},
{
"path": "examples/profile-cards-react-with-styles/package.json",
"chars": 669,
"preview": "{\n \"name\": \"profile-cards-react-with-styles\",\n \"private\": true,\n \"skpm\": {\n \"main\": \"profile-cards-react-with-styl"
},
{
"path": "examples/profile-cards-react-with-styles/src/components/Profile.js",
"chars": 1090,
"preview": "import * as React from 'react';\nimport { Image, View, Text } from 'react-sketchapp';\nimport { css, withStyles } from '.."
},
{
"path": "examples/profile-cards-react-with-styles/src/main.js",
"chars": 1828,
"preview": "import * as React from 'react';\nimport { render, Text, View } from 'react-sketchapp';\nimport Profile from './components/"
},
{
"path": "examples/profile-cards-react-with-styles/src/manifest.json",
"chars": 280,
"preview": "{\n \"compatibleVersion\": 1,\n \"bundleVersion\": 1,\n \"commands\": [\n {\n \"name\": \"react-sketchapp: Profile Cards w/"
},
{
"path": "examples/profile-cards-react-with-styles/src/theme.js",
"chars": 1324,
"preview": "export const colors = {\n Haus: '#F3F4F4',\n Night: '#333',\n Sur: '#96DBE4',\n 'Sur a11y': '#24828F',\n Peach: '#EFADA0"
},
{
"path": "examples/profile-cards-react-with-styles/src/types.js",
"chars": 150,
"preview": "export type User = {\n screen_name: string,\n name: string,\n description: string,\n profile_image_url: string,\n locati"
},
{
"path": "examples/profile-cards-react-with-styles/src/withStyles.js",
"chars": 519,
"preview": "import ThemedStyleSheet from 'react-with-styles/lib/ThemedStyleSheet';\nimport { css, withStyles, ThemeProvider } from 'r"
},
{
"path": "examples/profile-cards-react-with-styles/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/react-router-prototyping/README.md",
"chars": 934,
"preview": "# React Router setup\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/react-sketchapp):"
},
{
"path": "examples/react-router-prototyping/package.json",
"chars": 771,
"preview": "{\n \"name\": \"react-router-prototyping\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"skpm\": {\n \"main\": \"react-router"
},
{
"path": "examples/react-router-prototyping/src/App.js",
"chars": 878,
"preview": "import React from 'react';\nimport { SketchRouter, Switch, Route } from 'react-sketchapp-router';\n\nimport Home from './ro"
},
{
"path": "examples/react-router-prototyping/src/components/AppBar.js",
"chars": 719,
"preview": "import React from 'react';\nimport { View, Text } from 'react-sketchapp';\nimport { Link } from 'react-router-primitives';"
},
{
"path": "examples/react-router-prototyping/src/components/NavBar.js",
"chars": 695,
"preview": "import React from 'react';\nimport { View, Text } from 'react-sketchapp';\nimport { Link } from 'react-router-primitives';"
},
{
"path": "examples/react-router-prototyping/src/main.js",
"chars": 592,
"preview": "/* global context */\nimport * as React from 'react';\nimport { render, Page, Document as RootDocument } from 'react-sketc"
},
{
"path": "examples/react-router-prototyping/src/manifest.json",
"chars": 242,
"preview": "{\n\t\"compatibleVersion\": 3,\n\t\"bundleVersion\": 1,\n\t\"commands\": [\n\t\t{\n\t\t\t\"name\": \"react-sketchapp: React Router prototyping"
},
{
"path": "examples/react-router-prototyping/src/routes/about.js",
"chars": 624,
"preview": "import React from 'react';\nimport { View, Text } from 'react-sketchapp';\n\nimport AppBar from '../components/AppBar';\n\nco"
},
{
"path": "examples/react-router-prototyping/src/routes/home.js",
"chars": 1073,
"preview": "import React from 'react';\nimport { View, Text } from 'react-sketchapp';\nimport { Link } from 'react-router-primitives';"
},
{
"path": "examples/react-router-prototyping/src/routes/post.js",
"chars": 1916,
"preview": "import React from 'react';\nimport { View, Text } from 'react-sketchapp';\nimport { Link } from 'react-router-primitives';"
},
{
"path": "examples/react-router-prototyping/src/routes/profile.js",
"chars": 615,
"preview": "import React from 'react';\nimport { View, Text } from 'react-sketchapp';\n\nimport AppBar from '../components/AppBar';\nimp"
},
{
"path": "examples/react-router-prototyping/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/styled-components/README.md",
"chars": 685,
"preview": "# Styled-components\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/react-sketchapp):\n"
},
{
"path": "examples/styled-components/package.json",
"chars": 767,
"preview": "{\n \"name\": \"styled-components-example\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"skpm\": {\n \"main\": \"styled-comp"
},
{
"path": "examples/styled-components/src/manifest.json",
"chars": 241,
"preview": "{\n\t\"compatibleVersion\": 3,\n\t\"bundleVersion\": 1,\n\t\"commands\": [\n\t\t{\n\t\t\t\"name\": \"react-sketchapp: Styled components\",\n\t\t\t\""
},
{
"path": "examples/styled-components/src/my-command.js",
"chars": 1921,
"preview": "import * as React from 'react';\nimport * as PropTypes from 'prop-types';\nimport styled from 'styled-components/primitive"
},
{
"path": "examples/styled-components/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/styleguide/.flowconfig",
"chars": 39,
"preview": "[ignore]\n\n[include]\n\n[libs]\n\n[options]\n"
},
{
"path": "examples/styleguide/README.md",
"chars": 869,
"preview": "# Styleguide\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/react-sketchapp):\n\n```bas"
},
{
"path": "examples/styleguide/package.json",
"chars": 619,
"preview": "{\n \"name\": \"styleguide\",\n \"private\": true,\n \"skpm\": {\n \"main\": \"styleguide.sketchplugin\",\n \"manifest\": \"src/man"
},
{
"path": "examples/styleguide/src/components/AccessibilityBadge.js",
"chars": 417,
"preview": "import * as React from 'react';\nimport Badge from './Badge';\n\nconst AccessibilityBadge = ({ level }) => {\n let text;\n "
},
{
"path": "examples/styleguide/src/components/Badge.js",
"chars": 480,
"preview": "import * as React from 'react';\nimport { View, Text } from 'react-sketchapp';\n\nconst Badge = ({ children, filled = false"
},
{
"path": "examples/styleguide/src/components/Label.js",
"chars": 303,
"preview": "import * as React from 'react';\nimport { Text } from 'react-sketchapp';\n\nconst Label = ({ bold, children }) => (\n <Text"
},
{
"path": "examples/styleguide/src/components/Palette.js",
"chars": 435,
"preview": "import * as React from 'react';\nimport { View } from 'react-sketchapp';\nimport Swatch from './Swatch';\n\nconst SWATCH_WID"
},
{
"path": "examples/styleguide/src/components/Section.js",
"chars": 351,
"preview": "import * as React from 'react';\nimport { View } from 'react-sketchapp';\nimport Label from './Label';\n\nconst Section = ({"
},
{
"path": "examples/styleguide/src/components/Swatch.js",
"chars": 629,
"preview": "import * as React from 'react';\nimport { View } from 'react-sketchapp';\nimport AccessibilityBadge from './AccessibilityB"
},
{
"path": "examples/styleguide/src/components/TypeSpecimen.js",
"chars": 479,
"preview": "import * as React from 'react';\nimport { View, Text } from 'react-sketchapp';\nimport Label from './Label';\n\nconst TypeSp"
},
{
"path": "examples/styleguide/src/designSystem.js",
"chars": 1495,
"preview": "import processColor from './processColor';\n\nexport const colors = {\n Haus: '#F3F4F4',\n Night: '#333',\n Sur: '#96DBE4'"
},
{
"path": "examples/styleguide/src/main.js",
"chars": 1108,
"preview": "import * as React from 'react';\nimport { render, TextStyles, View } from 'react-sketchapp';\nimport designSystem from './"
},
{
"path": "examples/styleguide/src/manifest.json",
"chars": 256,
"preview": "{\n \"compatibleVersion\": 1,\n \"bundleVersion\": 1,\n \"commands\": [\n {\n \"name\": \"react-sketchapp: Styleguide\",\n "
},
{
"path": "examples/styleguide/src/processColor.js",
"chars": 418,
"preview": "import chroma from 'chroma-js';\n\nconst minimums = {\n aa: 4.5,\n aaLarge: 3,\n aaa: 7,\n aaaLarge: 4.5,\n};\n\nexport defau"
},
{
"path": "examples/styleguide/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/symbols/README.md",
"chars": 625,
"preview": "# Symbol Support\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/react-sketchapp):\n\n``"
},
{
"path": "examples/symbols/package.json",
"chars": 610,
"preview": "{\n \"name\": \"symbols\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"skpm\": {\n \"main\": \"symbols.sketchplugin\",\n \"m"
},
{
"path": "examples/symbols/src/manifest.json",
"chars": 238,
"preview": "{\n\t\"compatibleVersion\": 3,\n\t\"bundleVersion\": 1,\n\t\"commands\": [\n\t\t{\n\t\t\t\"name\": \"react-sketchapp: Symbol Support\",\n\t\t\t\"ide"
},
{
"path": "examples/symbols/src/my-command.js",
"chars": 1576,
"preview": "import * as React from 'react';\nimport { render, Artboard, Text, View, Image, makeSymbol } from 'react-sketchapp';\n\ncons"
},
{
"path": "examples/symbols/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "examples/timeline-airtable/.eslintrc",
"chars": 39,
"preview": "{\n \"env\": {\n \"browser\": true\n }\n}\n"
},
{
"path": "examples/timeline-airtable/README.md",
"chars": 748,
"preview": "# Timeline w/ Airtable\n\n## How to use\n\nDownload the example or [clone the repo](http://github.com/airbnb/react-sketchapp"
},
{
"path": "examples/timeline-airtable/package.json",
"chars": 641,
"preview": "{\n \"name\": \"timeline-airtable\",\n \"private\": true,\n \"skpm\": {\n \"main\": \"timeline-airtable.sketchplugin\",\n \"manif"
},
{
"path": "examples/timeline-airtable/src/main.js",
"chars": 3564,
"preview": "import * as React from 'react';\nimport * as PropTypes from 'prop-types';\nimport { render, Artboard, Text, View, StyleShe"
},
{
"path": "examples/timeline-airtable/src/manifest.json",
"chars": 266,
"preview": "{\n \"compatibleVersion\": 1,\n \"bundleVersion\": 1,\n \"commands\": [\n {\n \"name\": \"react-sketchapp: Timeline w/ Airt"
},
{
"path": "examples/timeline-airtable/webpack.skpm.config.js",
"chars": 274,
"preview": "const path = require('path');\n\nmodule.exports = (config) => {\n if (process.env.LOCAL_DEV) {\n config.resolve = {\n "
},
{
"path": "jest.config.js",
"chars": 381,
"preview": "module.exports = {\n preset: 'ts-jest',\n testEnvironment: 'node',\n testRegex: '/__tests__/jest/.*\\\\.(j|t)sx?$',\n test"
},
{
"path": "package.json",
"chars": 4035,
"preview": "{\n \"name\": \"react-sketchapp\",\n \"version\": \"3.2.6\",\n \"description\": \"A React renderer for Sketch.app\",\n \"sideEffects\""
},
{
"path": "prettier.config.js",
"chars": 108,
"preview": "module.exports = {\n singleQuote: true,\n trailingComma: 'all',\n printWidth: 100,\n proseWrap: 'never',\n};\n"
},
{
"path": "src/Platform.ts",
"chars": 186,
"preview": "import { getSketchVersion } from './utils/getSketchVersion';\n\nexport const Platform = {\n OS: 'sketch',\n Version: getSk"
},
{
"path": "src/buildTree.ts",
"chars": 3685,
"preview": "import * as TestRenderer from 'react-test-renderer';\nimport yoga from 'yoga-layout-prebuilt';\nimport { Context } from '."
},
{
"path": "src/components/Artboard.tsx",
"chars": 1311,
"preview": "import * as React from 'react';\nimport * as PropTypes from 'prop-types';\nimport { or } from 'airbnb-prop-types';\nimport "
}
]
// ... and 120 more files (download for full content)
About this extraction
This page contains the full source code of the airbnb/react-sketchapp GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 320 files (477.5 KB), approximately 137.2k tokens, and a symbol index with 290 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.